From fcb99957d55a62b451d4f2e6103a93038412166e Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 09:57:53 +1000 Subject: [PATCH 0001/1263] Initial commit --- .argocd-source.yaml | 7 + .gitignore | 1 + ...s.operator.open-cluster-management.io.yaml | 287 ++++++++++++++++++ site/.helmignore | 1 + site/Chart.yaml | 7 + site/templates/applications.yaml | 35 +++ site/templates/argocd-super-role.yaml | 17 ++ site/templates/argocd.yaml | 86 ++++++ site/templates/projects.yaml | 20 ++ site/values.yaml | 15 + 10 files changed, 476 insertions(+) create mode 100644 .argocd-source.yaml create mode 100644 .gitignore create mode 100644 install/crds/multiclusterhubs.operator.open-cluster-management.io.yaml create mode 100644 site/.helmignore create mode 100644 site/Chart.yaml create mode 100644 site/templates/applications.yaml create mode 100644 site/templates/argocd-super-role.yaml create mode 100644 site/templates/argocd.yaml create mode 100644 site/templates/projects.yaml create mode 100644 site/values.yaml diff --git a/.argocd-source.yaml b/.argocd-source.yaml new file mode 100644 index 00000000..d5088e6b --- /dev/null +++ b/.argocd-source.yaml @@ -0,0 +1,7 @@ +helm: + global: + options: + syncPolicy: Automatic + + useCSV: "False" + installPlanApproval: Automatic diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e4e5f6c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*~ \ No newline at end of file diff --git a/install/crds/multiclusterhubs.operator.open-cluster-management.io.yaml b/install/crds/multiclusterhubs.operator.open-cluster-management.io.yaml new file mode 100644 index 00000000..9ffb6031 --- /dev/null +++ b/install/crds/multiclusterhubs.operator.open-cluster-management.io.yaml @@ -0,0 +1,287 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: multiclusterhubs.operator.open-cluster-management.io +spec: + group: operator.open-cluster-management.io + names: + kind: MultiClusterHub + listKind: MultiClusterHubList + plural: multiclusterhubs + shortNames: + - mch + singular: multiclusterhub + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The overall status of the multiclusterhub + jsonPath: .status.phase + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: MultiClusterHub defines the configuration for an instance of + the MultiCluster Hub + 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: MultiClusterHubSpec defines the desired state of MultiClusterHub + properties: + availabilityConfig: + description: 'Specifies deployment replication for improved availability. + Options are: Basic and High (default)' + type: string + customCAConfigmap: + description: Provide the customized OpenShift default ingress CA certificate + to RHACM + type: string + disableHubSelfManagement: + description: Disable automatic import of the hub cluster as a managed + cluster + type: boolean + disableUpdateClusterImageSets: + description: Disable automatic update of ClusterImageSets + type: boolean + hive: + description: (Deprecated) Overrides for the default HiveConfig spec + properties: + additionalCertificateAuthorities: + description: (Deprecated) AdditionalCertificateAuthorities is + a list of references to secrets in the 'hive' namespace that + contain an additional Certificate Authority to use when communicating + with target clusters. These certificate authorities will be + used in addition to any self-signed CA generated by each cluster + on installation. + items: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + type: array + backup: + description: (Deprecated) Backup specifies configuration for backup + integration. If absent, backup integration will be disabled. + properties: + minBackupPeriodSeconds: + description: (Deprecated) MinBackupPeriodSeconds specifies + that a minimum of MinBackupPeriodSeconds will occur in between + each backup. This is used to rate limit backups. This potentially + batches together multiple changes into 1 backup. No backups + will be lost as changes that happen during this interval + are queued up and will result in a backup happening once + the interval has been completed. + type: integer + velero: + description: (Deprecated) Velero specifies configuration for + the Velero backup integration. + properties: + enabled: + description: (Deprecated) Enabled dictates if Velero backup + integration is enabled. If not specified, the default + is disabled. + type: boolean + type: object + type: object + externalDNS: + description: (Deprecated) ExternalDNS specifies configuration + for external-dns if it is to be deployed by Hive. If absent, + external-dns will not be deployed. + properties: + aws: + description: (Deprecated) AWS contains AWS-specific settings + for external DNS + properties: + credentials: + description: (Deprecated) Credentials references a secret + that will be used to authenticate with AWS Route53. + It will need permission to manage entries in each of + the managed domains for this cluster. Secret should + have AWS keys named 'aws_access_key_id' and 'aws_secret_access_key'. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + type: object + gcp: + description: (Deprecated) GCP contains GCP-specific settings + for external DNS + properties: + credentials: + description: (Deprecated) Credentials references a secret + that will be used to authenticate with GCP DNS. It will + need permission to manage entries in each of the managed + domains for this cluster. Secret should have a key names + 'osServiceAccount.json'. The credentials must specify + the project to use. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + type: object + type: object + failedProvisionConfig: + description: (Deprecated) FailedProvisionConfig is used to configure + settings related to handling provision failures. + properties: + skipGatherLogs: + description: (Deprecated) SkipGatherLogs disables functionality + that attempts to gather full logs from the cluster if an + installation fails for any reason. The logs will be stored + in a persistent volume for up to 7 days. + type: boolean + type: object + globalPullSecret: + description: (Deprecated) GlobalPullSecret is used to specify + a pull secret that will be used globally by all of the cluster + deployments. For each cluster deployment, the contents of GlobalPullSecret + will be merged with the specific pull secret for a cluster deployment(if + specified), with precedence given to the contents of the pull + secret for the cluster deployment. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + maintenanceMode: + description: (Deprecated) MaintenanceMode can be set to true to + disable the hive controllers in situations where we need to + ensure nothing is running that will add or act upon finalizers + on Hive types. This should rarely be needed. Sets replicas to + 0 for the hive-controllers deployment to accomplish this. + type: boolean + required: + - failedProvisionConfig + type: object + imagePullSecret: + description: Override pull secret for accessing MultiClusterHub operand + and endpoint images + type: string + ingress: + description: Configuration options for ingress management + properties: + sslCiphers: + description: List of SSL ciphers enabled for management ingress. + Defaults to full list of supported ciphers + items: + type: string + type: array + type: object + nodeSelector: + additionalProperties: + type: string + description: Set the nodeselectors + type: object + overrides: + description: Developer Overrides + properties: + imagePullPolicy: + description: Pull policy of the MultiCluster hub images + type: string + type: object + separateCertificateManagement: + description: (Deprecated) Install cert-manager into its own namespace + type: boolean + type: object + status: + description: MultiClusterHubStatus defines the observed state of MultiClusterHub + properties: + components: + additionalProperties: + description: StatusCondition contains condition information. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + changed from one status to another. + format: date-time + type: string + message: + description: Message is a human-readable message indicating + details about the last status change. + type: string + reason: + description: Reason is a (brief) reason for the condition's + last status change. + type: string + status: + description: Status is the status of the condition. One of True, + False, Unknown. + type: string + type: + description: Type is the type of the cluster condition. + type: string + type: object + description: Components []ComponentCondition `json:"manifests,omitempty"` + type: object + conditions: + description: Conditions contains the different condition statuses + for the MultiClusterHub + items: + description: StatusCondition contains condition information. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + changed from one status to another. + format: date-time + type: string + lastUpdateTime: + description: The last time this condition was updated. + format: date-time + type: string + message: + description: Message is a human-readable message indicating + details about the last status change. + type: string + reason: + description: Reason is a (brief) reason for the condition's + last status change. + type: string + status: + description: Status is the status of the condition. One of True, + False, Unknown. + type: string + type: + description: Type is the type of the cluster condition. + type: string + type: object + type: array + currentVersion: + description: CurrentVersion indicates the current version + type: string + desiredVersion: + description: DesiredVersion indicates the desired version + type: string + phase: + description: Represents the running phase of the MultiClusterHub + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/site/.helmignore b/site/.helmignore new file mode 100644 index 00000000..b25c15b8 --- /dev/null +++ b/site/.helmignore @@ -0,0 +1 @@ +*~ diff --git a/site/Chart.yaml b/site/Chart.yaml new file mode 100644 index 00000000..c68ad3a0 --- /dev/null +++ b/site/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +description: A Helm chart to create the initial manuela application +keywords: +- manuela +- blueprint +name: manuela-datacenter +version: 0.0.1 diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml new file mode 100644 index 00000000..df3341fb --- /dev/null +++ b/site/templates/applications.yaml @@ -0,0 +1,35 @@ +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: default-app-set + namespace: openshift-gitops + labels: + app.kubernetes.io/instance: manuela +spec: + generators: + - list: + elements: + {{- range .Values.applications }} + - name: {{ .name }} + namespace: {{ default "openshift-gitops" .namespace }} + project: {{ default "default" .project }} + path: {{ .path }} + {{- end }} + template: + metadata: + name: {{ "'{{name}}'" }} + spec: + source: + repoURL: $ARGOCD_APP_SOURCE_REPO_URL + targetRevision: $ARGOCD_APP_SOURCE_TARGET_REVISION + path: {{ "'{{path}}'" }} + project: {{ "'{{project}}'" }} + destination: + name: in-cluster + namespace: {{ "'{{namespace}}'" }} +{{- if eq .Values.global.options.syncPolicy "Automatic" }} + syncPolicy: + automated: + prune: false + selfHeal: false +{{- end }} diff --git a/site/templates/argocd-super-role.yaml b/site/templates/argocd-super-role.yaml new file mode 100644 index 00000000..5898ce05 --- /dev/null +++ b/site/templates/argocd-super-role.yaml @@ -0,0 +1,17 @@ +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: openshift-gitops-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: openshift-gitops-argocd-application-controller + namespace: openshift-gitops + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + name: openshift-gitops-argocd-server + namespace: openshift-gitops diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml new file mode 100644 index 00000000..09e0f555 --- /dev/null +++ b/site/templates/argocd.yaml @@ -0,0 +1,86 @@ +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + finalizers: + - argoproj.io/finalizer + name: openshift-gitops + annotations: + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + applicationInstanceLabelKey: argocd.argoproj.io/instance + # Not the greatest way to pass git/quay info to sub-applications, but it will do until + # we can support helmChart with kustomize + # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION + configManagementPlugins: "- name: kustomize-version\n generate:\n command: [sh, + -c]\n args: [\"kustomize version 1>&2 && exit 1\"]\n- name: kustomize-with-helm\n + \ generate:\n command: [\"kustomize\"]\n args: [\"build\", \"--enable-helm\"]\n- name: + helm-with-kustomize\n init:\n command: [\"/bin/sh\", \"-c\"]\n args: [\"helm + dependency build\"]\n generate:\n command: [sh, -c]\n args: [\"helm template + --release-name ${ARGOCD_APP_NAME}-v0.2021.8 + --set global.bpversion=${BLUEPRINT_VERSION} . + --post-renderer ./kustomize\"] + \ \n" + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + controller: + processors: {} + resources: + limits: + cpu: "4" + memory: 4Gi + requests: + cpu: 500m + memory: 2Gi + dex: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + rbac: + defaultPolicy: role:admin + repo: + resources: + limits: + cpu: "1" + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + service: + type: "" + tls: + ca: {} +status: diff --git a/site/templates/projects.yaml b/site/templates/projects.yaml new file mode 100644 index 00000000..916c67bd --- /dev/null +++ b/site/templates/projects.yaml @@ -0,0 +1,20 @@ +{{- range .Values.projects | uniq }} +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: {{ . }} +spec: + description: "Blueprint {{ . }}" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +{{- end }} \ No newline at end of file diff --git a/site/values.yaml b/site/values.yaml new file mode 100644 index 00000000..141b7f53 --- /dev/null +++ b/site/values.yaml @@ -0,0 +1,15 @@ +global: + options: + syncPolicy: Automatic + + useCSV: "False" + installPlanApproval: Automatic + +projects: +- datacenter + +#applications: +#- name: acm +# namespace: default +# project: datacenter +# path: applications/acm From ec10f98795a688989ad4ac6367c6622b0420bf8a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 12:43:47 +1000 Subject: [PATCH 0002/1263] Automate the creation of common manifests --- site/templates/namespaces.yaml | 11 +++++++++++ site/templates/operatorgroup.yaml | 10 ++++++++++ site/templates/projects.yaml | 1 + site/templates/subscriptions.yaml | 17 +++++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 site/templates/namespaces.yaml create mode 100644 site/templates/operatorgroup.yaml create mode 100644 site/templates/subscriptions.yaml diff --git a/site/templates/namespaces.yaml b/site/templates/namespaces.yaml new file mode 100644 index 00000000..4ce3b644 --- /dev/null +++ b/site/templates/namespaces.yaml @@ -0,0 +1,11 @@ +{{- range .Values.namespaces | uniq }} +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: {{ Release.Name }} + argocd.argoproj.io/managed-by: openshift-gitops + name: {{ . }} +spec: +--- +{{- end }} \ No newline at end of file diff --git a/site/templates/operatorgroup.yaml b/site/templates/operatorgroup.yaml new file mode 100644 index 00000000..ce12aa7c --- /dev/null +++ b/site/templates/operatorgroup.yaml @@ -0,0 +1,10 @@ +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: {{ Release.Name }}-operator-group + namespace: openshift-gitops +spec: + targetNamespaces: +{{- range .Values.namespaces | uniq }} + - {{ . }} +{{- end }} diff --git a/site/templates/projects.yaml b/site/templates/projects.yaml index 916c67bd..0df57964 100644 --- a/site/templates/projects.yaml +++ b/site/templates/projects.yaml @@ -17,4 +17,5 @@ spec: sourceRepos: - '*' status: {} +--- {{- end }} \ No newline at end of file diff --git a/site/templates/subscriptions.yaml b/site/templates/subscriptions.yaml new file mode 100644 index 00000000..0f454c2b --- /dev/null +++ b/site/templates/subscriptions.yaml @@ -0,0 +1,17 @@ +{{- range .Values.applications }} +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: {{ .name }} + namespace: {{ .namespace }} +spec: + name: {{ .name }} + source: {{ .source }} + sourceNamespace: openshift-marketplace + channel: {{ .channel }} + installPlanApproval: {{ .Values.global.options.installPlanApproval }} + {{- if .Values.global.options.useCSV }} + startingCSV: {{ .name }}.{{ .csv }} + {{- end }} +--- +{{- end }} From cfe3a0cbe589f1889d6fed73032c1f93b2a3a1ff Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 13:00:39 +1000 Subject: [PATCH 0003/1263] Repair templating --- site/templates/namespaces.yaml | 4 ++-- site/templates/operatorgroup.yaml | 6 +++--- site/values.yaml | 5 +++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/site/templates/namespaces.yaml b/site/templates/namespaces.yaml index 4ce3b644..36dc2a5d 100644 --- a/site/templates/namespaces.yaml +++ b/site/templates/namespaces.yaml @@ -3,9 +3,9 @@ apiVersion: v1 kind: Namespace metadata: labels: - name: {{ Release.Name }} + name: {{ default "blueprint" $.Release.name }} argocd.argoproj.io/managed-by: openshift-gitops name: {{ . }} spec: --- -{{- end }} \ No newline at end of file +{{- end }} diff --git a/site/templates/operatorgroup.yaml b/site/templates/operatorgroup.yaml index ce12aa7c..d82485df 100644 --- a/site/templates/operatorgroup.yaml +++ b/site/templates/operatorgroup.yaml @@ -1,10 +1,10 @@ apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: - name: {{ Release.Name }}-operator-group + name: {{ .Release.Name }}-operator-group namespace: openshift-gitops spec: targetNamespaces: -{{- range .Values.namespaces | uniq }} +{{- range .Values.namespaces }} - {{ . }} -{{- end }} +{{- end }} \ No newline at end of file diff --git a/site/values.yaml b/site/values.yaml index 141b7f53..c4e80a41 100644 --- a/site/values.yaml +++ b/site/values.yaml @@ -8,6 +8,11 @@ global: projects: - datacenter +namespaces: +- blueprint +#- open-cluster-management +#- policy + #applications: #- name: acm # namespace: default From 1bd9f448e1f5f48284d4378c665d2bd4798002d1 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 15:08:43 +1000 Subject: [PATCH 0004/1263] test: try populating applications at the submodule level --- .argocd-source.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.argocd-source.yaml b/.argocd-source.yaml index d5088e6b..0b8cad47 100644 --- a/.argocd-source.yaml +++ b/.argocd-source.yaml @@ -5,3 +5,20 @@ helm: useCSV: "False" installPlanApproval: Automatic + + namespaces: + - open-cluster-management + - policy + + applications: + - name: acm + namespace: default + project: datacenter + path: applications/acm + + subscriptions: + - name: advanced-cluster-management + namespace: open-cluster-management + source: redhat-operators + channel: release-2.3 + csv: v2.3.1 From d8bc3298b74622023e1470678015e318786c25cd Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 15:54:44 +1000 Subject: [PATCH 0005/1263] Ensure we look from the top namespace for options --- site/templates/subscriptions.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/templates/subscriptions.yaml b/site/templates/subscriptions.yaml index 0f454c2b..c845dc93 100644 --- a/site/templates/subscriptions.yaml +++ b/site/templates/subscriptions.yaml @@ -9,8 +9,8 @@ spec: source: {{ .source }} sourceNamespace: openshift-marketplace channel: {{ .channel }} - installPlanApproval: {{ .Values.global.options.installPlanApproval }} - {{- if .Values.global.options.useCSV }} + installPlanApproval: {{ $.Values.global.options.installPlanApproval }} + {{- if $.Values.global.options.useCSV }} startingCSV: {{ .name }}.{{ .csv }} {{- end }} --- From 31271f649951daa82ca6dd3d04ca1bd9eb56e4c7 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 15:55:35 +1000 Subject: [PATCH 0006/1263] Avoid use of uniq and rely on the user --- site/templates/projects.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/templates/projects.yaml b/site/templates/projects.yaml index 0df57964..d9ac694f 100644 --- a/site/templates/projects.yaml +++ b/site/templates/projects.yaml @@ -1,4 +1,4 @@ -{{- range .Values.projects | uniq }} +{{- range .Values.projects }} apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -18,4 +18,4 @@ spec: - '*' status: {} --- -{{- end }} \ No newline at end of file +{{- end }} From cfbf33e2b0ea28e2835c6f9e42257c819262e287 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 16:55:50 +1000 Subject: [PATCH 0007/1263] ApplicationSets don't support random fields, use Helm ranges instead --- site/templates/applications.yaml | 51 +++++++++++++------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index df3341fb..e976fc37 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -1,35 +1,26 @@ +{{- range .Values.applications }} apiVersion: argoproj.io/v1alpha1 -kind: ApplicationSet +kind: Application metadata: - name: default-app-set + name: {{ .name }} namespace: openshift-gitops - labels: - app.kubernetes.io/instance: manuela spec: - generators: - - list: - elements: - {{- range .Values.applications }} - - name: {{ .name }} - namespace: {{ default "openshift-gitops" .namespace }} - project: {{ default "default" .project }} - path: {{ .path }} - {{- end }} - template: - metadata: - name: {{ "'{{name}}'" }} - spec: - source: - repoURL: $ARGOCD_APP_SOURCE_REPO_URL - targetRevision: $ARGOCD_APP_SOURCE_TARGET_REVISION - path: {{ "'{{path}}'" }} - project: {{ "'{{project}}'" }} - destination: - name: in-cluster - namespace: {{ "'{{namespace}}'" }} -{{- if eq .Values.global.options.syncPolicy "Automatic" }} - syncPolicy: - automated: - prune: false - selfHeal: false + destination: + name: in-cluster + namespace: {{ default "openshift-gitops" .namespace }} + project: {{ .project }} + source: + repoURL: $ARGOCD_APP_SOURCE_REPO_URL + targetRevision: $ARGOCD_APP_SOURCE_TARGET_REVISION + path: {{ .path }} + helm: + valueFiles: + - ../../values-blueprint.yaml +{{- if eq $.Values.main.options.syncPolicy "Automatic" }} + syncPolicy: + automated: + prune: false + selfHeal: false +{{- end }} +--- {{- end }} From c07600ea07b2c346d2835fb2275522d95a45a82f Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 16:59:18 +1000 Subject: [PATCH 0008/1263] Consume options from the global namespace --- site/templates/applications.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index e976fc37..a24865b1 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -16,7 +16,7 @@ spec: helm: valueFiles: - ../../values-blueprint.yaml -{{- if eq $.Values.main.options.syncPolicy "Automatic" }} +{{- if eq $.Values.global.options.syncPolicy "Automatic" }} syncPolicy: automated: prune: false From 08a25d04c5bb081fbb93d96580baba560757a910 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 17:56:06 +1000 Subject: [PATCH 0009/1263] Create subscriptions with the correct helm variable --- site/templates/subscriptions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/subscriptions.yaml b/site/templates/subscriptions.yaml index c845dc93..66b6519c 100644 --- a/site/templates/subscriptions.yaml +++ b/site/templates/subscriptions.yaml @@ -1,4 +1,4 @@ -{{- range .Values.applications }} +{{- range .Values.subscriptions }} apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: From 1a2e16eaefd245246da1ad1ef0efae9dc99ef4f8 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 17:59:32 +1000 Subject: [PATCH 0010/1263] Test: Drop the ACM CRD to see if we can use sync-waves --- ...s.operator.open-cluster-management.io.yaml | 287 ------------------ 1 file changed, 287 deletions(-) delete mode 100644 install/crds/multiclusterhubs.operator.open-cluster-management.io.yaml diff --git a/install/crds/multiclusterhubs.operator.open-cluster-management.io.yaml b/install/crds/multiclusterhubs.operator.open-cluster-management.io.yaml deleted file mode 100644 index 9ffb6031..00000000 --- a/install/crds/multiclusterhubs.operator.open-cluster-management.io.yaml +++ /dev/null @@ -1,287 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: multiclusterhubs.operator.open-cluster-management.io -spec: - group: operator.open-cluster-management.io - names: - kind: MultiClusterHub - listKind: MultiClusterHubList - plural: multiclusterhubs - shortNames: - - mch - singular: multiclusterhub - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The overall status of the multiclusterhub - jsonPath: .status.phase - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1 - schema: - openAPIV3Schema: - description: MultiClusterHub defines the configuration for an instance of - the MultiCluster Hub - 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: MultiClusterHubSpec defines the desired state of MultiClusterHub - properties: - availabilityConfig: - description: 'Specifies deployment replication for improved availability. - Options are: Basic and High (default)' - type: string - customCAConfigmap: - description: Provide the customized OpenShift default ingress CA certificate - to RHACM - type: string - disableHubSelfManagement: - description: Disable automatic import of the hub cluster as a managed - cluster - type: boolean - disableUpdateClusterImageSets: - description: Disable automatic update of ClusterImageSets - type: boolean - hive: - description: (Deprecated) Overrides for the default HiveConfig spec - properties: - additionalCertificateAuthorities: - description: (Deprecated) AdditionalCertificateAuthorities is - a list of references to secrets in the 'hive' namespace that - contain an additional Certificate Authority to use when communicating - with target clusters. These certificate authorities will be - used in addition to any self-signed CA generated by each cluster - on installation. - items: - description: LocalObjectReference contains enough information - to let you locate the referenced object inside the same namespace. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - type: array - backup: - description: (Deprecated) Backup specifies configuration for backup - integration. If absent, backup integration will be disabled. - properties: - minBackupPeriodSeconds: - description: (Deprecated) MinBackupPeriodSeconds specifies - that a minimum of MinBackupPeriodSeconds will occur in between - each backup. This is used to rate limit backups. This potentially - batches together multiple changes into 1 backup. No backups - will be lost as changes that happen during this interval - are queued up and will result in a backup happening once - the interval has been completed. - type: integer - velero: - description: (Deprecated) Velero specifies configuration for - the Velero backup integration. - properties: - enabled: - description: (Deprecated) Enabled dictates if Velero backup - integration is enabled. If not specified, the default - is disabled. - type: boolean - type: object - type: object - externalDNS: - description: (Deprecated) ExternalDNS specifies configuration - for external-dns if it is to be deployed by Hive. If absent, - external-dns will not be deployed. - properties: - aws: - description: (Deprecated) AWS contains AWS-specific settings - for external DNS - properties: - credentials: - description: (Deprecated) Credentials references a secret - that will be used to authenticate with AWS Route53. - It will need permission to manage entries in each of - the managed domains for this cluster. Secret should - have AWS keys named 'aws_access_key_id' and 'aws_secret_access_key'. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - type: object - type: object - gcp: - description: (Deprecated) GCP contains GCP-specific settings - for external DNS - properties: - credentials: - description: (Deprecated) Credentials references a secret - that will be used to authenticate with GCP DNS. It will - need permission to manage entries in each of the managed - domains for this cluster. Secret should have a key names - 'osServiceAccount.json'. The credentials must specify - the project to use. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - type: object - type: object - type: object - failedProvisionConfig: - description: (Deprecated) FailedProvisionConfig is used to configure - settings related to handling provision failures. - properties: - skipGatherLogs: - description: (Deprecated) SkipGatherLogs disables functionality - that attempts to gather full logs from the cluster if an - installation fails for any reason. The logs will be stored - in a persistent volume for up to 7 days. - type: boolean - type: object - globalPullSecret: - description: (Deprecated) GlobalPullSecret is used to specify - a pull secret that will be used globally by all of the cluster - deployments. For each cluster deployment, the contents of GlobalPullSecret - will be merged with the specific pull secret for a cluster deployment(if - specified), with precedence given to the contents of the pull - secret for the cluster deployment. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - maintenanceMode: - description: (Deprecated) MaintenanceMode can be set to true to - disable the hive controllers in situations where we need to - ensure nothing is running that will add or act upon finalizers - on Hive types. This should rarely be needed. Sets replicas to - 0 for the hive-controllers deployment to accomplish this. - type: boolean - required: - - failedProvisionConfig - type: object - imagePullSecret: - description: Override pull secret for accessing MultiClusterHub operand - and endpoint images - type: string - ingress: - description: Configuration options for ingress management - properties: - sslCiphers: - description: List of SSL ciphers enabled for management ingress. - Defaults to full list of supported ciphers - items: - type: string - type: array - type: object - nodeSelector: - additionalProperties: - type: string - description: Set the nodeselectors - type: object - overrides: - description: Developer Overrides - properties: - imagePullPolicy: - description: Pull policy of the MultiCluster hub images - type: string - type: object - separateCertificateManagement: - description: (Deprecated) Install cert-manager into its own namespace - type: boolean - type: object - status: - description: MultiClusterHubStatus defines the observed state of MultiClusterHub - properties: - components: - additionalProperties: - description: StatusCondition contains condition information. - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition - changed from one status to another. - format: date-time - type: string - message: - description: Message is a human-readable message indicating - details about the last status change. - type: string - reason: - description: Reason is a (brief) reason for the condition's - last status change. - type: string - status: - description: Status is the status of the condition. One of True, - False, Unknown. - type: string - type: - description: Type is the type of the cluster condition. - type: string - type: object - description: Components []ComponentCondition `json:"manifests,omitempty"` - type: object - conditions: - description: Conditions contains the different condition statuses - for the MultiClusterHub - items: - description: StatusCondition contains condition information. - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition - changed from one status to another. - format: date-time - type: string - lastUpdateTime: - description: The last time this condition was updated. - format: date-time - type: string - message: - description: Message is a human-readable message indicating - details about the last status change. - type: string - reason: - description: Reason is a (brief) reason for the condition's - last status change. - type: string - status: - description: Status is the status of the condition. One of True, - False, Unknown. - type: string - type: - description: Type is the type of the cluster condition. - type: string - type: object - type: array - currentVersion: - description: CurrentVersion indicates the current version - type: string - desiredVersion: - description: DesiredVersion indicates the desired version - type: string - phase: - description: Represents the running phase of the MultiClusterHub - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} From 97d24ba209141d7b44554f9a338c79f6cbd35c59 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 20:37:11 +1000 Subject: [PATCH 0011/1263] Use the repo and revision from argo, and pass to per application charts --- site/templates/applications.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index a24865b1..322ec94f 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -10,12 +10,17 @@ spec: namespace: {{ default "openshift-gitops" .namespace }} project: {{ .project }} source: - repoURL: $ARGOCD_APP_SOURCE_REPO_URL - targetRevision: $ARGOCD_APP_SOURCE_TARGET_REVISION + repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} + targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} path: {{ .path }} helm: valueFiles: - ../../values-blueprint.yaml + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION {{- if eq $.Values.global.options.syncPolicy "Automatic" }} syncPolicy: automated: From 4a63a221e22aa1a229f65ff0e86e2147cb6b6b88 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 20:58:39 +1000 Subject: [PATCH 0012/1263] Enable self healing --- site/templates/applications.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 322ec94f..89e9eaa6 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -14,8 +14,6 @@ spec: targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} path: {{ .path }} helm: - valueFiles: - - ../../values-blueprint.yaml parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -25,7 +23,7 @@ spec: syncPolicy: automated: prune: false - selfHeal: false + selfHeal: true {{- end }} --- {{- end }} From 52fa445e8fc16058d634036aa6344eae954b0ff9 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 23:43:56 +1000 Subject: [PATCH 0013/1263] Use and pass on the global blueprint values --- site/templates/applications.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 89e9eaa6..2f47515b 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -14,11 +14,15 @@ spec: targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} path: {{ .path }} helm: + valueFiles: + - {{ .Values.global.valuesURL }} parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL - name: global.targetRevision value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.valuesURL + value: {{ .Values.global.valuesURL }} {{- if eq $.Values.global.options.syncPolicy "Automatic" }} syncPolicy: automated: From 3049379a93e4270cfbf082c5d0afcf3e4f7c10f7 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 23:48:22 +1000 Subject: [PATCH 0014/1263] Escape the range scope --- site/templates/applications.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 2f47515b..3ebeef76 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -22,7 +22,7 @@ spec: - name: global.targetRevision value: $ARGOCD_APP_SOURCE_TARGET_REVISION - name: global.valuesURL - value: {{ .Values.global.valuesURL }} + value: {{ $.Values.global.valuesURL }} {{- if eq $.Values.global.options.syncPolicy "Automatic" }} syncPolicy: automated: From fb0ebccf3245269c16eb357f29ecc4ff306eb0aa Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 23:50:04 +1000 Subject: [PATCH 0015/1263] Escape the range scope --- site/templates/applications.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 3ebeef76..15c1d6ba 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -15,7 +15,7 @@ spec: path: {{ .path }} helm: valueFiles: - - {{ .Values.global.valuesURL }} + - {{ $.Values.global.valuesURL }} parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL From ae1b483d3b5b93ddba06e068b48c88d7db87c892 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 11:30:20 +1000 Subject: [PATCH 0016/1263] Turn off argo validation for when apps include a subscription and its config --- site/templates/applications.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 15c1d6ba..7cf93c7d 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -23,8 +23,14 @@ spec: value: $ARGOCD_APP_SOURCE_TARGET_REVISION - name: global.valuesURL value: {{ $.Values.global.valuesURL }} + {{- range .overrides }} + - name: {{ .name }} + value: {{ .value }} + {{- end }} {{- if eq $.Values.global.options.syncPolicy "Automatic" }} syncPolicy: + syncOptions: + - Validate=false automated: prune: false selfHeal: true From 0a663523295359dc09762228ae9f18c2f29b3e03 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 11:31:27 +1000 Subject: [PATCH 0017/1263] Differentiate between hub/datacenter and edge applications --- site/templates/applications.yaml | 2 +- site/templates/namespaces.yaml | 2 +- site/templates/operatorgroup.yaml | 2 +- site/templates/projects.yaml | 2 +- site/templates/subscriptions.yaml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 7cf93c7d..fcc7c573 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -1,4 +1,4 @@ -{{- range .Values.applications }} +{{- range .Values.datacenter.applications }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: diff --git a/site/templates/namespaces.yaml b/site/templates/namespaces.yaml index 36dc2a5d..8c7428b2 100644 --- a/site/templates/namespaces.yaml +++ b/site/templates/namespaces.yaml @@ -1,4 +1,4 @@ -{{- range .Values.namespaces | uniq }} +{{- range .Values.datacenter.namespaces }} apiVersion: v1 kind: Namespace metadata: diff --git a/site/templates/operatorgroup.yaml b/site/templates/operatorgroup.yaml index d82485df..bedc5705 100644 --- a/site/templates/operatorgroup.yaml +++ b/site/templates/operatorgroup.yaml @@ -5,6 +5,6 @@ metadata: namespace: openshift-gitops spec: targetNamespaces: -{{- range .Values.namespaces }} +{{- range .Values.datacenter.namespaces }} - {{ . }} {{- end }} \ No newline at end of file diff --git a/site/templates/projects.yaml b/site/templates/projects.yaml index d9ac694f..3d3b5f18 100644 --- a/site/templates/projects.yaml +++ b/site/templates/projects.yaml @@ -1,4 +1,4 @@ -{{- range .Values.projects }} +{{- range .Values.datacenter.projects }} apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: diff --git a/site/templates/subscriptions.yaml b/site/templates/subscriptions.yaml index 66b6519c..58413a0f 100644 --- a/site/templates/subscriptions.yaml +++ b/site/templates/subscriptions.yaml @@ -1,4 +1,4 @@ -{{- range .Values.subscriptions }} +{{- range .Values.datacenter.subscriptions }} apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: From 80431d2d554f57a1bb58edee430e878a2f40b2e8 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 15:20:48 +1000 Subject: [PATCH 0018/1263] Ignore loggingCA changes --- site/templates/applications.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index fcc7c573..54d82b52 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -27,6 +27,11 @@ spec: - name: {{ .name }} value: {{ .value }} {{- end }} + ignoreDifferences: + - group: internal.open-cluster-management.io + kind: ManagedClusterInfo + jsonPointers: + - /spec/loggingCA {{- if eq $.Values.global.options.syncPolicy "Automatic" }} syncPolicy: syncOptions: From 710036fe287e8352a0ae499141cce75a082acc76 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 15:32:11 +1000 Subject: [PATCH 0019/1263] Avoid using the site location in values files --- site/templates/applications.yaml | 2 +- site/templates/namespaces.yaml | 2 +- site/templates/operatorgroup.yaml | 2 +- site/templates/projects.yaml | 2 +- site/templates/subscriptions.yaml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 54d82b52..75b80d61 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -1,4 +1,4 @@ -{{- range .Values.datacenter.applications }} +{{- range .Values.site.applications }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: diff --git a/site/templates/namespaces.yaml b/site/templates/namespaces.yaml index 8c7428b2..ec6bda70 100644 --- a/site/templates/namespaces.yaml +++ b/site/templates/namespaces.yaml @@ -1,4 +1,4 @@ -{{- range .Values.datacenter.namespaces }} +{{- range .Values.site.namespaces }} apiVersion: v1 kind: Namespace metadata: diff --git a/site/templates/operatorgroup.yaml b/site/templates/operatorgroup.yaml index bedc5705..4c60ba4f 100644 --- a/site/templates/operatorgroup.yaml +++ b/site/templates/operatorgroup.yaml @@ -5,6 +5,6 @@ metadata: namespace: openshift-gitops spec: targetNamespaces: -{{- range .Values.datacenter.namespaces }} +{{- range .Values.site.namespaces }} - {{ . }} {{- end }} \ No newline at end of file diff --git a/site/templates/projects.yaml b/site/templates/projects.yaml index 3d3b5f18..ba2c222b 100644 --- a/site/templates/projects.yaml +++ b/site/templates/projects.yaml @@ -1,4 +1,4 @@ -{{- range .Values.datacenter.projects }} +{{- range .Values.site.projects }} apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: diff --git a/site/templates/subscriptions.yaml b/site/templates/subscriptions.yaml index 58413a0f..37d55e4c 100644 --- a/site/templates/subscriptions.yaml +++ b/site/templates/subscriptions.yaml @@ -1,4 +1,4 @@ -{{- range .Values.datacenter.subscriptions }} +{{- range .Values.site.subscriptions }} apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: From c949048a36983d68ea75fea38d03f03395f34dee Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 21:44:47 +1000 Subject: [PATCH 0020/1263] Consume and pass on a valuesFile directory to allow multiple levels of configuration --- site/templates/applications.yaml | 8 +++-- site/values.yaml | 55 ++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 75b80d61..28346ca8 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -15,14 +15,18 @@ spec: path: {{ .path }} helm: valueFiles: + - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" + - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Values.site.name }}.yaml" + - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Release.Name }}.yaml" + - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ .name }}.yaml" - {{ $.Values.global.valuesURL }} parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL - name: global.targetRevision value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.valuesURL - value: {{ $.Values.global.valuesURL }} + - name: global.valuesDirectoryURL + value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} {{- range .overrides }} - name: {{ .name }} value: {{ .value }} diff --git a/site/values.yaml b/site/values.yaml index c4e80a41..55e4c69d 100644 --- a/site/values.yaml +++ b/site/values.yaml @@ -1,20 +1,49 @@ global: + valuesDirectoryURL: https://github.com/dagger-refuse-cool/edge-gitops/raw/main/ options: + useCSV: True syncPolicy: Automatic - - useCSV: "False" installPlanApproval: Automatic -projects: -- datacenter +site: + name: datacenter -namespaces: -- blueprint -#- open-cluster-management -#- policy + proposedOptions: + manageGitops: True + isHubCluster: True -#applications: -#- name: acm -# namespace: default -# project: datacenter -# path: applications/acm +# managedSites: +# - name: factory +# # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git +# # Location of values-global.yaml, values-{name}.yaml, values-{app}.yaml +# # valuesDirectoryURL: https://github.com/dagger-refuse-cool/edge-gitops/raw/main/ +# targetRevision: main +# path: applications/factory +# helmOverrides: +# - name: site.isHubCluster +# value: false +# clusterSelector: +# matchExpressions: +# - key: vendor +# operator: In +# values: +# - OpenShift +# +# namespaces: +# - open-cluster-management +# +# subscriptions: +# - name: advanced-cluster-management +# namespace: open-cluster-management +# source: redhat-operators +# channel: release-2.3 +# csv: v2.3.2 +# +# projects: +# - datacenter +# +# applications: +# - name: acm +# namespace: default +# project: datacenter +# path: applications/acm From bee6108711fe199b02465cd5cf5fd90705475cc6 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 21:59:13 +1000 Subject: [PATCH 0021/1263] Remove invalid parameter reference --- site/templates/applications.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 28346ca8..c11aa3ed 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -19,7 +19,6 @@ spec: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Values.site.name }}.yaml" - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Release.Name }}.yaml" - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ .name }}.yaml" - - {{ $.Values.global.valuesURL }} parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL From 0898e7f5ed60ce2d4c282162379b6eb8cd50be7b Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 22:55:38 +1000 Subject: [PATCH 0022/1263] Not too many valueFiles, argo borks on anything thats missing --- site/templates/applications.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index c11aa3ed..ec300451 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -17,8 +17,7 @@ spec: valueFiles: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Values.site.name }}.yaml" - - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Release.Name }}.yaml" - - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ .name }}.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL From eab253ce2af226d718046e8089437dbfec6819e1 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 08:17:42 +1000 Subject: [PATCH 0023/1263] Disabiguate argocd application names --- site/templates/applications.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index ec300451..4a7f1d38 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -2,7 +2,7 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: {{ .name }} + name: {{ $.Release.Name }}-{{ $.Values.site.name }}-{{ .name }} namespace: openshift-gitops spec: destination: From 3b4214c29364dd3ed960249d29fc566521ebcfbb Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 12:20:51 +1000 Subject: [PATCH 0024/1263] Add the ACM application --- .argocd-source.yaml | 10 -- acm/Chart.yaml | 7 ++ .../argocd-operator-subs/channel.yaml | 8 ++ .../argocd-operator-subs/namespace.yaml | 6 ++ .../argocd-operator-subs/placementrule.yaml | 9 ++ .../managed-clusters/baremetal-edge.yaml | 59 ++++++++++++ .../managed-clusters/staging-aws.yaml | 57 ++++++++++++ .../managed-clusters/staging-gcp.yaml | 57 ++++++++++++ .../manuela-factory-subs/channel.yaml | 8 ++ .../manuela-factory-subs/namespace.yaml | 6 ++ .../placementrule-labels.yaml | 11 +++ .../policy/factory-application-policy.yaml | 92 +++++++++++++++++++ .../policy/ocp-gitops-placement.yaml | 17 ++++ acm/templates/policy/ocp-gitops-policy.yaml | 59 ++++++++++++ acm/templates/policy/policy-namespace.yaml | 5 + acm/values.yaml | 11 +++ 16 files changed, 412 insertions(+), 10 deletions(-) create mode 100644 acm/Chart.yaml create mode 100644 acm/templates/argocd-operator-subs/channel.yaml create mode 100644 acm/templates/argocd-operator-subs/namespace.yaml create mode 100644 acm/templates/argocd-operator-subs/placementrule.yaml create mode 100644 acm/templates/managed-clusters/baremetal-edge.yaml create mode 100644 acm/templates/managed-clusters/staging-aws.yaml create mode 100644 acm/templates/managed-clusters/staging-gcp.yaml create mode 100644 acm/templates/manuela-factory-subs/channel.yaml create mode 100644 acm/templates/manuela-factory-subs/namespace.yaml create mode 100644 acm/templates/manuela-factory-subs/placementrule-labels.yaml create mode 100644 acm/templates/policy/factory-application-policy.yaml create mode 100644 acm/templates/policy/ocp-gitops-placement.yaml create mode 100644 acm/templates/policy/ocp-gitops-policy.yaml create mode 100644 acm/templates/policy/policy-namespace.yaml create mode 100644 acm/values.yaml diff --git a/.argocd-source.yaml b/.argocd-source.yaml index 0b8cad47..a6354d0c 100644 --- a/.argocd-source.yaml +++ b/.argocd-source.yaml @@ -6,19 +6,9 @@ helm: useCSV: "False" installPlanApproval: Automatic - namespaces: - - open-cluster-management - - policy - applications: - name: acm namespace: default project: datacenter path: applications/acm - subscriptions: - - name: advanced-cluster-management - namespace: open-cluster-management - source: redhat-operators - channel: release-2.3 - csv: v2.3.1 diff --git a/acm/Chart.yaml b/acm/Chart.yaml new file mode 100644 index 00000000..68619876 --- /dev/null +++ b/acm/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +description: A Helm chart to configure opendatahub +keywords: +- manuela +- blueprint +name: opendatahub +version: 0.0.1 diff --git a/acm/templates/argocd-operator-subs/channel.yaml b/acm/templates/argocd-operator-subs/channel.yaml new file mode 100644 index 00000000..23a598b7 --- /dev/null +++ b/acm/templates/argocd-operator-subs/channel.yaml @@ -0,0 +1,8 @@ +apiVersion: apps.open-cluster-management.io/v1 +kind: Channel +metadata: + name: argocd-operator-channel + namespace: argocd-operator-gitops +spec: + type: Git + pathname: https://github.com/dagger-refuse-cool/blueprints.git diff --git a/acm/templates/argocd-operator-subs/namespace.yaml b/acm/templates/argocd-operator-subs/namespace.yaml new file mode 100644 index 00000000..33d7585f --- /dev/null +++ b/acm/templates/argocd-operator-subs/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: argocd-operator-gitops + labels: + argocd.argoproj.io/managed-by: openshift-gitops diff --git a/acm/templates/argocd-operator-subs/placementrule.yaml b/acm/templates/argocd-operator-subs/placementrule.yaml new file mode 100644 index 00000000..311fa953 --- /dev/null +++ b/acm/templates/argocd-operator-subs/placementrule.yaml @@ -0,0 +1,9 @@ +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: apply-argocd-to-ready-clusters + namespace: openshift-gitops +spec: + clusterConditions: + - status: "True" + type: ManagedClusterConditionAvailable diff --git a/acm/templates/managed-clusters/baremetal-edge.yaml b/acm/templates/managed-clusters/baremetal-edge.yaml new file mode 100644 index 00000000..96783ffb --- /dev/null +++ b/acm/templates/managed-clusters/baremetal-edge.yaml @@ -0,0 +1,59 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: baremetal-edge + labels: + argocd.argoproj.io/managed-by: openshift-gitops +--- +apiVersion: cluster.open-cluster-management.io/v1 +kind: ManagedCluster +metadata: + labels: + cloud: auto-detect + vendor: auto-detect + name: baremetal-edge + gitops-mgmt: argocd + manuela: factory + name: baremetal-edge + namespace: openshift-gitops +spec: + hubAcceptsClient: true +--- +apiVersion: agent.open-cluster-management.io/v1 +kind: KlusterletAddonConfig +metadata: + name: baremetal-edge + namespace: baremetal-edge +spec: + clusterName: baremetal-edge + clusterNamespace: baremetal-edge + clusterLabels: + cloud: auto-detect + vendor: auto-detect + applicationManager: + enabled: true + policyController: + enabled: true + searchCollector: + enabled: true + certPolicyController: + enabled: true + iamPolicyController: + enabled: true + version: "2.0" +--- +apiVersion: internal.open-cluster-management.io/v1beta1 +kind: ManagedClusterInfo +metadata: + labels: + argocd.argoproj.io/instance: manuela-acm + cloud: auto-detect + gitops-mgmt: argocd + manuela: factory + name: baremetal-edge + vendor: auto-detect + name: baremetal-edge + namespace: baremetal-edge +spec: + loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + \ No newline at end of file diff --git a/acm/templates/managed-clusters/staging-aws.yaml b/acm/templates/managed-clusters/staging-aws.yaml new file mode 100644 index 00000000..b003e9c5 --- /dev/null +++ b/acm/templates/managed-clusters/staging-aws.yaml @@ -0,0 +1,57 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: staging-aws + labels: + argocd.argoproj.io/managed-by: openshift-gitops +--- +apiVersion: cluster.open-cluster-management.io/v1 +kind: ManagedCluster +metadata: + labels: + cloud: auto-detect + vendor: auto-detect + name: staging-aws + gitops-mgmt: argocd + manuela: factory + name: staging-aws +spec: + hubAcceptsClient: true +--- +apiVersion: agent.open-cluster-management.io/v1 +kind: KlusterletAddonConfig +metadata: + name: staging-aws + namespace: staging-aws +spec: + clusterName: staging-aws + clusterNamespace: staging-aws + clusterLabels: + cloud: auto-detect + vendor: auto-detect + applicationManager: + enabled: true + policyController: + enabled: true + searchCollector: + enabled: true + certPolicyController: + enabled: true + iamPolicyController: + enabled: true + version: "2.0" +--- +apiVersion: internal.open-cluster-management.io/v1beta1 +kind: ManagedClusterInfo +metadata: + labels: + argocd.argoproj.io/instance: manuela-acm + cloud: auto-detect + gitops-mgmt: argocd + manuela: factory + name: staging-aws + vendor: auto-detect + name: staging-aws + namespace: staging-aws +spec: + loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K diff --git a/acm/templates/managed-clusters/staging-gcp.yaml b/acm/templates/managed-clusters/staging-gcp.yaml new file mode 100644 index 00000000..3c950d97 --- /dev/null +++ b/acm/templates/managed-clusters/staging-gcp.yaml @@ -0,0 +1,57 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: staging + labels: + argocd.argoproj.io/managed-by: openshift-gitops +--- +apiVersion: cluster.open-cluster-management.io/v1 +kind: ManagedCluster +metadata: + labels: + cloud: auto-detect + vendor: auto-detect + name: staging + gitops-mgmt: argocd + manuela: factory + name: staging +spec: + hubAcceptsClient: true +--- +apiVersion: agent.open-cluster-management.io/v1 +kind: KlusterletAddonConfig +metadata: + name: staging + namespace: staging +spec: + clusterName: staging + clusterNamespace: staging + clusterLabels: + cloud: auto-detect + vendor: auto-detect + applicationManager: + enabled: true + policyController: + enabled: true + searchCollector: + enabled: true + certPolicyController: + enabled: true + iamPolicyController: + enabled: true + version: "2.0" +--- +apiVersion: internal.open-cluster-management.io/v1beta1 +kind: ManagedClusterInfo +metadata: + labels: + argocd.argoproj.io/instance: manuela-acm + cloud: auto-detect + gitops-mgmt: argocd + manuela: factory + name: staging + vendor: auto-detect + name: staging + namespace: staging +spec: + loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K \ No newline at end of file diff --git a/acm/templates/manuela-factory-subs/channel.yaml b/acm/templates/manuela-factory-subs/channel.yaml new file mode 100644 index 00000000..b666d105 --- /dev/null +++ b/acm/templates/manuela-factory-subs/channel.yaml @@ -0,0 +1,8 @@ +apiVersion: apps.open-cluster-management.io/v1 +kind: Channel +metadata: + name: acm-channel-blueprint-industrial-edge + namespace: acm-blueprint-industrial-edge-gitops +spec: + pathname: https://{{ .Values.global.git.provider }} /{{ .Values.global.git.account }}/blueprints.git + type: Git diff --git a/acm/templates/manuela-factory-subs/namespace.yaml b/acm/templates/manuela-factory-subs/namespace.yaml new file mode 100644 index 00000000..e2463be4 --- /dev/null +++ b/acm/templates/manuela-factory-subs/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: acm-blueprint-industrial-edge-gitops + labels: + argocd.argoproj.io/managed-by: openshift-gitops diff --git a/acm/templates/manuela-factory-subs/placementrule-labels.yaml b/acm/templates/manuela-factory-subs/placementrule-labels.yaml new file mode 100644 index 00000000..58cfe7bd --- /dev/null +++ b/acm/templates/manuela-factory-subs/placementrule-labels.yaml @@ -0,0 +1,11 @@ +# Install the Manuela Factory+Line applications on manuela=factory servers running ArgoCD +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: apply-argocd-factory-to-label-clusters + namespace: openshift-gitops +spec: + clusterSelector: + matchLabels: + gitops-mgmt: argocd + manuela: factory diff --git a/acm/templates/policy/factory-application-policy.yaml b/acm/templates/policy/factory-application-policy.yaml new file mode 100644 index 00000000..ed818992 --- /dev/null +++ b/acm/templates/policy/factory-application-policy.yaml @@ -0,0 +1,92 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: argo-factorydatacenter-application-installed + namespace: policy + annotations: +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: argo-factorydatacenter-application-installed + spec: + remediationAction: enforce + severity: med + namespaceSelector: + exclude: + - kube-* + include: + - default + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: argoproj.io/v1alpha1 + kind: Application + metadata: + name: factorydatacenter + namespace: openshift-gitops + finalizers: + - argoproj.io/finalizer + spec: + project: default + source: + repoURL: "https://{{ .Values.global.git.provider }}/{{ .Values.global.git.username }}/blueprints.git" + targetRevision: "{{ .Values.global.git.ops_revision }}" + path: manufacturing-edge-ai-ml/factory/main + helm: + # All other values come from /.argocd-source.yaml + parameters: + - name: global.git.provider + value: "{{ .Values.global.git.provider }}" + - name: global.git.account + value: "{{ .Values.global.git.account }}" + - name: global.git.ops_revision + value: "{{ .Values.global.git.ops_revision }}" + destination: + server: https://kubernetes.default.svc + namespace: openshift-gitops + syncPolicy: + automated: + prune: false + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: binding-argo-factorydatacenter + namespace: policy +placementRef: + name: all-factory + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: argo-factorydatacenter-application-installed + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: all-factory + namespace: policy +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchLabels: + manuela: factory + diff --git a/acm/templates/policy/ocp-gitops-placement.yaml b/acm/templates/policy/ocp-gitops-placement.yaml new file mode 100644 index 00000000..f4bf151b --- /dev/null +++ b/acm/templates/policy/ocp-gitops-placement.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: all-openshift + namespace: policy +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift + diff --git a/acm/templates/policy/ocp-gitops-policy.yaml b/acm/templates/policy/ocp-gitops-policy.yaml new file mode 100644 index 00000000..a6e2d1bd --- /dev/null +++ b/acm/templates/policy/ocp-gitops-policy.yaml @@ -0,0 +1,59 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: openshift-gitops-installed + namespace: policy + annotations: + policy.open-cluster-management.io/standards: NIST-CSF + policy.open-cluster-management.io/categories: PR.DS Data Security + policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: openshift-gitops-installed + spec: + remediationAction: enforce + severity: med + namespaceSelector: + exclude: + - kube-* + include: + - default + object-templates: + - complianceType: musthave + objectDefinition: + # This is an auto-generated file. DO NOT EDIT + apiVersion: operators.coreos.com/v1alpha1 + kind: Subscription + metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: '' + spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + startingCSV: openshift-gitops-operator.v1.1.0 +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: binding-argo-development + namespace: policy +placementRef: + name: all-openshift + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: openshift-gitops-installed + kind: Policy + apiGroup: policy.open-cluster-management.io + diff --git a/acm/templates/policy/policy-namespace.yaml b/acm/templates/policy/policy-namespace.yaml new file mode 100644 index 00000000..18e746a2 --- /dev/null +++ b/acm/templates/policy/policy-namespace.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: policy diff --git a/acm/values.yaml b/acm/values.yaml new file mode 100644 index 00000000..6bf05ad4 --- /dev/null +++ b/acm/values.yaml @@ -0,0 +1,11 @@ +global: + git: + provider: github.com + account: PLAINTEXT + username: PLAINTEXT + email: SOMEWHERE@EXAMPLE.COM + target: HEAD + + quay: + provider: quay.io + account: PLAINTEXT From bae69fab81eb2ab640a068cb246882eda46bce3a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 13:08:36 +1000 Subject: [PATCH 0025/1263] Removed old ACM policies and subscriptions, add back the multiclusterhub --- .../argocd-operator-subs/channel.yaml | 8 --- .../argocd-operator-subs/namespace.yaml | 6 -- .../argocd-operator-subs/placementrule.yaml | 9 --- .../managed-clusters/baremetal-edge.yaml | 59 ------------------- .../managed-clusters/staging-aws.yaml | 57 ------------------ .../{staging-gcp.yaml => staging.yaml} | 0 .../manuela-factory-subs/channel.yaml | 8 --- .../manuela-factory-subs/namespace.yaml | 6 -- .../placementrule-labels.yaml | 11 ---- acm/templates/multiclusterhub.yaml | 6 ++ .../factory-application-policy.yaml | 0 .../ocp-gitops-placement.yaml | 0 .../ocp-gitops-policy.yaml | 0 .../policy-namespace.yaml | 0 14 files changed, 6 insertions(+), 164 deletions(-) delete mode 100644 acm/templates/argocd-operator-subs/channel.yaml delete mode 100644 acm/templates/argocd-operator-subs/namespace.yaml delete mode 100644 acm/templates/argocd-operator-subs/placementrule.yaml delete mode 100644 acm/templates/managed-clusters/baremetal-edge.yaml delete mode 100644 acm/templates/managed-clusters/staging-aws.yaml rename acm/templates/managed-clusters/{staging-gcp.yaml => staging.yaml} (100%) delete mode 100644 acm/templates/manuela-factory-subs/channel.yaml delete mode 100644 acm/templates/manuela-factory-subs/namespace.yaml delete mode 100644 acm/templates/manuela-factory-subs/placementrule-labels.yaml create mode 100644 acm/templates/multiclusterhub.yaml rename acm/templates/{policy => policies}/factory-application-policy.yaml (100%) rename acm/templates/{policy => policies}/ocp-gitops-placement.yaml (100%) rename acm/templates/{policy => policies}/ocp-gitops-policy.yaml (100%) rename acm/templates/{policy => policies}/policy-namespace.yaml (100%) diff --git a/acm/templates/argocd-operator-subs/channel.yaml b/acm/templates/argocd-operator-subs/channel.yaml deleted file mode 100644 index 23a598b7..00000000 --- a/acm/templates/argocd-operator-subs/channel.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: apps.open-cluster-management.io/v1 -kind: Channel -metadata: - name: argocd-operator-channel - namespace: argocd-operator-gitops -spec: - type: Git - pathname: https://github.com/dagger-refuse-cool/blueprints.git diff --git a/acm/templates/argocd-operator-subs/namespace.yaml b/acm/templates/argocd-operator-subs/namespace.yaml deleted file mode 100644 index 33d7585f..00000000 --- a/acm/templates/argocd-operator-subs/namespace.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: argocd-operator-gitops - labels: - argocd.argoproj.io/managed-by: openshift-gitops diff --git a/acm/templates/argocd-operator-subs/placementrule.yaml b/acm/templates/argocd-operator-subs/placementrule.yaml deleted file mode 100644 index 311fa953..00000000 --- a/acm/templates/argocd-operator-subs/placementrule.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: apps.open-cluster-management.io/v1 -kind: PlacementRule -metadata: - name: apply-argocd-to-ready-clusters - namespace: openshift-gitops -spec: - clusterConditions: - - status: "True" - type: ManagedClusterConditionAvailable diff --git a/acm/templates/managed-clusters/baremetal-edge.yaml b/acm/templates/managed-clusters/baremetal-edge.yaml deleted file mode 100644 index 96783ffb..00000000 --- a/acm/templates/managed-clusters/baremetal-edge.yaml +++ /dev/null @@ -1,59 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: baremetal-edge - labels: - argocd.argoproj.io/managed-by: openshift-gitops ---- -apiVersion: cluster.open-cluster-management.io/v1 -kind: ManagedCluster -metadata: - labels: - cloud: auto-detect - vendor: auto-detect - name: baremetal-edge - gitops-mgmt: argocd - manuela: factory - name: baremetal-edge - namespace: openshift-gitops -spec: - hubAcceptsClient: true ---- -apiVersion: agent.open-cluster-management.io/v1 -kind: KlusterletAddonConfig -metadata: - name: baremetal-edge - namespace: baremetal-edge -spec: - clusterName: baremetal-edge - clusterNamespace: baremetal-edge - clusterLabels: - cloud: auto-detect - vendor: auto-detect - applicationManager: - enabled: true - policyController: - enabled: true - searchCollector: - enabled: true - certPolicyController: - enabled: true - iamPolicyController: - enabled: true - version: "2.0" ---- -apiVersion: internal.open-cluster-management.io/v1beta1 -kind: ManagedClusterInfo -metadata: - labels: - argocd.argoproj.io/instance: manuela-acm - cloud: auto-detect - gitops-mgmt: argocd - manuela: factory - name: baremetal-edge - vendor: auto-detect - name: baremetal-edge - namespace: baremetal-edge -spec: - loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - \ No newline at end of file diff --git a/acm/templates/managed-clusters/staging-aws.yaml b/acm/templates/managed-clusters/staging-aws.yaml deleted file mode 100644 index b003e9c5..00000000 --- a/acm/templates/managed-clusters/staging-aws.yaml +++ /dev/null @@ -1,57 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: staging-aws - labels: - argocd.argoproj.io/managed-by: openshift-gitops ---- -apiVersion: cluster.open-cluster-management.io/v1 -kind: ManagedCluster -metadata: - labels: - cloud: auto-detect - vendor: auto-detect - name: staging-aws - gitops-mgmt: argocd - manuela: factory - name: staging-aws -spec: - hubAcceptsClient: true ---- -apiVersion: agent.open-cluster-management.io/v1 -kind: KlusterletAddonConfig -metadata: - name: staging-aws - namespace: staging-aws -spec: - clusterName: staging-aws - clusterNamespace: staging-aws - clusterLabels: - cloud: auto-detect - vendor: auto-detect - applicationManager: - enabled: true - policyController: - enabled: true - searchCollector: - enabled: true - certPolicyController: - enabled: true - iamPolicyController: - enabled: true - version: "2.0" ---- -apiVersion: internal.open-cluster-management.io/v1beta1 -kind: ManagedClusterInfo -metadata: - labels: - argocd.argoproj.io/instance: manuela-acm - cloud: auto-detect - gitops-mgmt: argocd - manuela: factory - name: staging-aws - vendor: auto-detect - name: staging-aws - namespace: staging-aws -spec: - loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K diff --git a/acm/templates/managed-clusters/staging-gcp.yaml b/acm/templates/managed-clusters/staging.yaml similarity index 100% rename from acm/templates/managed-clusters/staging-gcp.yaml rename to acm/templates/managed-clusters/staging.yaml diff --git a/acm/templates/manuela-factory-subs/channel.yaml b/acm/templates/manuela-factory-subs/channel.yaml deleted file mode 100644 index b666d105..00000000 --- a/acm/templates/manuela-factory-subs/channel.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: apps.open-cluster-management.io/v1 -kind: Channel -metadata: - name: acm-channel-blueprint-industrial-edge - namespace: acm-blueprint-industrial-edge-gitops -spec: - pathname: https://{{ .Values.global.git.provider }} /{{ .Values.global.git.account }}/blueprints.git - type: Git diff --git a/acm/templates/manuela-factory-subs/namespace.yaml b/acm/templates/manuela-factory-subs/namespace.yaml deleted file mode 100644 index e2463be4..00000000 --- a/acm/templates/manuela-factory-subs/namespace.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: acm-blueprint-industrial-edge-gitops - labels: - argocd.argoproj.io/managed-by: openshift-gitops diff --git a/acm/templates/manuela-factory-subs/placementrule-labels.yaml b/acm/templates/manuela-factory-subs/placementrule-labels.yaml deleted file mode 100644 index 58cfe7bd..00000000 --- a/acm/templates/manuela-factory-subs/placementrule-labels.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# Install the Manuela Factory+Line applications on manuela=factory servers running ArgoCD -apiVersion: apps.open-cluster-management.io/v1 -kind: PlacementRule -metadata: - name: apply-argocd-factory-to-label-clusters - namespace: openshift-gitops -spec: - clusterSelector: - matchLabels: - gitops-mgmt: argocd - manuela: factory diff --git a/acm/templates/multiclusterhub.yaml b/acm/templates/multiclusterhub.yaml new file mode 100644 index 00000000..fb1c6f0c --- /dev/null +++ b/acm/templates/multiclusterhub.yaml @@ -0,0 +1,6 @@ +apiVersion: operator.open-cluster-management.io/v1 +kind: MultiClusterHub +metadata: + name: multiclusterhub + namespace: open-cluster-management +spec: {} diff --git a/acm/templates/policy/factory-application-policy.yaml b/acm/templates/policies/factory-application-policy.yaml similarity index 100% rename from acm/templates/policy/factory-application-policy.yaml rename to acm/templates/policies/factory-application-policy.yaml diff --git a/acm/templates/policy/ocp-gitops-placement.yaml b/acm/templates/policies/ocp-gitops-placement.yaml similarity index 100% rename from acm/templates/policy/ocp-gitops-placement.yaml rename to acm/templates/policies/ocp-gitops-placement.yaml diff --git a/acm/templates/policy/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml similarity index 100% rename from acm/templates/policy/ocp-gitops-policy.yaml rename to acm/templates/policies/ocp-gitops-policy.yaml diff --git a/acm/templates/policy/policy-namespace.yaml b/acm/templates/policies/policy-namespace.yaml similarity index 100% rename from acm/templates/policy/policy-namespace.yaml rename to acm/templates/policies/policy-namespace.yaml From 2fb9a080ad0fb102e08d41c5f1b52c32f678b97d Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 22:56:56 +1000 Subject: [PATCH 0026/1263] bootstrap creates the namespaces --- acm/templates/policies/policy-namespace.yaml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 acm/templates/policies/policy-namespace.yaml diff --git a/acm/templates/policies/policy-namespace.yaml b/acm/templates/policies/policy-namespace.yaml deleted file mode 100644 index 18e746a2..00000000 --- a/acm/templates/policies/policy-namespace.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: policy From aa629bea15d34b84d7b8f78f94c9cc3b15ceb341 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 23:31:41 +1000 Subject: [PATCH 0027/1263] Try pre-creating the multiclusterhub --- acm/templates/multiclusterhub.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/acm/templates/multiclusterhub.yaml b/acm/templates/multiclusterhub.yaml index fb1c6f0c..f925d5a6 100644 --- a/acm/templates/multiclusterhub.yaml +++ b/acm/templates/multiclusterhub.yaml @@ -3,4 +3,6 @@ kind: MultiClusterHub metadata: name: multiclusterhub namespace: open-cluster-management + annotations: + argocd.argoproj.io/sync-wave: "-1" spec: {} From e505acbcb812baa6bc2faf7da0b0c6790a3b4463 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 23:41:55 +1000 Subject: [PATCH 0028/1263] Fix the acm application --- .../policies/factory-application-policy.yaml | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/acm/templates/policies/factory-application-policy.yaml b/acm/templates/policies/factory-application-policy.yaml index ed818992..38f2441f 100644 --- a/acm/templates/policies/factory-application-policy.yaml +++ b/acm/templates/policies/factory-application-policy.yaml @@ -34,18 +34,19 @@ spec: spec: project: default source: - repoURL: "https://{{ .Values.global.git.provider }}/{{ .Values.global.git.username }}/blueprints.git" - targetRevision: "{{ .Values.global.git.ops_revision }}" - path: manufacturing-edge-ai-ml/factory/main + repoURL: {{ .Values.global.repoURL }} + targetRevision: {{ .Values.global.targetRevision }} + path: applications/factory/main helm: - # All other values come from /.argocd-source.yaml + valueFiles: + - {{ .Values.global.valuesURL }} parameters: - - name: global.git.provider - value: "{{ .Values.global.git.provider }}" - - name: global.git.account - value: "{{ .Values.global.git.account }}" - - name: global.git.ops_revision - value: "{{ .Values.global.git.ops_revision }}" + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.valuesURL + value: {{ .Values.global.valuesURL }} destination: server: https://kubernetes.default.svc namespace: openshift-gitops From 3b20e537aeee484d7ed26fa1d0e27b15477a2ed5 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 14:31:54 +1000 Subject: [PATCH 0029/1263] Tell argo to ignore missing acm CRDs that appear once the multiclusterhub is defined --- acm/templates/managed-clusters/staging.yaml | 6 ++++++ acm/templates/policies/factory-application-policy.yaml | 5 +++++ acm/templates/policies/ocp-gitops-placement.yaml | 2 ++ acm/templates/policies/ocp-gitops-policy.yaml | 3 +++ 4 files changed, 16 insertions(+) diff --git a/acm/templates/managed-clusters/staging.yaml b/acm/templates/managed-clusters/staging.yaml index 3c950d97..2dcbe9bf 100644 --- a/acm/templates/managed-clusters/staging.yaml +++ b/acm/templates/managed-clusters/staging.yaml @@ -15,6 +15,8 @@ metadata: gitops-mgmt: argocd manuela: factory name: staging + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: hubAcceptsClient: true --- @@ -23,6 +25,8 @@ kind: KlusterletAddonConfig metadata: name: staging namespace: staging + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterName: staging clusterNamespace: staging @@ -44,6 +48,8 @@ spec: apiVersion: internal.open-cluster-management.io/v1beta1 kind: ManagedClusterInfo metadata: + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true labels: argocd.argoproj.io/instance: manuela-acm cloud: auto-detect diff --git a/acm/templates/policies/factory-application-policy.yaml b/acm/templates/policies/factory-application-policy.yaml index 38f2441f..745f3260 100644 --- a/acm/templates/policies/factory-application-policy.yaml +++ b/acm/templates/policies/factory-application-policy.yaml @@ -4,6 +4,7 @@ metadata: name: argo-factorydatacenter-application-installed namespace: policy annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: remediationAction: enforce disabled: false @@ -69,6 +70,8 @@ kind: PlacementBinding metadata: name: binding-argo-factorydatacenter namespace: policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true placementRef: name: all-factory kind: PlacementRule @@ -83,6 +86,8 @@ kind: PlacementRule metadata: name: all-factory namespace: policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' diff --git a/acm/templates/policies/ocp-gitops-placement.yaml b/acm/templates/policies/ocp-gitops-placement.yaml index f4bf151b..b4d922db 100644 --- a/acm/templates/policies/ocp-gitops-placement.yaml +++ b/acm/templates/policies/ocp-gitops-placement.yaml @@ -4,6 +4,8 @@ kind: PlacementRule metadata: name: all-openshift namespace: policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index a6e2d1bd..21ec5b56 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -7,6 +7,7 @@ metadata: policy.open-cluster-management.io/standards: NIST-CSF policy.open-cluster-management.io/categories: PR.DS Data Security policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: remediationAction: enforce disabled: false @@ -48,6 +49,8 @@ kind: PlacementBinding metadata: name: binding-argo-development namespace: policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true placementRef: name: all-openshift kind: PlacementRule From 093a4a4ea1be82643a102aae1a043b9ce5d8b2b4 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 15:13:26 +1000 Subject: [PATCH 0030/1263] Allow for multiple site types driven by the shared values file --- ...-policy.yaml => application-policies.yaml} | 35 ++++++++++--------- .../policies/ocp-gitops-placement.yaml | 19 ---------- acm/templates/policies/ocp-gitops-policy.yaml | 32 ++++++++++++----- 3 files changed, 41 insertions(+), 45 deletions(-) rename acm/templates/policies/{factory-application-policy.yaml => application-policies.yaml} (76%) delete mode 100644 acm/templates/policies/ocp-gitops-placement.yaml diff --git a/acm/templates/policies/factory-application-policy.yaml b/acm/templates/policies/application-policies.yaml similarity index 76% rename from acm/templates/policies/factory-application-policy.yaml rename to acm/templates/policies/application-policies.yaml index 745f3260..30c90dda 100644 --- a/acm/templates/policies/factory-application-policy.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -1,8 +1,8 @@ +{{- range .Values.site.managedSites }} apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: - name: argo-factorydatacenter-application-installed - namespace: policy + name: {{ .name }}-site-policy annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: @@ -13,7 +13,7 @@ spec: apiVersion: policy.open-cluster-management.io/v1 kind: ConfigurationPolicy metadata: - name: argo-factorydatacenter-application-installed + name: {{ .name }}-site-config spec: remediationAction: enforce severity: med @@ -35,19 +35,23 @@ spec: spec: project: default source: - repoURL: {{ .Values.global.repoURL }} - targetRevision: {{ .Values.global.targetRevision }} - path: applications/factory/main + repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} + targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} + path: {{ .path }} helm: valueFiles: - - {{ .Values.global.valuesURL }} + - {{ coalesce .valuesURL $.Values.global.valuesURL }} parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL - name: global.targetRevision value: $ARGOCD_APP_SOURCE_TARGET_REVISION - name: global.valuesURL - value: {{ .Values.global.valuesURL }} + value: {{ coalesce .valuesURL $.Values.global.valuesURL }} + {{- range .helmOverrides }} + - name: {{ .name }} + value: {{ .value }} + {{- end }} destination: server: https://kubernetes.default.svc namespace: openshift-gitops @@ -68,12 +72,11 @@ spec: apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding metadata: - name: binding-argo-factorydatacenter - namespace: policy + name: {{ .name }}-placement-binding annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true placementRef: - name: all-factory + name: {{ .name }}-placement kind: PlacementRule apiGroup: apps.open-cluster-management.io subjects: @@ -84,15 +87,13 @@ subjects: apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: - name: all-factory - namespace: policy + name: {{ .name }}-placement annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' type: ManagedClusterConditionAvailable - clusterSelector: - matchLabels: - manuela: factory - + clusterSelector: {{ .clusterSelector | toPrettyJson }} +--- +{{- end }} diff --git a/acm/templates/policies/ocp-gitops-placement.yaml b/acm/templates/policies/ocp-gitops-placement.yaml deleted file mode 100644 index b4d922db..00000000 --- a/acm/templates/policies/ocp-gitops-placement.yaml +++ /dev/null @@ -1,19 +0,0 @@ ---- -apiVersion: apps.open-cluster-management.io/v1 -kind: PlacementRule -metadata: - name: all-openshift - namespace: policy - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -spec: - clusterConditions: - - status: 'True' - type: ManagedClusterConditionAvailable - clusterSelector: - matchExpressions: - - key: vendor - operator: In - values: - - OpenShift - diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index 21ec5b56..11c89324 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -1,8 +1,7 @@ apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: - name: openshift-gitops-installed - namespace: policy + name: openshift-gitops-policy annotations: policy.open-cluster-management.io/standards: NIST-CSF policy.open-cluster-management.io/categories: PR.DS Data Security @@ -16,7 +15,7 @@ spec: apiVersion: policy.open-cluster-management.io/v1 kind: ConfigurationPolicy metadata: - name: openshift-gitops-installed + name: openshift-gitops-config spec: remediationAction: enforce severity: med @@ -42,21 +41,36 @@ spec: name: openshift-gitops-operator source: redhat-operators sourceNamespace: openshift-marketplace - startingCSV: openshift-gitops-operator.v1.1.0 --- apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding metadata: - name: binding-argo-development - namespace: policy + name: openshift-gitops-placement-binding annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true placementRef: - name: all-openshift + name: openshift-gitops-placement kind: PlacementRule apiGroup: apps.open-cluster-management.io subjects: - - name: openshift-gitops-installed + - name: openshift-gitops-policy kind: Policy apiGroup: policy.open-cluster-management.io - +--- +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: openshift-gitops-placement + namespace: policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift From 6f42eaaefd755b4153daad6cec1497d4fb69c142 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 22:01:22 +1000 Subject: [PATCH 0031/1263] Support a per-site valuesFile --- .../policies/application-policies.yaml | 9 ++-- values-datacenter.yaml | 48 +++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 values-datacenter.yaml diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 30c90dda..a9654956 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -40,14 +40,17 @@ spec: path: {{ .path }} helm: valueFiles: - - {{ coalesce .valuesURL $.Values.global.valuesURL }} + - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" + - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Values.site.name }}.yaml" + - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Release.Name }}.yaml" + - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ .name }}.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL - name: global.targetRevision value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.valuesURL - value: {{ coalesce .valuesURL $.Values.global.valuesURL }} + - name: global.valuesDirectoryURL + value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} {{- range .helmOverrides }} - name: {{ .name }} value: {{ .value }} diff --git a/values-datacenter.yaml b/values-datacenter.yaml new file mode 100644 index 00000000..e9ee928e --- /dev/null +++ b/values-datacenter.yaml @@ -0,0 +1,48 @@ +global: + valuesDirectoryURL: https://github.com/dagger-refuse-cool/edge-gitops/raw/main/ + options: + useCSV: True + syncPolicy: Automatic + installPlanApproval: Automatic + +site: + name: datacenter + + proposedOptions: + manageGitops: True + isHubCluster: True + + namespaces: + - open-cluster-management + + subscriptions: + - name: advanced-cluster-management + namespace: open-cluster-management + source: redhat-operators + channel: release-2.3 + csv: v2.3.2 + + projects: + - datacenter + + applications: + - name: acm + namespace: open-cluster-management + project: datacenter + path: applications/acm + + managedSites: + - name: factory + # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git + targetRevision: main + path: applications/factory + helmOverrides: + - name: site.isHubCluster + value: false + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift + From 11485b346f30c519987f3deae12ab614d325ea3d Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 22:30:49 +1000 Subject: [PATCH 0032/1263] Placement rules are defined by the operator --- acm/templates/policies/application-policies.yaml | 3 +-- acm/templates/policies/ocp-gitops-policy.yaml | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index a9654956..2a419afc 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -1,3 +1,4 @@ +# TODO: Also create a GitOpsCluster.apps.open-cluster-management.io {{- range .Values.site.managedSites }} apiVersion: policy.open-cluster-management.io/v1 kind: Policy @@ -91,8 +92,6 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: {{ .name }}-placement - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index 11c89324..956bc7f5 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -62,8 +62,6 @@ kind: PlacementRule metadata: name: openshift-gitops-placement namespace: policy - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' From 85e9122c000c00feebe10f9c36da8b496fdbed82 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 22:57:01 +1000 Subject: [PATCH 0033/1263] Not too many valueFiles, argo borks on anything thats missing --- .../policies/application-policies.yaml | 2 -- values-datacenter.yaml | 31 +++++++++++++++++++ values-global.yaml | 6 ++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 values-global.yaml diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 2a419afc..f34cec8c 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -43,8 +43,6 @@ spec: valueFiles: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Values.site.name }}.yaml" - - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Release.Name }}.yaml" - - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ .name }}.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL diff --git a/values-datacenter.yaml b/values-datacenter.yaml index e9ee928e..90780c63 100644 --- a/values-datacenter.yaml +++ b/values-datacenter.yaml @@ -30,6 +30,37 @@ site: namespace: open-cluster-management project: datacenter path: applications/acm +# +# To have apps in multiple flavors, use namespaces and use helm overrides as appropriate +# +# - name: pipelines +# namespace: production +# project: datacenter +# path: applications/pipeline +# repoURL: https://github.com/you/applications.git +# targetRevision: stable +# overrides: +# - name: myparam +# value: myparam +# +# - name: pipelines +# namespace: staging +# project: datacenter +# path: applications/pipeline +# repoURL: https://github.com/you/applications.git +# targetRevision: main +# +# Additional applications +# Be sure to include additional resources your apps will require +# +X machines +# +Y RAM +# +Z CPU +# - name: vendor-app +# namespace: default +# project: vendor +# path: path/to/myapp +# repoURL: https://github.com/vendor/applications.git +# targetRevision: main managedSites: - name: factory diff --git a/values-global.yaml b/values-global.yaml new file mode 100644 index 00000000..faa2d905 --- /dev/null +++ b/values-global.yaml @@ -0,0 +1,6 @@ +global: + valuesDirectoryURL: https://github.com/dagger-refuse-cool/edge-gitops/raw/main/ + options: + useCSV: True + syncPolicy: Manual + installPlanApproval: Automatic From d15836869008307bf3172f92f498ca58732c0ef3 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 08:19:47 +1000 Subject: [PATCH 0034/1263] Disabiguate argocd application names and enable acm --- acm/templates/policies/application-policies.yaml | 2 +- values-datacenter.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index f34cec8c..a70d7b1b 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -29,7 +29,7 @@ spec: apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: factorydatacenter + name: {{ $.Values.site.name }}-site namespace: openshift-gitops finalizers: - argoproj.io/finalizer diff --git a/values-datacenter.yaml b/values-datacenter.yaml index 90780c63..233f20ac 100644 --- a/values-datacenter.yaml +++ b/values-datacenter.yaml @@ -30,6 +30,7 @@ site: namespace: open-cluster-management project: datacenter path: applications/acm + # # To have apps in multiple flavors, use namespaces and use helm overrides as appropriate # From ba5ceb493b0a8bae5ecfdf2cdd67f283ce08bac5 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 11:25:01 +1000 Subject: [PATCH 0035/1263] .argocd-source.yaml is not useful because it needs to be in the chart directory --- .argocd-source.yaml | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .argocd-source.yaml diff --git a/.argocd-source.yaml b/.argocd-source.yaml deleted file mode 100644 index a6354d0c..00000000 --- a/.argocd-source.yaml +++ /dev/null @@ -1,14 +0,0 @@ -helm: - global: - options: - syncPolicy: Automatic - - useCSV: "False" - installPlanApproval: Automatic - - applications: - - name: acm - namespace: default - project: datacenter - path: applications/acm - From 7d88e3c7dfa4dab4e1ef03ca1cb1e5d6a21bc6c7 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 09:53:08 +1000 Subject: [PATCH 0036/1263] Initial commit --- install/.helmignore | 1 + install/Chart.yaml | 7 + install/crds/applications.argoproj.io.yaml | 2162 ++++++++++++++++++ install/templates/argocd/application.yaml | 33 + install/templates/argocd/namespace.yaml | 7 + install/templates/argocd/subscription.yaml | 19 + install/templates/secrets/github-secret.yaml | 12 + install/templates/secrets/namespace.yaml | 11 + install/templates/secrets/quay-secret.yaml | 19 + install/values.yaml | 31 + 10 files changed, 2302 insertions(+) create mode 100644 install/.helmignore create mode 100644 install/Chart.yaml create mode 100644 install/crds/applications.argoproj.io.yaml create mode 100644 install/templates/argocd/application.yaml create mode 100644 install/templates/argocd/namespace.yaml create mode 100644 install/templates/argocd/subscription.yaml create mode 100644 install/templates/secrets/github-secret.yaml create mode 100644 install/templates/secrets/namespace.yaml create mode 100644 install/templates/secrets/quay-secret.yaml create mode 100644 install/values.yaml diff --git a/install/.helmignore b/install/.helmignore new file mode 100644 index 00000000..b25c15b8 --- /dev/null +++ b/install/.helmignore @@ -0,0 +1 @@ +*~ diff --git a/install/Chart.yaml b/install/Chart.yaml new file mode 100644 index 00000000..47fc460d --- /dev/null +++ b/install/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +description: A Helm chart to build and deploy MANUela +keywords: +- runtimes +- blueprint +name: manufacturing-edge-ai-ml +version: 0.0.1 diff --git a/install/crds/applications.argoproj.io.yaml b/install/crds/applications.argoproj.io.yaml new file mode 100644 index 00000000..094bf055 --- /dev/null +++ b/install/crds/applications.argoproj.io.yaml @@ -0,0 +1,2162 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + generation: 1 + labels: + app.kubernetes.io/name: applications.argoproj.io + app.kubernetes.io/part-of: argocd + operators.coreos.com/openshift-gitops-operator.openshift-operators: "" + name: applications.argoproj.io +spec: + conversion: + strategy: None + group: argoproj.io + names: + kind: Application + listKind: ApplicationList + plural: applications + shortNames: + - app + - apps + singular: application + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.sync.status + name: Sync Status + type: string + - jsonPath: .status.health.status + name: Health Status + type: string + - jsonPath: .status.sync.revision + name: Revision + priority: 10 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Application is a definition of Application resource. + 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 + operation: + description: Operation contains information about a requested or running + operation + properties: + info: + description: Info is a list of informational items for this operation + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + initiatedBy: + description: InitiatedBy contains information about who initiated + the operations + properties: + automated: + description: Automated is set to true if operation was initiated + automatically by the application controller. + type: boolean + username: + description: Username contains the name of a user who started + operation + type: string + type: object + retry: + description: Retry controls the strategy to apply if a sync fails + properties: + backoff: + description: Backoff controls how to backoff on subsequent retries + of failed syncs + properties: + duration: + description: Duration is the amount to back off. Default unit + is seconds, but could also be a duration (e.g. "2m", "1h") + type: string + factor: + description: Factor is a factor to multiply the base duration + after each failed retry + format: int64 + type: integer + maxDuration: + description: MaxDuration is the maximum amount of time allowed + for the backoff strategy + type: string + type: object + limit: + description: Limit is the maximum number of attempts for retrying + a failed sync. If set to 0, no retries will be performed. + format: int64 + type: integer + type: object + sync: + description: Sync contains parameters for the operation + properties: + dryRun: + description: DryRun specifies to perform a `kubectl apply --dry-run` + without actually performing the sync + type: boolean + manifests: + description: Manifests is an optional field that overrides sync + source with a local directory for development + items: + type: string + type: array + prune: + description: Prune specifies to delete resources from the cluster + that are no longer tracked in git + type: boolean + resources: + description: Resources describes which resources shall be part + of the sync + items: + description: SyncOperationResource contains resources to sync. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + type: array + revision: + description: Revision is the revision (Git) or chart version (Helm) + which to sync the application to If omitted, will use the revision + specified in app spec. + type: string + source: + description: Source overrides the source definition set in the + application. This is typically set in a Rollback operation and + is nil during a Sync operation + properties: + chart: + description: Chart is a Helm chart name, and must be specified + for applications sourced from a Helm repo. + type: string + directory: + description: Directory holds path/directory specific options + properties: + exclude: + description: Exclude contains a glob pattern to match + paths against that should be explicitly excluded from + being used during manifest generation + type: string + include: + description: Include contains a glob pattern to match + paths against that should be explicitly included during + manifest generation + type: string + jsonnet: + description: Jsonnet holds options specific to Jsonnet + properties: + extVars: + description: ExtVars is a list of Jsonnet External + Variables + items: + description: JsonnetVar represents a variable to + be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + description: Additional library search dirs + items: + type: string + type: array + tlas: + description: TLAS is a list of Jsonnet Top-level Arguments + items: + description: JsonnetVar represents a variable to + be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + description: Recurse specifies whether to scan a directory + recursively for manifests + type: boolean + type: object + helm: + description: Helm holds helm specific options + properties: + fileParameters: + description: FileParameters are file parameters to the + helm template + items: + description: HelmFileParameter is a file parameter that's + passed to helm template during manifest generation + properties: + name: + description: Name is the name of the Helm parameter + type: string + path: + description: Path is the path to the file containing + the values for the Helm parameter + type: string + type: object + type: array + parameters: + description: Parameters is a list of Helm parameters which + are passed to the helm template command upon manifest + generation + items: + description: HelmParameter is a parameter that's passed + to helm template during manifest generation + properties: + forceString: + description: ForceString determines whether to tell + Helm to interpret booleans and numbers as strings + type: boolean + name: + description: Name is the name of the Helm parameter + type: string + value: + description: Value is the value for the Helm parameter + type: string + type: object + type: array + releaseName: + description: ReleaseName is the Helm release name to use. + If omitted it will use the application name + type: string + valueFiles: + description: ValuesFiles is a list of Helm value files + to use when generating a template + items: + type: string + type: array + values: + description: Values specifies Helm values to be passed + to helm template, typically defined as a block + type: string + version: + description: Version is the Helm version to use for templating + (either "2" or "3") + type: string + type: object + ksonnet: + description: Ksonnet holds ksonnet specific options + properties: + environment: + description: Environment is a ksonnet application environment + name + type: string + parameters: + description: Parameters are a list of ksonnet component + parameter override values + items: + description: KsonnetParameter is a ksonnet component + parameter + properties: + component: + type: string + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + kustomize: + description: Kustomize holds kustomize specific options + properties: + commonAnnotations: + additionalProperties: + type: string + description: CommonAnnotations is a list of additional + annotations to add to rendered manifests + type: object + commonLabels: + additionalProperties: + type: string + description: CommonLabels is a list of additional labels + to add to rendered manifests + type: object + images: + description: Images is a list of Kustomize image override + specifications + items: + description: KustomizeImage represents a Kustomize image + definition in the format [old_image_name=]: + type: string + type: array + namePrefix: + description: NamePrefix is a prefix appended to resources + for Kustomize apps + type: string + nameSuffix: + description: NameSuffix is a suffix appended to resources + for Kustomize apps + type: string + version: + description: Version controls which version of Kustomize + to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git repository, + and is only valid for applications sourced from Git. + type: string + plugin: + description: ConfigManagementPlugin holds config management + plugin specific options + properties: + env: + description: Env is a list of environment variable entries + items: + description: EnvEntry represents an entry in the application's + environment + properties: + name: + description: Name is the name of the variable, usually + expressed in uppercase + type: string + value: + description: Value is the value of the variable + type: string + required: + - name + - value + type: object + type: array + name: + type: string + type: object + repoURL: + description: RepoURL is the URL to the repository (Git or + Helm) that contains the application manifests + type: string + targetRevision: + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be + commit, tag, or branch. If omitted, will equal to HEAD. + In case of Helm, this is a semver tag for the Chart's version. + type: string + required: + - repoURL + type: object + syncOptions: + description: SyncOptions provide per-sync sync-options, e.g. Validate=false + items: + type: string + type: array + syncStrategy: + description: SyncStrategy describes how to perform the sync + properties: + apply: + description: Apply will perform a `kubectl apply` to perform + the sync. + properties: + force: + description: Force indicates whether or not to supply + the --force flag to `kubectl apply`. The --force flag + deletes and re-create the resource, when PATCH encounters + conflict and has retried for 5 times. + type: boolean + type: object + hook: + description: Hook will submit any referenced resources to + perform the sync. This is the default strategy + properties: + force: + description: Force indicates whether or not to supply + the --force flag to `kubectl apply`. The --force flag + deletes and re-create the resource, when PATCH encounters + conflict and has retried for 5 times. + type: boolean + type: object + type: object + type: object + type: object + spec: + description: ApplicationSpec represents desired application state. Contains + link to repository with application definition and additional parameters + link definition revision. + properties: + destination: + description: Destination is a reference to the target Kubernetes server + and namespace + properties: + name: + description: Name is an alternate way of specifying the target + cluster by its symbolic name + type: string + namespace: + description: Namespace specifies the target namespace for the + application's resources. The namespace will only be set for + namespace-scoped resources that have not set a value for .metadata.namespace + type: string + server: + description: Server specifies the URL of the target cluster and + must be set to the Kubernetes control plane API + type: string + type: object + ignoreDifferences: + description: IgnoreDifferences is a list of resources and their fields + which should be ignored during comparison + items: + description: ResourceIgnoreDifferences contains resource filter + and list of json paths which should be ignored during comparison + with live state. + properties: + group: + type: string + jsonPointers: + items: + type: string + type: array + kind: + type: string + name: + type: string + namespace: + type: string + required: + - jsonPointers + - kind + type: object + type: array + info: + description: Info contains a list of information (URLs, email addresses, + and plain text) that relates to the application + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + description: Project is a reference to the project this application + belongs to. The empty string means that application belongs to the + 'default' project. + type: string + revisionHistoryLimit: + description: RevisionHistoryLimit limits the number of items kept + in the application's revision history, which is used for informational + purposes as well as for rollbacks to previous versions. This should + only be changed in exceptional circumstances. Setting to zero will + store no history. This will reduce storage used. Increasing will + increase the space used to store the history, so we do not recommend + increasing it. Default is 10. + format: int64 + type: integer + source: + description: Source is a reference to the location of the application's + manifests or chart + properties: + chart: + description: Chart is a Helm chart name, and must be specified + for applications sourced from a Helm repo. + type: string + directory: + description: Directory holds path/directory specific options + properties: + exclude: + description: Exclude contains a glob pattern to match paths + against that should be explicitly excluded from being used + during manifest generation + type: string + include: + description: Include contains a glob pattern to match paths + against that should be explicitly included during manifest + generation + type: string + jsonnet: + description: Jsonnet holds options specific to Jsonnet + properties: + extVars: + description: ExtVars is a list of Jsonnet External Variables + items: + description: JsonnetVar represents a variable to be + passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + description: Additional library search dirs + items: + type: string + type: array + tlas: + description: TLAS is a list of Jsonnet Top-level Arguments + items: + description: JsonnetVar represents a variable to be + passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + description: Recurse specifies whether to scan a directory + recursively for manifests + type: boolean + type: object + helm: + description: Helm holds helm specific options + properties: + fileParameters: + description: FileParameters are file parameters to the helm + template + items: + description: HelmFileParameter is a file parameter that's + passed to helm template during manifest generation + properties: + name: + description: Name is the name of the Helm parameter + type: string + path: + description: Path is the path to the file containing + the values for the Helm parameter + type: string + type: object + type: array + parameters: + description: Parameters is a list of Helm parameters which + are passed to the helm template command upon manifest generation + items: + description: HelmParameter is a parameter that's passed + to helm template during manifest generation + properties: + forceString: + description: ForceString determines whether to tell + Helm to interpret booleans and numbers as strings + type: boolean + name: + description: Name is the name of the Helm parameter + type: string + value: + description: Value is the value for the Helm parameter + type: string + type: object + type: array + releaseName: + description: ReleaseName is the Helm release name to use. + If omitted it will use the application name + type: string + valueFiles: + description: ValuesFiles is a list of Helm value files to + use when generating a template + items: + type: string + type: array + values: + description: Values specifies Helm values to be passed to + helm template, typically defined as a block + type: string + version: + description: Version is the Helm version to use for templating + (either "2" or "3") + type: string + type: object + ksonnet: + description: Ksonnet holds ksonnet specific options + properties: + environment: + description: Environment is a ksonnet application environment + name + type: string + parameters: + description: Parameters are a list of ksonnet component parameter + override values + items: + description: KsonnetParameter is a ksonnet component parameter + properties: + component: + type: string + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + kustomize: + description: Kustomize holds kustomize specific options + properties: + commonAnnotations: + additionalProperties: + type: string + description: CommonAnnotations is a list of additional annotations + to add to rendered manifests + type: object + commonLabels: + additionalProperties: + type: string + description: CommonLabels is a list of additional labels to + add to rendered manifests + type: object + images: + description: Images is a list of Kustomize image override + specifications + items: + description: KustomizeImage represents a Kustomize image + definition in the format [old_image_name=]: + type: string + type: array + namePrefix: + description: NamePrefix is a prefix appended to resources + for Kustomize apps + type: string + nameSuffix: + description: NameSuffix is a suffix appended to resources + for Kustomize apps + type: string + version: + description: Version controls which version of Kustomize to + use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git repository, + and is only valid for applications sourced from Git. + type: string + plugin: + description: ConfigManagementPlugin holds config management plugin + specific options + properties: + env: + description: Env is a list of environment variable entries + items: + description: EnvEntry represents an entry in the application's + environment + properties: + name: + description: Name is the name of the variable, usually + expressed in uppercase + type: string + value: + description: Value is the value of the variable + type: string + required: + - name + - value + type: object + type: array + name: + type: string + type: object + repoURL: + description: RepoURL is the URL to the repository (Git or Helm) + that contains the application manifests + type: string + targetRevision: + description: TargetRevision defines the revision of the source + to sync the application to. In case of Git, this can be commit, + tag, or branch. If omitted, will equal to HEAD. In case of Helm, + this is a semver tag for the Chart's version. + type: string + required: + - repoURL + type: object + syncPolicy: + description: SyncPolicy controls when and how a sync will be performed + properties: + automated: + description: Automated will keep an application synced to the + target revision + properties: + allowEmpty: + description: 'AllowEmpty allows apps have zero live resources + (default: false)' + type: boolean + prune: + description: 'Prune specifies whether to delete resources + from the cluster that are not found in the sources anymore + as part of automated sync (default: false)' + type: boolean + selfHeal: + description: 'SelfHeal specifes whether to revert resources + back to their desired state upon modification in the cluster + (default: false)' + type: boolean + type: object + retry: + description: Retry controls failed sync retry behavior + properties: + backoff: + description: Backoff controls how to backoff on subsequent + retries of failed syncs + properties: + duration: + description: Duration is the amount to back off. Default + unit is seconds, but could also be a duration (e.g. + "2m", "1h") + type: string + factor: + description: Factor is a factor to multiply the base duration + after each failed retry + format: int64 + type: integer + maxDuration: + description: MaxDuration is the maximum amount of time + allowed for the backoff strategy + type: string + type: object + limit: + description: Limit is the maximum number of attempts for retrying + a failed sync. If set to 0, no retries will be performed. + format: int64 + type: integer + type: object + syncOptions: + description: Options allow you to specify whole app sync-options + items: + type: string + type: array + type: object + required: + - destination + - project + - source + type: object + status: + description: ApplicationStatus contains status information for the application + properties: + conditions: + description: Conditions is a list of currently observed application + conditions + items: + description: ApplicationCondition contains details about an application + condition, which is usally an error or warning + properties: + lastTransitionTime: + description: LastTransitionTime is the time the condition was + last observed + format: date-time + type: string + message: + description: Message contains human-readable message indicating + details about condition + type: string + type: + description: Type is an application condition type + type: string + required: + - message + - type + type: object + type: array + health: + description: Health contains information about the application's current + health status + properties: + message: + description: Message is a human-readable informational message + describing the health status + type: string + status: + description: Status holds the status code of the application or + resource + type: string + type: object + history: + description: History contains information about the application's + sync history + items: + description: RevisionHistory contains history information about + a previous sync + properties: + deployStartedAt: + description: DeployStartedAt holds the time the sync operation + started + format: date-time + type: string + deployedAt: + description: DeployedAt holds the time the sync operation completed + format: date-time + type: string + id: + description: ID is an auto incrementing identifier of the RevisionHistory + format: int64 + type: integer + revision: + description: Revision holds the revision the sync was performed + against + type: string + source: + description: Source is a reference to the application source + used for the sync operation + properties: + chart: + description: Chart is a Helm chart name, and must be specified + for applications sourced from a Helm repo. + type: string + directory: + description: Directory holds path/directory specific options + properties: + exclude: + description: Exclude contains a glob pattern to match + paths against that should be explicitly excluded from + being used during manifest generation + type: string + include: + description: Include contains a glob pattern to match + paths against that should be explicitly included during + manifest generation + type: string + jsonnet: + description: Jsonnet holds options specific to Jsonnet + properties: + extVars: + description: ExtVars is a list of Jsonnet External + Variables + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + description: Additional library search dirs + items: + type: string + type: array + tlas: + description: TLAS is a list of Jsonnet Top-level + Arguments + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + description: Recurse specifies whether to scan a directory + recursively for manifests + type: boolean + type: object + helm: + description: Helm holds helm specific options + properties: + fileParameters: + description: FileParameters are file parameters to the + helm template + items: + description: HelmFileParameter is a file parameter + that's passed to helm template during manifest generation + properties: + name: + description: Name is the name of the Helm parameter + type: string + path: + description: Path is the path to the file containing + the values for the Helm parameter + type: string + type: object + type: array + parameters: + description: Parameters is a list of Helm parameters + which are passed to the helm template command upon + manifest generation + items: + description: HelmParameter is a parameter that's passed + to helm template during manifest generation + properties: + forceString: + description: ForceString determines whether to + tell Helm to interpret booleans and numbers + as strings + type: boolean + name: + description: Name is the name of the Helm parameter + type: string + value: + description: Value is the value for the Helm parameter + type: string + type: object + type: array + releaseName: + description: ReleaseName is the Helm release name to + use. If omitted it will use the application name + type: string + valueFiles: + description: ValuesFiles is a list of Helm value files + to use when generating a template + items: + type: string + type: array + values: + description: Values specifies Helm values to be passed + to helm template, typically defined as a block + type: string + version: + description: Version is the Helm version to use for + templating (either "2" or "3") + type: string + type: object + ksonnet: + description: Ksonnet holds ksonnet specific options + properties: + environment: + description: Environment is a ksonnet application environment + name + type: string + parameters: + description: Parameters are a list of ksonnet component + parameter override values + items: + description: KsonnetParameter is a ksonnet component + parameter + properties: + component: + type: string + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + kustomize: + description: Kustomize holds kustomize specific options + properties: + commonAnnotations: + additionalProperties: + type: string + description: CommonAnnotations is a list of additional + annotations to add to rendered manifests + type: object + commonLabels: + additionalProperties: + type: string + description: CommonLabels is a list of additional labels + to add to rendered manifests + type: object + images: + description: Images is a list of Kustomize image override + specifications + items: + description: KustomizeImage represents a Kustomize + image definition in the format [old_image_name=]: + type: string + type: array + namePrefix: + description: NamePrefix is a prefix appended to resources + for Kustomize apps + type: string + nameSuffix: + description: NameSuffix is a suffix appended to resources + for Kustomize apps + type: string + version: + description: Version controls which version of Kustomize + to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git repository, + and is only valid for applications sourced from Git. + type: string + plugin: + description: ConfigManagementPlugin holds config management + plugin specific options + properties: + env: + description: Env is a list of environment variable entries + items: + description: EnvEntry represents an entry in the application's + environment + properties: + name: + description: Name is the name of the variable, + usually expressed in uppercase + type: string + value: + description: Value is the value of the variable + type: string + required: + - name + - value + type: object + type: array + name: + type: string + type: object + repoURL: + description: RepoURL is the URL to the repository (Git or + Helm) that contains the application manifests + type: string + targetRevision: + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. + type: string + required: + - repoURL + type: object + required: + - deployedAt + - id + - revision + type: object + type: array + observedAt: + description: 'ObservedAt indicates when the application state was + updated without querying latest git state Deprecated: controller + no longer updates ObservedAt field' + format: date-time + type: string + operationState: + description: OperationState contains information about any ongoing + operations, such as a sync + properties: + finishedAt: + description: FinishedAt contains time of operation completion + format: date-time + type: string + message: + description: Message holds any pertinent messages when attempting + to perform operation (typically errors). + type: string + operation: + description: Operation is the original requested operation + properties: + info: + description: Info is a list of informational items for this + operation + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + initiatedBy: + description: InitiatedBy contains information about who initiated + the operations + properties: + automated: + description: Automated is set to true if operation was + initiated automatically by the application controller. + type: boolean + username: + description: Username contains the name of a user who + started operation + type: string + type: object + retry: + description: Retry controls the strategy to apply if a sync + fails + properties: + backoff: + description: Backoff controls how to backoff on subsequent + retries of failed syncs + properties: + duration: + description: Duration is the amount to back off. Default + unit is seconds, but could also be a duration (e.g. + "2m", "1h") + type: string + factor: + description: Factor is a factor to multiply the base + duration after each failed retry + format: int64 + type: integer + maxDuration: + description: MaxDuration is the maximum amount of + time allowed for the backoff strategy + type: string + type: object + limit: + description: Limit is the maximum number of attempts for + retrying a failed sync. If set to 0, no retries will + be performed. + format: int64 + type: integer + type: object + sync: + description: Sync contains parameters for the operation + properties: + dryRun: + description: DryRun specifies to perform a `kubectl apply + --dry-run` without actually performing the sync + type: boolean + manifests: + description: Manifests is an optional field that overrides + sync source with a local directory for development + items: + type: string + type: array + prune: + description: Prune specifies to delete resources from + the cluster that are no longer tracked in git + type: boolean + resources: + description: Resources describes which resources shall + be part of the sync + items: + description: SyncOperationResource contains resources + to sync. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + type: array + revision: + description: Revision is the revision (Git) or chart version + (Helm) which to sync the application to If omitted, + will use the revision specified in app spec. + type: string + source: + description: Source overrides the source definition set + in the application. This is typically set in a Rollback + operation and is nil during a Sync operation + properties: + chart: + description: Chart is a Helm chart name, and must + be specified for applications sourced from a Helm + repo. + type: string + directory: + description: Directory holds path/directory specific + options + properties: + exclude: + description: Exclude contains a glob pattern to + match paths against that should be explicitly + excluded from being used during manifest generation + type: string + include: + description: Include contains a glob pattern to + match paths against that should be explicitly + included during manifest generation + type: string + jsonnet: + description: Jsonnet holds options specific to + Jsonnet + properties: + extVars: + description: ExtVars is a list of Jsonnet + External Variables + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest + generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + description: Additional library search dirs + items: + type: string + type: array + tlas: + description: TLAS is a list of Jsonnet Top-level + Arguments + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest + generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + description: Recurse specifies whether to scan + a directory recursively for manifests + type: boolean + type: object + helm: + description: Helm holds helm specific options + properties: + fileParameters: + description: FileParameters are file parameters + to the helm template + items: + description: HelmFileParameter is a file parameter + that's passed to helm template during manifest + generation + properties: + name: + description: Name is the name of the Helm + parameter + type: string + path: + description: Path is the path to the file + containing the values for the Helm parameter + type: string + type: object + type: array + parameters: + description: Parameters is a list of Helm parameters + which are passed to the helm template command + upon manifest generation + items: + description: HelmParameter is a parameter that's + passed to helm template during manifest generation + properties: + forceString: + description: ForceString determines whether + to tell Helm to interpret booleans and + numbers as strings + type: boolean + name: + description: Name is the name of the Helm + parameter + type: string + value: + description: Value is the value for the + Helm parameter + type: string + type: object + type: array + releaseName: + description: ReleaseName is the Helm release name + to use. If omitted it will use the application + name + type: string + valueFiles: + description: ValuesFiles is a list of Helm value + files to use when generating a template + items: + type: string + type: array + values: + description: Values specifies Helm values to be + passed to helm template, typically defined as + a block + type: string + version: + description: Version is the Helm version to use + for templating (either "2" or "3") + type: string + type: object + ksonnet: + description: Ksonnet holds ksonnet specific options + properties: + environment: + description: Environment is a ksonnet application + environment name + type: string + parameters: + description: Parameters are a list of ksonnet + component parameter override values + items: + description: KsonnetParameter is a ksonnet component + parameter + properties: + component: + type: string + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + kustomize: + description: Kustomize holds kustomize specific options + properties: + commonAnnotations: + additionalProperties: + type: string + description: CommonAnnotations is a list of additional + annotations to add to rendered manifests + type: object + commonLabels: + additionalProperties: + type: string + description: CommonLabels is a list of additional + labels to add to rendered manifests + type: object + images: + description: Images is a list of Kustomize image + override specifications + items: + description: KustomizeImage represents a Kustomize + image definition in the format [old_image_name=]: + type: string + type: array + namePrefix: + description: NamePrefix is a prefix appended to + resources for Kustomize apps + type: string + nameSuffix: + description: NameSuffix is a suffix appended to + resources for Kustomize apps + type: string + version: + description: Version controls which version of + Kustomize to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git + repository, and is only valid for applications sourced + from Git. + type: string + plugin: + description: ConfigManagementPlugin holds config management + plugin specific options + properties: + env: + description: Env is a list of environment variable + entries + items: + description: EnvEntry represents an entry in + the application's environment + properties: + name: + description: Name is the name of the variable, + usually expressed in uppercase + type: string + value: + description: Value is the value of the variable + type: string + required: + - name + - value + type: object + type: array + name: + type: string + type: object + repoURL: + description: RepoURL is the URL to the repository + (Git or Helm) that contains the application manifests + type: string + targetRevision: + description: TargetRevision defines the revision of + the source to sync the application to. In case of + Git, this can be commit, tag, or branch. If omitted, + will equal to HEAD. In case of Helm, this is a semver + tag for the Chart's version. + type: string + required: + - repoURL + type: object + syncOptions: + description: SyncOptions provide per-sync sync-options, + e.g. Validate=false + items: + type: string + type: array + syncStrategy: + description: SyncStrategy describes how to perform the + sync + properties: + apply: + description: Apply will perform a `kubectl apply` + to perform the sync. + properties: + force: + description: Force indicates whether or not to + supply the --force flag to `kubectl apply`. + The --force flag deletes and re-create the resource, + when PATCH encounters conflict and has retried + for 5 times. + type: boolean + type: object + hook: + description: Hook will submit any referenced resources + to perform the sync. This is the default strategy + properties: + force: + description: Force indicates whether or not to + supply the --force flag to `kubectl apply`. + The --force flag deletes and re-create the resource, + when PATCH encounters conflict and has retried + for 5 times. + type: boolean + type: object + type: object + type: object + type: object + phase: + description: Phase is the current phase of the operation + type: string + retryCount: + description: RetryCount contains time of operation retries + format: int64 + type: integer + startedAt: + description: StartedAt contains time of operation start + format: date-time + type: string + syncResult: + description: SyncResult is the result of a Sync operation + properties: + resources: + description: Resources contains a list of sync result items + for each individual resource in a sync operation + items: + description: ResourceResult holds the operation result details + of a specific resource + properties: + group: + description: Group specifies the API group of the resource + type: string + hookPhase: + description: HookPhase contains the state of any operation + associated with this resource OR hook This can also + contain values for non-hook resources. + type: string + hookType: + description: HookType specifies the type of the hook. + Empty for non-hook resources + type: string + kind: + description: Kind specifies the API kind of the resource + type: string + message: + description: Message contains an informational or error + message for the last sync OR operation + type: string + name: + description: Name specifies the name of the resource + type: string + namespace: + description: Namespace specifies the target namespace + of the resource + type: string + status: + description: Status holds the final result of the sync. + Will be empty if the resources is yet to be applied/pruned + and is always zero-value for hooks + type: string + syncPhase: + description: SyncPhase indicates the particular phase + of the sync that this result was acquired in + type: string + version: + description: Version specifies the API version of the + resource + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + type: array + revision: + description: Revision holds the revision this sync operation + was performed to + type: string + source: + description: Source records the application source information + of the sync, used for comparing auto-sync + properties: + chart: + description: Chart is a Helm chart name, and must be specified + for applications sourced from a Helm repo. + type: string + directory: + description: Directory holds path/directory specific options + properties: + exclude: + description: Exclude contains a glob pattern to match + paths against that should be explicitly excluded + from being used during manifest generation + type: string + include: + description: Include contains a glob pattern to match + paths against that should be explicitly included + during manifest generation + type: string + jsonnet: + description: Jsonnet holds options specific to Jsonnet + properties: + extVars: + description: ExtVars is a list of Jsonnet External + Variables + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + description: Additional library search dirs + items: + type: string + type: array + tlas: + description: TLAS is a list of Jsonnet Top-level + Arguments + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + description: Recurse specifies whether to scan a directory + recursively for manifests + type: boolean + type: object + helm: + description: Helm holds helm specific options + properties: + fileParameters: + description: FileParameters are file parameters to + the helm template + items: + description: HelmFileParameter is a file parameter + that's passed to helm template during manifest + generation + properties: + name: + description: Name is the name of the Helm parameter + type: string + path: + description: Path is the path to the file containing + the values for the Helm parameter + type: string + type: object + type: array + parameters: + description: Parameters is a list of Helm parameters + which are passed to the helm template command upon + manifest generation + items: + description: HelmParameter is a parameter that's + passed to helm template during manifest generation + properties: + forceString: + description: ForceString determines whether + to tell Helm to interpret booleans and numbers + as strings + type: boolean + name: + description: Name is the name of the Helm parameter + type: string + value: + description: Value is the value for the Helm + parameter + type: string + type: object + type: array + releaseName: + description: ReleaseName is the Helm release name + to use. If omitted it will use the application name + type: string + valueFiles: + description: ValuesFiles is a list of Helm value files + to use when generating a template + items: + type: string + type: array + values: + description: Values specifies Helm values to be passed + to helm template, typically defined as a block + type: string + version: + description: Version is the Helm version to use for + templating (either "2" or "3") + type: string + type: object + ksonnet: + description: Ksonnet holds ksonnet specific options + properties: + environment: + description: Environment is a ksonnet application + environment name + type: string + parameters: + description: Parameters are a list of ksonnet component + parameter override values + items: + description: KsonnetParameter is a ksonnet component + parameter + properties: + component: + type: string + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + kustomize: + description: Kustomize holds kustomize specific options + properties: + commonAnnotations: + additionalProperties: + type: string + description: CommonAnnotations is a list of additional + annotations to add to rendered manifests + type: object + commonLabels: + additionalProperties: + type: string + description: CommonLabels is a list of additional + labels to add to rendered manifests + type: object + images: + description: Images is a list of Kustomize image override + specifications + items: + description: KustomizeImage represents a Kustomize + image definition in the format [old_image_name=]: + type: string + type: array + namePrefix: + description: NamePrefix is a prefix appended to resources + for Kustomize apps + type: string + nameSuffix: + description: NameSuffix is a suffix appended to resources + for Kustomize apps + type: string + version: + description: Version controls which version of Kustomize + to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git repository, + and is only valid for applications sourced from Git. + type: string + plugin: + description: ConfigManagementPlugin holds config management + plugin specific options + properties: + env: + description: Env is a list of environment variable + entries + items: + description: EnvEntry represents an entry in the + application's environment + properties: + name: + description: Name is the name of the variable, + usually expressed in uppercase + type: string + value: + description: Value is the value of the variable + type: string + required: + - name + - value + type: object + type: array + name: + type: string + type: object + repoURL: + description: RepoURL is the URL to the repository (Git + or Helm) that contains the application manifests + type: string + targetRevision: + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. + type: string + required: + - repoURL + type: object + required: + - revision + type: object + required: + - operation + - phase + - startedAt + type: object + reconciledAt: + description: ReconciledAt indicates when the application state was + reconciled using the latest git version + format: date-time + type: string + resources: + description: Resources is a list of Kubernetes resources managed by + this application + items: + description: 'ResourceStatus holds the current sync and health status + of a resource TODO: describe members of this type' + properties: + group: + type: string + health: + description: HealthStatus contains information about the currently + observed health state of an application or resource + properties: + message: + description: Message is a human-readable informational message + describing the health status + type: string + status: + description: Status holds the status code of the application + or resource + type: string + type: object + hook: + type: boolean + kind: + type: string + name: + type: string + namespace: + type: string + requiresPruning: + type: boolean + status: + description: SyncStatusCode is a type which represents possible + comparison results + type: string + version: + type: string + type: object + type: array + sourceType: + description: SourceType specifies the type of this application + type: string + summary: + description: Summary contains a list of URLs and container images + used by this application + properties: + externalURLs: + description: ExternalURLs holds all external URLs of application + child resources. + items: + type: string + type: array + images: + description: Images holds all images of application child resources. + items: + type: string + type: array + type: object + sync: + description: Sync contains information about the application's current + sync status + properties: + comparedTo: + description: ComparedTo contains information about what has been + compared + properties: + destination: + description: Destination is a reference to the application's + destination used for comparison + properties: + name: + description: Name is an alternate way of specifying the + target cluster by its symbolic name + type: string + namespace: + description: Namespace specifies the target namespace + for the application's resources. The namespace will + only be set for namespace-scoped resources that have + not set a value for .metadata.namespace + type: string + server: + description: Server specifies the URL of the target cluster + and must be set to the Kubernetes control plane API + type: string + type: object + source: + description: Source is a reference to the application's source + used for comparison + properties: + chart: + description: Chart is a Helm chart name, and must be specified + for applications sourced from a Helm repo. + type: string + directory: + description: Directory holds path/directory specific options + properties: + exclude: + description: Exclude contains a glob pattern to match + paths against that should be explicitly excluded + from being used during manifest generation + type: string + include: + description: Include contains a glob pattern to match + paths against that should be explicitly included + during manifest generation + type: string + jsonnet: + description: Jsonnet holds options specific to Jsonnet + properties: + extVars: + description: ExtVars is a list of Jsonnet External + Variables + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + description: Additional library search dirs + items: + type: string + type: array + tlas: + description: TLAS is a list of Jsonnet Top-level + Arguments + items: + description: JsonnetVar represents a variable + to be passed to jsonnet during manifest generation + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + description: Recurse specifies whether to scan a directory + recursively for manifests + type: boolean + type: object + helm: + description: Helm holds helm specific options + properties: + fileParameters: + description: FileParameters are file parameters to + the helm template + items: + description: HelmFileParameter is a file parameter + that's passed to helm template during manifest + generation + properties: + name: + description: Name is the name of the Helm parameter + type: string + path: + description: Path is the path to the file containing + the values for the Helm parameter + type: string + type: object + type: array + parameters: + description: Parameters is a list of Helm parameters + which are passed to the helm template command upon + manifest generation + items: + description: HelmParameter is a parameter that's + passed to helm template during manifest generation + properties: + forceString: + description: ForceString determines whether + to tell Helm to interpret booleans and numbers + as strings + type: boolean + name: + description: Name is the name of the Helm parameter + type: string + value: + description: Value is the value for the Helm + parameter + type: string + type: object + type: array + releaseName: + description: ReleaseName is the Helm release name + to use. If omitted it will use the application name + type: string + valueFiles: + description: ValuesFiles is a list of Helm value files + to use when generating a template + items: + type: string + type: array + values: + description: Values specifies Helm values to be passed + to helm template, typically defined as a block + type: string + version: + description: Version is the Helm version to use for + templating (either "2" or "3") + type: string + type: object + ksonnet: + description: Ksonnet holds ksonnet specific options + properties: + environment: + description: Environment is a ksonnet application + environment name + type: string + parameters: + description: Parameters are a list of ksonnet component + parameter override values + items: + description: KsonnetParameter is a ksonnet component + parameter + properties: + component: + type: string + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + kustomize: + description: Kustomize holds kustomize specific options + properties: + commonAnnotations: + additionalProperties: + type: string + description: CommonAnnotations is a list of additional + annotations to add to rendered manifests + type: object + commonLabels: + additionalProperties: + type: string + description: CommonLabels is a list of additional + labels to add to rendered manifests + type: object + images: + description: Images is a list of Kustomize image override + specifications + items: + description: KustomizeImage represents a Kustomize + image definition in the format [old_image_name=]: + type: string + type: array + namePrefix: + description: NamePrefix is a prefix appended to resources + for Kustomize apps + type: string + nameSuffix: + description: NameSuffix is a suffix appended to resources + for Kustomize apps + type: string + version: + description: Version controls which version of Kustomize + to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git repository, + and is only valid for applications sourced from Git. + type: string + plugin: + description: ConfigManagementPlugin holds config management + plugin specific options + properties: + env: + description: Env is a list of environment variable + entries + items: + description: EnvEntry represents an entry in the + application's environment + properties: + name: + description: Name is the name of the variable, + usually expressed in uppercase + type: string + value: + description: Value is the value of the variable + type: string + required: + - name + - value + type: object + type: array + name: + type: string + type: object + repoURL: + description: RepoURL is the URL to the repository (Git + or Helm) that contains the application manifests + type: string + targetRevision: + description: TargetRevision defines the revision of the + source to sync the application to. In case of Git, this + can be commit, tag, or branch. If omitted, will equal + to HEAD. In case of Helm, this is a semver tag for the + Chart's version. + type: string + required: + - repoURL + type: object + required: + - destination + - source + type: object + revision: + description: Revision contains information about the revision + the comparison has been performed to + type: string + status: + description: Status is the sync state of the comparison + type: string + required: + - status + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true + subresources: {} diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml new file mode 100644 index 00000000..2ecaa257 --- /dev/null +++ b/install/templates/argocd/application.yaml @@ -0,0 +1,33 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: "manuela-{{ .Values.global.deploy.site }}" + namespace: openshift-gitops + labels: + app.kubernetes.io/instance: manuela +spec: + destination: + name: in-cluster + namespace: openshift-gitops + project: default + source: + repoURL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/blueprints.git + path: applications/bootstrap + targetRevision: {{ .Values.global.git.ops_revision }} + helm: + releaseName: blueprint-core + parameters: + - name: global.git.provider + value: "{{ .Values.global.git.provider }}" + - name: global.git.account + value: "{{ .Values.global.git.account }}" + - name: global.git.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.git.ops_revision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION +{{- if eq .Values.gitops.syncPolicy "Automatic" }} + syncPolicy: + automated: + prune: false + selfHeal: false +{{- end }} diff --git a/install/templates/argocd/namespace.yaml b/install/templates/argocd/namespace.yaml new file mode 100644 index 00000000..ee8a78bd --- /dev/null +++ b/install/templates/argocd/namespace.yaml @@ -0,0 +1,7 @@ +# Pre-create so we can create our argo app for keeping subscriptions in sync +# Do it here so that we don't try to sync it in the future +apiVersion: v1 +kind: Namespace +metadata: + name: openshift-gitops + diff --git a/install/templates/argocd/subscription.yaml b/install/templates/argocd/subscription.yaml new file mode 100644 index 00000000..b49a7bab --- /dev/null +++ b/install/templates/argocd/subscription.yaml @@ -0,0 +1,19 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: "" +spec: + channel: {{ .Values.subscriptions.version.gitops.channel }} + installPlanApproval: {{ .Values.subscriptions.installPlanApproval }} + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace +{{- if eq .Values.subscriptions.useCSV "True" }} + startingCSV: openshift-gitops-operator.{{ .Values.subscriptions.version.gitops.csv }} +{{- end }} + + + diff --git a/install/templates/secrets/github-secret.yaml b/install/templates/secrets/github-secret.yaml new file mode 100644 index 00000000..14557e9f --- /dev/null +++ b/install/templates/secrets/github-secret.yaml @@ -0,0 +1,12 @@ +kind: Secret +apiVersion: v1 +metadata: + name: github + namespace: manuela-ci +type: Opaque +data: + # Go to: https://github.com/settings/tokens + # Then: echo -n 'your string value' | base64 + token: {{ .Values.secrets.git.authToken }} + user: {{ .Values.secrets.git.accountToken }} + diff --git a/install/templates/secrets/namespace.yaml b/install/templates/secrets/namespace.yaml new file mode 100644 index 00000000..54c06793 --- /dev/null +++ b/install/templates/secrets/namespace.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: manuela-ci + labels: + manuela-role: pipeline + app.kubernetes.io/instance: manuela + #argocd.argoproj.io/managed-by: openshift-gitops +spec: + finalizers: + - kubernetes diff --git a/install/templates/secrets/quay-secret.yaml b/install/templates/secrets/quay-secret.yaml new file mode 100644 index 00000000..952d4566 --- /dev/null +++ b/install/templates/secrets/quay-secret.yaml @@ -0,0 +1,19 @@ +kind: Secret +apiVersion: v1 +metadata: + name: quay-build-secret + namespace: manuela-ci +type: kubernetes.io/dockerconfigjson +data: + # Quay -> Robot Accounts -> Kubernetes Secret -> View + # .dockerconfigjson is base64 encoded, conforming to... + #{ + # "auths": { + # "quay.io": { + # "auth": "", + # "email": "" + # } + # } + #} + # auths["quay.io"]["auth"] is a base64 encoded string of IMAGE_REGISTRY_USER:IMAGE_REGISTRY_PASSWORD + .dockerconfigjson: {{ .Values.secrets.quay.authToken }} diff --git a/install/values.yaml b/install/values.yaml new file mode 100644 index 00000000..653bae8d --- /dev/null +++ b/install/values.yaml @@ -0,0 +1,31 @@ +global: + git: + provider: github.com + account: PLAINTEXT + ops_revision: main + +gitops: + syncPolicy: Automatic + +subscriptions: + installPlanApproval: Automatic + useCSV: "False" + version: + gitops: + channel: stable + csv: v1.2.0 + source: redhat-operators + syncPolicy: Automatic + +secrets: + # NEVER COMMIT THESE VALUES TO GIT + enabled: false + quay: + # Quay -> Robot Accounts -> Kubernetes Secret -> View + authToken: BASE64STRING + + git: + # Go to: https://github.com/settings/tokens + # Then: echo -n 'your string value' | base64 + authToken: BASE64STRING + accountToken: BASE64STRING From 99def8d2d30cab9713d7556c92f6edf84fcc1bdc Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 11:16:31 +1000 Subject: [PATCH 0037/1263] Clean up and minimalize helm template variables --- install/templates/argocd/application.yaml | 16 +++++-------- install/templates/argocd/subscription.yaml | 8 +++---- install/values.yaml | 27 ++++++++++------------ 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 2ecaa257..2aafa57a 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -1,31 +1,27 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: "manuela-{{ .Values.global.deploy.site }}" + name: {{ .Release.Name }} namespace: openshift-gitops labels: - app.kubernetes.io/instance: manuela + app.kubernetes.io/instance: {{ .Release.Name }} spec: destination: name: in-cluster namespace: openshift-gitops project: default source: - repoURL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/blueprints.git + repoURL: {{ .Values.main.git.repoURL }} + targetRevision: {{ .Values.main.git.revision }} path: applications/bootstrap - targetRevision: {{ .Values.global.git.ops_revision }} helm: - releaseName: blueprint-core + releaseName: {{ .Release.Name }}-core parameters: - - name: global.git.provider - value: "{{ .Values.global.git.provider }}" - - name: global.git.account - value: "{{ .Values.global.git.account }}" - name: global.git.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL - name: global.git.ops_revision value: $ARGOCD_APP_SOURCE_TARGET_REVISION -{{- if eq .Values.gitops.syncPolicy "Automatic" }} +{{- if eq .Values.main.options.syncPolicy "Automatic" }} syncPolicy: automated: prune: false diff --git a/install/templates/argocd/subscription.yaml b/install/templates/argocd/subscription.yaml index b49a7bab..98545b5a 100644 --- a/install/templates/argocd/subscription.yaml +++ b/install/templates/argocd/subscription.yaml @@ -6,13 +6,13 @@ metadata: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: "" spec: - channel: {{ .Values.subscriptions.version.gitops.channel }} - installPlanApproval: {{ .Values.subscriptions.installPlanApproval }} + channel: {{ .Values.main.gitops.channel }} + installPlanApproval: {{ .Values.main.options.installPlanApproval }} name: openshift-gitops-operator source: redhat-operators sourceNamespace: openshift-marketplace -{{- if eq .Values.subscriptions.useCSV "True" }} - startingCSV: openshift-gitops-operator.{{ .Values.subscriptions.version.gitops.csv }} +{{- if eq .Values.main.options.useCSV "True" }} + startingCSV: openshift-gitops-operator.{{ .Values.main.gitops.csv }} {{- end }} diff --git a/install/values.yaml b/install/values.yaml index 653bae8d..0bf0cdf1 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -1,21 +1,18 @@ -global: +main: git: - provider: github.com - account: PLAINTEXT - ops_revision: main + repoURL: git@github.com:dagger-refuse-cool/gitops.git + revision: main -gitops: - syncPolicy: Automatic + options: + syncPolicy: Automatic + installPlanApproval: Automatic + useCSV: "False" -subscriptions: - installPlanApproval: Automatic - useCSV: "False" - version: - gitops: - channel: stable - csv: v1.2.0 - source: redhat-operators - syncPolicy: Automatic + gitops: + channel: stable + csv: v1.2.0 + source: redhat-operators + syncPolicy: Automatic secrets: # NEVER COMMIT THESE VALUES TO GIT From 92b9d924c7715c7b1737ba2dcc7e4c3743a3ffa8 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 15:18:32 +1000 Subject: [PATCH 0038/1263] Simplify the argo application, and see if .argocd-source.yaml gets applied --- install/templates/argocd/application.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 2aafa57a..b7e0abaa 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -14,13 +14,6 @@ spec: repoURL: {{ .Values.main.git.repoURL }} targetRevision: {{ .Values.main.git.revision }} path: applications/bootstrap - helm: - releaseName: {{ .Release.Name }}-core - parameters: - - name: global.git.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.git.ops_revision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION {{- if eq .Values.main.options.syncPolicy "Automatic" }} syncPolicy: automated: From 273e054a26a53de0f8eba11c3065d8a3f1ace7f3 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 15:22:06 +1000 Subject: [PATCH 0039/1263] Drop the instance label so argo doesn't try to manage itself --- install/templates/argocd/application.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index b7e0abaa..ab979df1 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -3,8 +3,6 @@ kind: Application metadata: name: {{ .Release.Name }} namespace: openshift-gitops - labels: - app.kubernetes.io/instance: {{ .Release.Name }} spec: destination: name: in-cluster From 7fa88ecd53d38290a33e7d475287d496a92ff57d Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 20:36:19 +1000 Subject: [PATCH 0040/1263] Pass the repo and revision to the bootstrap helm chart --- install/templates/argocd/application.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index ab979df1..d9f9597b 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -12,6 +12,12 @@ spec: repoURL: {{ .Values.main.git.repoURL }} targetRevision: {{ .Values.main.git.revision }} path: applications/bootstrap + helm: + parameters: + - name: golbal.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: golbal.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION {{- if eq .Values.main.options.syncPolicy "Automatic" }} syncPolicy: automated: From 220c5b0ab80d992404e1d29f9aa181fb407eb5c7 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 20:49:24 +1000 Subject: [PATCH 0041/1263] Fix typo and enable self healing --- install/templates/argocd/application.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index d9f9597b..4daae73c 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -14,13 +14,13 @@ spec: path: applications/bootstrap helm: parameters: - - name: golbal.repoURL + - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL - - name: golbal.targetRevision + - name: global.targetRevision value: $ARGOCD_APP_SOURCE_TARGET_REVISION {{- if eq .Values.main.options.syncPolicy "Automatic" }} syncPolicy: automated: prune: false - selfHeal: false + selfHeal: true {{- end }} From c2ceac649b1ba85b717a9281ac8cff8bad8e4820 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 13 Sep 2021 20:57:44 +1000 Subject: [PATCH 0042/1263] Argo seems to be ignoring .argocd-sources.yaml, try a different approach for now --- install/templates/argocd/application.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 4daae73c..64933aac 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -13,6 +13,8 @@ spec: targetRevision: {{ .Values.main.git.revision }} path: applications/bootstrap helm: + valueFiles: + - ../../values-blueprint.yaml parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL From 1b9ae76aec3e22041556b1b49eb5aa35c9348522 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 13:59:50 +1000 Subject: [PATCH 0043/1263] Accept a full URL to the values file and pass to child apps --- install/templates/argocd/application.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 64933aac..a72a165a 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -1,3 +1,5 @@ +{{- $valuesURL := cat .Values.main.git.repoURL "/raw/" .Values.main.git.revision "/values-blueprint.yaml" -}} +{{- $valuesURLFixed := $valuesURL | replace " " "" | replace ".git" "" }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -14,12 +16,15 @@ spec: path: applications/bootstrap helm: valueFiles: - - ../../values-blueprint.yaml + - {{ coalesce .Values.main.git.valuesURL $valuesURLFixed }} + #- ../../values-blueprint.yaml parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL - name: global.targetRevision value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.valuesURL + value: {{ coalesce .Values.main.git.valuesURL $valuesURLFixed }} {{- if eq .Values.main.options.syncPolicy "Automatic" }} syncPolicy: automated: From 917aaf7c8234394b2be328e120910a930896fa12 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 21:34:56 +1000 Subject: [PATCH 0044/1263] Use a valuesFile directory to allow multiple levels of configuration --- install/templates/argocd/application.yaml | 13 ++++++++----- install/values.yaml | 6 ++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index a72a165a..626126bc 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -1,5 +1,5 @@ -{{- $valuesURL := cat .Values.main.git.repoURL "/raw/" .Values.main.git.revision "/values-blueprint.yaml" -}} -{{- $valuesURLFixed := $valuesURL | replace " " "" | replace ".git" "" }} +{{- $valuesDirectoryURL := cat .Values.main.git.repoURL "/raw/" .Values.main.git.revision -}} +{{- $valuesDirectoryURLFixed := $valuesDirectoryURL | replace " " "" | replace ".git" "" }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -16,15 +16,18 @@ spec: path: applications/bootstrap helm: valueFiles: - - {{ coalesce .Values.main.git.valuesURL $valuesURLFixed }} + - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-global.yaml" + - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-{{ .Values.main.siteName }}.yaml" + - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-{{ .Release.Name }}.yaml" #- ../../values-blueprint.yaml + # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL - name: global.targetRevision value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.valuesURL - value: {{ coalesce .Values.main.git.valuesURL $valuesURLFixed }} + - name: global.valuesDirectoryURL + value: {{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }} {{- if eq .Values.main.options.syncPolicy "Automatic" }} syncPolicy: automated: diff --git a/install/values.yaml b/install/values.yaml index 0bf0cdf1..932e0d74 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -2,6 +2,7 @@ main: git: repoURL: git@github.com:dagger-refuse-cool/gitops.git revision: main + #valuesDirectoryURL: https://github.com/dagger-refuse-cool/edge-gitops/raw/main/ options: syncPolicy: Automatic @@ -10,9 +11,10 @@ main: gitops: channel: stable - csv: v1.2.0 source: redhat-operators - syncPolicy: Automatic + csv: v1.2.0 + + siteName: datacenter secrets: # NEVER COMMIT THESE VALUES TO GIT From e57a06c2f4bf399a89c90131c1621d80cfcf4041 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 14 Sep 2021 22:55:24 +1000 Subject: [PATCH 0045/1263] Not too many valueFiles, argo borks on anything thats missing --- install/templates/argocd/application.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 626126bc..a47ed165 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -18,8 +18,6 @@ spec: valueFiles: - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-global.yaml" - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-{{ .Values.main.siteName }}.yaml" - - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-{{ .Release.Name }}.yaml" - #- ../../values-blueprint.yaml # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 parameters: - name: global.repoURL From 34f3d22e8ba4d8415dc234d34660dee82755a1e3 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 12:05:11 +1000 Subject: [PATCH 0046/1263] Add symlink for development --- common | 1 + 1 file changed, 1 insertion(+) create mode 120000 common diff --git a/common b/common new file mode 120000 index 00000000..d66298e1 --- /dev/null +++ b/common @@ -0,0 +1 @@ +common \ No newline at end of file From d89cbaf8a05f577e428bf02bb4f2ed971d700049 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 12:07:42 +1000 Subject: [PATCH 0047/1263] Adjust paths to the new structure --- install/templates/argocd/application.yaml | 2 +- values-datacenter.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index a47ed165..3dedab4c 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -13,7 +13,7 @@ spec: source: repoURL: {{ .Values.main.git.repoURL }} targetRevision: {{ .Values.main.git.revision }} - path: applications/bootstrap + path: common/site helm: valueFiles: - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-global.yaml" diff --git a/values-datacenter.yaml b/values-datacenter.yaml index 233f20ac..1536e577 100644 --- a/values-datacenter.yaml +++ b/values-datacenter.yaml @@ -29,7 +29,7 @@ site: - name: acm namespace: open-cluster-management project: datacenter - path: applications/acm + path: common/acm # # To have apps in multiple flavors, use namespaces and use helm overrides as appropriate From f8b7a20c597acff0cc6dcd7f3f015f1b8af09f44 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 12:09:34 +1000 Subject: [PATCH 0048/1263] See if self healing is what prevents subscriptions without a CSV --- install/templates/argocd/application.yaml | 2 +- values-datacenter.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 3dedab4c..cf227938 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -30,5 +30,5 @@ spec: syncPolicy: automated: prune: false - selfHeal: true + selfHeal: false {{- end }} diff --git a/values-datacenter.yaml b/values-datacenter.yaml index 1536e577..42f52d3a 100644 --- a/values-datacenter.yaml +++ b/values-datacenter.yaml @@ -1,7 +1,7 @@ global: valuesDirectoryURL: https://github.com/dagger-refuse-cool/edge-gitops/raw/main/ options: - useCSV: True + useCSV: False syncPolicy: Automatic installPlanApproval: Automatic From 57e5da67cdd472c179102f9c573fbe9b564280c5 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 13:01:23 +1000 Subject: [PATCH 0049/1263] Use the default namespace for the gitops placement rule --- acm/templates/policies/ocp-gitops-policy.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index 956bc7f5..daaecdee 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -61,7 +61,6 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: openshift-gitops-placement - namespace: policy spec: clusterConditions: - status: 'True' From 94ba819b0de43240cea85c53f086ea2085b4f127 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 13:07:15 +1000 Subject: [PATCH 0050/1263] Fix symbolic link --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index d66298e1..945c9b46 120000 --- a/common +++ b/common @@ -1 +1 @@ -common \ No newline at end of file +. \ No newline at end of file From ee5d58fd6810399bb676c3bcff86deaba5731698 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 13:30:49 +1000 Subject: [PATCH 0051/1263] Create a operator group per namespace to make OLM happy --- site/templates/operatorgroup.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/site/templates/operatorgroup.yaml b/site/templates/operatorgroup.yaml index 4c60ba4f..52b873c3 100644 --- a/site/templates/operatorgroup.yaml +++ b/site/templates/operatorgroup.yaml @@ -1,10 +1,11 @@ +{{- range .Values.site.namespaces }} apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: - name: {{ .Release.Name }}-operator-group - namespace: openshift-gitops + name: {{ . }}-operator-group + namespace: {{ . }} spec: targetNamespaces: -{{- range .Values.site.namespaces }} - {{ . }} +--- {{- end }} \ No newline at end of file From f63b3a059abc1263c54404a66a11cd8f3ca3aca5 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 13:31:05 +1000 Subject: [PATCH 0052/1263] Provide more subscription defaults --- site/templates/subscriptions.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/templates/subscriptions.yaml b/site/templates/subscriptions.yaml index 37d55e4c..f3eecad1 100644 --- a/site/templates/subscriptions.yaml +++ b/site/templates/subscriptions.yaml @@ -3,13 +3,13 @@ apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: {{ .name }} - namespace: {{ .namespace }} + namespace: {{ default "openshift-operators" .namespace }} spec: name: {{ .name }} source: {{ .source }} - sourceNamespace: openshift-marketplace + sourceNamespace: {{ default "openshift-marketplace" .sourceNamespace }} channel: {{ .channel }} - installPlanApproval: {{ $.Values.global.options.installPlanApproval }} + installPlanApproval: {{ coalesce .installPlanApproval $.Values.global.options.installPlanApproval }} {{- if $.Values.global.options.useCSV }} startingCSV: {{ .name }}.{{ .csv }} {{- end }} From ab93eaad5ca6a0c769a02d2f94f756444615d93e Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 13:41:36 +1000 Subject: [PATCH 0053/1263] Review and simplify argo sync options --- install/templates/argocd/application.yaml | 4 +--- site/templates/applications.yaml | 7 ++----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index cf227938..d1906347 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -28,7 +28,5 @@ spec: value: {{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }} {{- if eq .Values.main.options.syncPolicy "Automatic" }} syncPolicy: - automated: - prune: false - selfHeal: false + automated: {} {{- end }} diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 4a7f1d38..aa269098 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -36,11 +36,8 @@ spec: - /spec/loggingCA {{- if eq $.Values.global.options.syncPolicy "Automatic" }} syncPolicy: - syncOptions: - - Validate=false - automated: - prune: false - selfHeal: true + automated: {} + # selfHeal: true {{- end }} --- {{- end }} From 900da2a6e85dd8c33543fdd5f20408799f446825 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 13:53:22 +1000 Subject: [PATCH 0054/1263] Ignore objects derived from our ACM policies --- acm/templates/policies/application-policies.yaml | 1 + acm/templates/policies/ocp-gitops-policy.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index a70d7b1b..1ddaec06 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -6,6 +6,7 @@ metadata: name: {{ .name }}-site-policy annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous spec: remediationAction: enforce disabled: false diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index daaecdee..477108d9 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -7,6 +7,7 @@ metadata: policy.open-cluster-management.io/categories: PR.DS Data Security policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous spec: remediationAction: enforce disabled: false From 2ca42ed9d8c5f9673a8fe9617bd9ac1bf394bdd2 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 13:55:15 +1000 Subject: [PATCH 0055/1263] See if we can set SkipDryRunOnMissingResource att the application level --- acm/templates/policies/application-policies.yaml | 1 - site/templates/applications.yaml | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 1ddaec06..61969474 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -5,7 +5,6 @@ kind: Policy metadata: name: {{ .name }}-site-policy annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true argocd.argoproj.io/compare-options: IgnoreExtraneous spec: remediationAction: enforce diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index aa269098..e7607aeb 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -36,6 +36,8 @@ spec: - /spec/loggingCA {{- if eq $.Values.global.options.syncPolicy "Automatic" }} syncPolicy: + syncOptions: + - SkipDryRunOnMissingResource=true automated: {} # selfHeal: true {{- end }} From 8c5fcae6e4cd17581c08619e57a2d680c28700ac Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 15:32:47 +1000 Subject: [PATCH 0056/1263] Revert "See if we can set SkipDryRunOnMissingResource att the application level" It was not possible This reverts commit 2ca42ed9d8c5f9673a8fe9617bd9ac1bf394bdd2. --- acm/templates/policies/application-policies.yaml | 1 + site/templates/applications.yaml | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 61969474..1ddaec06 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -5,6 +5,7 @@ kind: Policy metadata: name: {{ .name }}-site-policy annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true argocd.argoproj.io/compare-options: IgnoreExtraneous spec: remediationAction: enforce diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index e7607aeb..aa269098 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -36,8 +36,6 @@ spec: - /spec/loggingCA {{- if eq $.Values.global.options.syncPolicy "Automatic" }} syncPolicy: - syncOptions: - - SkipDryRunOnMissingResource=true automated: {} # selfHeal: true {{- end }} From c4de68ad011c1c3ea075d1566b9e317bf6a67d94 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 16:27:03 +1000 Subject: [PATCH 0057/1263] Add more subscription defaults --- site/templates/subscriptions.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/templates/subscriptions.yaml b/site/templates/subscriptions.yaml index f3eecad1..6d384b85 100644 --- a/site/templates/subscriptions.yaml +++ b/site/templates/subscriptions.yaml @@ -6,9 +6,9 @@ metadata: namespace: {{ default "openshift-operators" .namespace }} spec: name: {{ .name }} - source: {{ .source }} + source: {{ default "redhat-operators" .source }} sourceNamespace: {{ default "openshift-marketplace" .sourceNamespace }} - channel: {{ .channel }} + channel: {{ default "stable" .channel }} installPlanApproval: {{ coalesce .installPlanApproval $.Values.global.options.installPlanApproval }} {{- if $.Values.global.options.useCSV }} startingCSV: {{ .name }}.{{ .csv }} From 1b4eae2f72dba3004ad58ddab29fd8adc4fbda80 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 16:27:17 +1000 Subject: [PATCH 0058/1263] Support to kustomize targets --- site/templates/applications.yaml | 2 ++ values-datacenter.yaml | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index aa269098..dd80b251 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -13,6 +13,7 @@ spec: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} path: {{ .path }} + {{- if not .kustomize }} helm: valueFiles: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" @@ -29,6 +30,7 @@ spec: - name: {{ .name }} value: {{ .value }} {{- end }} + {{- end }} ignoreDifferences: - group: internal.open-cluster-management.io kind: ManagedClusterInfo diff --git a/values-datacenter.yaml b/values-datacenter.yaml index 42f52d3a..a1e17c76 100644 --- a/values-datacenter.yaml +++ b/values-datacenter.yaml @@ -30,6 +30,11 @@ site: namespace: open-cluster-management project: datacenter path: common/acm + - name: odh + namespace: open-cluster-management + project: datacenter + path: charts/opendatahub + kustomize: True # # To have apps in multiple flavors, use namespaces and use helm overrides as appropriate From 59faf21583b72d4f741339721bf96a997203153f Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 16:58:40 +1000 Subject: [PATCH 0059/1263] Support applications using plugins --- site/templates/applications.yaml | 4 +++- values-datacenter.yaml | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index dd80b251..7fce6371 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -13,7 +13,9 @@ spec: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} path: {{ .path }} - {{- if not .kustomize }} + {{- if .plugin }} + plugin: {{ .plugin | toPrettyJson }} + {{- else if not .kustomize }} helm: valueFiles: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" diff --git a/values-datacenter.yaml b/values-datacenter.yaml index a1e17c76..a681cb11 100644 --- a/values-datacenter.yaml +++ b/values-datacenter.yaml @@ -35,6 +35,12 @@ site: project: datacenter path: charts/opendatahub kustomize: True + - name: pipelines + namespace: manuela-ci + project: datacenter + path: charts/opendatahub + plugin: + name: helm-with-kustomize # # To have apps in multiple flavors, use namespaces and use helm overrides as appropriate From c4fcb378ba8839e65badb6bf528f46cd2527734d Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 17:45:31 +1000 Subject: [PATCH 0060/1263] Cannot assume the subscription name and csv prefix match --- site/templates/subscriptions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/subscriptions.yaml b/site/templates/subscriptions.yaml index 6d384b85..65ec71f2 100644 --- a/site/templates/subscriptions.yaml +++ b/site/templates/subscriptions.yaml @@ -11,7 +11,7 @@ spec: channel: {{ default "stable" .channel }} installPlanApproval: {{ coalesce .installPlanApproval $.Values.global.options.installPlanApproval }} {{- if $.Values.global.options.useCSV }} - startingCSV: {{ .name }}.{{ .csv }} + startingCSV: {{ .csv }} {{- end }} --- {{- end }} From 7f4c6bf9b3543d07e671982df540724ebcd2358a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 17:45:53 +1000 Subject: [PATCH 0061/1263] Allow custom ignoreDifferences values --- site/templates/applications.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 7fce6371..10457aab 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -33,11 +33,9 @@ spec: value: {{ .value }} {{- end }} {{- end }} - ignoreDifferences: - - group: internal.open-cluster-management.io - kind: ManagedClusterInfo - jsonPointers: - - /spec/loggingCA + {{- if .ignoreDifferences }} + ignoreDifferences: {{ .ignoreDifferences | toPrettyJson }} + {{- end }} {{- if eq $.Values.global.options.syncPolicy "Automatic" }} syncPolicy: automated: {} From fb500d115d9bc143d7fdd7a8fb7aa0cd73f3b57b Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 20:57:50 +1000 Subject: [PATCH 0062/1263] Helper for deploying the repo accurately --- Makefile | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..f4bc83ca --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +NAME=$(shell basename `pwd`) +TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:%/%' -e 's%git@%https://%') +TARGET_BRANCH=$(shell git branch --show-current) +deploy: + helm install $(NAME) install/ -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) + +upgrade: + helm upgrade $(NAME) install/ -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) + From 83b819a1ba050e74c6dd458b973e0d54ad1b778f Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 20:58:26 +1000 Subject: [PATCH 0063/1263] Include the site name in the initial app --- install/templates/argocd/application.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index d1906347..c8898787 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -3,7 +3,7 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: {{ .Release.Name }} + name: {{ .Release.Name }}-{{ .Values.main.siteName }} namespace: openshift-gitops spec: destination: From 8262c20f7c7716cb6a2e2c9f05a6f6b9fb18b484 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 20:44:55 +1000 Subject: [PATCH 0064/1263] Simple site to test argo plugins --- example/Chart.yaml | 7 ++++++ example/kustomization.yaml | 5 +++++ example/kustomize | 11 ++++++++++ example/templates/environment.yaml | 35 ++++++++++++++++++++++++++++++ example/values.yaml | 12 ++++++++++ 5 files changed, 70 insertions(+) create mode 100644 example/Chart.yaml create mode 100644 example/kustomization.yaml create mode 100755 example/kustomize create mode 100644 example/templates/environment.yaml create mode 100644 example/values.yaml diff --git a/example/Chart.yaml b/example/Chart.yaml new file mode 100644 index 00000000..d3d4d1d9 --- /dev/null +++ b/example/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +description: A Helm chart to deploy MANUela pipelines +keywords: +- runtimes +- blueprint +name: manuela-pipelines +version: 0.0.1 diff --git a/example/kustomization.yaml b/example/kustomization.yaml new file mode 100644 index 00000000..8d8bcd10 --- /dev/null +++ b/example/kustomization.yaml @@ -0,0 +1,5 @@ +resources: + - environment.yaml + +patches: +- helm.patch.yaml diff --git a/example/kustomize b/example/kustomize new file mode 100755 index 00000000..191a4fc3 --- /dev/null +++ b/example/kustomize @@ -0,0 +1,11 @@ +#!/bin/bash -x + +BASE=`dirname $0` +if [ $BASE = $PWD ]; then + BASE=./ +fi + +cat <&0 > "$BASE/helm.patch.yaml" + +#kubectl kustomize "$BASE" > "$BASE/result.yaml" +kubectl kustomize "$BASE" && rm "$BASE/helm.patch.yaml" diff --git a/example/templates/environment.yaml b/example/templates/environment.yaml new file mode 100644 index 00000000..6a1752a7 --- /dev/null +++ b/example/templates/environment.yaml @@ -0,0 +1,35 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: environment +data: + IMAGE_PROVIDER: {{ .Values.global.quay.provider }} + IMAGE_ACCOUNT: {{ .Values.global.quay.account }} + GIT_EMAIL: {{ .Values.global.git.email }} + GIT_USERNAME: {{ .Values.global.git.username }} + GIT_DEV_REPO_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/manuela-dev.git + GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} + GIT_OPS_REPO_TEST_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/blueprints.git + GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.git.ops_revision }} + GIT_OPS_REPO_PROD_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/blueprints.git + GIT_OPS_REPO_PROD_REVISION: {{ .Values.global.git.ops_revision }} + IOT_CONSUMER_IMAGE: iot-consumer + IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag + IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: config/instances/manuela-tst/kustomization.yaml + IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: config/templates/manuela-openshift-prod/messaging/kustomization.yaml + IOT_CONSUMER_PROD_IMAGESTREAM_PATH: config/templates/manuela-openshift-prod/messaging/messaging-is.yaml + IOT_FRONTEND_IMAGE: iot-frontend + IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag + IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: config/instances/manuela-tst/kustomization.yaml + IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: config/templates/manuela-openshift-prod/line-dashboard/kustomization.yaml + IOT_FRONTEND_PROD_IMAGESTREAM_PATH: config/templates/manuela-openshift-prod/line-dashboard/line-dashboard-is.yaml + IOT_SWSENSOR_IMAGE: iot-software-sensor + IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag + IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: config/instances/manuela-tst/kustomization.yaml + IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: config/templates/manuela-openshift-prod/machine-sensor/kustomization.yaml + IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: config/templates/manuela-openshift-prod/machine-sensor/machine-sensor-is.yaml + IOT_ANOMALY_IMAGE: iot-anomaly-detection + IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag + IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: config/instances/manuela-tst/kustomization.yaml + IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: config/templates/manuela-openshift-prod/anomaly-detection/kustomization.yaml + IOT_ANOMALY_PROD_IMAGESTREAM_PATH: config/templates/manuela-openshift-prod/anomaly-detection/anomaly-detection-is.yaml diff --git a/example/values.yaml b/example/values.yaml new file mode 100644 index 00000000..213057a4 --- /dev/null +++ b/example/values.yaml @@ -0,0 +1,12 @@ +global: + git: + provider: github.com + account: PLAINTEXT + username: PLAINTEXT + email: SOMEWHERE@EXAMPLE.COM + ops_revision: main + dev_revision: main + + quay: + provider: quay.io + account: PLAINTEXT From f458a9cb87d5e07abcd0df058052e8bfedfca809 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 15 Sep 2021 22:44:00 +1000 Subject: [PATCH 0065/1263] Pass site value files when using the helm-with-kustomize plugin --- site/templates/argocd.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index 09e0f555..817000ca 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -15,9 +15,13 @@ spec: -c]\n args: [\"kustomize version 1>&2 && exit 1\"]\n- name: kustomize-with-helm\n \ generate:\n command: [\"kustomize\"]\n args: [\"build\", \"--enable-helm\"]\n- name: helm-with-kustomize\n init:\n command: [\"/bin/sh\", \"-c\"]\n args: [\"helm - dependency build\"]\n generate:\n command: [sh, -c]\n args: [\"helm template + dependency build\"]\n generate:\n command: [sh, -c]\n args: [\"helm template . --release-name ${ARGOCD_APP_NAME}-v0.2021.8 - --set global.bpversion=${BLUEPRINT_VERSION} . + -f {{ .Values.global.valuesDirectoryURL }}/values-global.yaml + -f {{ .Values.global.valuesDirectoryURL }}/values-{{ .Values.site.name }}.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.valuesDirectoryURL={{ .Values.global.valuesDirectoryURL }} --post-renderer ./kustomize\"] \ \n" applicationSet: From 070b4473dde95106a1c1875aad616899f8fd05ba Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 08:58:20 +1000 Subject: [PATCH 0066/1263] Use the app name when rendering the helm template --- site/templates/argocd.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index 817000ca..422497b6 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -15,8 +15,7 @@ spec: -c]\n args: [\"kustomize version 1>&2 && exit 1\"]\n- name: kustomize-with-helm\n \ generate:\n command: [\"kustomize\"]\n args: [\"build\", \"--enable-helm\"]\n- name: helm-with-kustomize\n init:\n command: [\"/bin/sh\", \"-c\"]\n args: [\"helm - dependency build\"]\n generate:\n command: [sh, -c]\n args: [\"helm template . - --release-name ${ARGOCD_APP_NAME}-v0.2021.8 + dependency build\"]\n generate:\n command: [sh, -c]\n args: [\"helm template . --name-template $ARGOCD_APP_NAME -f {{ .Values.global.valuesDirectoryURL }}/values-global.yaml -f {{ .Values.global.valuesDirectoryURL }}/values-{{ .Values.site.name }}.yaml --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL From 01c414d6b59ab53752c7807726e901dd8facc1aa Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 09:12:11 +1000 Subject: [PATCH 0067/1263] Add missing file --- example/environment.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 example/environment.yaml diff --git a/example/environment.yaml b/example/environment.yaml new file mode 100644 index 00000000..28e06c67 --- /dev/null +++ b/example/environment.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: environment +data: + DESCRIPTION: "Config keys for openshift-pipelines" From bea3e6319ba687bcba810c4394c06de1335f0ba4 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 08:55:33 +1000 Subject: [PATCH 0068/1263] kustomize debugging --- example/kustomize | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/example/kustomize b/example/kustomize index 191a4fc3..5f62b40c 100755 --- a/example/kustomize +++ b/example/kustomize @@ -7,5 +7,9 @@ fi cat <&0 > "$BASE/helm.patch.yaml" -#kubectl kustomize "$BASE" > "$BASE/result.yaml" +# Including at least one log to stderr allows us to see the full -x output +echo $HOME $PWD 1>&2 +ls -al 1>&2 + kubectl kustomize "$BASE" && rm "$BASE/helm.patch.yaml" +#kubectl kustomize "$BASE" > "$BASE/result.yaml" From 4a617085218fb9f0825c1f53b4f669d1a6f04444 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 09:18:17 +1000 Subject: [PATCH 0069/1263] The helm-with-kustomize example is useful to keep around --- {example => examples/kustomize-renderer}/Chart.yaml | 0 {example => examples/kustomize-renderer}/environment.yaml | 0 {example => examples/kustomize-renderer}/kustomization.yaml | 0 {example => examples/kustomize-renderer}/kustomize | 0 .../kustomize-renderer}/templates/environment.yaml | 0 {example => examples/kustomize-renderer}/values.yaml | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {example => examples/kustomize-renderer}/Chart.yaml (100%) rename {example => examples/kustomize-renderer}/environment.yaml (100%) rename {example => examples/kustomize-renderer}/kustomization.yaml (100%) rename {example => examples/kustomize-renderer}/kustomize (100%) rename {example => examples/kustomize-renderer}/templates/environment.yaml (100%) rename {example => examples/kustomize-renderer}/values.yaml (100%) diff --git a/example/Chart.yaml b/examples/kustomize-renderer/Chart.yaml similarity index 100% rename from example/Chart.yaml rename to examples/kustomize-renderer/Chart.yaml diff --git a/example/environment.yaml b/examples/kustomize-renderer/environment.yaml similarity index 100% rename from example/environment.yaml rename to examples/kustomize-renderer/environment.yaml diff --git a/example/kustomization.yaml b/examples/kustomize-renderer/kustomization.yaml similarity index 100% rename from example/kustomization.yaml rename to examples/kustomize-renderer/kustomization.yaml diff --git a/example/kustomize b/examples/kustomize-renderer/kustomize similarity index 100% rename from example/kustomize rename to examples/kustomize-renderer/kustomize diff --git a/example/templates/environment.yaml b/examples/kustomize-renderer/templates/environment.yaml similarity index 100% rename from example/templates/environment.yaml rename to examples/kustomize-renderer/templates/environment.yaml diff --git a/example/values.yaml b/examples/kustomize-renderer/values.yaml similarity index 100% rename from example/values.yaml rename to examples/kustomize-renderer/values.yaml From 9e0180b8bb155e56635fc3325bf5dda21fb597b4 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 09:29:32 +1000 Subject: [PATCH 0070/1263] Additional help targets --- Makefile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Makefile b/Makefile index f4bc83ca..b5a1fe9d 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,16 @@ NAME=$(shell basename `pwd`) TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:%/%' -e 's%git@%https://%') TARGET_BRANCH=$(shell git branch --show-current) +show: + helm template install/ --name-template $(NAME) -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) + deploy: helm install $(NAME) install/ -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) upgrade: helm upgrade $(NAME) install/ -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) +uninstall: + helm uninstall $(NAME) + +.phony: install From e4254d775f3ff28a0970b866645854c8407bf091 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 09:36:33 +1000 Subject: [PATCH 0071/1263] Support loading just the site application on install --- install/templates/argocd/namespace.yaml | 3 ++- install/templates/argocd/subscription.yaml | 7 +++---- install/templates/secrets/github-secret.yaml | 3 ++- install/templates/secrets/namespace.yaml | 2 ++ install/templates/secrets/quay-secret.yaml | 2 ++ install/values.yaml | 3 ++- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/install/templates/argocd/namespace.yaml b/install/templates/argocd/namespace.yaml index ee8a78bd..f852fe1b 100644 --- a/install/templates/argocd/namespace.yaml +++ b/install/templates/argocd/namespace.yaml @@ -1,7 +1,8 @@ # Pre-create so we can create our argo app for keeping subscriptions in sync # Do it here so that we don't try to sync it in the future +{{- if .Values.main.options.bootstrap }} apiVersion: v1 kind: Namespace metadata: name: openshift-gitops - +{{- end }} diff --git a/install/templates/argocd/subscription.yaml b/install/templates/argocd/subscription.yaml index 98545b5a..1f40e25d 100644 --- a/install/templates/argocd/subscription.yaml +++ b/install/templates/argocd/subscription.yaml @@ -1,3 +1,4 @@ +{{- if .Values.main.options.bootstrap }} apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -11,9 +12,7 @@ spec: name: openshift-gitops-operator source: redhat-operators sourceNamespace: openshift-marketplace -{{- if eq .Values.main.options.useCSV "True" }} +{{- if .Values.main.options.useCSV }} startingCSV: openshift-gitops-operator.{{ .Values.main.gitops.csv }} {{- end }} - - - +{{- end }} diff --git a/install/templates/secrets/github-secret.yaml b/install/templates/secrets/github-secret.yaml index 14557e9f..7458a79e 100644 --- a/install/templates/secrets/github-secret.yaml +++ b/install/templates/secrets/github-secret.yaml @@ -1,3 +1,4 @@ +{{- if .Values.main.options.bootstrap }} kind: Secret apiVersion: v1 metadata: @@ -9,4 +10,4 @@ data: # Then: echo -n 'your string value' | base64 token: {{ .Values.secrets.git.authToken }} user: {{ .Values.secrets.git.accountToken }} - +{{- end }} \ No newline at end of file diff --git a/install/templates/secrets/namespace.yaml b/install/templates/secrets/namespace.yaml index 54c06793..e51255d8 100644 --- a/install/templates/secrets/namespace.yaml +++ b/install/templates/secrets/namespace.yaml @@ -1,3 +1,4 @@ +{{- if .Values.main.options.bootstrap }} apiVersion: v1 kind: Namespace metadata: @@ -9,3 +10,4 @@ metadata: spec: finalizers: - kubernetes +{{- end }} \ No newline at end of file diff --git a/install/templates/secrets/quay-secret.yaml b/install/templates/secrets/quay-secret.yaml index 952d4566..6fb00ac0 100644 --- a/install/templates/secrets/quay-secret.yaml +++ b/install/templates/secrets/quay-secret.yaml @@ -1,3 +1,4 @@ +{{- if .Values.main.options.bootstrap }} kind: Secret apiVersion: v1 metadata: @@ -17,3 +18,4 @@ data: #} # auths["quay.io"]["auth"] is a base64 encoded string of IMAGE_REGISTRY_USER:IMAGE_REGISTRY_PASSWORD .dockerconfigjson: {{ .Values.secrets.quay.authToken }} +{{- end }} \ No newline at end of file diff --git a/install/values.yaml b/install/values.yaml index 932e0d74..6beda204 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -7,7 +7,8 @@ main: options: syncPolicy: Automatic installPlanApproval: Automatic - useCSV: "False" + useCSV: False + bootstrap: True gitops: channel: stable From 0bf8c8fbaee2dc2448c7528f060000c211fc3082 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 09:50:04 +1000 Subject: [PATCH 0072/1263] Make it easy to drive the bootstrap option from the helper --- Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index b5a1fe9d..a1014ff5 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,16 @@ +BOOTSTRAP=1 NAME=$(shell basename `pwd`) TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:%/%' -e 's%git@%https://%') TARGET_BRANCH=$(shell git branch --show-current) + show: - helm template install/ --name-template $(NAME) -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) + helm template install/ --name-template $(NAME) -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) deploy: - helm install $(NAME) install/ -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) + helm install $(NAME) install/ -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) upgrade: - helm upgrade $(NAME) install/ -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) + helm upgrade $(NAME) install/ -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) uninstall: helm uninstall $(NAME) From 5a88454b14e7568656e46843ca82bc2c6e187aa1 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 09:51:20 +1000 Subject: [PATCH 0073/1263] Make it easy to kustomize the secrets location from the helper --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a1014ff5..76d5954c 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,17 @@ BOOTSTRAP=1 +SECRETS=~/values-secret.yaml NAME=$(shell basename `pwd`) TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:%/%' -e 's%git@%https://%') TARGET_BRANCH=$(shell git branch --show-current) show: - helm template install/ --name-template $(NAME) -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) + helm template install/ --name-template $(NAME) -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) deploy: - helm install $(NAME) install/ -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) + helm install $(NAME) install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) upgrade: - helm upgrade $(NAME) install/ -f ~/values-secret.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) + helm upgrade $(NAME) install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) uninstall: helm uninstall $(NAME) From afe590ef64b2af0519be99bd2b4a1f375a3f76c5 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 11:03:41 +1000 Subject: [PATCH 0074/1263] Limit helm's template name to under 53 chars --- site/templates/argocd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index 422497b6..582c237e 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -15,7 +15,7 @@ spec: -c]\n args: [\"kustomize version 1>&2 && exit 1\"]\n- name: kustomize-with-helm\n \ generate:\n command: [\"kustomize\"]\n args: [\"build\", \"--enable-helm\"]\n- name: helm-with-kustomize\n init:\n command: [\"/bin/sh\", \"-c\"]\n args: [\"helm - dependency build\"]\n generate:\n command: [sh, -c]\n args: [\"helm template . --name-template $ARGOCD_APP_NAME + dependency build\"]\n generate:\n command: [/bin/bash, -c]\n args: [\"helm template . --name-template ${ARGOCD_APP_NAME:0:52} -f {{ .Values.global.valuesDirectoryURL }}/values-global.yaml -f {{ .Values.global.valuesDirectoryURL }}/values-{{ .Values.site.name }}.yaml --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL From ad4bce5e68dd9eaaab5df0d978c9618d75479a47 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 11:08:47 +1000 Subject: [PATCH 0075/1263] Create the application name ordered by significance --- site/templates/applications.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 10457aab..a54106fb 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -2,7 +2,7 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: {{ $.Release.Name }}-{{ $.Values.site.name }}-{{ .name }} + name: {{ .name }}-{{ $.Values.site.name }}-{{ $.Release.Name }} namespace: openshift-gitops spec: destination: From 9e43545b748ca7ad0e0143188d3d075da733e4df Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 11:15:34 +1000 Subject: [PATCH 0076/1263] Site name is included in the site's .Release.Name --- site/templates/applications.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index a54106fb..c8624538 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -2,7 +2,7 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: {{ .name }}-{{ $.Values.site.name }}-{{ $.Release.Name }} + name: {{ .name }}-{{ $.Release.Name }} namespace: openshift-gitops spec: destination: From 6b460265267c28002db3ce58f0eaa4feb194f36f Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 11:20:51 +1000 Subject: [PATCH 0077/1263] Try to stay under helm's name template max of 53 chars --- site/templates/applications.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index c8624538..fb201e49 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -2,7 +2,7 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: {{ .name }}-{{ $.Release.Name }} + name: {{ .name }}-{{ $.Release.Name | trunc 40 }} namespace: openshift-gitops spec: destination: From cf57b237a19efdabfd1427987b4119a28b3a1d94 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 22:09:24 +1000 Subject: [PATCH 0078/1263] Fix the application role binding to refer to the actual policy --- acm/templates/policies/application-policies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 1ddaec06..8e3e3af0 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -83,7 +83,7 @@ placementRef: kind: PlacementRule apiGroup: apps.open-cluster-management.io subjects: - - name: argo-factorydatacenter-application-installed + - name: {{ .name }}-site-policy kind: Policy apiGroup: policy.open-cluster-management.io --- From 4950aca6a0f7552c35ff8ec5e925077adae4413f Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 22:21:32 +1000 Subject: [PATCH 0079/1263] Use the managed site name for the application and values file --- acm/templates/policies/application-policies.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 8e3e3af0..a8a521ac 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -30,7 +30,7 @@ spec: apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: {{ $.Values.site.name }}-site + name: {{ .name }}-site namespace: openshift-gitops finalizers: - argoproj.io/finalizer @@ -43,7 +43,7 @@ spec: helm: valueFiles: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" - - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Values.site.name }}.yaml" + - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ .name }}.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL From b27be14b015ec344a76cd47f34c372c88bff0ffa Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 22:27:19 +1000 Subject: [PATCH 0080/1263] Enforce quoting of helmOverrides for acm policy applications --- acm/templates/policies/application-policies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index a8a521ac..d32d928b 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -53,7 +53,7 @@ spec: value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} {{- range .helmOverrides }} - name: {{ .name }} - value: {{ .value }} + value: {{ .value | quote }} {{- end }} destination: server: https://kubernetes.default.svc From f99ccdd2030ded48d4f218bb1c18b59c14610e82 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 16 Sep 2021 22:31:23 +1000 Subject: [PATCH 0081/1263] Provide a default path to the site chart --- acm/templates/policies/application-policies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index d32d928b..16278c14 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -39,7 +39,7 @@ spec: source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} - path: {{ .path }} + path: {{ default "common/site" .path }} helm: valueFiles: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" From 377b35a6fba0683b49b9dddbdf8e1e3fcce40a0e Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 20 Sep 2021 17:00:50 +1000 Subject: [PATCH 0082/1263] Drop references to manuela in common --- Makefile | 3 ++ acm/Chart.yaml | 7 ++--- examples/kustomize-renderer/Chart.yaml | 7 ++--- .../templates/environment.yaml | 28 +++++++++---------- install/Chart.yaml | 7 ++--- site/Chart.yaml | 6 ++-- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Makefile b/Makefile index 76d5954c..36df5e9a 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,9 @@ TARGET_BRANCH=$(shell git branch --show-current) show: helm template install/ --name-template $(NAME) -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) +init: + git submodule update --init --recursive + deploy: helm install $(NAME) install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) diff --git a/acm/Chart.yaml b/acm/Chart.yaml index 68619876..1c3db911 100644 --- a/acm/Chart.yaml +++ b/acm/Chart.yaml @@ -1,7 +1,6 @@ apiVersion: v2 -description: A Helm chart to configure opendatahub +description: A Helm chart to configure Advanced Cluster Manager for OpenShift keywords: -- manuela -- blueprint -name: opendatahub +- pattern +name: acm version: 0.0.1 diff --git a/examples/kustomize-renderer/Chart.yaml b/examples/kustomize-renderer/Chart.yaml index d3d4d1d9..88a786c9 100644 --- a/examples/kustomize-renderer/Chart.yaml +++ b/examples/kustomize-renderer/Chart.yaml @@ -1,7 +1,6 @@ apiVersion: v2 -description: A Helm chart to deploy MANUela pipelines +description: A Helm chart to demonstrate how to use with kustomize keywords: -- runtimes -- blueprint -name: manuela-pipelines +- pattern +name: example version: 0.0.1 diff --git a/examples/kustomize-renderer/templates/environment.yaml b/examples/kustomize-renderer/templates/environment.yaml index 6a1752a7..ab597357 100644 --- a/examples/kustomize-renderer/templates/environment.yaml +++ b/examples/kustomize-renderer/templates/environment.yaml @@ -9,27 +9,27 @@ data: GIT_USERNAME: {{ .Values.global.git.username }} GIT_DEV_REPO_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/manuela-dev.git GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} - GIT_OPS_REPO_TEST_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/blueprints.git + GIT_OPS_REPO_TEST_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/manufacturing-edge-ai-ml.git GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.git.ops_revision }} - GIT_OPS_REPO_PROD_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/blueprints.git + GIT_OPS_REPO_PROD_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/manufacturing-edge-ai-ml.git GIT_OPS_REPO_PROD_REVISION: {{ .Values.global.git.ops_revision }} IOT_CONSUMER_IMAGE: iot-consumer IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag - IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: config/instances/manuela-tst/kustomization.yaml - IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: config/templates/manuela-openshift-prod/messaging/kustomization.yaml - IOT_CONSUMER_PROD_IMAGESTREAM_PATH: config/templates/manuela-openshift-prod/messaging/messaging-is.yaml + IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml + IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml IOT_FRONTEND_IMAGE: iot-frontend IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag - IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: config/instances/manuela-tst/kustomization.yaml - IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: config/templates/manuela-openshift-prod/line-dashboard/kustomization.yaml - IOT_FRONTEND_PROD_IMAGESTREAM_PATH: config/templates/manuela-openshift-prod/line-dashboard/line-dashboard-is.yaml + IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml + IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml IOT_SWSENSOR_IMAGE: iot-software-sensor IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag - IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: config/instances/manuela-tst/kustomization.yaml - IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: config/templates/manuela-openshift-prod/machine-sensor/kustomization.yaml - IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: config/templates/manuela-openshift-prod/machine-sensor/machine-sensor-is.yaml + IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml + IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml IOT_ANOMALY_IMAGE: iot-anomaly-detection IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag - IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: config/instances/manuela-tst/kustomization.yaml - IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: config/templates/manuela-openshift-prod/anomaly-detection/kustomization.yaml - IOT_ANOMALY_PROD_IMAGESTREAM_PATH: config/templates/manuela-openshift-prod/anomaly-detection/anomaly-detection-is.yaml + IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml + IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/install/Chart.yaml b/install/Chart.yaml index 47fc460d..d1c80ab3 100644 --- a/install/Chart.yaml +++ b/install/Chart.yaml @@ -1,7 +1,6 @@ apiVersion: v2 -description: A Helm chart to build and deploy MANUela +description: A Helm chart to build and deploy a Cloud Pattern keywords: -- runtimes -- blueprint -name: manufacturing-edge-ai-ml +- pattern +name: pattern-install version: 0.0.1 diff --git a/site/Chart.yaml b/site/Chart.yaml index c68ad3a0..4216d6be 100644 --- a/site/Chart.yaml +++ b/site/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 -description: A Helm chart to create the initial manuela application +description: A Helm chart to create per-site ArgoCD applications and any required namespaces or subscriptions keywords: - manuela -- blueprint -name: manuela-datacenter +- pattern +name: pattern-site version: 0.0.1 From 92e5ce2be0f3881dd67c0f98c6b79c708e827a72 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 21 Sep 2021 14:11:42 +1000 Subject: [PATCH 0083/1263] Allow namespaces without operatorgroups for charts that already include them --- site/templates/operatorgroup.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/site/templates/operatorgroup.yaml b/site/templates/operatorgroup.yaml index 52b873c3..d22b6299 100644 --- a/site/templates/operatorgroup.yaml +++ b/site/templates/operatorgroup.yaml @@ -1,4 +1,6 @@ {{- range .Values.site.namespaces }} + +{{- if empty $.Values.site.operatorgroupExcludes }} apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -8,4 +10,16 @@ spec: targetNamespaces: - {{ . }} --- +{{- else if not (has . $.Values.site.operatorgroupExcludes) }} +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: {{ . }}-operator-group + namespace: {{ . }} +spec: + targetNamespaces: + - {{ . }} +--- +{{- end }} + {{- end }} \ No newline at end of file From d6a7888c3241b91c6eb5f4866e70b3fe1b1c51b8 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 21 Sep 2021 13:33:22 -0500 Subject: [PATCH 0084/1263] Add argocd secret handling --- Makefile | 6 ++++++ util/argo-secret.sh | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100755 util/argo-secret.sh diff --git a/Makefile b/Makefile index 36df5e9a..f51ca688 100644 --- a/Makefile +++ b/Makefile @@ -12,9 +12,15 @@ init: deploy: helm install $(NAME) install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) +ifeq ($(BOOTSTRAP),1) + bash util/argocd-secret.sh +endif upgrade: helm upgrade $(NAME) install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) +ifeq ($(BOOTSTRAP),1) + bash util/argocd-secret.sh +endif uninstall: helm uninstall $(NAME) diff --git a/util/argo-secret.sh b/util/argo-secret.sh new file mode 100755 index 00000000..f98ed6ac --- /dev/null +++ b/util/argo-secret.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +ns=0 +gitops=0 + +# Check for Namespaces and Secrets to be ready (it takes the cluster a while to deploy them) +while [ 1 ]; do + if [ oc get namespace manuela-ci >/dev/null 2>/dev/null ]; then + ns=0 + else + ns=1 + fi + + if [ oc -n openshift-gitops extract secrets/openshift-gitops-cluster --to=- 1>/dev/null 2>/dev/null ]; then + gitops=0 + else + gitops=1 + fi + + if [ "$gitops" == 1 -a "$ns" == 1 ]; then + break + fi +done + +user=$(echo admin | base64) +password=$(oc -n openshift-gitops extract secrets/openshift-gitops-cluster --to=- 2>/dev/null | base64) + +cat < Date: Tue, 21 Sep 2021 15:32:14 -0500 Subject: [PATCH 0085/1263] Add argosecret target --- Makefile | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Makefile b/Makefile index f51ca688..053ecd62 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,43 @@ NAME=$(shell basename `pwd`) TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:%/%' -e 's%git@%https://%') TARGET_BRANCH=$(shell git branch --show-current) +# This is to eliminate the need to install and worry about a separate shell script somewhere in the directory structure +# There's a lot of GNU Make magic happening here: +# .ONESHELL passes the whole task into a single shell instance +# $$ is a Makefile idiom to preserve the single $ otherwise make consumes them +# tabs are necessary +# The patch to oc apply uses JSON because it's not as sensitive to indentation and doesn't need heredoc +.ONESHELL: +SHELL = bash +argosecret: + target_ns=$(TARGET_NAMESPACE) + ns=0 + gitops=0 + + # Check for Namespaces and Secrets to be ready (it takes the cluster a while to deploy them) + while [ 1 ]; do + if [ oc get namespace $$target_ns >/dev/null 2>/dev/null ]; then + ns=0 + else + ns=1 + fi + + if [ oc -n openshift-gitops extract secrets/openshift-gitops-cluster --to=- 1>/dev/null 2>/dev/null ]; then + gitops=0 + else + gitops=1 + fi + + if [ "$$gitops" == 1 -a "$$ns" == 1 ]; then + break + fi + done + + user=$$(echo admin | base64) + password=$$(oc -n openshift-gitops extract secrets/openshift-gitops-cluster --to=- 2>/dev/null | base64) + + echo "{ \"apiVersion\": \"v1\", \"kind\": \"Secret\", \"metadata\": { \"name\": \"argocd-env\", \"namespace\": \"$$target_ns\" }, \"data\": { \"ARGOCD_PASSWORD\": \"$$password\", \"ARGOCD_USERNAME\": \"$$user\" }, \"type\": \"Opaque\" }" | oc apply -f- + show: helm template install/ --name-template $(NAME) -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) From be4ecad99c244cf2491b43d5f11c6526ccba83dc Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 21 Sep 2021 15:44:33 -0500 Subject: [PATCH 0086/1263] Remove unneeded util dir --- util/argo-secret.sh | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100755 util/argo-secret.sh diff --git a/util/argo-secret.sh b/util/argo-secret.sh deleted file mode 100755 index f98ed6ac..00000000 --- a/util/argo-secret.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash - -ns=0 -gitops=0 - -# Check for Namespaces and Secrets to be ready (it takes the cluster a while to deploy them) -while [ 1 ]; do - if [ oc get namespace manuela-ci >/dev/null 2>/dev/null ]; then - ns=0 - else - ns=1 - fi - - if [ oc -n openshift-gitops extract secrets/openshift-gitops-cluster --to=- 1>/dev/null 2>/dev/null ]; then - gitops=0 - else - gitops=1 - fi - - if [ "$gitops" == 1 -a "$ns" == 1 ]; then - break - fi -done - -user=$(echo admin | base64) -password=$(oc -n openshift-gitops extract secrets/openshift-gitops-cluster --to=- 2>/dev/null | base64) - -cat < Date: Tue, 21 Sep 2021 15:46:58 -0500 Subject: [PATCH 0087/1263] Explain how to use common makefile --- Makefile | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 053ecd62..207d0d69 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,8 @@ argosecret: echo "{ \"apiVersion\": \"v1\", \"kind\": \"Secret\", \"metadata\": { \"name\": \"argocd-env\", \"namespace\": \"$$target_ns\" }, \"data\": { \"ARGOCD_PASSWORD\": \"$$password\", \"ARGOCD_USERNAME\": \"$$user\" }, \"type\": \"Opaque\" }" | oc apply -f- +# Makefiles in the individual patterns should call these targets explicitly +# e.g. from manufacturing-ai-ml-edge: make -f common/Makefile show show: helm template install/ --name-template $(NAME) -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) @@ -49,15 +51,9 @@ init: deploy: helm install $(NAME) install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -ifeq ($(BOOTSTRAP),1) - bash util/argocd-secret.sh -endif upgrade: helm upgrade $(NAME) install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -ifeq ($(BOOTSTRAP),1) - bash util/argocd-secret.sh -endif uninstall: helm uninstall $(NAME) From eda487e16ba13a5c5eeed4d51947c413276831bb Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 21 Sep 2021 14:11:42 +1000 Subject: [PATCH 0088/1263] Allow namespaces without operatorgroups for charts that already include them --- site/templates/operatorgroup.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/site/templates/operatorgroup.yaml b/site/templates/operatorgroup.yaml index 52b873c3..d22b6299 100644 --- a/site/templates/operatorgroup.yaml +++ b/site/templates/operatorgroup.yaml @@ -1,4 +1,6 @@ {{- range .Values.site.namespaces }} + +{{- if empty $.Values.site.operatorgroupExcludes }} apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -8,4 +10,16 @@ spec: targetNamespaces: - {{ . }} --- +{{- else if not (has . $.Values.site.operatorgroupExcludes) }} +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: {{ . }}-operator-group + namespace: {{ . }} +spec: + targetNamespaces: + - {{ . }} +--- +{{- end }} + {{- end }} \ No newline at end of file From 2635a7db35b1ab02d4c51b7d0ffcf8e34d3e5d34 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 21 Sep 2021 18:13:27 -0500 Subject: [PATCH 0089/1263] Add template toplevel makefile --- Makefile.toplevel | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Makefile.toplevel diff --git a/Makefile.toplevel b/Makefile.toplevel new file mode 100644 index 00000000..b82b5d77 --- /dev/null +++ b/Makefile.toplevel @@ -0,0 +1,26 @@ +# This is an example top-level makefile for a new pattern. It delegates the tasks to the common makefile. +BOOTSTRAP=1 +ARGO_TARGET_NAMESPACE=replaceme + +show: + make -f common/Makefile show + +init: + make -f common/Makefile init + +deploy: + make -f common/Makefile deploy +ifeq ($(BOOTSTRAP),1) + make -f common/Makefile TARGET_NAMESPACE=$(ARGO_TARGET_NAMESPACE) argosecret +endif + +upgrade: + make -f common/Makefile upgrade +ifeq ($(BOOTSTRAP),1) + make -f common/Makefile TARGET_NAMESPACE=$(ARGO_TARGET_NAMESPACE) argosecret +endif + +uninstall: + make -f common/Makefile uninstall + +.phony: install From b81ea5f2a4bac4d01732f2d3405c0d6425ab7a9c Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 22 Sep 2021 14:51:44 +1000 Subject: [PATCH 0090/1263] Allow templates to know which namespace they're being deployed into --- acm/templates/policies/application-policies.yaml | 2 ++ install/templates/argocd/application.yaml | 2 ++ site/templates/applications.yaml | 2 ++ site/templates/argocd.yaml | 1 + 4 files changed, 7 insertions(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 16278c14..11b838bc 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -49,6 +49,8 @@ spec: value: $ARGOCD_APP_SOURCE_REPO_URL - name: global.targetRevision value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE - name: global.valuesDirectoryURL value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} {{- range .helmOverrides }} diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index c8898787..bcedce67 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -24,6 +24,8 @@ spec: value: $ARGOCD_APP_SOURCE_REPO_URL - name: global.targetRevision value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE - name: global.valuesDirectoryURL value: {{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }} {{- if eq .Values.main.options.syncPolicy "Automatic" }} diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index fb201e49..46a36b6c 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -26,6 +26,8 @@ spec: value: $ARGOCD_APP_SOURCE_REPO_URL - name: global.targetRevision value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE - name: global.valuesDirectoryURL value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} {{- range .overrides }} diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index 582c237e..af44d84d 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -20,6 +20,7 @@ spec: -f {{ .Values.global.valuesDirectoryURL }}/values-{{ .Values.site.name }}.yaml --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.valuesDirectoryURL={{ .Values.global.valuesDirectoryURL }} --post-renderer ./kustomize\"] \ \n" From fbfe57ccc91acd02540a5321b56bd1158cb81674 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 22 Sep 2021 14:52:37 +1000 Subject: [PATCH 0091/1263] Refresh the kustomize-renderer example --- examples/kustomize-renderer/environment.yaml | 31 ++++++++++++++++++- .../templates/environment.yaml | 8 ++--- examples/kustomize-renderer/values.yaml | 1 - 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/examples/kustomize-renderer/environment.yaml b/examples/kustomize-renderer/environment.yaml index 28e06c67..c027f82a 100644 --- a/examples/kustomize-renderer/environment.yaml +++ b/examples/kustomize-renderer/environment.yaml @@ -3,4 +3,33 @@ kind: ConfigMap metadata: name: environment data: - DESCRIPTION: "Config keys for openshift-pipelines" + IMAGE_PROVIDER: {{ .Values.global.quay.provider }} + IMAGE_ACCOUNT: {{ .Values.global.quay.account }} + GIT_EMAIL: {{ .Values.global.git.email }} + GIT_USERNAME: {{ .Values.global.git.username }} + GIT_DEV_REPO_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/manuela-dev.git + GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} + GIT_OPS_REPO_TEST_URL: {{ .Values.global.repoURL }} + GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.targetRevision }} + GIT_OPS_REPO_PROD_URL: {{ .Values.global.repoURL }} + GIT_OPS_REPO_PROD_REVISION: {{ .Values.global.targetRevision }} + IOT_CONSUMER_IMAGE: iot-consumer + IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag + IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml + IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml + IOT_FRONTEND_IMAGE: iot-frontend + IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag + IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml + IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml + IOT_SWSENSOR_IMAGE: iot-software-sensor + IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag + IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml + IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml + IOT_ANOMALY_IMAGE: iot-anomaly-detection + IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag + IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml + IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/examples/kustomize-renderer/templates/environment.yaml b/examples/kustomize-renderer/templates/environment.yaml index ab597357..c027f82a 100644 --- a/examples/kustomize-renderer/templates/environment.yaml +++ b/examples/kustomize-renderer/templates/environment.yaml @@ -9,10 +9,10 @@ data: GIT_USERNAME: {{ .Values.global.git.username }} GIT_DEV_REPO_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/manuela-dev.git GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} - GIT_OPS_REPO_TEST_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/manufacturing-edge-ai-ml.git - GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.git.ops_revision }} - GIT_OPS_REPO_PROD_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/manufacturing-edge-ai-ml.git - GIT_OPS_REPO_PROD_REVISION: {{ .Values.global.git.ops_revision }} + GIT_OPS_REPO_TEST_URL: {{ .Values.global.repoURL }} + GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.targetRevision }} + GIT_OPS_REPO_PROD_URL: {{ .Values.global.repoURL }} + GIT_OPS_REPO_PROD_REVISION: {{ .Values.global.targetRevision }} IOT_CONSUMER_IMAGE: iot-consumer IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml diff --git a/examples/kustomize-renderer/values.yaml b/examples/kustomize-renderer/values.yaml index 213057a4..e713c393 100644 --- a/examples/kustomize-renderer/values.yaml +++ b/examples/kustomize-renderer/values.yaml @@ -4,7 +4,6 @@ global: account: PLAINTEXT username: PLAINTEXT email: SOMEWHERE@EXAMPLE.COM - ops_revision: main dev_revision: main quay: From b79a2a93d3d11cb48aafa1cd0ab40ed9f459e5b0 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 22 Sep 2021 14:53:16 +1000 Subject: [PATCH 0092/1263] Refresh the sample datacenter from manuela --- values-datacenter.yaml | 115 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 7 deletions(-) diff --git a/values-datacenter.yaml b/values-datacenter.yaml index a681cb11..d6814967 100644 --- a/values-datacenter.yaml +++ b/values-datacenter.yaml @@ -14,31 +14,130 @@ site: namespaces: - open-cluster-management + - manuela-ml-workspace + - manuela-tst-all + - manuela-ci + - manuela-data-lake-central-s3-store + - manuela-data-lake-central-kafka-cluster + + operatorgroupsExcludes: + - manuela-ml-workspace subscriptions: - name: advanced-cluster-management namespace: open-cluster-management - source: redhat-operators channel: release-2.3 - csv: v2.3.2 + csv: advanced-cluster-management.v2.3.2 + + - name: seldon-operator + namespace: manuela-ml-workspace + source: community-operators + csv: seldon-operator.v1.7.0 + + - name: opendatahub-operator + source: community-operators + csv: opendatahub-operator.v1.1.0 + + - name: openshift-pipelines-operator-rh + csv: redhat-openshift-pipelines.v1.5.1 + + # TODO: Allow namespace to be a list + - name: amq-streams + namespace: manuela-data-lake-central-kafka-cluster + channel: amq-streams-1.7.x + csv: amqstreams.v1.7.1 + + - name: amq-streams + namespace: manuela-tst-all + channel: amq-streams-1.7.x + csv: amqstreams.v1.7.1 + + - name: red-hat-camel-k + namespace: manuela-data-lake-central-s3-store + channel: 1.4.x + csv: red-hat-camel-k-operator.v1.4.0 + + - name: red-hat-camel-k + namespace: manuela-tst-all + channel: 1.4.x + csv: red-hat-camel-k-operator.v1.4.0 projects: - datacenter + - datalake applications: - name: acm namespace: open-cluster-management project: datacenter path: common/acm + ignoreDifferences: + - group: internal.open-cluster-management.io + kind: ManagedClusterInfo + jsonPointers: + - /spec/loggingCA + - name: odh - namespace: open-cluster-management + namespace: default project: datacenter - path: charts/opendatahub + path: charts/datacenter/opendatahub kustomize: True + - name: pipelines namespace: manuela-ci project: datacenter - path: charts/opendatahub + path: charts/datacenter/pipelines + + - name: central-kafka + namespace: manuela-data-lake-central-kafka-cluster + project: datalake + path: charts/datacenter/kafka + kustomize: True + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status + - group: image.openshift.io + kind: ImageStream + jsonPointers: + - /spec/tags + - group: apps.openshift.io + kind: DeploymentConfig + jsonPointers: + - /spec/template/spec/containers/0/image + + - name: central-s3 + namespace: manuela-data-lake-central-s3-store + project: datalake + path: charts/datacenter/central-s3-store + kustomize: True + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status + - group: image.openshift.io + kind: ImageStream + jsonPointers: + - /spec/tags + - group: apps.openshift.io + kind: DeploymentConfig + jsonPointers: + - /spec/template/spec/containers/0/image + + - name: manuela-test + namespace: manuela-tst-all + project: datacenter + path: charts/datacenter/manuela-tst plugin: name: helm-with-kustomize @@ -78,11 +177,13 @@ site: - name: factory # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git targetRevision: main - path: applications/factory helmOverrides: + # Values must be strings! - name: site.isHubCluster - value: false + value: "false" clusterSelector: +# matchLabels: +# site: factory matchExpressions: - key: vendor operator: In From 9a539abf5122a7a02f173e148540efbd66b439be Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 22 Sep 2021 19:29:55 +1000 Subject: [PATCH 0093/1263] Don't assume the install symlink is present --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 207d0d69..0e09b046 100644 --- a/Makefile +++ b/Makefile @@ -44,16 +44,16 @@ argosecret: # Makefiles in the individual patterns should call these targets explicitly # e.g. from manufacturing-ai-ml-edge: make -f common/Makefile show show: - helm template install/ --name-template $(NAME) -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) + helm template common/install/ --name-template $(NAME) -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) init: git submodule update --init --recursive deploy: - helm install $(NAME) install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) + helm install $(NAME) common/install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) upgrade: - helm upgrade $(NAME) install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) + helm upgrade $(NAME) common/install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) uninstall: helm uninstall $(NAME) From c463cbc4ae387f14889c2b5a2f24a3f0134dfb05 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 22 Sep 2021 16:38:53 -0500 Subject: [PATCH 0094/1263] Attempt new template format --- site/templates/subscriptions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/subscriptions.yaml b/site/templates/subscriptions.yaml index 65ec71f2..ffdf933b 100644 --- a/site/templates/subscriptions.yaml +++ b/site/templates/subscriptions.yaml @@ -10,7 +10,7 @@ spec: sourceNamespace: {{ default "openshift-marketplace" .sourceNamespace }} channel: {{ default "stable" .channel }} installPlanApproval: {{ coalesce .installPlanApproval $.Values.global.options.installPlanApproval }} - {{- if $.Values.global.options.useCSV }} + {{- if coalesce .useCSV .Values.site.subscriptions.useCSV .Values.global.options.useCSV }} startingCSV: {{ .csv }} {{- end }} --- From da21117463bc60ba957800174146349c622daec7 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 23 Sep 2021 10:58:40 -0500 Subject: [PATCH 0095/1263] Ensure password has length before exiting wait loop --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 0e09b046..5fe7d130 100644 --- a/Makefile +++ b/Makefile @@ -25,19 +25,20 @@ argosecret: ns=1 fi - if [ oc -n openshift-gitops extract secrets/openshift-gitops-cluster --to=- 1>/dev/null 2>/dev/null ]; then + pw=`oc -n openshift-gitops extract secrets/openshift-gitops-cluster --to=- 2>/dev/null` + if [ "$$?" == 0 ] && [ -n "$$pw" ]; gitops=0 else gitops=1 fi - if [ "$$gitops" == 1 -a "$$ns" == 1 ]; then + if [ "$$gitops" == 1 ] && [ "$$ns" == 1 ]; then break fi done user=$$(echo admin | base64) - password=$$(oc -n openshift-gitops extract secrets/openshift-gitops-cluster --to=- 2>/dev/null | base64) + password=$$(echo $$pw | base64) echo "{ \"apiVersion\": \"v1\", \"kind\": \"Secret\", \"metadata\": { \"name\": \"argocd-env\", \"namespace\": \"$$target_ns\" }, \"data\": { \"ARGOCD_PASSWORD\": \"$$password\", \"ARGOCD_USERNAME\": \"$$user\" }, \"type\": \"Opaque\" }" | oc apply -f- From 2ea0f3e2d015d5908f4f39b3c63e02b7b7e2d4b0 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 23 Sep 2021 11:00:20 -0500 Subject: [PATCH 0096/1263] Replace makefile template and make embedded shell wait for password to have length as well as 0 exit --- Makefile.toplevel | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/Makefile.toplevel b/Makefile.toplevel index b82b5d77..1317d8b9 100644 --- a/Makefile.toplevel +++ b/Makefile.toplevel @@ -1,26 +1,16 @@ -# This is an example top-level makefile for a new pattern. It delegates the tasks to the common makefile. BOOTSTRAP=1 ARGO_TARGET_NAMESPACE=replaceme -show: - make -f common/Makefile show +.PHONY: default +default: show -init: - make -f common/Makefile init +%: + make -f common/Makefile $* -deploy: - make -f common/Makefile deploy +install: deploy ifeq ($(BOOTSTRAP),1) make -f common/Makefile TARGET_NAMESPACE=$(ARGO_TARGET_NAMESPACE) argosecret endif -upgrade: - make -f common/Makefile upgrade -ifeq ($(BOOTSTRAP),1) +secret: make -f common/Makefile TARGET_NAMESPACE=$(ARGO_TARGET_NAMESPACE) argosecret -endif - -uninstall: - make -f common/Makefile uninstall - -.phony: install From 69a57b9504450c1d7bc10b71fe37f2dd088298ee Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 23 Sep 2021 11:03:06 -0500 Subject: [PATCH 0097/1263] Add then --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5fe7d130..2086e751 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ argosecret: fi pw=`oc -n openshift-gitops extract secrets/openshift-gitops-cluster --to=- 2>/dev/null` - if [ "$$?" == 0 ] && [ -n "$$pw" ]; + if [ "$$?" == 0 ] && [ -n "$$pw" ]; then gitops=0 else gitops=1 From ec744e952c6ed422c33a06788d9c401823013903 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 23 Sep 2021 11:09:48 -0500 Subject: [PATCH 0098/1263] Make script explain what it's doing --- Makefile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 2086e751..fa0b0893 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ argosecret: # Check for Namespaces and Secrets to be ready (it takes the cluster a while to deploy them) while [ 1 ]; do if [ oc get namespace $$target_ns >/dev/null 2>/dev/null ]; then + echo "Waiting for namespace $$target_ns to be created" ns=0 else ns=1 @@ -27,13 +28,17 @@ argosecret: pw=`oc -n openshift-gitops extract secrets/openshift-gitops-cluster --to=- 2>/dev/null` if [ "$$?" == 0 ] && [ -n "$$pw" ]; then - gitops=0 - else gitops=1 + else + echo "Waiting for password to be populated" + gitops=0 fi if [ "$$gitops" == 1 ] && [ "$$ns" == 1 ]; then + echo "Conditions met, creating resource" break + else + sleep 2 fi done From 21eddba3270f445c73aee67d377b2c20442a4ade Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 23 Sep 2021 11:12:24 -0500 Subject: [PATCH 0099/1263] make output clearer --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fa0b0893..49b4b116 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ argosecret: fi if [ "$$gitops" == 1 ] && [ "$$ns" == 1 ]; then - echo "Conditions met, creating resource" + echo "Conditions met, managing secret in $$target_ns" break else sleep 2 From 4011679f228aa96830ef1522d55dc8b82949d772 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 23 Sep 2021 13:47:27 -0500 Subject: [PATCH 0100/1263] Revert "Attempt new template format" This reverts commit c463cbc4ae387f14889c2b5a2f24a3f0134dfb05. --- site/templates/subscriptions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/subscriptions.yaml b/site/templates/subscriptions.yaml index ffdf933b..65ec71f2 100644 --- a/site/templates/subscriptions.yaml +++ b/site/templates/subscriptions.yaml @@ -10,7 +10,7 @@ spec: sourceNamespace: {{ default "openshift-marketplace" .sourceNamespace }} channel: {{ default "stable" .channel }} installPlanApproval: {{ coalesce .installPlanApproval $.Values.global.options.installPlanApproval }} - {{- if coalesce .useCSV .Values.site.subscriptions.useCSV .Values.global.options.useCSV }} + {{- if $.Values.global.options.useCSV }} startingCSV: {{ .csv }} {{- end }} --- From 8ee2740555268582a67af8c2629e8cf27038f27a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 24 Sep 2021 21:47:10 +1000 Subject: [PATCH 0101/1263] Try putting applications into a dedicated argo instance --- site/templates/applications.yaml | 4 ++-- site/templates/argocd-super-role.yaml | 18 ++++++++++++++++++ site/templates/argocd.yaml | 2 +- site/templates/gitops-namespace.yaml | 7 +++++++ site/templates/namespaces.yaml | 2 +- site/templates/projects.yaml | 1 + 6 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 site/templates/gitops-namespace.yaml diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 46a36b6c..7f23aa8a 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -3,11 +3,11 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: {{ .name }}-{{ $.Release.Name | trunc 40 }} - namespace: openshift-gitops + namespace: {{ $.Values.site.name }}-gitops spec: destination: name: in-cluster - namespace: {{ default "openshift-gitops" .namespace }} + namespace: {{ default "{{ $.Values.site.name }}-gitops" .namespace }} project: {{ .project }} source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} diff --git a/site/templates/argocd-super-role.yaml b/site/templates/argocd-super-role.yaml index 5898ce05..a754658e 100644 --- a/site/templates/argocd-super-role.yaml +++ b/site/templates/argocd-super-role.yaml @@ -15,3 +15,21 @@ subjects: - kind: ServiceAccount name: openshift-gitops-argocd-server namespace: openshift-gitops +--- +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ .Values.site.name }}-gitops-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: {{ .Values.site.name }}-gitops-argocd-application-controller + namespace: {{ .Values.site.name }}-gitops + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + name: {{ .Values.site.name }}-gitops-argocd-server + namespace: {{ .Values.site.name }}-gitops diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index af44d84d..94c47d0e 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -3,7 +3,7 @@ kind: ArgoCD metadata: finalizers: - argoproj.io/finalizer - name: openshift-gitops + name: {{ .Values.site.name }}-gitops annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: diff --git a/site/templates/gitops-namespace.yaml b/site/templates/gitops-namespace.yaml new file mode 100644 index 00000000..7256299f --- /dev/null +++ b/site/templates/gitops-namespace.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: {{ .Values.site.name }} + name: {{ .Values.site.name }}-gitops +spec: {} diff --git a/site/templates/namespaces.yaml b/site/templates/namespaces.yaml index ec6bda70..b799e30a 100644 --- a/site/templates/namespaces.yaml +++ b/site/templates/namespaces.yaml @@ -4,7 +4,7 @@ kind: Namespace metadata: labels: name: {{ default "blueprint" $.Release.name }} - argocd.argoproj.io/managed-by: openshift-gitops + argocd.argoproj.io/managed-by: {{ $.Values.site.name }}-gitops name: {{ . }} spec: --- diff --git a/site/templates/projects.yaml b/site/templates/projects.yaml index ba2c222b..f71305f8 100644 --- a/site/templates/projects.yaml +++ b/site/templates/projects.yaml @@ -3,6 +3,7 @@ apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: {{ . }} + namespace: {{ $.Values.site.name }}-gitops spec: description: "Blueprint {{ . }}" destinations: From f5b554acbd7a78eb8dd59e257817e77bb2409ecc Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 10:35:57 +1000 Subject: [PATCH 0102/1263] All namespaces are created by the site chart --- acm/templates/managed-clusters/staging.yaml | 9 +-------- install/templates/argocd/application.yaml | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/acm/templates/managed-clusters/staging.yaml b/acm/templates/managed-clusters/staging.yaml index 2dcbe9bf..1c6fd818 100644 --- a/acm/templates/managed-clusters/staging.yaml +++ b/acm/templates/managed-clusters/staging.yaml @@ -1,10 +1,3 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: staging - labels: - argocd.argoproj.io/managed-by: openshift-gitops ---- apiVersion: cluster.open-cluster-management.io/v1 kind: ManagedCluster metadata: @@ -60,4 +53,4 @@ metadata: name: staging namespace: staging spec: - loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K \ No newline at end of file + loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index bcedce67..578e51ef 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -8,7 +8,7 @@ metadata: spec: destination: name: in-cluster - namespace: openshift-gitops + namespace: {{ .Values.main.siteName }} project: default source: repoURL: {{ .Values.main.git.repoURL }} From ac05a52c9298dd478ef1ab691ba0b389c5a817fe Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 12:43:33 +1000 Subject: [PATCH 0103/1263] Fix default applicaiton namespace handling --- site/templates/applications.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 7f23aa8a..a6b9de59 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -1,13 +1,14 @@ +{{- $namespace := cat $.Values.site.name "gitops" }} {{- range .Values.site.applications }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: {{ .name }}-{{ $.Release.Name | trunc 40 }} - namespace: {{ $.Values.site.name }}-gitops + namespace: {{ $namespace }} spec: destination: name: in-cluster - namespace: {{ default "{{ $.Values.site.name }}-gitops" .namespace }} + namespace: {{ default $namespace .namespace }} project: {{ .project }} source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} From 53133107e191c19053ef307516c98f5e0b58e5a3 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 14:04:00 +1000 Subject: [PATCH 0104/1263] Make the pattern name availble to argo applications --- acm/templates/policies/application-policies.yaml | 4 +++- install/templates/argocd/application.yaml | 2 ++ site/templates/applications.yaml | 2 ++ site/templates/argocd.yaml | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 11b838bc..6ada0993 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -30,7 +30,7 @@ spec: apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: {{ .name }}-site + name: {{ $.Values.global.pattern }}-{{ .name }} namespace: openshift-gitops finalizers: - argoproj.io/finalizer @@ -51,6 +51,8 @@ spec: value: $ARGOCD_APP_SOURCE_TARGET_REVISION - name: global.namespace value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: {{ $.Values.global.pattern }} - name: global.valuesDirectoryURL value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} {{- range .helmOverrides }} diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 578e51ef..0fd1fad7 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -28,6 +28,8 @@ spec: value: $ARGOCD_APP_NAMESPACE - name: global.valuesDirectoryURL value: {{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }} + - name: global.pattern + value: {{ .Release.Name }} {{- if eq .Values.main.options.syncPolicy "Automatic" }} syncPolicy: automated: {} diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index a6b9de59..12ce78f1 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -29,6 +29,8 @@ spec: value: $ARGOCD_APP_SOURCE_TARGET_REVISION - name: global.namespace value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: {{ $.Values.global.pattern }} - name: global.valuesDirectoryURL value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} {{- range .overrides }} diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index 94c47d0e..9926bb8f 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -21,6 +21,7 @@ spec: --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern={{ .Values.global.pattern }} --set global.valuesDirectoryURL={{ .Values.global.valuesDirectoryURL }} --post-renderer ./kustomize\"] \ \n" From e70f3912ac92c44a06c9561655bc84368fa5bc8c Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 14:05:12 +1000 Subject: [PATCH 0105/1263] Give preference to whatever was specified in the secrets file --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 49b4b116..23ae754c 100644 --- a/Makefile +++ b/Makefile @@ -50,16 +50,16 @@ argosecret: # Makefiles in the individual patterns should call these targets explicitly # e.g. from manufacturing-ai-ml-edge: make -f common/Makefile show show: - helm template common/install/ --name-template $(NAME) -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) + helm template common/install/ --name-template $(NAME) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) init: git submodule update --init --recursive deploy: - helm install $(NAME) common/install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) + helm install $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) upgrade: - helm upgrade $(NAME) common/install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) + helm upgrade $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) uninstall: helm uninstall $(NAME) From 5eb6bfba47b42e0f4d54f6153180a664b4400693 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 14:09:12 +1000 Subject: [PATCH 0106/1263] Strip off any auth tokens when calculating the target repo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 23ae754c..83521213 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ BOOTSTRAP=1 SECRETS=~/values-secret.yaml NAME=$(shell basename `pwd`) -TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:%/%' -e 's%git@%https://%') +TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:[a-z].*@%@%' -e 's%:%/%' -e 's%git@%https://%' ) TARGET_BRANCH=$(shell git branch --show-current) # This is to eliminate the need to install and worry about a separate shell script somewhere in the directory structure From 4d03187eb10aea47174cd3f022731dadd6a29f25 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 14:13:28 +1000 Subject: [PATCH 0107/1263] Ensure there are no spaces in the namespace --- site/templates/applications.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 12ce78f1..6586af24 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -1,4 +1,4 @@ -{{- $namespace := cat $.Values.site.name "gitops" }} +{{- $namespace := cat $.Values.site.name "gitops" | replace " " "-" }} {{- range .Values.site.applications }} apiVersion: argoproj.io/v1alpha1 kind: Application From a0a42f7f608f7c1a9bc5a9c8b02370c4f408a307 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 18:40:12 +1000 Subject: [PATCH 0108/1263] Fix namespace for applications --- install/templates/argocd/application.yaml | 2 +- site/templates/applications.yaml | 2 +- site/templates/projects.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 0fd1fad7..83ec55b7 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -8,7 +8,7 @@ metadata: spec: destination: name: in-cluster - namespace: {{ .Values.main.siteName }} + namespace: {{ .Values.main.siteName }}-gitops project: default source: repoURL: {{ .Values.main.git.repoURL }} diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 6586af24..0a983be2 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -4,7 +4,7 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: {{ .name }}-{{ $.Release.Name | trunc 40 }} - namespace: {{ $namespace }} + namespace: {{ $.Values.site.name }}-gitops spec: destination: name: in-cluster diff --git a/site/templates/projects.yaml b/site/templates/projects.yaml index f71305f8..d7dff705 100644 --- a/site/templates/projects.yaml +++ b/site/templates/projects.yaml @@ -5,7 +5,7 @@ metadata: name: {{ . }} namespace: {{ $.Values.site.name }}-gitops spec: - description: "Blueprint {{ . }}" + description: "Pattern {{ . }}" destinations: - namespace: '*' server: '*' From da7963e1d5587ba253222e5ecc092a819296c70c Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 18:54:56 +1000 Subject: [PATCH 0109/1263] Fix namespace for application acm policies --- acm/templates/policies/application-policies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 6ada0993..c9cc289a 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -61,7 +61,7 @@ spec: {{- end }} destination: server: https://kubernetes.default.svc - namespace: openshift-gitops + namespace: {{ .name }}-gitops syncPolicy: automated: prune: false From 704c5842421981a4e9aae39e49aaeda6c2e1f025 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 30 Sep 2021 16:28:24 +1000 Subject: [PATCH 0110/1263] Include the pattern name to ensure uniqueness --- site/templates/applications.yaml | 4 ++-- site/templates/argocd-super-role.yaml | 10 +++++----- site/templates/argocd.yaml | 2 +- site/templates/gitops-namespace.yaml | 4 ++-- site/templates/namespaces.yaml | 2 +- site/templates/projects.yaml | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 0a983be2..9ecb5ef4 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -1,10 +1,10 @@ -{{- $namespace := cat $.Values.site.name "gitops" | replace " " "-" }} +{{- $namespace := cat $.Values.global.pattern $.Values.site.name "gitops" | replace " " "-" }} {{- range .Values.site.applications }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: {{ .name }}-{{ $.Release.Name | trunc 40 }} - namespace: {{ $.Values.site.name }}-gitops + namespace: {{ $namespace }} spec: destination: name: in-cluster diff --git a/site/templates/argocd-super-role.yaml b/site/templates/argocd-super-role.yaml index a754658e..1fbe38c9 100644 --- a/site/templates/argocd-super-role.yaml +++ b/site/templates/argocd-super-role.yaml @@ -20,16 +20,16 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: {{ .Values.site.name }}-gitops-cluster-admin-rolebinding + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-cluster-admin-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount - name: {{ .Values.site.name }}-gitops-argocd-application-controller - namespace: {{ .Values.site.name }}-gitops + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-argocd-application-controller + namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - kind: ServiceAccount - name: {{ .Values.site.name }}-gitops-argocd-server - namespace: {{ .Values.site.name }}-gitops + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-argocd-server + namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index 9926bb8f..386c04ba 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -3,7 +3,7 @@ kind: ArgoCD metadata: finalizers: - argoproj.io/finalizer - name: {{ .Values.site.name }}-gitops + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: diff --git a/site/templates/gitops-namespace.yaml b/site/templates/gitops-namespace.yaml index 7256299f..13f794ab 100644 --- a/site/templates/gitops-namespace.yaml +++ b/site/templates/gitops-namespace.yaml @@ -2,6 +2,6 @@ apiVersion: v1 kind: Namespace metadata: labels: - name: {{ .Values.site.name }} - name: {{ .Values.site.name }}-gitops + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }} + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops spec: {} diff --git a/site/templates/namespaces.yaml b/site/templates/namespaces.yaml index b799e30a..cb9ce669 100644 --- a/site/templates/namespaces.yaml +++ b/site/templates/namespaces.yaml @@ -4,7 +4,7 @@ kind: Namespace metadata: labels: name: {{ default "blueprint" $.Release.name }} - argocd.argoproj.io/managed-by: {{ $.Values.site.name }}-gitops + argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.site.name }}-gitops name: {{ . }} spec: --- diff --git a/site/templates/projects.yaml b/site/templates/projects.yaml index d7dff705..721e8955 100644 --- a/site/templates/projects.yaml +++ b/site/templates/projects.yaml @@ -3,7 +3,7 @@ apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: {{ . }} - namespace: {{ $.Values.site.name }}-gitops + namespace: {{ $.Values.global.pattern }}-{{ $.Values.site.name }}-gitops spec: description: "Pattern {{ . }}" destinations: From 6ba620d913604d95d83e6d7574a3af82b68752e2 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 1 Oct 2021 15:36:23 -0500 Subject: [PATCH 0111/1263] Update repo name --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 49b4b116..b17bfd1c 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ argosecret: echo "{ \"apiVersion\": \"v1\", \"kind\": \"Secret\", \"metadata\": { \"name\": \"argocd-env\", \"namespace\": \"$$target_ns\" }, \"data\": { \"ARGOCD_PASSWORD\": \"$$password\", \"ARGOCD_USERNAME\": \"$$user\" }, \"type\": \"Opaque\" }" | oc apply -f- # Makefiles in the individual patterns should call these targets explicitly -# e.g. from manufacturing-ai-ml-edge: make -f common/Makefile show +# e.g. from industrial-edge: make -f common/Makefile show show: helm template common/install/ --name-template $(NAME) -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) From 7609fecaa1f056f450c7a0f7a45413aa1044971e Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 24 Sep 2021 21:47:10 +1000 Subject: [PATCH 0112/1263] Try putting applications into a dedicated argo instance --- site/templates/applications.yaml | 4 ++-- site/templates/argocd-super-role.yaml | 18 ++++++++++++++++++ site/templates/argocd.yaml | 2 +- site/templates/gitops-namespace.yaml | 7 +++++++ site/templates/namespaces.yaml | 2 +- site/templates/projects.yaml | 1 + 6 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 site/templates/gitops-namespace.yaml diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 46a36b6c..7f23aa8a 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -3,11 +3,11 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: {{ .name }}-{{ $.Release.Name | trunc 40 }} - namespace: openshift-gitops + namespace: {{ $.Values.site.name }}-gitops spec: destination: name: in-cluster - namespace: {{ default "openshift-gitops" .namespace }} + namespace: {{ default "{{ $.Values.site.name }}-gitops" .namespace }} project: {{ .project }} source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} diff --git a/site/templates/argocd-super-role.yaml b/site/templates/argocd-super-role.yaml index 5898ce05..a754658e 100644 --- a/site/templates/argocd-super-role.yaml +++ b/site/templates/argocd-super-role.yaml @@ -15,3 +15,21 @@ subjects: - kind: ServiceAccount name: openshift-gitops-argocd-server namespace: openshift-gitops +--- +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ .Values.site.name }}-gitops-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: {{ .Values.site.name }}-gitops-argocd-application-controller + namespace: {{ .Values.site.name }}-gitops + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + name: {{ .Values.site.name }}-gitops-argocd-server + namespace: {{ .Values.site.name }}-gitops diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index af44d84d..94c47d0e 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -3,7 +3,7 @@ kind: ArgoCD metadata: finalizers: - argoproj.io/finalizer - name: openshift-gitops + name: {{ .Values.site.name }}-gitops annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: diff --git a/site/templates/gitops-namespace.yaml b/site/templates/gitops-namespace.yaml new file mode 100644 index 00000000..7256299f --- /dev/null +++ b/site/templates/gitops-namespace.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: {{ .Values.site.name }} + name: {{ .Values.site.name }}-gitops +spec: {} diff --git a/site/templates/namespaces.yaml b/site/templates/namespaces.yaml index ec6bda70..b799e30a 100644 --- a/site/templates/namespaces.yaml +++ b/site/templates/namespaces.yaml @@ -4,7 +4,7 @@ kind: Namespace metadata: labels: name: {{ default "blueprint" $.Release.name }} - argocd.argoproj.io/managed-by: openshift-gitops + argocd.argoproj.io/managed-by: {{ $.Values.site.name }}-gitops name: {{ . }} spec: --- diff --git a/site/templates/projects.yaml b/site/templates/projects.yaml index ba2c222b..f71305f8 100644 --- a/site/templates/projects.yaml +++ b/site/templates/projects.yaml @@ -3,6 +3,7 @@ apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: {{ . }} + namespace: {{ $.Values.site.name }}-gitops spec: description: "Blueprint {{ . }}" destinations: From c0c10c53a099f8155151f3cc91e3cd082e854430 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 10:35:57 +1000 Subject: [PATCH 0113/1263] All namespaces are created by the site chart --- acm/templates/managed-clusters/staging.yaml | 9 +-------- install/templates/argocd/application.yaml | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/acm/templates/managed-clusters/staging.yaml b/acm/templates/managed-clusters/staging.yaml index 2dcbe9bf..1c6fd818 100644 --- a/acm/templates/managed-clusters/staging.yaml +++ b/acm/templates/managed-clusters/staging.yaml @@ -1,10 +1,3 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: staging - labels: - argocd.argoproj.io/managed-by: openshift-gitops ---- apiVersion: cluster.open-cluster-management.io/v1 kind: ManagedCluster metadata: @@ -60,4 +53,4 @@ metadata: name: staging namespace: staging spec: - loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K \ No newline at end of file + loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index bcedce67..578e51ef 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -8,7 +8,7 @@ metadata: spec: destination: name: in-cluster - namespace: openshift-gitops + namespace: {{ .Values.main.siteName }} project: default source: repoURL: {{ .Values.main.git.repoURL }} From dbfb497874251d5f10aada73c1a83e8bd3e18f52 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 12:43:33 +1000 Subject: [PATCH 0114/1263] Fix default applicaiton namespace handling --- site/templates/applications.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 7f23aa8a..a6b9de59 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -1,13 +1,14 @@ +{{- $namespace := cat $.Values.site.name "gitops" }} {{- range .Values.site.applications }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: {{ .name }}-{{ $.Release.Name | trunc 40 }} - namespace: {{ $.Values.site.name }}-gitops + namespace: {{ $namespace }} spec: destination: name: in-cluster - namespace: {{ default "{{ $.Values.site.name }}-gitops" .namespace }} + namespace: {{ default $namespace .namespace }} project: {{ .project }} source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} From 2534d12f0749af7c01ec7513626f37d355346295 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 14:04:00 +1000 Subject: [PATCH 0115/1263] Make the pattern name availble to argo applications --- acm/templates/policies/application-policies.yaml | 4 +++- install/templates/argocd/application.yaml | 2 ++ site/templates/applications.yaml | 2 ++ site/templates/argocd.yaml | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 11b838bc..6ada0993 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -30,7 +30,7 @@ spec: apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: {{ .name }}-site + name: {{ $.Values.global.pattern }}-{{ .name }} namespace: openshift-gitops finalizers: - argoproj.io/finalizer @@ -51,6 +51,8 @@ spec: value: $ARGOCD_APP_SOURCE_TARGET_REVISION - name: global.namespace value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: {{ $.Values.global.pattern }} - name: global.valuesDirectoryURL value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} {{- range .helmOverrides }} diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 578e51ef..0fd1fad7 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -28,6 +28,8 @@ spec: value: $ARGOCD_APP_NAMESPACE - name: global.valuesDirectoryURL value: {{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }} + - name: global.pattern + value: {{ .Release.Name }} {{- if eq .Values.main.options.syncPolicy "Automatic" }} syncPolicy: automated: {} diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index a6b9de59..12ce78f1 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -29,6 +29,8 @@ spec: value: $ARGOCD_APP_SOURCE_TARGET_REVISION - name: global.namespace value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: {{ $.Values.global.pattern }} - name: global.valuesDirectoryURL value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} {{- range .overrides }} diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index 94c47d0e..9926bb8f 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -21,6 +21,7 @@ spec: --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern={{ .Values.global.pattern }} --set global.valuesDirectoryURL={{ .Values.global.valuesDirectoryURL }} --post-renderer ./kustomize\"] \ \n" From aea7efc20743daf7c099ac19b841d58a004cbcd4 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 14:05:12 +1000 Subject: [PATCH 0116/1263] Give preference to whatever was specified in the secrets file --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index b17bfd1c..32d85b27 100644 --- a/Makefile +++ b/Makefile @@ -50,16 +50,16 @@ argosecret: # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show show: - helm template common/install/ --name-template $(NAME) -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) + helm template common/install/ --name-template $(NAME) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) init: git submodule update --init --recursive deploy: - helm install $(NAME) common/install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) + helm install $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) upgrade: - helm upgrade $(NAME) common/install/ -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) + helm upgrade $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) uninstall: helm uninstall $(NAME) From a60af74d462552c612dfbc725a7eedf4a609b8b0 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 14:09:12 +1000 Subject: [PATCH 0117/1263] Strip off any auth tokens when calculating the target repo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 32d85b27..5bed4804 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ BOOTSTRAP=1 SECRETS=~/values-secret.yaml NAME=$(shell basename `pwd`) -TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:%/%' -e 's%git@%https://%') +TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:[a-z].*@%@%' -e 's%:%/%' -e 's%git@%https://%' ) TARGET_BRANCH=$(shell git branch --show-current) # This is to eliminate the need to install and worry about a separate shell script somewhere in the directory structure From b4c8d69d5f2dc343b86f78fad1f7cfc81c13dace Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 14:13:28 +1000 Subject: [PATCH 0118/1263] Ensure there are no spaces in the namespace --- site/templates/applications.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 12ce78f1..6586af24 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -1,4 +1,4 @@ -{{- $namespace := cat $.Values.site.name "gitops" }} +{{- $namespace := cat $.Values.site.name "gitops" | replace " " "-" }} {{- range .Values.site.applications }} apiVersion: argoproj.io/v1alpha1 kind: Application From 0d7e94534cc7c15266b6fe57cb62e20efbec09a1 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 18:40:12 +1000 Subject: [PATCH 0119/1263] Fix namespace for applications --- install/templates/argocd/application.yaml | 2 +- site/templates/applications.yaml | 2 +- site/templates/projects.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 0fd1fad7..83ec55b7 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -8,7 +8,7 @@ metadata: spec: destination: name: in-cluster - namespace: {{ .Values.main.siteName }} + namespace: {{ .Values.main.siteName }}-gitops project: default source: repoURL: {{ .Values.main.git.repoURL }} diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 6586af24..0a983be2 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -4,7 +4,7 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: {{ .name }}-{{ $.Release.Name | trunc 40 }} - namespace: {{ $namespace }} + namespace: {{ $.Values.site.name }}-gitops spec: destination: name: in-cluster diff --git a/site/templates/projects.yaml b/site/templates/projects.yaml index f71305f8..d7dff705 100644 --- a/site/templates/projects.yaml +++ b/site/templates/projects.yaml @@ -5,7 +5,7 @@ metadata: name: {{ . }} namespace: {{ $.Values.site.name }}-gitops spec: - description: "Blueprint {{ . }}" + description: "Pattern {{ . }}" destinations: - namespace: '*' server: '*' From 5b225e0f694c682506d06d84168625d2ea830d2c Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 25 Sep 2021 18:54:56 +1000 Subject: [PATCH 0120/1263] Fix namespace for application acm policies --- acm/templates/policies/application-policies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 6ada0993..c9cc289a 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -61,7 +61,7 @@ spec: {{- end }} destination: server: https://kubernetes.default.svc - namespace: openshift-gitops + namespace: {{ .name }}-gitops syncPolicy: automated: prune: false From 0ee2cc164b07b8466b00f05749dd1ca5c758fa76 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 30 Sep 2021 16:28:24 +1000 Subject: [PATCH 0121/1263] Include the pattern name to ensure uniqueness --- site/templates/applications.yaml | 4 ++-- site/templates/argocd-super-role.yaml | 10 +++++----- site/templates/argocd.yaml | 2 +- site/templates/gitops-namespace.yaml | 4 ++-- site/templates/namespaces.yaml | 2 +- site/templates/projects.yaml | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 0a983be2..9ecb5ef4 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -1,10 +1,10 @@ -{{- $namespace := cat $.Values.site.name "gitops" | replace " " "-" }} +{{- $namespace := cat $.Values.global.pattern $.Values.site.name "gitops" | replace " " "-" }} {{- range .Values.site.applications }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: {{ .name }}-{{ $.Release.Name | trunc 40 }} - namespace: {{ $.Values.site.name }}-gitops + namespace: {{ $namespace }} spec: destination: name: in-cluster diff --git a/site/templates/argocd-super-role.yaml b/site/templates/argocd-super-role.yaml index a754658e..1fbe38c9 100644 --- a/site/templates/argocd-super-role.yaml +++ b/site/templates/argocd-super-role.yaml @@ -20,16 +20,16 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: {{ .Values.site.name }}-gitops-cluster-admin-rolebinding + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-cluster-admin-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount - name: {{ .Values.site.name }}-gitops-argocd-application-controller - namespace: {{ .Values.site.name }}-gitops + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-argocd-application-controller + namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - kind: ServiceAccount - name: {{ .Values.site.name }}-gitops-argocd-server - namespace: {{ .Values.site.name }}-gitops + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-argocd-server + namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index 9926bb8f..386c04ba 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -3,7 +3,7 @@ kind: ArgoCD metadata: finalizers: - argoproj.io/finalizer - name: {{ .Values.site.name }}-gitops + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: diff --git a/site/templates/gitops-namespace.yaml b/site/templates/gitops-namespace.yaml index 7256299f..13f794ab 100644 --- a/site/templates/gitops-namespace.yaml +++ b/site/templates/gitops-namespace.yaml @@ -2,6 +2,6 @@ apiVersion: v1 kind: Namespace metadata: labels: - name: {{ .Values.site.name }} - name: {{ .Values.site.name }}-gitops + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }} + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops spec: {} diff --git a/site/templates/namespaces.yaml b/site/templates/namespaces.yaml index b799e30a..cb9ce669 100644 --- a/site/templates/namespaces.yaml +++ b/site/templates/namespaces.yaml @@ -4,7 +4,7 @@ kind: Namespace metadata: labels: name: {{ default "blueprint" $.Release.name }} - argocd.argoproj.io/managed-by: {{ $.Values.site.name }}-gitops + argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.site.name }}-gitops name: {{ . }} spec: --- diff --git a/site/templates/projects.yaml b/site/templates/projects.yaml index d7dff705..721e8955 100644 --- a/site/templates/projects.yaml +++ b/site/templates/projects.yaml @@ -3,7 +3,7 @@ apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: {{ . }} - namespace: {{ $.Values.site.name }}-gitops + namespace: {{ $.Values.global.pattern }}-{{ $.Values.site.name }}-gitops spec: description: "Pattern {{ . }}" destinations: From 0260ec232908c4e153c6a8ba8add20a27ec0df66 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 2 Oct 2021 18:42:51 +1000 Subject: [PATCH 0122/1263] Fix the destination namespace for datacenter manifests --- install/templates/argocd/application.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 83ec55b7..d6c2eea6 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -8,7 +8,7 @@ metadata: spec: destination: name: in-cluster - namespace: {{ .Values.main.siteName }}-gitops + namespace: {{ .Release.Name }}-{{ .Values.main.siteName }}-gitops project: default source: repoURL: {{ .Values.main.git.repoURL }} From 59dd6991b0dd0f430fd98551b6e19b1f7921bf08 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 2 Oct 2021 20:59:58 +1000 Subject: [PATCH 0123/1263] Try a simpler argo name --- site/templates/argocd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index 386c04ba..9926bb8f 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -3,7 +3,7 @@ kind: ArgoCD metadata: finalizers: - argoproj.io/finalizer - name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops + name: {{ .Values.site.name }}-gitops annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: From 58c171a266c748447898c8e4dae107f75cc1dd3b Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 2 Oct 2021 21:25:50 +1000 Subject: [PATCH 0124/1263] Use a shorter namespace --- acm/templates/policies/application-policies.yaml | 2 +- install/templates/argocd/application.yaml | 2 +- site/templates/argocd-super-role.yaml | 10 +++++----- site/templates/argocd.yaml | 2 +- site/templates/gitops-namespace.yaml | 2 +- site/templates/namespaces.yaml | 2 +- site/templates/projects.yaml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index c9cc289a..8cfb1414 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -61,7 +61,7 @@ spec: {{- end }} destination: server: https://kubernetes.default.svc - namespace: {{ .name }}-gitops + namespace: {{ .name }} syncPolicy: automated: prune: false diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index d6c2eea6..4da1b595 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -8,7 +8,7 @@ metadata: spec: destination: name: in-cluster - namespace: {{ .Release.Name }}-{{ .Values.main.siteName }}-gitops + namespace: {{ .Release.Name }}-{{ .Values.main.siteName }} project: default source: repoURL: {{ .Values.main.git.repoURL }} diff --git a/site/templates/argocd-super-role.yaml b/site/templates/argocd-super-role.yaml index 1fbe38c9..d51265bf 100644 --- a/site/templates/argocd-super-role.yaml +++ b/site/templates/argocd-super-role.yaml @@ -20,16 +20,16 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-cluster-admin-rolebinding + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-cluster-admin-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount - name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-argocd-application-controller - namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-argocd-application-controller + namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }} # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - kind: ServiceAccount - name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-argocd-server - namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-argocd-server + namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }} diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index 9926bb8f..c3b48cb4 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -3,7 +3,7 @@ kind: ArgoCD metadata: finalizers: - argoproj.io/finalizer - name: {{ .Values.site.name }}-gitops + name: {{ .Values.site.name }} annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: diff --git a/site/templates/gitops-namespace.yaml b/site/templates/gitops-namespace.yaml index 13f794ab..54a0c29c 100644 --- a/site/templates/gitops-namespace.yaml +++ b/site/templates/gitops-namespace.yaml @@ -3,5 +3,5 @@ kind: Namespace metadata: labels: name: {{ $.Values.global.pattern }}-{{ .Values.site.name }} - name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops + name: {{ $.Values.global.pattern }}-{{ .Values.site.name }} spec: {} diff --git a/site/templates/namespaces.yaml b/site/templates/namespaces.yaml index cb9ce669..6f08952a 100644 --- a/site/templates/namespaces.yaml +++ b/site/templates/namespaces.yaml @@ -4,7 +4,7 @@ kind: Namespace metadata: labels: name: {{ default "blueprint" $.Release.name }} - argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.site.name }}-gitops + argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.site.name }} name: {{ . }} spec: --- diff --git a/site/templates/projects.yaml b/site/templates/projects.yaml index 721e8955..43242026 100644 --- a/site/templates/projects.yaml +++ b/site/templates/projects.yaml @@ -3,7 +3,7 @@ apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: {{ . }} - namespace: {{ $.Values.global.pattern }}-{{ $.Values.site.name }}-gitops + namespace: {{ $.Values.global.pattern }}-{{ $.Values.site.name }} spec: description: "Pattern {{ . }}" destinations: From 6c416cf3fa4ec701ad700be3445cc3326ac7c2ee Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 2 Oct 2021 21:26:50 +1000 Subject: [PATCH 0125/1263] acm: Fix the target namespace for the site application policy --- acm/templates/policies/application-policies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 8cfb1414..669a3f51 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -61,7 +61,7 @@ spec: {{- end }} destination: server: https://kubernetes.default.svc - namespace: {{ .name }} + namespace: {{ $.Values.global.pattern }}-{{ .name }} syncPolicy: automated: prune: false From b01c626d754314e53b88c2c63cb0a58b97209fc8 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sun, 3 Oct 2021 17:17:54 +1100 Subject: [PATCH 0126/1263] Fix application namespace --- site/templates/applications.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 9ecb5ef4..204784ba 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -1,4 +1,4 @@ -{{- $namespace := cat $.Values.global.pattern $.Values.site.name "gitops" | replace " " "-" }} +{{- $namespace := cat $.Values.global.pattern $.Values.site.name | replace " " "-" }} {{- range .Values.site.applications }} apiVersion: argoproj.io/v1alpha1 kind: Application From 5b927027572c3398716da5e67d2c0a8df87a83cf Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sun, 3 Oct 2021 17:26:29 +1100 Subject: [PATCH 0127/1263] The application name is already unique to the namespace --- site/templates/applications.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 204784ba..51d6b359 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -3,7 +3,7 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: {{ .name }}-{{ $.Release.Name | trunc 40 }} + name: {{ .name }} namespace: {{ $namespace }} spec: destination: From cf65a6f5822a0ccc8b3899548c11a83e2415dc03 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sun, 3 Oct 2021 17:27:33 +1100 Subject: [PATCH 0128/1263] Restore gitops to the name of the argocd CR --- site/templates/argocd.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index c3b48cb4..f03f6b53 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -3,7 +3,8 @@ kind: ArgoCD metadata: finalizers: - argoproj.io/finalizer - name: {{ .Values.site.name }} + #name: {{ .Values.global.pattern }}-{{ .Values.site.name }} + name: {{ .Values.site.name }}-gitops annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: From 9dba5a355e98a51c4e89c3cb644817cd1f6ebf3b Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sun, 3 Oct 2021 17:30:35 +1100 Subject: [PATCH 0129/1263] Match the service accounts to the argocd name --- site/templates/argocd-super-role.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/site/templates/argocd-super-role.yaml b/site/templates/argocd-super-role.yaml index d51265bf..aa42900a 100644 --- a/site/templates/argocd-super-role.yaml +++ b/site/templates/argocd-super-role.yaml @@ -27,9 +27,11 @@ roleRef: name: cluster-admin subjects: - kind: ServiceAccount - name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-argocd-application-controller + #name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-argocd-application-controller + name: {{ .Values.site.name }}-gitops-argocd-application-controller namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }} # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - kind: ServiceAccount - name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-argocd-server + #name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-argocd-server + name: {{ .Values.site.name }}-gitops-argocd-server namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }} From 30fdb5fa5ba09265bbd6bca56a2cf06ffab6f4d6 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sun, 3 Oct 2021 22:21:29 +1100 Subject: [PATCH 0130/1263] Document what argocd values need to be kept in sync --- site/templates/argocd-super-role.yaml | 4 ++-- site/templates/argocd.yaml | 2 +- site/templates/gitops-namespace.yaml | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/site/templates/argocd-super-role.yaml b/site/templates/argocd-super-role.yaml index aa42900a..f4744b2d 100644 --- a/site/templates/argocd-super-role.yaml +++ b/site/templates/argocd-super-role.yaml @@ -27,11 +27,11 @@ roleRef: name: cluster-admin subjects: - kind: ServiceAccount - #name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-argocd-application-controller + # This is the {ArgoCD.name}-argocd-application-controller name: {{ .Values.site.name }}-gitops-argocd-application-controller namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }} # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - kind: ServiceAccount - #name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-gitops-argocd-server + # This is the {ArgoCD.name}-argocd-server name: {{ .Values.site.name }}-gitops-argocd-server namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }} diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index f03f6b53..fddcae4d 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -3,7 +3,7 @@ kind: ArgoCD metadata: finalizers: - argoproj.io/finalizer - #name: {{ .Values.global.pattern }}-{{ .Values.site.name }} + # Changing the name affects the ClusterRoleBinding, the generated secret, and route URL name: {{ .Values.site.name }}-gitops annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous diff --git a/site/templates/gitops-namespace.yaml b/site/templates/gitops-namespace.yaml index 54a0c29c..b6af2263 100644 --- a/site/templates/gitops-namespace.yaml +++ b/site/templates/gitops-namespace.yaml @@ -3,5 +3,9 @@ kind: Namespace metadata: labels: name: {{ $.Values.global.pattern }}-{{ .Values.site.name }} + # The name here needs to be consistent with + # - acm/templates/policies/application-policies.yaml + # - site/templates/applications.yaml + # - any references to secrets and route URLs in documentation name: {{ $.Values.global.pattern }}-{{ .Values.site.name }} spec: {} From f466ac5db02145eac8af2bb2adb8f7f5b6223e37 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 4 Oct 2021 08:43:03 +1100 Subject: [PATCH 0131/1263] Updated note regarding argo name --- site/templates/argocd.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index fddcae4d..44a0edba 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -3,7 +3,8 @@ kind: ArgoCD metadata: finalizers: - argoproj.io/finalizer - # Changing the name affects the ClusterRoleBinding, the generated secret, and route URL + # Changing the name affects the ClusterRoleBinding, the generated secret, + # route URL, and argocd.argoproj.io/managed-by annotations name: {{ .Values.site.name }}-gitops annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous From 08ad6c91df0ee3d5ff188ca4f07f1a30f69587c7 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 4 Oct 2021 08:43:45 +1100 Subject: [PATCH 0132/1263] Change the default 'name' label for namespaces --- site/templates/namespaces.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/templates/namespaces.yaml b/site/templates/namespaces.yaml index 6f08952a..5e5353bf 100644 --- a/site/templates/namespaces.yaml +++ b/site/templates/namespaces.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: Namespace metadata: labels: - name: {{ default "blueprint" $.Release.name }} + name: {{ default "pattern" $.Release.name }} argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.site.name }} name: {{ . }} spec: From 78df54ffd59d486697a5e728d06fcf655e16a6de Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 4 Oct 2021 14:07:35 -0500 Subject: [PATCH 0133/1263] Update common Makefile to have more parameters for secret making --- Makefile | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 5bed4804..321d7a0c 100644 --- a/Makefile +++ b/Makefile @@ -10,42 +10,57 @@ TARGET_BRANCH=$(shell git branch --show-current) # $$ is a Makefile idiom to preserve the single $ otherwise make consumes them # tabs are necessary # The patch to oc apply uses JSON because it's not as sensitive to indentation and doesn't need heredoc +# +# Makefiles that use this target must provide: +# PATTERN: The name of the pattern that is using it. This will be used programmatically for the source namespace +# TARGET_NAMESPACE: target namespace to install the secret into +# COMPONENT: The component of the target namespace. In industrial edge, factory or datacenter - and for the secret +# it needs to be datacenter because that's where the CI components run. .ONESHELL: SHELL = bash argosecret: + pattern=$(PATTERN) target_ns=$(TARGET_NAMESPACE) + src_ns="$(PATTERN)-$(COMPONENT)" + component=$(COMPONENT) + passwd_resource="secrets/$(COMPONENT)-gitops-cluster" + secret_name='argocd-env' ns=0 gitops=0 # Check for Namespaces and Secrets to be ready (it takes the cluster a while to deploy them) - while [ 1 ]; do + while : ; do + echo -n "Checking for namespace $$target_ns to exist..." if [ oc get namespace $$target_ns >/dev/null 2>/dev/null ]; then - echo "Waiting for namespace $$target_ns to be created" + echo "not yet" ns=0 + sleep 2 + continue else + echo "OK" ns=1 fi - pw=`oc -n openshift-gitops extract secrets/openshift-gitops-cluster --to=- 2>/dev/null` + echo -n "Checking for $$passwd_resource to be populated in $$src_ns..." + pw=`oc -n $$src_ns extract $$passwd_resource --to=- 2>/dev/null` if [ "$$?" == 0 ] && [ -n "$$pw" ]; then + echo "OK" gitops=1 else - echo "Waiting for password to be populated" + echo "not yet" gitops=0 - fi - - if [ "$$gitops" == 1 ] && [ "$$ns" == 1 ]; then - echo "Conditions met, managing secret in $$target_ns" - break - else sleep 2 + continue fi + + echo "Conditions met, managing secret $$secret_name in $$target_ns" + break done user=$$(echo admin | base64) password=$$(echo $$pw | base64) - echo "{ \"apiVersion\": \"v1\", \"kind\": \"Secret\", \"metadata\": { \"name\": \"argocd-env\", \"namespace\": \"$$target_ns\" }, \"data\": { \"ARGOCD_PASSWORD\": \"$$password\", \"ARGOCD_USERNAME\": \"$$user\" }, \"type\": \"Opaque\" }" | oc apply -f- + echo "{ \"apiVersion\": \"v1\", \"kind\": \"Secret\", \"metadata\": { \"name\": \"$$secret_name\", \"namespace\": \"$$target_ns\" }, \"data\": { \"ARGOCD_PASSWORD\": \"$$password\", \"ARGOCD_USERNAME\": \"$$user\" }, \"type\": \"Opaque\" }" | oc apply -f- # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show From ce95fceaa62de72c57a491812305847d7d722bdc Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 7 Oct 2021 09:18:53 -0500 Subject: [PATCH 0134/1263] Re-factor install to not require .ONESHELL features as Mac doesn't support them out of the box --- Makefile | 57 ++++++++----------------------------------------------- secret.sh | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 49 deletions(-) create mode 100755 secret.sh diff --git a/Makefile b/Makefile index 321d7a0c..06980703 100644 --- a/Makefile +++ b/Makefile @@ -10,73 +10,32 @@ TARGET_BRANCH=$(shell git branch --show-current) # $$ is a Makefile idiom to preserve the single $ otherwise make consumes them # tabs are necessary # The patch to oc apply uses JSON because it's not as sensitive to indentation and doesn't need heredoc -# +# # Makefiles that use this target must provide: # PATTERN: The name of the pattern that is using it. This will be used programmatically for the source namespace # TARGET_NAMESPACE: target namespace to install the secret into # COMPONENT: The component of the target namespace. In industrial edge, factory or datacenter - and for the secret # it needs to be datacenter because that's where the CI components run. -.ONESHELL: -SHELL = bash +# +# Note that this makefile *requires* GNU Make 3.82 or higher (for .ONESHELL). MacOS X comes with GNU Make 3.81. argosecret: - pattern=$(PATTERN) - target_ns=$(TARGET_NAMESPACE) - src_ns="$(PATTERN)-$(COMPONENT)" - component=$(COMPONENT) - passwd_resource="secrets/$(COMPONENT)-gitops-cluster" - secret_name='argocd-env' - ns=0 - gitops=0 - - # Check for Namespaces and Secrets to be ready (it takes the cluster a while to deploy them) - while : ; do - echo -n "Checking for namespace $$target_ns to exist..." - if [ oc get namespace $$target_ns >/dev/null 2>/dev/null ]; then - echo "not yet" - ns=0 - sleep 2 - continue - else - echo "OK" - ns=1 - fi - - echo -n "Checking for $$passwd_resource to be populated in $$src_ns..." - pw=`oc -n $$src_ns extract $$passwd_resource --to=- 2>/dev/null` - if [ "$$?" == 0 ] && [ -n "$$pw" ]; then - echo "OK" - gitops=1 - else - echo "not yet" - gitops=0 - sleep 2 - continue - fi - - echo "Conditions met, managing secret $$secret_name in $$target_ns" - break - done - - user=$$(echo admin | base64) - password=$$(echo $$pw | base64) - - echo "{ \"apiVersion\": \"v1\", \"kind\": \"Secret\", \"metadata\": { \"name\": \"$$secret_name\", \"namespace\": \"$$target_ns\" }, \"data\": { \"ARGOCD_PASSWORD\": \"$$password\", \"ARGOCD_USERNAME\": \"$$user\" }, \"type\": \"Opaque\" }" | oc apply -f- + PATTERN="$(PATTERN)" TARGET_NAMESPACE="$(TARGET_NAMESPACE)" COMPONENT="$(COMPONENT)" secret_name='argocd-env' common/secret.sh # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show show: - helm template common/install/ --name-template $(NAME) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) + helm template common/install/ --name-template $(NAME) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) init: git submodule update --init --recursive deploy: - helm install $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) + helm install $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) upgrade: - helm upgrade $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) + helm upgrade $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) uninstall: - helm uninstall $(NAME) + helm uninstall $(NAME) .phony: install diff --git a/secret.sh b/secret.sh new file mode 100755 index 00000000..fc9bde81 --- /dev/null +++ b/secret.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +#This script must receive arguments for the following parameters; it is easiest to pass them as environment variables from the command line. +#Example values from the first use for it, in industrial-edge +# +#This script is written to be POSIX-compliant, as not all sh are created equal - many are bash but +#Debian-derivatives use dash which doesn't support some bash syntax +# +#PATTERN=industrial-edge +#TARGET_NAMESPACE=manuela-ci +#COMPONENT=datacenter +#SECRET_NAME='argocd-env' + +passwd_resource="secrets/${COMPONENT}-gitops-cluster" +src_ns="${PATTERN}-${COMPONENT}" + +ns=0 +gitops=0 + +# Check for Namespaces and Secrets to be ready (it takes the cluster a few minutes to deploy them) +while [ 1 ] ; do + echo -n "Checking for namespace $TARGET_NAMESPACE to exist..." + if [ oc get namespace $TARGET_NAMESPACE >/dev/null 2>/dev/null ]; then + echo "not yet" + ns=0 + sleep 2 + continue + else + echo "OK" + ns=1 + fi + + echo -n "Checking for $passwd_resource to be populated in $src_ns..." + pw=`oc -n $src_ns extract $passwd_resource --to=- 2>/dev/null` + if [ "$?" = 0 ] && [ -n "$pw" ]; then + echo "OK" + gitops=1 + else + echo "not yet" + gitops=0 + sleep 2 + continue + fi + + echo "Conditions met, managing secret $SECRET_NAME in $TARGET_NAMESPACE" + break +done + +user=$(echo admin | base64) +password=$(echo $pw | base64) + +echo "{ \"apiVersion\": \"v1\", \"kind\": \"Secret\", \"metadata\": { \"name\": \"$SECRET_NAME\", \"namespace\": \"$TARGET_NAMESPACE\" }, \"data\": { \"ARGOCD_PASSWORD\": \"$password\", \"ARGOCD_USERNAME\": \"$user\" }, \"type\": \"Opaque\" }" | oc apply -f- From b77ff286a0bf396624943291adef28b2a310a50d Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 7 Oct 2021 09:23:09 -0500 Subject: [PATCH 0135/1263] Update Makefile doc and SECRET_NAME parameter --- Makefile | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 06980703..454f831b 100644 --- a/Makefile +++ b/Makefile @@ -4,22 +4,14 @@ NAME=$(shell basename `pwd`) TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:[a-z].*@%@%' -e 's%:%/%' -e 's%git@%https://%' ) TARGET_BRANCH=$(shell git branch --show-current) -# This is to eliminate the need to install and worry about a separate shell script somewhere in the directory structure -# There's a lot of GNU Make magic happening here: -# .ONESHELL passes the whole task into a single shell instance -# $$ is a Makefile idiom to preserve the single $ otherwise make consumes them -# tabs are necessary -# The patch to oc apply uses JSON because it's not as sensitive to indentation and doesn't need heredoc -# # Makefiles that use this target must provide: # PATTERN: The name of the pattern that is using it. This will be used programmatically for the source namespace # TARGET_NAMESPACE: target namespace to install the secret into # COMPONENT: The component of the target namespace. In industrial edge, factory or datacenter - and for the secret # it needs to be datacenter because that's where the CI components run. -# -# Note that this makefile *requires* GNU Make 3.82 or higher (for .ONESHELL). MacOS X comes with GNU Make 3.81. +# SECRET_NAME: The name of the secret to manage argosecret: - PATTERN="$(PATTERN)" TARGET_NAMESPACE="$(TARGET_NAMESPACE)" COMPONENT="$(COMPONENT)" secret_name='argocd-env' common/secret.sh + PATTERN="$(PATTERN)" TARGET_NAMESPACE="$(TARGET_NAMESPACE)" COMPONENT="$(COMPONENT)" SECRET_NAME='argocd-env' common/secret.sh # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show From 088bd27ea8b477e7ee3b078ef51aaf245ebc7d80 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 7 Oct 2021 09:25:29 -0500 Subject: [PATCH 0136/1263] Don't hardcode SECRET_NAME here --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 454f831b..3cd296c1 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ TARGET_BRANCH=$(shell git branch --show-current) # it needs to be datacenter because that's where the CI components run. # SECRET_NAME: The name of the secret to manage argosecret: - PATTERN="$(PATTERN)" TARGET_NAMESPACE="$(TARGET_NAMESPACE)" COMPONENT="$(COMPONENT)" SECRET_NAME='argocd-env' common/secret.sh + PATTERN="$(PATTERN)" TARGET_NAMESPACE="$(TARGET_NAMESPACE)" COMPONENT="$(COMPONENT)" SECRET_NAME="$(SECRET_NAME)" common/secret.sh # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show From cbee5dc2b69fd0c49be48edcc8b0759097b0c50d Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 7 Oct 2021 09:32:49 -0500 Subject: [PATCH 0137/1263] Move script to scripts dir and call it from there --- Makefile | 2 +- secret.sh => scripts/secret.sh | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename secret.sh => scripts/secret.sh (100%) diff --git a/Makefile b/Makefile index 3cd296c1..43867eb6 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ TARGET_BRANCH=$(shell git branch --show-current) # it needs to be datacenter because that's where the CI components run. # SECRET_NAME: The name of the secret to manage argosecret: - PATTERN="$(PATTERN)" TARGET_NAMESPACE="$(TARGET_NAMESPACE)" COMPONENT="$(COMPONENT)" SECRET_NAME="$(SECRET_NAME)" common/secret.sh + PATTERN="$(PATTERN)" TARGET_NAMESPACE="$(TARGET_NAMESPACE)" COMPONENT="$(COMPONENT)" SECRET_NAME="$(SECRET_NAME)" common/scripts/secret.sh # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show diff --git a/secret.sh b/scripts/secret.sh similarity index 100% rename from secret.sh rename to scripts/secret.sh From d11c1328bea11fc8cb7cd9118d403095eb22c57e Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 19 Oct 2021 10:06:38 -0600 Subject: [PATCH 0138/1263] New s3 secrets file for central-s3 support --- install/templates/secrets/s3-secret.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 install/templates/secrets/s3-secret.yaml diff --git a/install/templates/secrets/s3-secret.yaml b/install/templates/secrets/s3-secret.yaml new file mode 100644 index 00000000..1bfe126b --- /dev/null +++ b/install/templates/secrets/s3-secret.yaml @@ -0,0 +1,13 @@ +kind: Secret +apiVersion: v1 +metadata: + name: s3-secret +type: Opaque +data: + # As the original developers point out to themselves in kafka-tls-certificate-and-key.yaml + # + # It is beyond a bad practise to check in private keys into git repos + # + # Pre-create as part of the initial 'helm install' chart + #application.properties: ICBzMy5hY2Nlc3NLZXk6IEN4ZkJCQlZGV1hEZGo1ek50MjZhCiAgczMuc2VjcmV0S2V5OiBjMXpNaVVJSnhJVjBMR3RyeXVDaTJEaDB2R0JtdUdtRzdNbE42YzdSCg== + application.properties: {{ .Values.secrets.aws.s3Secret }} From 7e6141d9d1c4fa539345f78c2cfa3dc338de86a9 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 19 Oct 2021 10:17:26 -0600 Subject: [PATCH 0139/1263] Took care of merge conflicts with s3-secret.yaml --- install/templates/secrets/s3-secret.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/install/templates/secrets/s3-secret.yaml b/install/templates/secrets/s3-secret.yaml index 1bfe126b..5dd37281 100644 --- a/install/templates/secrets/s3-secret.yaml +++ b/install/templates/secrets/s3-secret.yaml @@ -4,10 +4,10 @@ metadata: name: s3-secret type: Opaque data: - # As the original developers point out to themselves in kafka-tls-certificate-and-key.yaml - # - # It is beyond a bad practise to check in private keys into git repos - # # Pre-create as part of the initial 'helm install' chart - #application.properties: ICBzMy5hY2Nlc3NLZXk6IEN4ZkJCQlZGV1hEZGo1ek50MjZhCiAgczMuc2VjcmV0S2V5OiBjMXpNaVVJSnhJVjBMR3RyeXVDaTJEaDB2R0JtdUdtRzdNbE42YzdSCg== + # Create a file with the following: + # s3.accessKey: KEY + # s3.secretKey: secret key + #application.properties: base64 encrypted value of the above file + # This should live in the values-secret.yaml file application.properties: {{ .Values.secrets.aws.s3Secret }} From 54eedbb681fdb5bbde7d05a120beca1ec1cf583d Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Mon, 8 Nov 2021 15:21:45 -0700 Subject: [PATCH 0140/1263] Adding functionalist to have a list of namespaces for a particular subscription --- site/templates/subsciptions.yaml | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 site/templates/subsciptions.yaml diff --git a/site/templates/subsciptions.yaml b/site/templates/subsciptions.yaml new file mode 100644 index 00000000..c1cbcd49 --- /dev/null +++ b/site/templates/subsciptions.yaml @@ -0,0 +1,40 @@ +{{- range .Values.site.subscriptions }} +{{- $subs := . }} +{{- $installPlanValue := .installPlanApproval }} + +{{- if $subs.namespaces }} +{{- range .namespaces }} +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: {{ $subs.name }} + namespace: {{ default "openshift-operators" . }} +spec: + name: {{ $subs.name }} + source: {{ default "redhat-operators" $subs.source }} + sourceNamespace: {{ default "openshift-marketplace" $subs.sourceNamespace }} + channel: {{ default "stable" $subs.channel }} + installPlanApproval: {{ coalesce $installPlanValue $.Values.global.options.installPlanApproval }} + {{- if $.Values.global.options.useCSV }} + startingCSV: {{ $subs.csv }} + {{- end }} +--- +{{- end }} +{{- else }} +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: {{ $subs.name }} + namespace: {{ default "openshift-operators" $subs.namespace }} +spec: + name: {{ $subs.name }} + source: {{ default "redhat-operators" $subs.source }} + sourceNamespace: {{ default "openshift-marketplace" $subs.sourceNamespace }} + channel: {{ default "stable" $subs.channel }} + installPlanApproval: {{ coalesce $installPlanValue $.Values.global.options.installPlanApproval }} + {{- if $.Values.global.options.useCSV }} + startingCSV: {{ $subs.csv }} + {{- end }} +--- +{{- end }} +{{- end }} From f071de888e6da8ea6643c8255e780d45ab9a0c6f Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 9 Nov 2021 14:00:09 -0600 Subject: [PATCH 0141/1263] Enhance compatibility with older versions of git --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 43867eb6..6edb4ec6 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,8 @@ BOOTSTRAP=1 SECRETS=~/values-secret.yaml NAME=$(shell basename `pwd`) TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:[a-z].*@%@%' -e 's%:%/%' -e 's%git@%https://%' ) -TARGET_BRANCH=$(shell git branch --show-current) +# git branch --show-current is also available as of git 2.22, but we will use this for compatibility +TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) # Makefiles that use this target must provide: # PATTERN: The name of the pattern that is using it. This will be used programmatically for the source namespace From 13aeeb051e889773cf66c15509d3304bfdb1379d Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 17 Nov 2021 16:09:23 +1100 Subject: [PATCH 0142/1263] Trim the example datacenter site --- values-datacenter.yaml | 118 ++++------------------------------------- 1 file changed, 10 insertions(+), 108 deletions(-) diff --git a/values-datacenter.yaml b/values-datacenter.yaml index d6814967..28da54ad 100644 --- a/values-datacenter.yaml +++ b/values-datacenter.yaml @@ -8,20 +8,9 @@ global: site: name: datacenter - proposedOptions: - manageGitops: True - isHubCluster: True - namespaces: - open-cluster-management - - manuela-ml-workspace - - manuela-tst-all - - manuela-ci - - manuela-data-lake-central-s3-store - - manuela-data-lake-central-kafka-cluster - - operatorgroupsExcludes: - - manuela-ml-workspace + - application-ci subscriptions: - name: advanced-cluster-management @@ -29,42 +18,11 @@ site: channel: release-2.3 csv: advanced-cluster-management.v2.3.2 - - name: seldon-operator - namespace: manuela-ml-workspace - source: community-operators - csv: seldon-operator.v1.7.0 - - - name: opendatahub-operator - source: community-operators - csv: opendatahub-operator.v1.1.0 - - name: openshift-pipelines-operator-rh csv: redhat-openshift-pipelines.v1.5.1 - # TODO: Allow namespace to be a list - - name: amq-streams - namespace: manuela-data-lake-central-kafka-cluster - channel: amq-streams-1.7.x - csv: amqstreams.v1.7.1 - - - name: amq-streams - namespace: manuela-tst-all - channel: amq-streams-1.7.x - csv: amqstreams.v1.7.1 - - - name: red-hat-camel-k - namespace: manuela-data-lake-central-s3-store - channel: 1.4.x - csv: red-hat-camel-k-operator.v1.4.0 - - - name: red-hat-camel-k - namespace: manuela-tst-all - channel: 1.4.x - csv: red-hat-camel-k-operator.v1.4.0 - projects: - datacenter - - datalake applications: - name: acm @@ -77,82 +35,24 @@ site: jsonPointers: - /spec/loggingCA - - name: odh - namespace: default - project: datacenter - path: charts/datacenter/opendatahub - kustomize: True - - name: pipelines - namespace: manuela-ci + namespace: application-ci project: datacenter path: charts/datacenter/pipelines - - name: central-kafka - namespace: manuela-data-lake-central-kafka-cluster - project: datalake - path: charts/datacenter/kafka - kustomize: True - ignoreDifferences: - - group: apps - kind: Deployment - jsonPointers: - - /spec/replicas - - group: route.openshift.io - kind: Route - jsonPointers: - - /status - - group: image.openshift.io - kind: ImageStream - jsonPointers: - - /spec/tags - - group: apps.openshift.io - kind: DeploymentConfig - jsonPointers: - - /spec/template/spec/containers/0/image - - - name: central-s3 - namespace: manuela-data-lake-central-s3-store - project: datalake - path: charts/datacenter/central-s3-store - kustomize: True - ignoreDifferences: - - group: apps - kind: Deployment - jsonPointers: - - /spec/replicas - - group: route.openshift.io - kind: Route - jsonPointers: - - /status - - group: image.openshift.io - kind: ImageStream - jsonPointers: - - /spec/tags - - group: apps.openshift.io - kind: DeploymentConfig - jsonPointers: - - /spec/template/spec/containers/0/image - - - name: manuela-test - namespace: manuela-tst-all - project: datacenter - path: charts/datacenter/manuela-tst - plugin: - name: helm-with-kustomize # # To have apps in multiple flavors, use namespaces and use helm overrides as appropriate # # - name: pipelines -# namespace: production +# namespace: testing # project: datacenter # path: applications/pipeline # repoURL: https://github.com/you/applications.git -# targetRevision: stable +# targetRevision: main # overrides: -# - name: myparam -# value: myparam +# - name: test +# value: true # # - name: pipelines # namespace: staging @@ -174,8 +74,10 @@ site: # targetRevision: main managedSites: - - name: factory - # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git + - name: edge + # Optional - Point to a different repo + # repoURL: https://github.com/dagger-refuse-cool/mySite.git + # Must contain values-{sitename}.yaml at the top level targetRevision: main helmOverrides: # Values must be strings! From 2f3becce92d9030126d7a0fc6c30f63cf618435f Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 17 Nov 2021 16:13:08 +1100 Subject: [PATCH 0143/1263] Support real helm charts too --- site/templates/applications.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 51d6b359..7442dd7f 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -13,7 +13,11 @@ spec: source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} + {{- if .chart }} + chart: {{ .chart }} + {{- else }} path: {{ .path }} + {{- end }} {{- if .plugin }} plugin: {{ .plugin | toPrettyJson }} {{- else if not .kustomize }} From a6aa1175a06aaba81b7c19582837fed73bab1240 Mon Sep 17 00:00:00 2001 From: jrickard Date: Wed, 17 Nov 2021 19:31:14 -0600 Subject: [PATCH 0144/1263] Adds the if control to force booleans to be string type for argo on helm/vault overrides --- site/templates/applications.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/site/templates/applications.yaml b/site/templates/applications.yaml index 7442dd7f..4f2c345e 100644 --- a/site/templates/applications.yaml +++ b/site/templates/applications.yaml @@ -40,6 +40,9 @@ spec: {{- range .overrides }} - name: {{ .name }} value: {{ .value }} + {{- if .forceString }} + forceString: true + {{- end }} {{- end }} {{- end }} {{- if .ignoreDifferences }} From 465c45fef409b6c2525b2838476a506973f62735 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 23 Nov 2021 15:43:26 +1100 Subject: [PATCH 0145/1263] Improved secrets handling in pipelines (#10) * Update common with new secret management * Add values-global to Makefile Co-authored-by: Wolfgang Kulhanek --- Makefile | 6 +++--- .../templates/{secrets => }/namespace.yaml | 7 ++----- install/templates/pipeline/rolebinding.yaml | 18 ++++++++++++++++ .../templates/pipeline/serviceaccount.yaml | 13 ++++++++++++ install/templates/secrets/github-secret.yaml | 13 ------------ install/templates/secrets/quay-secret.yaml | 21 ------------------- .../secrets/secret-git-repo-credentials.yaml | 14 +++++++++++++ .../secret-image-registry-credentials.yaml | 16 ++++++++++++++ 8 files changed, 66 insertions(+), 42 deletions(-) rename install/templates/{secrets => }/namespace.yaml (64%) create mode 100644 install/templates/pipeline/rolebinding.yaml create mode 100644 install/templates/pipeline/serviceaccount.yaml delete mode 100644 install/templates/secrets/github-secret.yaml delete mode 100644 install/templates/secrets/quay-secret.yaml create mode 100644 install/templates/secrets/secret-git-repo-credentials.yaml create mode 100644 install/templates/secrets/secret-image-registry-credentials.yaml diff --git a/Makefile b/Makefile index 6edb4ec6..e6a02a76 100644 --- a/Makefile +++ b/Makefile @@ -17,16 +17,16 @@ argosecret: # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show show: - helm template common/install/ --name-template $(NAME) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) + helm template common/install/ --name-template $(NAME) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) -f values-global.yaml init: git submodule update --init --recursive deploy: - helm install $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) + helm install $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) -f values-global.yaml upgrade: - helm upgrade $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) + helm upgrade $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) -f values-global.yaml uninstall: helm uninstall $(NAME) diff --git a/install/templates/secrets/namespace.yaml b/install/templates/namespace.yaml similarity index 64% rename from install/templates/secrets/namespace.yaml rename to install/templates/namespace.yaml index e51255d8..936decd8 100644 --- a/install/templates/secrets/namespace.yaml +++ b/install/templates/namespace.yaml @@ -1,4 +1,5 @@ {{- if .Values.main.options.bootstrap }} +--- apiVersion: v1 kind: Namespace metadata: @@ -6,8 +7,4 @@ metadata: labels: manuela-role: pipeline app.kubernetes.io/instance: manuela - #argocd.argoproj.io/managed-by: openshift-gitops -spec: - finalizers: - - kubernetes -{{- end }} \ No newline at end of file +{{- end }} diff --git a/install/templates/pipeline/rolebinding.yaml b/install/templates/pipeline/rolebinding.yaml new file mode 100644 index 00000000..24fb7e2d --- /dev/null +++ b/install/templates/pipeline/rolebinding.yaml @@ -0,0 +1,18 @@ +{{- if .Values.main.options.bootstrap }} +{{- if eq .Values.global.imageregistry.type "openshift" }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: pipeline-edit + namespace: openshift +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: edit +subjects: +- kind: ServiceAccount + name: pipeline + namespace: manuela-ci +{{- end }} +{{- end }} diff --git a/install/templates/pipeline/serviceaccount.yaml b/install/templates/pipeline/serviceaccount.yaml new file mode 100644 index 00000000..2f250d29 --- /dev/null +++ b/install/templates/pipeline/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.main.options.bootstrap }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: pipeline + namespace: manuela-ci +secrets: +- name: git-repo-credentials +{{- if eq .Values.global.imageregistry.type "quay" }} +- name: image-registry-credentials +{{- end }} +{{- end }} diff --git a/install/templates/secrets/github-secret.yaml b/install/templates/secrets/github-secret.yaml deleted file mode 100644 index 7458a79e..00000000 --- a/install/templates/secrets/github-secret.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.main.options.bootstrap }} -kind: Secret -apiVersion: v1 -metadata: - name: github - namespace: manuela-ci -type: Opaque -data: - # Go to: https://github.com/settings/tokens - # Then: echo -n 'your string value' | base64 - token: {{ .Values.secrets.git.authToken }} - user: {{ .Values.secrets.git.accountToken }} -{{- end }} \ No newline at end of file diff --git a/install/templates/secrets/quay-secret.yaml b/install/templates/secrets/quay-secret.yaml deleted file mode 100644 index 6fb00ac0..00000000 --- a/install/templates/secrets/quay-secret.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if .Values.main.options.bootstrap }} -kind: Secret -apiVersion: v1 -metadata: - name: quay-build-secret - namespace: manuela-ci -type: kubernetes.io/dockerconfigjson -data: - # Quay -> Robot Accounts -> Kubernetes Secret -> View - # .dockerconfigjson is base64 encoded, conforming to... - #{ - # "auths": { - # "quay.io": { - # "auth": "", - # "email": "" - # } - # } - #} - # auths["quay.io"]["auth"] is a base64 encoded string of IMAGE_REGISTRY_USER:IMAGE_REGISTRY_PASSWORD - .dockerconfigjson: {{ .Values.secrets.quay.authToken }} -{{- end }} \ No newline at end of file diff --git a/install/templates/secrets/secret-git-repo-credentials.yaml b/install/templates/secrets/secret-git-repo-credentials.yaml new file mode 100644 index 00000000..b5ca3713 --- /dev/null +++ b/install/templates/secrets/secret-git-repo-credentials.yaml @@ -0,0 +1,14 @@ +{{- if .Values.main.options.bootstrap }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: git-repo-credentials + namespace: manuela-ci + annotations: + tekton.dev/git-0: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }} +type: kubernetes.io/basic-auth +stringData: + username: {{ .Values.global.git.username }} + password: {{ .Values.secrets.git.token }} +{{- end }} diff --git a/install/templates/secrets/secret-image-registry-credentials.yaml b/install/templates/secrets/secret-image-registry-credentials.yaml new file mode 100644 index 00000000..047099df --- /dev/null +++ b/install/templates/secrets/secret-image-registry-credentials.yaml @@ -0,0 +1,16 @@ +{{- if .Values.main.options.bootstrap }} +{{- if eq .Values.global.imageregistry.type "quay" }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: openshift-registry-credentials + namespace: manuela-ci + annotations: + tekton.dev/docker-0: "https://{{ .Values.global.imageregistry.hostname }}" +type: kubernetes.io/basic-auth +stringData: + username: {{ .Values.global.imageregistry.account }} + password: {{ .Values.secrets.imageregistry.token }} +{{- end }} +{{- end }} From 9cf93ccc1fd236166a7a96fdd41dd46a3b29bffa Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 23 Nov 2021 15:45:41 +1100 Subject: [PATCH 0146/1263] Add note regarding tekton annotation --- install/templates/secrets/secret-git-repo-credentials.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/install/templates/secrets/secret-git-repo-credentials.yaml b/install/templates/secrets/secret-git-repo-credentials.yaml index b5ca3713..d726603d 100644 --- a/install/templates/secrets/secret-git-repo-credentials.yaml +++ b/install/templates/secrets/secret-git-repo-credentials.yaml @@ -6,6 +6,7 @@ metadata: name: git-repo-credentials namespace: manuela-ci annotations: + # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ tekton.dev/git-0: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }} type: kubernetes.io/basic-auth stringData: From ecb5a52c6a2ab8b7c06331c27fea7ce5e13370a5 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 23 Nov 2021 15:46:05 +1100 Subject: [PATCH 0147/1263] Add note regarding tekon annotation --- install/templates/secrets/secret-image-registry-credentials.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/install/templates/secrets/secret-image-registry-credentials.yaml b/install/templates/secrets/secret-image-registry-credentials.yaml index 047099df..2aa5eebd 100644 --- a/install/templates/secrets/secret-image-registry-credentials.yaml +++ b/install/templates/secrets/secret-image-registry-credentials.yaml @@ -7,6 +7,7 @@ metadata: name: openshift-registry-credentials namespace: manuela-ci annotations: + # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ tekton.dev/docker-0: "https://{{ .Values.global.imageregistry.hostname }}" type: kubernetes.io/basic-auth stringData: From 323331c02839647778b260908a1ace779624b001 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 23 Nov 2021 15:50:52 +1100 Subject: [PATCH 0148/1263] Ensure updated secret template variables are defined --- install/values.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/install/values.yaml b/install/values.yaml index 6beda204..bb946466 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -1,8 +1,8 @@ main: git: - repoURL: git@github.com:dagger-refuse-cool/gitops.git + repoURL: git@github.com:hybrid-cloud-patterns/gitops.git revision: main - #valuesDirectoryURL: https://github.com/dagger-refuse-cool/edge-gitops/raw/main/ + #valuesDirectoryURL: https://github.com/hybrid-cloud-patterns/industrial-edge/raw/main/ options: syncPolicy: Automatic @@ -20,12 +20,12 @@ main: secrets: # NEVER COMMIT THESE VALUES TO GIT enabled: false - quay: - # Quay -> Robot Accounts -> Kubernetes Secret -> View - authToken: BASE64STRING + imageregistry: + # Quay -> Robot Accounts -> Docker Login + account: STRING + token: STRING git: # Go to: https://github.com/settings/tokens - # Then: echo -n 'your string value' | base64 - authToken: BASE64STRING - accountToken: BASE64STRING + username: STRING + token: STRING From 924eb475a25f71a838903234fff41c2c10a161de Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 23 Nov 2021 15:51:49 +1100 Subject: [PATCH 0149/1263] Missing template variable --- install/values.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/install/values.yaml b/install/values.yaml index bb946466..f18a85db 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -21,6 +21,7 @@ secrets: # NEVER COMMIT THESE VALUES TO GIT enabled: false imageregistry: + type: quay # Quay -> Robot Accounts -> Docker Login account: STRING token: STRING From 6ed2f363a60fa5705b63ab636a77e4a3ac239128 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 23 Nov 2021 15:59:06 +1100 Subject: [PATCH 0150/1263] Update values.yaml --- install/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/values.yaml b/install/values.yaml index f18a85db..49678d36 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -22,7 +22,7 @@ secrets: enabled: false imageregistry: type: quay - # Quay -> Robot Accounts -> Docker Login + # Quay -> Robot Accounts -> Robot Login account: STRING token: STRING From 330e08b1b99fd3a5dcd4f6aeb68a1a46273018b2 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 1 Dec 2021 14:30:03 +1100 Subject: [PATCH 0151/1263] Avoid assumptions about the site being called datacenter leaking into patterns --- install/values.yaml | 2 +- values-datacenter.yaml => values-example.yaml | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename values-datacenter.yaml => values-example.yaml (100%) diff --git a/install/values.yaml b/install/values.yaml index 49678d36..4acd68e0 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -15,7 +15,7 @@ main: source: redhat-operators csv: v1.2.0 - siteName: datacenter + siteName: example secrets: # NEVER COMMIT THESE VALUES TO GIT diff --git a/values-datacenter.yaml b/values-example.yaml similarity index 100% rename from values-datacenter.yaml rename to values-example.yaml From 1bfc67e53017334e69036820da5aceb913e3f37a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 1 Dec 2021 15:33:29 +1100 Subject: [PATCH 0152/1263] Add missing template variables --- install/values.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/install/values.yaml b/install/values.yaml index 4acd68e0..3948174d 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -17,11 +17,14 @@ main: siteName: example +global: + imageregistry: + type: quay + secrets: # NEVER COMMIT THESE VALUES TO GIT enabled: false imageregistry: - type: quay # Quay -> Robot Accounts -> Robot Login account: STRING token: STRING @@ -30,3 +33,6 @@ secrets: # Go to: https://github.com/settings/tokens username: STRING token: STRING + + aws: + s3Secret: BASE64STRING \ No newline at end of file From 2687bb1b4a3a47d9e630c1098f79de2b497bbd12 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 1 Dec 2021 15:34:45 +1100 Subject: [PATCH 0153/1263] Standardize on Values.secrets for usernames as well as passwords --- install/templates/secrets/secret-git-repo-credentials.yaml | 2 +- .../templates/secrets/secret-image-registry-credentials.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/templates/secrets/secret-git-repo-credentials.yaml b/install/templates/secrets/secret-git-repo-credentials.yaml index d726603d..a7ffbaa4 100644 --- a/install/templates/secrets/secret-git-repo-credentials.yaml +++ b/install/templates/secrets/secret-git-repo-credentials.yaml @@ -10,6 +10,6 @@ metadata: tekton.dev/git-0: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }} type: kubernetes.io/basic-auth stringData: - username: {{ .Values.global.git.username }} + username: {{ .Values.secrets.git.username }} password: {{ .Values.secrets.git.token }} {{- end }} diff --git a/install/templates/secrets/secret-image-registry-credentials.yaml b/install/templates/secrets/secret-image-registry-credentials.yaml index 2aa5eebd..5ec43e87 100644 --- a/install/templates/secrets/secret-image-registry-credentials.yaml +++ b/install/templates/secrets/secret-image-registry-credentials.yaml @@ -11,7 +11,7 @@ metadata: tekton.dev/docker-0: "https://{{ .Values.global.imageregistry.hostname }}" type: kubernetes.io/basic-auth stringData: - username: {{ .Values.global.imageregistry.account }} + username: {{ .Values.secrets.imageregistry.account }} password: {{ .Values.secrets.imageregistry.token }} {{- end }} {{- end }} From a69820137d79d7a2085e2a721b551157bdb427a2 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 1 Dec 2021 15:35:35 +1100 Subject: [PATCH 0154/1263] Sync the example global values file with the i-e pattern --- values-global.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/values-global.yaml b/values-global.yaml index faa2d905..10438a7e 100644 --- a/values-global.yaml +++ b/values-global.yaml @@ -1,6 +1,14 @@ global: - valuesDirectoryURL: https://github.com/dagger-refuse-cool/edge-gitops/raw/main/ + valuesDirectoryURL: https://github.com/hybrid-cloud-patterns/common/raw/main/ options: useCSV: True syncPolicy: Manual installPlanApproval: Automatic + + git: + hostname: github.com + # Account is the user or organization under which the pattern repos lives + account: hybrid-cloud-patterns + email: someone@somewhere.com + dev_revision: main + From bfd9eb360bb82a39afcc628043f727473bcc2977 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 1 Dec 2021 15:36:02 +1100 Subject: [PATCH 0155/1263] Sync the plugin example application with the i-e pattern --- examples/kustomize-renderer/environment.yaml | 3 +-- examples/kustomize-renderer/templates/environment.yaml | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/kustomize-renderer/environment.yaml b/examples/kustomize-renderer/environment.yaml index c027f82a..b3ad1ea2 100644 --- a/examples/kustomize-renderer/environment.yaml +++ b/examples/kustomize-renderer/environment.yaml @@ -6,8 +6,7 @@ data: IMAGE_PROVIDER: {{ .Values.global.quay.provider }} IMAGE_ACCOUNT: {{ .Values.global.quay.account }} GIT_EMAIL: {{ .Values.global.git.email }} - GIT_USERNAME: {{ .Values.global.git.username }} - GIT_DEV_REPO_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/manuela-dev.git + GIT_DEV_REPO_URL: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }}/manuela-dev.git GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} GIT_OPS_REPO_TEST_URL: {{ .Values.global.repoURL }} GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.targetRevision }} diff --git a/examples/kustomize-renderer/templates/environment.yaml b/examples/kustomize-renderer/templates/environment.yaml index c027f82a..b3ad1ea2 100644 --- a/examples/kustomize-renderer/templates/environment.yaml +++ b/examples/kustomize-renderer/templates/environment.yaml @@ -6,8 +6,7 @@ data: IMAGE_PROVIDER: {{ .Values.global.quay.provider }} IMAGE_ACCOUNT: {{ .Values.global.quay.account }} GIT_EMAIL: {{ .Values.global.git.email }} - GIT_USERNAME: {{ .Values.global.git.username }} - GIT_DEV_REPO_URL: https://{{ .Values.global.git.provider }}/{{ .Values.global.git.account }}/manuela-dev.git + GIT_DEV_REPO_URL: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }}/manuela-dev.git GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} GIT_OPS_REPO_TEST_URL: {{ .Values.global.repoURL }} GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.targetRevision }} From a0210755f210a62708a001c6bd323037c0e3a8a9 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 1 Dec 2021 15:36:38 +1100 Subject: [PATCH 0156/1263] Ensure helm options stay in sync and add a simple test --- Makefile | 12 +++-- reference-output.yaml | 119 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 reference-output.yaml diff --git a/Makefile b/Makefile index e6a02a76..2f77bf93 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ NAME=$(shell basename `pwd`) TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:[a-z].*@%@%' -e 's%:%/%' -e 's%git@%https://%' ) # git branch --show-current is also available as of git 2.22, but we will use this for compatibility TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) +HELM_OPTS=--set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f values-global.yaml -f $(SECRETS) # Makefiles that use this target must provide: # PATTERN: The name of the pattern that is using it. This will be used programmatically for the source namespace @@ -17,16 +18,21 @@ argosecret: # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show show: - helm template common/install/ --name-template $(NAME) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) -f values-global.yaml + helm template common/install/ --name-template $(NAME) $(HELM_OPTS) + +test: + make -s show > .output + diff -u reference-output.yaml .output + rm -f .output init: git submodule update --init --recursive deploy: - helm install $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) -f values-global.yaml + helm install $(NAME) common/install/ $(HELM_OPTS) upgrade: - helm upgrade $(NAME) common/install/ --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f $(SECRETS) -f values-global.yaml + helm upgrade $(NAME) common/install/ $(HELM_OPTS) uninstall: helm uninstall $(NAME) diff --git a/reference-output.yaml b/reference-output.yaml new file mode 100644 index 00000000..5294bfcc --- /dev/null +++ b/reference-output.yaml @@ -0,0 +1,119 @@ +--- +# Source: pattern-install/templates/argocd/namespace.yaml +# Pre-create so we can create our argo app for keeping subscriptions in sync +# Do it here so that we don't try to sync it in the future +apiVersion: v1 +kind: Namespace +metadata: + name: openshift-gitops +--- +# Source: pattern-install/templates/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: manuela-ci + labels: + manuela-role: pipeline + app.kubernetes.io/instance: manuela +--- +# Source: pattern-install/templates/pipeline/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: pipeline + namespace: manuela-ci +secrets: +- name: git-repo-credentials +- name: image-registry-credentials +--- +# Source: pattern-install/templates/secrets/s3-secret.yaml +kind: Secret +apiVersion: v1 +metadata: + name: s3-secret +type: Opaque +data: + # Pre-create as part of the initial 'helm install' chart + # Create a file with the following: + # s3.accessKey: KEY + # s3.secretKey: secret key + #application.properties: base64 encrypted value of the above file + # This should live in the values-secret.yaml file + application.properties: BASE64STRING +--- +# Source: pattern-install/templates/secrets/secret-git-repo-credentials.yaml +apiVersion: v1 +kind: Secret +metadata: + name: git-repo-credentials + namespace: manuela-ci + annotations: + # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ + tekton.dev/git-0: https://github.com/hybrid-cloud-patterns +type: kubernetes.io/basic-auth +stringData: + username: STRING + password: STRING +--- +# Source: pattern-install/templates/secrets/secret-image-registry-credentials.yaml +apiVersion: v1 +kind: Secret +metadata: + name: openshift-registry-credentials + namespace: manuela-ci + annotations: + # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ + tekton.dev/docker-0: "https://" +type: kubernetes.io/basic-auth +stringData: + username: STRING + password: STRING +--- +# Source: pattern-install/templates/argocd/application.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: common-example + namespace: openshift-gitops +spec: + destination: + name: in-cluster + namespace: common-example + project: default + source: + repoURL: https://github.com/beekhof/common.git + targetRevision: main + path: common/site + helm: + valueFiles: + - "https://github.com/beekhof/patterns/raw/main/values-global.yaml" + - "https://github.com/beekhof/patterns/raw/main/values-example.yaml" + # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.valuesDirectoryURL + value: https://github.com/beekhof/patterns/raw/main + - name: global.pattern + value: common + syncPolicy: + automated: {} +--- +# Source: pattern-install/templates/argocd/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: "" +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace From 1ac2280ebf9526116526314ad95c19970e4588ea Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 1 Dec 2021 16:01:40 +1100 Subject: [PATCH 0157/1263] Make the test more stable and add missing values --- Makefile | 6 ++++-- examples/values-secret.yaml | 22 ++++++++++++++++++++++ install/values.yaml | 7 ++++++- values-global.yaml | 1 - 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 examples/values-secret.yaml diff --git a/Makefile b/Makefile index 2f77bf93..232704ed 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,9 @@ NAME=$(shell basename `pwd`) TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:[a-z].*@%@%' -e 's%:%/%' -e 's%git@%https://%' ) # git branch --show-current is also available as of git 2.22, but we will use this for compatibility TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) -HELM_OPTS=--set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -f values-global.yaml -f $(SECRETS) + +# --set values always take precedence over the contents of -f +HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) # Makefiles that use this target must provide: # PATTERN: The name of the pattern that is using it. This will be used programmatically for the source namespace @@ -21,7 +23,7 @@ show: helm template common/install/ --name-template $(NAME) $(HELM_OPTS) test: - make -s show > .output + make -s TARGET_REPO=https://github.com/pattern-clone/common SECRETS=common/examples/values-secret.yaml show > .output diff -u reference-output.yaml .output rm -f .output diff --git a/examples/values-secret.yaml b/examples/values-secret.yaml new file mode 100644 index 00000000..a4f08195 --- /dev/null +++ b/examples/values-secret.yaml @@ -0,0 +1,22 @@ +main: + git: + repoURL: https://github.com/example/common2 + +secrets: + # NEVER COMMIT THESE VALUES TO GIT + enabled: true + imageregistry: + # Quay -> Robot Accounts -> Robot Login + account: test-account + token: test-quay-token + + git: + # Go to: https://github.com/settings/tokens + username: test-user + token: test-git-token + + aws: + s3Secret: test-secret + + + diff --git a/install/values.yaml b/install/values.yaml index 3948174d..e26b0117 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -21,6 +21,11 @@ global: imageregistry: type: quay + git: + hostname: github.com + # Account is the user or organization under which the pattern repo lives + account: hybrid-cloud-patterns + secrets: # NEVER COMMIT THESE VALUES TO GIT enabled: false @@ -35,4 +40,4 @@ secrets: token: STRING aws: - s3Secret: BASE64STRING \ No newline at end of file + s3Secret: BASE64STRING diff --git a/values-global.yaml b/values-global.yaml index 10438a7e..f4ed30a2 100644 --- a/values-global.yaml +++ b/values-global.yaml @@ -1,5 +1,4 @@ global: - valuesDirectoryURL: https://github.com/hybrid-cloud-patterns/common/raw/main/ options: useCSV: True syncPolicy: Manual From 083ae461c4745528c4818f34372f514c12e9e6a0 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 2 Dec 2021 10:26:50 +1100 Subject: [PATCH 0158/1263] Extend the unit tests to the acm and site charts --- Makefile | 13 +- acm/values.yaml | 17 +- .../values-example.yaml | 36 +- examples/values-secret.yaml | 2 +- install/values.yaml | 4 +- scripts/test.sh | 32 ++ site/values.yaml | 5 +- tests/acm-naked.expected.yaml | 151 ++++++++ tests/acm-normal.expected.yaml | 261 ++++++++++++++ tests/install-naked.expected.yaml | 119 ++++++ tests/install-normal.expected.yaml | 119 ++++++ tests/site-naked.expected.yaml | 148 ++++++++ tests/site-normal.expected.yaml | 340 ++++++++++++++++++ values-global.yaml | 3 + 14 files changed, 1197 insertions(+), 53 deletions(-) rename values-example.yaml => examples/values-example.yaml (58%) create mode 100755 scripts/test.sh create mode 100644 tests/acm-naked.expected.yaml create mode 100644 tests/acm-normal.expected.yaml create mode 100644 tests/install-naked.expected.yaml create mode 100644 tests/install-normal.expected.yaml create mode 100644 tests/site-naked.expected.yaml create mode 100644 tests/site-normal.expected.yaml diff --git a/Makefile b/Makefile index 232704ed..bbb04457 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,8 @@ TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) # --set values always take precedence over the contents of -f HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) +TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set main.options.bootstrap=$(BOOTSTRAP) --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" +PATTERN_OPTS=-f common/examples/values-example.yaml # Makefiles that use this target must provide: # PATTERN: The name of the pattern that is using it. This will be used programmatically for the source namespace @@ -22,10 +24,13 @@ argosecret: show: helm template common/install/ --name-template $(NAME) $(HELM_OPTS) +CHARTS=install site acm + test: - make -s TARGET_REPO=https://github.com/pattern-clone/common SECRETS=common/examples/values-secret.yaml show > .output - diff -u reference-output.yaml .output - rm -f .output +# Test that all values used by the chart are in values.yaml with the same defaults as the pattern + @for t in $(CHARTS); do common/scripts/test.sh $$t naked ""; if [ $$? != 0 ]; then exit 1; fi; done +# Test the charts as the pattern would drive them + @for t in $(CHARTS); do common/scripts/test.sh $$t normal "$(TEST_OPTS) $(PATTERN_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done init: git submodule update --init --recursive @@ -39,4 +44,4 @@ upgrade: uninstall: helm uninstall $(NAME) -.phony: install +.phony: install test diff --git a/acm/values.yaml b/acm/values.yaml index 6bf05ad4..51d3e936 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -1,11 +1,10 @@ global: - git: - provider: github.com - account: PLAINTEXT - username: PLAINTEXT - email: SOMEWHERE@EXAMPLE.COM - target: HEAD + pattern: none + valuesDirectoryURL: none + repoURL: none + targetRevision: main + + +site: + managedSites: - quay: - provider: quay.io - account: PLAINTEXT diff --git a/values-example.yaml b/examples/values-example.yaml similarity index 58% rename from values-example.yaml rename to examples/values-example.yaml index 28da54ad..fac57e81 100644 --- a/values-example.yaml +++ b/examples/values-example.yaml @@ -1,12 +1,11 @@ global: - valuesDirectoryURL: https://github.com/dagger-refuse-cool/edge-gitops/raw/main/ options: useCSV: False syncPolicy: Automatic installPlanApproval: Automatic site: - name: datacenter + name: example namespaces: - open-cluster-management @@ -40,39 +39,6 @@ site: project: datacenter path: charts/datacenter/pipelines - -# -# To have apps in multiple flavors, use namespaces and use helm overrides as appropriate -# -# - name: pipelines -# namespace: testing -# project: datacenter -# path: applications/pipeline -# repoURL: https://github.com/you/applications.git -# targetRevision: main -# overrides: -# - name: test -# value: true -# -# - name: pipelines -# namespace: staging -# project: datacenter -# path: applications/pipeline -# repoURL: https://github.com/you/applications.git -# targetRevision: main -# -# Additional applications -# Be sure to include additional resources your apps will require -# +X machines -# +Y RAM -# +Z CPU -# - name: vendor-app -# namespace: default -# project: vendor -# path: path/to/myapp -# repoURL: https://github.com/vendor/applications.git -# targetRevision: main - managedSites: - name: edge # Optional - Point to a different repo diff --git a/examples/values-secret.yaml b/examples/values-secret.yaml index a4f08195..89904c63 100644 --- a/examples/values-secret.yaml +++ b/examples/values-secret.yaml @@ -1,6 +1,6 @@ main: git: - repoURL: https://github.com/example/common2 + repoURL: https://github.com/example/common secrets: # NEVER COMMIT THESE VALUES TO GIT diff --git a/install/values.yaml b/install/values.yaml index e26b0117..a7887bf0 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -1,6 +1,6 @@ main: git: - repoURL: git@github.com:hybrid-cloud-patterns/gitops.git + repoURL: https://github.com/pattern-clone/mypattern revision: main #valuesDirectoryURL: https://github.com/hybrid-cloud-patterns/industrial-edge/raw/main/ @@ -15,7 +15,7 @@ main: source: redhat-operators csv: v1.2.0 - siteName: example + siteName: default global: imageregistry: diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 00000000..6f26bd3d --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,32 @@ +#!/bin/bash +target=$1 +name=$(echo $1 | sed -e s@/@-@g -e s@charts-@@) +TEST_VARIANT="$2" +CHART_OPTS="$3" + +TESTDIR=tests +REFERENCE=${TESTDIR}/${name}-${TEST_VARIANT}.expected.yaml +OUTPUT=${TESTDIR}/.${name}-${TEST_VARIANT}.expected.yaml +#REFERENCE=${TESTDIR}/${name}.expected.yaml +#OUTPUT=${TESTDIR}/.${name}.expected.yaml + +echo "Testing $1 chart (${TEST_VARIANT})" >&2 +helm template $target --name-template $name ${CHART_OPTS} > ${OUTPUT} +#cp ${OUTPUT} ${REFERENCE} +if [ ! -e ${REFERENCE} ]; then + touch ${REFERENCE} +fi +diff -u ${REFERENCE} ${OUTPUT} +rc=$? +if [ $rc = 0 ]; then + rm -f ${OUTPUT} +fi + +if [ $TEST_VARIANT = normal -a $rc = 0 ]; then + # Another method of finding variables missing from values.yaml, eg. + # - name: -datacenter + # + name: pattern-name-datacenter + # Alas we can't make it fatal because there *should* be some differences + diff -u ${TESTDIR}/${name}-naked.expected.yaml ${TESTDIR}/${name}-normal.expected.yaml +fi +exit $rc diff --git a/site/values.yaml b/site/values.yaml index 55e4c69d..d02a9c82 100644 --- a/site/values.yaml +++ b/site/values.yaml @@ -1,12 +1,13 @@ global: - valuesDirectoryURL: https://github.com/dagger-refuse-cool/edge-gitops/raw/main/ + pattern: common + valuesDirectoryURL: https://github.com/pattern-clone/common/raw/main options: useCSV: True syncPolicy: Automatic installPlanApproval: Automatic site: - name: datacenter + name: example proposedOptions: manageGitops: True diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml new file mode 100644 index 00000000..ed109ec5 --- /dev/null +++ b/tests/acm-naked.expected.yaml @@ -0,0 +1,151 @@ +--- +# Source: acm/templates/policies/application-policies.yaml +# TODO: Also create a GitOpsCluster.apps.open-cluster-management.io +--- +# Source: acm/templates/managed-clusters/staging.yaml +apiVersion: agent.open-cluster-management.io/v1 +kind: KlusterletAddonConfig +metadata: + name: staging + namespace: staging + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterName: staging + clusterNamespace: staging + clusterLabels: + cloud: auto-detect + vendor: auto-detect + applicationManager: + enabled: true + policyController: + enabled: true + searchCollector: + enabled: true + certPolicyController: + enabled: true + iamPolicyController: + enabled: true + version: "2.0" +--- +# Source: acm/templates/managed-clusters/staging.yaml +apiVersion: cluster.open-cluster-management.io/v1 +kind: ManagedCluster +metadata: + labels: + cloud: auto-detect + vendor: auto-detect + name: staging + gitops-mgmt: argocd + manuela: factory + name: staging + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + hubAcceptsClient: true +--- +# Source: acm/templates/managed-clusters/staging.yaml +apiVersion: internal.open-cluster-management.io/v1beta1 +kind: ManagedClusterInfo +metadata: + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + labels: + argocd.argoproj.io/instance: manuela-acm + cloud: auto-detect + gitops-mgmt: argocd + manuela: factory + name: staging + vendor: auto-detect + name: staging + namespace: staging +spec: + loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K +--- +# Source: acm/templates/multiclusterhub.yaml +apiVersion: operator.open-cluster-management.io/v1 +kind: MultiClusterHub +metadata: + name: multiclusterhub + namespace: open-cluster-management + annotations: + argocd.argoproj.io/sync-wave: "-1" +spec: {} +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: openshift-gitops-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: openshift-gitops-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: openshift-gitops-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: openshift-gitops-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: openshift-gitops-policy + annotations: + policy.open-cluster-management.io/standards: NIST-CSF + policy.open-cluster-management.io/categories: PR.DS Data Security + policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: openshift-gitops-config + spec: + remediationAction: enforce + severity: med + namespaceSelector: + exclude: + - kube-* + include: + - default + object-templates: + - complianceType: musthave + objectDefinition: + # This is an auto-generated file. DO NOT EDIT + apiVersion: operators.coreos.com/v1alpha1 + kind: Subscription + metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: '' + spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml new file mode 100644 index 00000000..9fac7095 --- /dev/null +++ b/tests/acm-normal.expected.yaml @@ -0,0 +1,261 @@ +--- +# Source: acm/templates/managed-clusters/staging.yaml +apiVersion: agent.open-cluster-management.io/v1 +kind: KlusterletAddonConfig +metadata: + name: staging + namespace: staging + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterName: staging + clusterNamespace: staging + clusterLabels: + cloud: auto-detect + vendor: auto-detect + applicationManager: + enabled: true + policyController: + enabled: true + searchCollector: + enabled: true + certPolicyController: + enabled: true + iamPolicyController: + enabled: true + version: "2.0" +--- +# Source: acm/templates/managed-clusters/staging.yaml +apiVersion: cluster.open-cluster-management.io/v1 +kind: ManagedCluster +metadata: + labels: + cloud: auto-detect + vendor: auto-detect + name: staging + gitops-mgmt: argocd + manuela: factory + name: staging + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + hubAcceptsClient: true +--- +# Source: acm/templates/managed-clusters/staging.yaml +apiVersion: internal.open-cluster-management.io/v1beta1 +kind: ManagedClusterInfo +metadata: + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + labels: + argocd.argoproj.io/instance: manuela-acm + cloud: auto-detect + gitops-mgmt: argocd + manuela: factory + name: staging + vendor: auto-detect + name: staging + namespace: staging +spec: + loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K +--- +# Source: acm/templates/multiclusterhub.yaml +apiVersion: operator.open-cluster-management.io/v1 +kind: MultiClusterHub +metadata: + name: multiclusterhub + namespace: open-cluster-management + annotations: + argocd.argoproj.io/sync-wave: "-1" +spec: {} +--- +# Source: acm/templates/policies/application-policies.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: edge-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: edge-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: edge-site-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: openshift-gitops-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: openshift-gitops-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: openshift-gitops-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/application-policies.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: edge-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: { + "matchExpressions": [ + { + "key": "vendor", + "operator": "In", + "values": [ + "OpenShift" + ] + } + ] +} +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: openshift-gitops-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift +--- +# Source: acm/templates/policies/application-policies.yaml +# TODO: Also create a GitOpsCluster.apps.open-cluster-management.io +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: edge-site-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: edge-site-config + spec: + remediationAction: enforce + severity: med + namespaceSelector: + exclude: + - kube-* + include: + - default + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: argoproj.io/v1alpha1 + kind: Application + metadata: + name: mypattern-edge + namespace: openshift-gitops + finalizers: + - argoproj.io/finalizer + spec: + project: default + source: + repoURL: none + targetRevision: main + path: common/site + helm: + valueFiles: + - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" + - "https://github.com/pattern-clone/mypattern/raw/main/values-edge.yaml" + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.valuesDirectoryURL + value: https://github.com/pattern-clone/mypattern/raw/main + - name: site.isHubCluster + value: "false" + destination: + server: https://kubernetes.default.svc + namespace: mypattern-edge + syncPolicy: + automated: + prune: false + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: openshift-gitops-policy + annotations: + policy.open-cluster-management.io/standards: NIST-CSF + policy.open-cluster-management.io/categories: PR.DS Data Security + policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: openshift-gitops-config + spec: + remediationAction: enforce + severity: med + namespaceSelector: + exclude: + - kube-* + include: + - default + object-templates: + - complianceType: musthave + objectDefinition: + # This is an auto-generated file. DO NOT EDIT + apiVersion: operators.coreos.com/v1alpha1 + kind: Subscription + metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: '' + spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace diff --git a/tests/install-naked.expected.yaml b/tests/install-naked.expected.yaml new file mode 100644 index 00000000..832cf068 --- /dev/null +++ b/tests/install-naked.expected.yaml @@ -0,0 +1,119 @@ +--- +# Source: pattern-install/templates/argocd/namespace.yaml +# Pre-create so we can create our argo app for keeping subscriptions in sync +# Do it here so that we don't try to sync it in the future +apiVersion: v1 +kind: Namespace +metadata: + name: openshift-gitops +--- +# Source: pattern-install/templates/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: manuela-ci + labels: + manuela-role: pipeline + app.kubernetes.io/instance: manuela +--- +# Source: pattern-install/templates/pipeline/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: pipeline + namespace: manuela-ci +secrets: +- name: git-repo-credentials +- name: image-registry-credentials +--- +# Source: pattern-install/templates/secrets/s3-secret.yaml +kind: Secret +apiVersion: v1 +metadata: + name: s3-secret +type: Opaque +data: + # Pre-create as part of the initial 'helm install' chart + # Create a file with the following: + # s3.accessKey: KEY + # s3.secretKey: secret key + #application.properties: base64 encrypted value of the above file + # This should live in the values-secret.yaml file + application.properties: BASE64STRING +--- +# Source: pattern-install/templates/secrets/secret-git-repo-credentials.yaml +apiVersion: v1 +kind: Secret +metadata: + name: git-repo-credentials + namespace: manuela-ci + annotations: + # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ + tekton.dev/git-0: https://github.com/hybrid-cloud-patterns +type: kubernetes.io/basic-auth +stringData: + username: STRING + password: STRING +--- +# Source: pattern-install/templates/secrets/secret-image-registry-credentials.yaml +apiVersion: v1 +kind: Secret +metadata: + name: openshift-registry-credentials + namespace: manuela-ci + annotations: + # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ + tekton.dev/docker-0: "https://" +type: kubernetes.io/basic-auth +stringData: + username: STRING + password: STRING +--- +# Source: pattern-install/templates/argocd/application.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: install-default + namespace: openshift-gitops +spec: + destination: + name: in-cluster + namespace: install-default + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/site + helm: + valueFiles: + - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" + - "https://github.com/pattern-clone/mypattern/raw/main/values-default.yaml" + # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.valuesDirectoryURL + value: https://github.com/pattern-clone/mypattern/raw/main + - name: global.pattern + value: install + syncPolicy: + automated: {} +--- +# Source: pattern-install/templates/argocd/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: "" +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yaml new file mode 100644 index 00000000..d140986a --- /dev/null +++ b/tests/install-normal.expected.yaml @@ -0,0 +1,119 @@ +--- +# Source: pattern-install/templates/argocd/namespace.yaml +# Pre-create so we can create our argo app for keeping subscriptions in sync +# Do it here so that we don't try to sync it in the future +apiVersion: v1 +kind: Namespace +metadata: + name: openshift-gitops +--- +# Source: pattern-install/templates/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: manuela-ci + labels: + manuela-role: pipeline + app.kubernetes.io/instance: manuela +--- +# Source: pattern-install/templates/pipeline/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: pipeline + namespace: manuela-ci +secrets: +- name: git-repo-credentials +- name: image-registry-credentials +--- +# Source: pattern-install/templates/secrets/s3-secret.yaml +kind: Secret +apiVersion: v1 +metadata: + name: s3-secret +type: Opaque +data: + # Pre-create as part of the initial 'helm install' chart + # Create a file with the following: + # s3.accessKey: KEY + # s3.secretKey: secret key + #application.properties: base64 encrypted value of the above file + # This should live in the values-secret.yaml file + application.properties: test-secret +--- +# Source: pattern-install/templates/secrets/secret-git-repo-credentials.yaml +apiVersion: v1 +kind: Secret +metadata: + name: git-repo-credentials + namespace: manuela-ci + annotations: + # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ + tekton.dev/git-0: https://github.com/hybrid-cloud-patterns +type: kubernetes.io/basic-auth +stringData: + username: test-user + password: test-git-token +--- +# Source: pattern-install/templates/secrets/secret-image-registry-credentials.yaml +apiVersion: v1 +kind: Secret +metadata: + name: openshift-registry-credentials + namespace: manuela-ci + annotations: + # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ + tekton.dev/docker-0: "https://" +type: kubernetes.io/basic-auth +stringData: + username: test-account + password: test-quay-token +--- +# Source: pattern-install/templates/argocd/application.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: install-example + namespace: openshift-gitops +spec: + destination: + name: in-cluster + namespace: install-example + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/site + helm: + valueFiles: + - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" + - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" + # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.valuesDirectoryURL + value: https://github.com/pattern-clone/mypattern/raw/main + - name: global.pattern + value: install + syncPolicy: + automated: {} +--- +# Source: pattern-install/templates/argocd/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: "" +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace diff --git a/tests/site-naked.expected.yaml b/tests/site-naked.expected.yaml new file mode 100644 index 00000000..e0d32ae0 --- /dev/null +++ b/tests/site-naked.expected.yaml @@ -0,0 +1,148 @@ +--- +# Source: pattern-site/templates/gitops-namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: common-example + # The name here needs to be consistent with + # - acm/templates/policies/application-policies.yaml + # - site/templates/applications.yaml + # - any references to secrets and route URLs in documentation + name: common-example +spec: {} +--- +# Source: pattern-site/templates/argocd-super-role.yaml +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: openshift-gitops-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: openshift-gitops-argocd-application-controller + namespace: openshift-gitops + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + name: openshift-gitops-argocd-server + namespace: openshift-gitops +--- +# Source: pattern-site/templates/argocd-super-role.yaml +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: common-example-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + # This is the {ArgoCD.name}-argocd-application-controller + name: example-gitops-argocd-application-controller + namespace: common-example + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + # This is the {ArgoCD.name}-argocd-server + name: example-gitops-argocd-server + namespace: common-example +--- +# Source: pattern-site/templates/argocd.yaml +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + finalizers: + - argoproj.io/finalizer + # Changing the name affects the ClusterRoleBinding, the generated secret, + # route URL, and argocd.argoproj.io/managed-by annotations + name: example-gitops + annotations: + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + applicationInstanceLabelKey: argocd.argoproj.io/instance + # Not the greatest way to pass git/quay info to sub-applications, but it will do until + # we can support helmChart with kustomize + # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION + configManagementPlugins: "- name: kustomize-version\n generate:\n command: [sh, + -c]\n args: [\"kustomize version 1>&2 && exit 1\"]\n- name: kustomize-with-helm\n + \ generate:\n command: [\"kustomize\"]\n args: [\"build\", \"--enable-helm\"]\n- name: + helm-with-kustomize\n init:\n command: [\"/bin/sh\", \"-c\"]\n args: [\"helm + dependency build\"]\n generate:\n command: [/bin/bash, -c]\n args: [\"helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f https://github.com/pattern-clone/common/raw/main/values-global.yaml + -f https://github.com/pattern-clone/common/raw/main/values-example.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern=common + --set global.valuesDirectoryURL=https://github.com/pattern-clone/common/raw/main + --post-renderer ./kustomize\"] + \ \n" + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + controller: + processors: {} + resources: + limits: + cpu: "4" + memory: 4Gi + requests: + cpu: 500m + memory: 2Gi + dex: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + rbac: + defaultPolicy: role:admin + repo: + resources: + limits: + cpu: "1" + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + service: + type: "" + tls: + ca: {} +status: diff --git a/tests/site-normal.expected.yaml b/tests/site-normal.expected.yaml new file mode 100644 index 00000000..8d85d303 --- /dev/null +++ b/tests/site-normal.expected.yaml @@ -0,0 +1,340 @@ +--- +# Source: pattern-site/templates/gitops-namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: mypattern-example + # The name here needs to be consistent with + # - acm/templates/policies/application-policies.yaml + # - site/templates/applications.yaml + # - any references to secrets and route URLs in documentation + name: mypattern-example +spec: {} +--- +# Source: pattern-site/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: pattern + argocd.argoproj.io/managed-by: mypattern-example + name: open-cluster-management +spec: +--- +# Source: pattern-site/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: pattern + argocd.argoproj.io/managed-by: mypattern-example + name: application-ci +spec: +--- +# Source: pattern-site/templates/argocd-super-role.yaml +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: openshift-gitops-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: openshift-gitops-argocd-application-controller + namespace: openshift-gitops + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + name: openshift-gitops-argocd-server + namespace: openshift-gitops +--- +# Source: pattern-site/templates/argocd-super-role.yaml +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: mypattern-example-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + # This is the {ArgoCD.name}-argocd-application-controller + name: example-gitops-argocd-application-controller + namespace: mypattern-example + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + # This is the {ArgoCD.name}-argocd-server + name: example-gitops-argocd-server + namespace: mypattern-example +--- +# Source: pattern-site/templates/projects.yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: datacenter + namespace: mypattern-example +spec: + description: "Pattern datacenter" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- +# Source: pattern-site/templates/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: acm + namespace: mypattern-example +spec: + destination: + name: in-cluster + namespace: open-cluster-management + project: datacenter + source: + repoURL: + targetRevision: + path: common/acm + helm: + valueFiles: + - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" + - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.valuesDirectoryURL + value: https://github.com/pattern-clone/mypattern/raw/main + ignoreDifferences: [ + { + "group": "internal.open-cluster-management.io", + "jsonPointers": [ + "/spec/loggingCA" + ], + "kind": "ManagedClusterInfo" + } +] + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-site/templates/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: pipelines + namespace: mypattern-example +spec: + destination: + name: in-cluster + namespace: application-ci + project: datacenter + source: + repoURL: + targetRevision: + path: charts/datacenter/pipelines + helm: + valueFiles: + - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" + - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.valuesDirectoryURL + value: https://github.com/pattern-clone/mypattern/raw/main + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-site/templates/argocd.yaml +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + finalizers: + - argoproj.io/finalizer + # Changing the name affects the ClusterRoleBinding, the generated secret, + # route URL, and argocd.argoproj.io/managed-by annotations + name: example-gitops + annotations: + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + applicationInstanceLabelKey: argocd.argoproj.io/instance + # Not the greatest way to pass git/quay info to sub-applications, but it will do until + # we can support helmChart with kustomize + # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION + configManagementPlugins: "- name: kustomize-version\n generate:\n command: [sh, + -c]\n args: [\"kustomize version 1>&2 && exit 1\"]\n- name: kustomize-with-helm\n + \ generate:\n command: [\"kustomize\"]\n args: [\"build\", \"--enable-helm\"]\n- name: + helm-with-kustomize\n init:\n command: [\"/bin/sh\", \"-c\"]\n args: [\"helm + dependency build\"]\n generate:\n command: [/bin/bash, -c]\n args: [\"helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml + -f https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern=mypattern + --set global.valuesDirectoryURL=https://github.com/pattern-clone/mypattern/raw/main + --post-renderer ./kustomize\"] + \ \n" + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + controller: + processors: {} + resources: + limits: + cpu: "4" + memory: 4Gi + requests: + cpu: 500m + memory: 2Gi + dex: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + rbac: + defaultPolicy: role:admin + repo: + resources: + limits: + cpu: "1" + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + service: + type: "" + tls: + ca: {} +status: +--- +# Source: pattern-site/templates/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: open-cluster-management-operator-group + namespace: open-cluster-management +spec: + targetNamespaces: + - open-cluster-management +--- +# Source: pattern-site/templates/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: application-ci-operator-group + namespace: application-ci +spec: + targetNamespaces: + - application-ci +--- +# Source: pattern-site/templates/subsciptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: advanced-cluster-management + namespace: open-cluster-management +spec: + name: advanced-cluster-management + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: release-2.3 + installPlanApproval: Automatic +--- +# Source: pattern-site/templates/subsciptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-pipelines-operator-rh + namespace: openshift-operators +spec: + name: openshift-pipelines-operator-rh + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic +--- +# Source: pattern-site/templates/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: advanced-cluster-management + namespace: open-cluster-management +spec: + name: advanced-cluster-management + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: release-2.3 + installPlanApproval: Automatic +--- +# Source: pattern-site/templates/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-pipelines-operator-rh + namespace: openshift-operators +spec: + name: openshift-pipelines-operator-rh + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic diff --git a/values-global.yaml b/values-global.yaml index f4ed30a2..c273d217 100644 --- a/values-global.yaml +++ b/values-global.yaml @@ -11,3 +11,6 @@ global: email: someone@somewhere.com dev_revision: main +main: + siteName: example + From a3074c3e6d4b297fb8de796940da6247169825c5 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 2 Dec 2021 12:45:10 +1100 Subject: [PATCH 0159/1263] Ensure the global.repoURL variable is set consistently in tests --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bbb04457..e43d43f6 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) # --set values always take precedence over the contents of -f HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) -TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set main.options.bootstrap=$(BOOTSTRAP) --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" +TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set main.options.bootstrap=$(BOOTSTRAP) --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" PATTERN_OPTS=-f common/examples/values-example.yaml # Makefiles that use this target must provide: From 2298a1ef94d51dbd9f10884908e10941bc8009d0 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 6 Dec 2021 10:45:50 -0600 Subject: [PATCH 0160/1263] Add some elements to .gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e4e5f6c8..22bdd307 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -*~ \ No newline at end of file +*~ +*.swp +*.swo +values-secret.yaml From 236f0f83305c199784a2086676c5a18d116e84e0 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 6 Dec 2021 10:55:10 -0600 Subject: [PATCH 0161/1263] Fix whitespace in repoURL value in a POSIX-friendly way --- Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index e43d43f6..4b3b1bc0 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ BOOTSTRAP=1 SECRETS=~/values-secret.yaml NAME=$(shell basename `pwd`) -TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL://' -e 's%:[a-z].*@%@%' -e 's%:%/%' -e 's%git@%https://%' ) +TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL:[[:space:]]+//' -e 's%:[a-z].*@%@%' -e 's%:%/%' -e 's%git@%https://%' ) # git branch --show-current is also available as of git 2.22, but we will use this for compatibility TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) -# --set values always take precedence over the contents of -f -HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) +# --set values always take precedence over the contents of -f +HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set main.options.bootstrap=$(BOOTSTRAP) --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" -PATTERN_OPTS=-f common/examples/values-example.yaml +PATTERN_OPTS=-f common/examples/values-example.yaml # Makefiles that use this target must provide: # PATTERN: The name of the pattern that is using it. This will be used programmatically for the source namespace @@ -24,10 +24,10 @@ argosecret: show: helm template common/install/ --name-template $(NAME) $(HELM_OPTS) -CHARTS=install site acm +CHARTS=install site acm test: -# Test that all values used by the chart are in values.yaml with the same defaults as the pattern +# Test that all values used by the chart are in values.yaml with the same defaults as the pattern @for t in $(CHARTS); do common/scripts/test.sh $$t naked ""; if [ $$? != 0 ]; then exit 1; fi; done # Test the charts as the pattern would drive them @for t in $(CHARTS); do common/scripts/test.sh $$t normal "$(TEST_OPTS) $(PATTERN_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done From 8635899d86e643cc3cb855fb1aad01e6d088016d Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 6 Dec 2021 11:02:01 -0600 Subject: [PATCH 0162/1263] Remove manuela-specific elements and secrets --- install/templates/namespace.yaml | 10 ---------- install/templates/pipeline/rolebinding.yaml | 18 ----------------- .../templates/pipeline/serviceaccount.yaml | 13 ------------ install/templates/secrets/s3-secret.yaml | 13 ------------ .../secrets/secret-git-repo-credentials.yaml | 15 -------------- .../secret-image-registry-credentials.yaml | 17 ---------------- install/values.yaml | 20 ++----------------- 7 files changed, 2 insertions(+), 104 deletions(-) delete mode 100644 install/templates/namespace.yaml delete mode 100644 install/templates/pipeline/rolebinding.yaml delete mode 100644 install/templates/pipeline/serviceaccount.yaml delete mode 100644 install/templates/secrets/s3-secret.yaml delete mode 100644 install/templates/secrets/secret-git-repo-credentials.yaml delete mode 100644 install/templates/secrets/secret-image-registry-credentials.yaml diff --git a/install/templates/namespace.yaml b/install/templates/namespace.yaml deleted file mode 100644 index 936decd8..00000000 --- a/install/templates/namespace.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- if .Values.main.options.bootstrap }} ---- -apiVersion: v1 -kind: Namespace -metadata: - name: manuela-ci - labels: - manuela-role: pipeline - app.kubernetes.io/instance: manuela -{{- end }} diff --git a/install/templates/pipeline/rolebinding.yaml b/install/templates/pipeline/rolebinding.yaml deleted file mode 100644 index 24fb7e2d..00000000 --- a/install/templates/pipeline/rolebinding.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if .Values.main.options.bootstrap }} -{{- if eq .Values.global.imageregistry.type "openshift" }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: pipeline-edit - namespace: openshift -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: edit -subjects: -- kind: ServiceAccount - name: pipeline - namespace: manuela-ci -{{- end }} -{{- end }} diff --git a/install/templates/pipeline/serviceaccount.yaml b/install/templates/pipeline/serviceaccount.yaml deleted file mode 100644 index 2f250d29..00000000 --- a/install/templates/pipeline/serviceaccount.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.main.options.bootstrap }} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: pipeline - namespace: manuela-ci -secrets: -- name: git-repo-credentials -{{- if eq .Values.global.imageregistry.type "quay" }} -- name: image-registry-credentials -{{- end }} -{{- end }} diff --git a/install/templates/secrets/s3-secret.yaml b/install/templates/secrets/s3-secret.yaml deleted file mode 100644 index 5dd37281..00000000 --- a/install/templates/secrets/s3-secret.yaml +++ /dev/null @@ -1,13 +0,0 @@ -kind: Secret -apiVersion: v1 -metadata: - name: s3-secret -type: Opaque -data: - # Pre-create as part of the initial 'helm install' chart - # Create a file with the following: - # s3.accessKey: KEY - # s3.secretKey: secret key - #application.properties: base64 encrypted value of the above file - # This should live in the values-secret.yaml file - application.properties: {{ .Values.secrets.aws.s3Secret }} diff --git a/install/templates/secrets/secret-git-repo-credentials.yaml b/install/templates/secrets/secret-git-repo-credentials.yaml deleted file mode 100644 index a7ffbaa4..00000000 --- a/install/templates/secrets/secret-git-repo-credentials.yaml +++ /dev/null @@ -1,15 +0,0 @@ -{{- if .Values.main.options.bootstrap }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: git-repo-credentials - namespace: manuela-ci - annotations: - # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ - tekton.dev/git-0: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }} -type: kubernetes.io/basic-auth -stringData: - username: {{ .Values.secrets.git.username }} - password: {{ .Values.secrets.git.token }} -{{- end }} diff --git a/install/templates/secrets/secret-image-registry-credentials.yaml b/install/templates/secrets/secret-image-registry-credentials.yaml deleted file mode 100644 index 5ec43e87..00000000 --- a/install/templates/secrets/secret-image-registry-credentials.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if .Values.main.options.bootstrap }} -{{- if eq .Values.global.imageregistry.type "quay" }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: openshift-registry-credentials - namespace: manuela-ci - annotations: - # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ - tekton.dev/docker-0: "https://{{ .Values.global.imageregistry.hostname }}" -type: kubernetes.io/basic-auth -stringData: - username: {{ .Values.secrets.imageregistry.account }} - password: {{ .Values.secrets.imageregistry.token }} -{{- end }} -{{- end }} diff --git a/install/values.yaml b/install/values.yaml index a7887bf0..859324d7 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -13,7 +13,7 @@ main: gitops: channel: stable source: redhat-operators - csv: v1.2.0 + csv: v1.3.0 siteName: default @@ -23,21 +23,5 @@ global: git: hostname: github.com - # Account is the user or organization under which the pattern repo lives + # Account is the user or organization under which the pattern repo lives account: hybrid-cloud-patterns - -secrets: - # NEVER COMMIT THESE VALUES TO GIT - enabled: false - imageregistry: - # Quay -> Robot Accounts -> Robot Login - account: STRING - token: STRING - - git: - # Go to: https://github.com/settings/tokens - username: STRING - token: STRING - - aws: - s3Secret: BASE64STRING From 3a0026b96db3e00d05a82112c1676a673370c70d Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 7 Dec 2021 14:58:09 -0600 Subject: [PATCH 0163/1263] Modify tests to match removal of secrets and namespace --- .gitignore | 1 + Makefile | 1 + scripts/test.sh | 7 ++++ tests/acm-normal.expected.yaml | 2 +- tests/install-naked.expected.yaml | 62 ------------------------------ tests/install-normal.expected.yaml | 62 ------------------------------ tests/site-normal.expected.yaml | 4 +- 7 files changed, 12 insertions(+), 127 deletions(-) diff --git a/.gitignore b/.gitignore index 22bdd307..0dc4d5a1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.swp *.swo values-secret.yaml +.*-expected.yaml diff --git a/Makefile b/Makefile index 4b3b1bc0..9915223a 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ show: helm template common/install/ --name-template $(NAME) $(HELM_OPTS) CHARTS=install site acm +#CHARTS=acm site install test: # Test that all values used by the chart are in values.yaml with the same defaults as the pattern diff --git a/scripts/test.sh b/scripts/test.sh index 6f26bd3d..6e400fc0 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -29,4 +29,11 @@ if [ $TEST_VARIANT = normal -a $rc = 0 ]; then # Alas we can't make it fatal because there *should* be some differences diff -u ${TESTDIR}/${name}-naked.expected.yaml ${TESTDIR}/${name}-normal.expected.yaml fi + +if [ $rc = 0 ]; then + echo "PASS on $target $TEST_VARIANT with opts [$CHART_OPTS]" +else + echo "FAIL on $target $TEST_VARIANT with opts [$CHART_OPTS]" +fi + exit $rc diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 9fac7095..495b80b4 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -177,7 +177,7 @@ spec: spec: project: default source: - repoURL: none + repoURL: https://github.com/pattern-clone/mypattern targetRevision: main path: common/site helm: diff --git a/tests/install-naked.expected.yaml b/tests/install-naked.expected.yaml index 832cf068..af97dbcc 100644 --- a/tests/install-naked.expected.yaml +++ b/tests/install-naked.expected.yaml @@ -7,68 +7,6 @@ kind: Namespace metadata: name: openshift-gitops --- -# Source: pattern-install/templates/namespace.yaml -apiVersion: v1 -kind: Namespace -metadata: - name: manuela-ci - labels: - manuela-role: pipeline - app.kubernetes.io/instance: manuela ---- -# Source: pattern-install/templates/pipeline/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: pipeline - namespace: manuela-ci -secrets: -- name: git-repo-credentials -- name: image-registry-credentials ---- -# Source: pattern-install/templates/secrets/s3-secret.yaml -kind: Secret -apiVersion: v1 -metadata: - name: s3-secret -type: Opaque -data: - # Pre-create as part of the initial 'helm install' chart - # Create a file with the following: - # s3.accessKey: KEY - # s3.secretKey: secret key - #application.properties: base64 encrypted value of the above file - # This should live in the values-secret.yaml file - application.properties: BASE64STRING ---- -# Source: pattern-install/templates/secrets/secret-git-repo-credentials.yaml -apiVersion: v1 -kind: Secret -metadata: - name: git-repo-credentials - namespace: manuela-ci - annotations: - # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ - tekton.dev/git-0: https://github.com/hybrid-cloud-patterns -type: kubernetes.io/basic-auth -stringData: - username: STRING - password: STRING ---- -# Source: pattern-install/templates/secrets/secret-image-registry-credentials.yaml -apiVersion: v1 -kind: Secret -metadata: - name: openshift-registry-credentials - namespace: manuela-ci - annotations: - # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ - tekton.dev/docker-0: "https://" -type: kubernetes.io/basic-auth -stringData: - username: STRING - password: STRING ---- # Source: pattern-install/templates/argocd/application.yaml apiVersion: argoproj.io/v1alpha1 kind: Application diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yaml index d140986a..3a7704ff 100644 --- a/tests/install-normal.expected.yaml +++ b/tests/install-normal.expected.yaml @@ -7,68 +7,6 @@ kind: Namespace metadata: name: openshift-gitops --- -# Source: pattern-install/templates/namespace.yaml -apiVersion: v1 -kind: Namespace -metadata: - name: manuela-ci - labels: - manuela-role: pipeline - app.kubernetes.io/instance: manuela ---- -# Source: pattern-install/templates/pipeline/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: pipeline - namespace: manuela-ci -secrets: -- name: git-repo-credentials -- name: image-registry-credentials ---- -# Source: pattern-install/templates/secrets/s3-secret.yaml -kind: Secret -apiVersion: v1 -metadata: - name: s3-secret -type: Opaque -data: - # Pre-create as part of the initial 'helm install' chart - # Create a file with the following: - # s3.accessKey: KEY - # s3.secretKey: secret key - #application.properties: base64 encrypted value of the above file - # This should live in the values-secret.yaml file - application.properties: test-secret ---- -# Source: pattern-install/templates/secrets/secret-git-repo-credentials.yaml -apiVersion: v1 -kind: Secret -metadata: - name: git-repo-credentials - namespace: manuela-ci - annotations: - # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ - tekton.dev/git-0: https://github.com/hybrid-cloud-patterns -type: kubernetes.io/basic-auth -stringData: - username: test-user - password: test-git-token ---- -# Source: pattern-install/templates/secrets/secret-image-registry-credentials.yaml -apiVersion: v1 -kind: Secret -metadata: - name: openshift-registry-credentials - namespace: manuela-ci - annotations: - # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ - tekton.dev/docker-0: "https://" -type: kubernetes.io/basic-auth -stringData: - username: test-account - password: test-quay-token ---- # Source: pattern-install/templates/argocd/application.yaml apiVersion: argoproj.io/v1alpha1 kind: Application diff --git a/tests/site-normal.expected.yaml b/tests/site-normal.expected.yaml index 8d85d303..31607a8a 100644 --- a/tests/site-normal.expected.yaml +++ b/tests/site-normal.expected.yaml @@ -105,7 +105,7 @@ spec: namespace: open-cluster-management project: datacenter source: - repoURL: + repoURL: https://github.com/pattern-clone/mypattern targetRevision: path: common/acm helm: @@ -149,7 +149,7 @@ spec: namespace: application-ci project: datacenter source: - repoURL: + repoURL: https://github.com/pattern-clone/mypattern targetRevision: path: charts/datacenter/pipelines helm: From 359db78c32daa489369c72404ad5392aa90ce8e1 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 7 Dec 2021 15:00:05 -0600 Subject: [PATCH 0164/1263] Remove cruft from makefile --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 9915223a..4b3b1bc0 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,6 @@ show: helm template common/install/ --name-template $(NAME) $(HELM_OPTS) CHARTS=install site acm -#CHARTS=acm site install test: # Test that all values used by the chart are in values.yaml with the same defaults as the pattern From 3e3192e2b2cdd3bae48c959f9e6ff36d47ba4d9f Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 8 Dec 2021 09:08:47 -0600 Subject: [PATCH 0165/1263] Loosen regex to extract target repo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4b3b1bc0..d1c2fa33 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ BOOTSTRAP=1 SECRETS=~/values-secret.yaml NAME=$(shell basename `pwd`) -TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL:[[:space:]]+//' -e 's%:[a-z].*@%@%' -e 's%:%/%' -e 's%git@%https://%' ) +TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL:[[:space:]]*//' -e 's%:[a-z].*@%@%' -e 's%:%/%' -e 's%git@%https://%' ) # git branch --show-current is also available as of git 2.22, but we will use this for compatibility TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) From dbcfa3219e2d99a0f7b20e1e6f13dcc810d5b03c Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 9 Dec 2021 10:16:55 -0600 Subject: [PATCH 0166/1263] Add structure for vault --- vault/Chart.yaml | 6 ++++++ vault/templates/.gitkeep | 0 vault/values.yaml | 1 + 3 files changed, 7 insertions(+) create mode 100644 vault/Chart.yaml create mode 100644 vault/templates/.gitkeep create mode 100644 vault/values.yaml diff --git a/vault/Chart.yaml b/vault/Chart.yaml new file mode 100644 index 00000000..3272e2e7 --- /dev/null +++ b/vault/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +description: A Helm chart to configure HashiCorp Vault for OpenShift +keywords: +- pattern +name: vault +version: 0.0.1 diff --git a/vault/templates/.gitkeep b/vault/templates/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/vault/values.yaml b/vault/values.yaml new file mode 100644 index 00000000..ed97d539 --- /dev/null +++ b/vault/values.yaml @@ -0,0 +1 @@ +--- From 2567327c451b8a5449808b5b46997124ec89d582 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 9 Dec 2021 11:12:04 -0600 Subject: [PATCH 0167/1263] Remove vault subdir to prep for alternate inclusion --- vault/Chart.yaml | 6 ------ vault/templates/.gitkeep | 0 vault/values.yaml | 1 - 3 files changed, 7 deletions(-) delete mode 100644 vault/Chart.yaml delete mode 100644 vault/templates/.gitkeep delete mode 100644 vault/values.yaml diff --git a/vault/Chart.yaml b/vault/Chart.yaml deleted file mode 100644 index 3272e2e7..00000000 --- a/vault/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -description: A Helm chart to configure HashiCorp Vault for OpenShift -keywords: -- pattern -name: vault -version: 0.0.1 diff --git a/vault/templates/.gitkeep b/vault/templates/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/vault/values.yaml b/vault/values.yaml deleted file mode 100644 index ed97d539..00000000 --- a/vault/values.yaml +++ /dev/null @@ -1 +0,0 @@ ---- From 3c48c3dac6c661b960c4589f079117d1932927ed Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 9 Dec 2021 11:12:45 -0600 Subject: [PATCH 0168/1263] Squashed 'vault/' content from commit 9fa25e9 git-subtree-dir: vault git-subtree-split: 9fa25e97c806073c7dd3274a851181cbb3d67868 --- .circleci/config.yml | 106 ++ .github/ISSUE_TEMPLATE/bug_report.md | 46 + .github/ISSUE_TEMPLATE/config.yml | 4 + .github/ISSUE_TEMPLATE/feature_request.md | 20 + .github/workflows/jira.yaml | 72 + .gitignore | 13 + .helmignore | 4 + CHANGELOG.md | 350 ++++ CONTRIBUTING.md | 239 +++ Chart.yaml | 14 + LICENSE.md | 353 ++++ Makefile | 101 + README.md | 44 + templates/NOTES.txt | 14 + templates/_helpers.tpl | 692 +++++++ templates/csi-clusterrole.yaml | 17 + templates/csi-clusterrolebinding.yaml | 18 + templates/csi-daemonset.yaml | 81 + templates/csi-serviceaccount.yaml | 12 + templates/injector-certs-secret.yaml | 10 + templates/injector-clusterrole.yaml | 18 + templates/injector-clusterrolebinding.yaml | 18 + templates/injector-deployment.yaml | 157 ++ templates/injector-mutating-webhook.yaml | 43 + templates/injector-network-policy.yaml | 21 + templates/injector-psp-role.yaml | 17 + templates/injector-psp-rolebinding.yaml | 18 + templates/injector-psp.yaml | 43 + templates/injector-role.yaml | 25 + templates/injector-rolebinding.yaml | 18 + templates/injector-service.yaml | 21 + templates/injector-serviceaccount.yaml | 11 + templates/server-clusterrolebinding.yaml | 24 + templates/server-config-configmap.yaml | 38 + templates/server-discovery-role.yaml | 19 + templates/server-discovery-rolebinding.yaml | 27 + templates/server-disruptionbudget.yaml | 24 + templates/server-ha-active-service.yaml | 42 + templates/server-ha-standby-service.yaml | 42 + templates/server-headless-service.yaml | 32 + templates/server-ingress.yaml | 74 + templates/server-network-policy.yaml | 26 + templates/server-psp-role.yaml | 18 + templates/server-psp-rolebinding.yaml | 19 + templates/server-psp.yaml | 47 + templates/server-route.yaml | 33 + templates/server-service.yaml | 43 + templates/server-serviceaccount.yaml | 16 + templates/server-statefulset.yaml | 208 ++ templates/tests/server-test.yaml | 40 + templates/ui-service.yaml | 37 + test/README.md | 55 + test/acceptance/_helpers.bash | 159 ++ test/acceptance/csi-test/nginx.yaml | 27 + .../vault-kv-secretproviderclass.yaml | 14 + test/acceptance/csi-test/vault-policy.hcl | 3 + test/acceptance/csi.bats | 60 + test/acceptance/helm-test.bats | 27 + test/acceptance/injector-leader-elector.bats | 52 + test/acceptance/injector-test/bootstrap.sh | 46 + test/acceptance/injector-test/job.yaml | 39 + .../injector-test/pg-deployment.yaml | 69 + .../injector-test/pgdump-policy.hcl | 3 + test/acceptance/injector.bats | 58 + test/acceptance/server-annotations.bats | 46 + test/acceptance/server-dev.bats | 64 + test/acceptance/server-ha-enterprise-dr.bats | 170 ++ .../acceptance/server-ha-enterprise-perf.bats | 168 ++ test/acceptance/server-ha-raft.bats | 119 ++ test/acceptance/server-ha.bats | 108 ++ .../server-test/annotations-overrides.yaml | 9 + test/acceptance/server.bats | 111 ++ test/chart/_helpers.bash | 18 + test/chart/verifier.bats | 85 + test/docker/Test.dockerfile | 51 + test/kind/config.yaml | 7 + test/terraform/.gitignore | 1 + test/terraform/main.tf | 72 + test/terraform/outputs.tf | 7 + test/terraform/variables.tf | 28 + test/unit/_helpers.bash | 4 + test/unit/csi-clusterrole.bats | 33 + test/unit/csi-clusterrolebinding.bats | 44 + test/unit/csi-daemonset.bats | 516 +++++ test/unit/csi-serviceaccount.bats | 59 + test/unit/injector-clusterrole.bats | 22 + test/unit/injector-clusterrolebinding.bats | 22 + test/unit/injector-deployment.bats | 723 +++++++ test/unit/injector-leader-elector.bats | 168 ++ test/unit/injector-mutating-webhook.bats | 155 ++ test/unit/injector-psp-role.bats | 35 + test/unit/injector-psp-rolebinding.bats | 35 + test/unit/injector-psp.bats | 70 + test/unit/injector-service.bats | 66 + test/unit/injector-serviceaccount.bats | 22 + test/unit/schema.bats | 46 + test/unit/server-clusterrolebinding.bats | 72 + test/unit/server-configmap.bats | 124 ++ test/unit/server-dev-statefulset.bats | 461 +++++ test/unit/server-ha-active-service.bats | 199 ++ test/unit/server-ha-disruptionbudget.bats | 99 + test/unit/server-ha-standby-service.bats | 210 ++ test/unit/server-ha-statefulset.bats | 682 +++++++ test/unit/server-ingress.bats | 267 +++ test/unit/server-network-policy.bats | 35 + test/unit/server-psp-role.bats | 111 ++ test/unit/server-psp-rolebinding.bats | 111 ++ test/unit/server-psp.bats | 285 +++ test/unit/server-route.bats | 143 ++ test/unit/server-service.bats | 426 +++++ test/unit/server-serviceaccount.bats | 119 ++ test/unit/server-statefulset.bats | 1681 +++++++++++++++++ test/unit/ui-service.bats | 375 ++++ values.openshift.yaml | 18 + values.schema.json | 841 +++++++++ values.yaml | 831 ++++++++ 116 files changed, 14215 insertions(+) create mode 100644 .circleci/config.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/jira.yaml create mode 100644 .gitignore create mode 100644 .helmignore create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 Chart.yaml create mode 100644 LICENSE.md create mode 100644 Makefile create mode 100644 README.md create mode 100644 templates/NOTES.txt create mode 100644 templates/_helpers.tpl create mode 100644 templates/csi-clusterrole.yaml create mode 100644 templates/csi-clusterrolebinding.yaml create mode 100644 templates/csi-daemonset.yaml create mode 100644 templates/csi-serviceaccount.yaml create mode 100644 templates/injector-certs-secret.yaml create mode 100644 templates/injector-clusterrole.yaml create mode 100644 templates/injector-clusterrolebinding.yaml create mode 100644 templates/injector-deployment.yaml create mode 100644 templates/injector-mutating-webhook.yaml create mode 100644 templates/injector-network-policy.yaml create mode 100644 templates/injector-psp-role.yaml create mode 100644 templates/injector-psp-rolebinding.yaml create mode 100644 templates/injector-psp.yaml create mode 100644 templates/injector-role.yaml create mode 100644 templates/injector-rolebinding.yaml create mode 100644 templates/injector-service.yaml create mode 100644 templates/injector-serviceaccount.yaml create mode 100644 templates/server-clusterrolebinding.yaml create mode 100644 templates/server-config-configmap.yaml create mode 100644 templates/server-discovery-role.yaml create mode 100644 templates/server-discovery-rolebinding.yaml create mode 100644 templates/server-disruptionbudget.yaml create mode 100644 templates/server-ha-active-service.yaml create mode 100644 templates/server-ha-standby-service.yaml create mode 100644 templates/server-headless-service.yaml create mode 100644 templates/server-ingress.yaml create mode 100644 templates/server-network-policy.yaml create mode 100644 templates/server-psp-role.yaml create mode 100644 templates/server-psp-rolebinding.yaml create mode 100644 templates/server-psp.yaml create mode 100644 templates/server-route.yaml create mode 100644 templates/server-service.yaml create mode 100644 templates/server-serviceaccount.yaml create mode 100644 templates/server-statefulset.yaml create mode 100644 templates/tests/server-test.yaml create mode 100644 templates/ui-service.yaml create mode 100644 test/README.md create mode 100644 test/acceptance/_helpers.bash create mode 100644 test/acceptance/csi-test/nginx.yaml create mode 100644 test/acceptance/csi-test/vault-kv-secretproviderclass.yaml create mode 100644 test/acceptance/csi-test/vault-policy.hcl create mode 100644 test/acceptance/csi.bats create mode 100644 test/acceptance/helm-test.bats create mode 100644 test/acceptance/injector-leader-elector.bats create mode 100755 test/acceptance/injector-test/bootstrap.sh create mode 100644 test/acceptance/injector-test/job.yaml create mode 100644 test/acceptance/injector-test/pg-deployment.yaml create mode 100644 test/acceptance/injector-test/pgdump-policy.hcl create mode 100644 test/acceptance/injector.bats create mode 100644 test/acceptance/server-annotations.bats create mode 100644 test/acceptance/server-dev.bats create mode 100644 test/acceptance/server-ha-enterprise-dr.bats create mode 100644 test/acceptance/server-ha-enterprise-perf.bats create mode 100644 test/acceptance/server-ha-raft.bats create mode 100644 test/acceptance/server-ha.bats create mode 100644 test/acceptance/server-test/annotations-overrides.yaml create mode 100644 test/acceptance/server.bats create mode 100644 test/chart/_helpers.bash create mode 100644 test/chart/verifier.bats create mode 100644 test/docker/Test.dockerfile create mode 100644 test/kind/config.yaml create mode 100644 test/terraform/.gitignore create mode 100644 test/terraform/main.tf create mode 100644 test/terraform/outputs.tf create mode 100644 test/terraform/variables.tf create mode 100644 test/unit/_helpers.bash create mode 100644 test/unit/csi-clusterrole.bats create mode 100644 test/unit/csi-clusterrolebinding.bats create mode 100644 test/unit/csi-daemonset.bats create mode 100644 test/unit/csi-serviceaccount.bats create mode 100755 test/unit/injector-clusterrole.bats create mode 100755 test/unit/injector-clusterrolebinding.bats create mode 100755 test/unit/injector-deployment.bats create mode 100644 test/unit/injector-leader-elector.bats create mode 100755 test/unit/injector-mutating-webhook.bats create mode 100644 test/unit/injector-psp-role.bats create mode 100644 test/unit/injector-psp-rolebinding.bats create mode 100644 test/unit/injector-psp.bats create mode 100755 test/unit/injector-service.bats create mode 100755 test/unit/injector-serviceaccount.bats create mode 100644 test/unit/schema.bats create mode 100755 test/unit/server-clusterrolebinding.bats create mode 100755 test/unit/server-configmap.bats create mode 100755 test/unit/server-dev-statefulset.bats create mode 100755 test/unit/server-ha-active-service.bats create mode 100755 test/unit/server-ha-disruptionbudget.bats create mode 100755 test/unit/server-ha-standby-service.bats create mode 100755 test/unit/server-ha-statefulset.bats create mode 100755 test/unit/server-ingress.bats create mode 100755 test/unit/server-network-policy.bats create mode 100644 test/unit/server-psp-role.bats create mode 100644 test/unit/server-psp-rolebinding.bats create mode 100644 test/unit/server-psp.bats create mode 100755 test/unit/server-route.bats create mode 100755 test/unit/server-service.bats create mode 100755 test/unit/server-serviceaccount.bats create mode 100755 test/unit/server-statefulset.bats create mode 100755 test/unit/ui-service.bats create mode 100644 values.openshift.yaml create mode 100644 values.schema.json create mode 100644 values.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..8de4c83c --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,106 @@ +version: 2.1 +orbs: + slack: circleci/slack@3.4.2 + +jobs: + bats-unit-test: + docker: + # This image is built from test/docker/Test.dockerfile + - image: docker.mirror.hashicorp.services/hashicorpdev/vault-helm-test:0.2.0 + steps: + - checkout + - run: bats ./test/unit -t + + chart-verifier: + docker: + - image: docker.mirror.hashicorp.services/cimg/go:1.16 + environment: + BATS_VERSION: "1.3.0" + CHART_VERIFIER_VERSION: "1.2.1" + steps: + - checkout + - run: + name: install chart-verifier + command: go get github.com/redhat-certification/chart-verifier@${CHART_VERIFIER_VERSION} + - run: + name: install bats + command: | + curl -sSL https://github.com/bats-core/bats-core/archive/v${BATS_VERSION}.tar.gz -o /tmp/bats.tgz + tar -zxf /tmp/bats.tgz -C /tmp + sudo /bin/bash /tmp/bats-core-${BATS_VERSION}/install.sh /usr/local + - run: + name: run chart-verifier tests + command: bats ./test/chart -t + + acceptance: + docker: + # This image is build from test/docker/Test.dockerfile + - image: docker.mirror.hashicorp.services/hashicorpdev/vault-helm-test:0.2.0 + + steps: + - checkout + - run: + name: terraform init & apply + command: | + echo -e "${GOOGLE_APP_CREDS}" | base64 -d > vault-helm-test.json + export GOOGLE_CREDENTIALS=vault-helm-test.json + make provision-cluster + - run: + name: Run acceptance tests + command: bats ./test/acceptance -t + + - run: + name: terraform destroy + command: | + export GOOGLE_CREDENTIALS=vault-helm-test.json + make destroy-cluster + when: always + update-helm-charts-index: + docker: + - image: docker.mirror.hashicorp.services/circleci/golang:1.15.3 + steps: + - checkout + - run: + name: verify Chart version matches tag version + command: | + GO111MODULE=on go get github.com/mikefarah/yq/v2 + git_tag=$(echo "${CIRCLE_TAG#v}") + chart_tag=$(yq r Chart.yaml version) + if [ "${git_tag}" != "${chart_tag}" ]; then + echo "chart version (${chart_tag}) did not match git version (${git_tag})" + exit 1 + fi + - run: + name: update helm-charts index + command: | + curl --show-error --silent --fail --user "${CIRCLE_TOKEN}:" \ + -X POST \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d "{\"branch\": \"master\",\"parameters\":{\"SOURCE_REPO\": \"${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}\",\"SOURCE_TAG\": \"${CIRCLE_TAG}\"}}" \ + "${CIRCLE_ENDPOINT}/${CIRCLE_PROJECT}/pipeline" + - slack/status: + fail_only: true + failure_message: "Failed to trigger an update to the helm charts index. Check the logs at: ${CIRCLE_BUILD_URL}" + +workflows: + version: 2 + build_and_test: + jobs: + - bats-unit-test + - chart-verifier + - acceptance: + requires: + - bats-unit-test + filters: + branches: + only: main + update-helm-charts-index: + jobs: + - update-helm-charts-index: + context: helm-charts-trigger-vault + filters: + tags: + only: /^v.*/ + branches: + ignore: /.*/ diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..cb69c513 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,46 @@ +--- +name: Bug report +about: Let us know about a bug! +title: '' +labels: bug +assignees: '' + +--- + + + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Install chart +2. Run vault command +3. See error (vault logs, etc.) + +Other useful info to include: vault pod logs, `kubectl describe statefulset vault` and `kubectl get statefulset vault -o yaml` output + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Environment** +* Kubernetes version: + * Distribution or cloud vendor (OpenShift, EKS, GKE, AKS, etc.): + * Other configuration options or runtime services (istio, etc.): +* vault-helm version: + +Chart values: + +```yaml +# Paste your user-supplied values here (`helm get values `). +# Be sure to scrub any sensitive values! +``` + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..b24b36b2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,4 @@ +contact_links: + - name: Ask a question + url: https://discuss.hashicorp.com/c/vault + about: For increased visibility, please post questions on the discussion forum, and tag with `k8s` diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..11fc491e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/jira.yaml b/.github/workflows/jira.yaml new file mode 100644 index 00000000..eb369f33 --- /dev/null +++ b/.github/workflows/jira.yaml @@ -0,0 +1,72 @@ +on: + issues: + types: [opened, closed, deleted, reopened] + pull_request_target: + types: [opened, closed, reopened] + issue_comment: # Also triggers when commenting on a PR from the conversation view + types: [created] + +name: Jira Sync + +jobs: + sync: + runs-on: ubuntu-latest + name: Jira sync + steps: + - name: Login + uses: atlassian/gajira-login@v2.0.0 + env: + JIRA_BASE_URL: ${{ secrets.JIRA_SYNC_BASE_URL }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_SYNC_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_SYNC_API_TOKEN }} + + - name: Preprocess + if: github.event.action == 'opened' || github.event.action == 'created' + id: preprocess + run: | + if [[ "${{ github.event_name }}" == "pull_request_target" ]]; then + echo "::set-output name=type::PR" + else + echo "::set-output name=type::ISS" + fi + + - name: Create ticket + if: github.event.action == 'opened' + uses: tomhjp/gh-action-jira-create@v0.2.0 + with: + project: VAULT + issuetype: "GH Issue" + summary: "${{ github.event.repository.name }} [${{ steps.preprocess.outputs.type }} #${{ github.event.issue.number || github.event.pull_request.number }}]: ${{ github.event.issue.title || github.event.pull_request.title }}" + description: "${{ github.event.issue.body || github.event.pull_request.body }}\n\n_Created from GitHub Action for ${{ github.event.issue.html_url || github.event.pull_request.html_url }} from ${{ github.actor }}_" + # customfield_10089 is Issue Link custom field + # customfield_10091 is team custom field + extraFields: '{"fixVersions": [{"name": "TBD"}], "customfield_10091": ["ecosystem", "runtime"], "customfield_10089": "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"}' + + - name: Search + if: github.event.action != 'opened' + id: search + uses: tomhjp/gh-action-jira-search@v0.2.1 + with: + # cf[10089] is Issue Link custom field + jql: 'project = "VAULT" and cf[10089]="${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' + + - name: Sync comment + if: github.event.action == 'created' && steps.search.outputs.issue + uses: tomhjp/gh-action-jira-comment@v0.2.0 + with: + issue: ${{ steps.search.outputs.issue }} + comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}" + + - name: Close ticket + if: (github.event.action == 'closed' || github.event.action == 'deleted') && steps.search.outputs.issue + uses: atlassian/gajira-transition@v2.0.1 + with: + issue: ${{ steps.search.outputs.issue }} + transition: Close + + - name: Reopen ticket + if: github.event.action == 'reopened' && steps.search.outputs.issue + uses: atlassian/gajira-transition@v2.0.1 + with: + issue: ${{ steps.search.outputs.issue }} + transition: "Pending Triage" diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2e23aca2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +.DS_Store +.terraform/ +.terraform.tfstate* +terraform.tfstate* +terraform.tfvars +values.dev.yaml +vaul-helm-dev-creds.json +./test/acceptance/vaul-helm-dev-creds.json +./test/terraform/vaul-helm-dev-creds.json +./test/unit/vaul-helm-dev-creds.json +./test/acceptance/values.yaml +./test/acceptance/values.yml +.idea diff --git a/.helmignore b/.helmignore new file mode 100644 index 00000000..d1180d2f --- /dev/null +++ b/.helmignore @@ -0,0 +1,4 @@ +.git/ +.terraform/ +bin/ +test/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..c596d515 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,350 @@ +## Unreleased + +## 0.18.0 (November 17th, 2021) + +CHANGES: +* Removed support for deploying a leader-elector container with the [vault-k8s injector](https://github.com/hashicorp/vault-k8s) injector since vault-k8s now uses an internal mechanism to determine leadership [GH-649](https://github.com/hashicorp/vault-helm/pull/649) +* Vault image default 1.9.0 +* Vault K8s image default 0.14.1 + +Improvements: +* Added templateConfig.staticSecretRenderInterval chart option for the injector [GH-621](https://github.com/hashicorp/vault-helm/pull/621) + +## 0.17.1 (October 25th, 2021) + +Improvements: + * Add option for Ingress PathType [GH-634](https://github.com/hashicorp/vault-helm/pull/634) + +## 0.17.0 (October 21st, 2021) + +KNOWN ISSUES: +* The chart will fail to deploy on Kubernetes 1.19+ with `server.ingress.enabled=true` because no `pathType` is set + +CHANGES: +* Vault image default 1.8.4 +* Vault K8s image default 0.14.0 + +Improvements: +* Support Ingress stable networking API [GH-590](https://github.com/hashicorp/vault-helm/pull/590) +* Support setting the `externalTrafficPolicy` for `LoadBalancer` and `NodePort` service types [GH-626](https://github.com/hashicorp/vault-helm/pull/626) +* Support setting ingressClassName on server Ingress [GH-630](https://github.com/hashicorp/vault-helm/pull/630) + +Bugs: +* Ensure `kubeletRootDir` volume path and mounts are the same when `csi.daemonSet.kubeletRootDir` is overridden [GH-628](https://github.com/hashicorp/vault-helm/pull/628) + +## 0.16.1 (September 29th, 2021) + +CHANGES: +* Vault image default 1.8.3 +* Vault K8s image default 0.13.1 + +## 0.16.0 (September 16th, 2021) + +CHANGES: +* Support for deploying a leader-elector container with the [vault-k8s injector](https://github.com/hashicorp/vault-k8s) injector will be removed in version 0.18.0 of this chart since vault-k8s now uses an internal mechanism to determine leadership. To enable the deployment of the leader-elector container for use with vault-k8s 0.12.0 and earlier, set `useContainer=true`. + +Improvements: + * Make CSI provider `hostPaths` configurable via `csi.daemonSet.providersDir` and `csi.daemonSet.kubeletRootDir` [GH-603](https://github.com/hashicorp/vault-helm/pull/603) + * Support vault-k8s internal leader election [GH-568](https://github.com/hashicorp/vault-helm/pull/568) [GH-607](https://github.com/hashicorp/vault-helm/pull/607) + +## 0.15.0 (August 23rd, 2021) + +Improvements: +* Add imagePullSecrets on server test [GH-572](https://github.com/hashicorp/vault-helm/pull/572) +* Add injector.webhookAnnotations chart option [GH-584](https://github.com/hashicorp/vault-helm/pull/584) + +## 0.14.0 (July 28th, 2021) + +Features: +* Added templateConfig.exitOnRetryFailure chart option for the injector [GH-560](https://github.com/hashicorp/vault-helm/pull/560) + +Improvements: +* Support configuring pod tolerations, pod affinity, and node selectors as YAML [GH-565](https://github.com/hashicorp/vault-helm/pull/565) +* Set the default vault image to come from the hashicorp organization [GH-567](https://github.com/hashicorp/vault-helm/pull/567) +* Add support for running the acceptance tests against a local `kind` cluster [GH-567](https://github.com/hashicorp/vault-helm/pull/567) +* Add `server.ingress.activeService` to configure if the ingress should use the active service [GH-570](https://github.com/hashicorp/vault-helm/pull/570) +* Add `server.route.activeService` to configure if the route should use the active service [GH-570](https://github.com/hashicorp/vault-helm/pull/570) +* Support configuring `global.imagePullSecrets` from a string array [GH-576](https://github.com/hashicorp/vault-helm/pull/576) + + +## 0.13.0 (June 17th, 2021) + +Improvements: +* Added a helm test for vault server [GH-531](https://github.com/hashicorp/vault-helm/pull/531) +* Added server.enterpriseLicense option [GH-547](https://github.com/hashicorp/vault-helm/pull/547) +* Added OpenShift overrides [GH-549](https://github.com/hashicorp/vault-helm/pull/549) + +Bugs: +* Fix ui.serviceNodePort schema [GH-537](https://github.com/hashicorp/vault-helm/pull/537) +* Fix server.ha.disruptionBudget.maxUnavailable schema [GH-535](https://github.com/hashicorp/vault-helm/pull/535) +* Added webhook-certs volume mount to sidecar injector [GH-545](https://github.com/hashicorp/vault-helm/pull/545) + +## 0.12.0 (May 25th, 2021) + +Features: +* Pass additional arguments to `vault-csi-provider` using `csi.extraArgs` [GH-526](https://github.com/hashicorp/vault-helm/pull/526) + +Improvements: +* Set chart kubeVersion and added chart-verifier tests [GH-510](https://github.com/hashicorp/vault-helm/pull/510) +* Added values json schema [GH-513](https://github.com/hashicorp/vault-helm/pull/513) +* Ability to set tolerations for CSI daemonset pods [GH-521](https://github.com/hashicorp/vault-helm/pull/521) +* UI target port is now configurable [GH-437](https://github.com/hashicorp/vault-helm/pull/437) + +Bugs: +* CSI: `global.imagePullSecrets` are now also used for CSI daemonset [GH-519](https://github.com/hashicorp/vault-helm/pull/519) + +## 0.11.0 (April 14th, 2021) + +Features: +* Added `server.enabled` to explicitly skip installing a Vault server [GH-486](https://github.com/hashicorp/vault-helm/pull/486) +* Injector now supports enabling host network [GH-471](https://github.com/hashicorp/vault-helm/pull/471) +* Injector port is now configurable [GH-489](https://github.com/hashicorp/vault-helm/pull/489) +* Injector Vault Agent resource defaults are now configurable [GH-493](https://github.com/hashicorp/vault-helm/pull/493) +* Extra paths can now be added to the Vault ingress service [GH-460](https://github.com/hashicorp/vault-helm/pull/460) +* Log level and format can now be set directly using `server.logFormat` and `server.logLevel` [GH-488](https://github.com/hashicorp/vault-helm/pull/488) + +Improvements: +* Added `https` name to injector service port [GH-495](https://github.com/hashicorp/vault-helm/pull/495) + +Bugs: +* CSI: Fix ClusterRole name and DaemonSet's service account to properly match deployment name [GH-486](https://github.com/hashicorp/vault-helm/pull/486) + +## 0.10.0 (March 25th, 2021) + +Features: +* Add support for [Vault CSI provider](https://github.com/hashicorp/vault-csi-provider) [GH-461](https://github.com/hashicorp/vault-helm/pull/461) + +Improvements: +* `objectSelector` can now be set on the mutating admission webhook [GH-456](https://github.com/hashicorp/vault-helm/pull/456) + +## 0.9.1 (February 2nd, 2021) + +Bugs: +* Injector: fix labels for default anti-affinity rule [GH-441](https://github.com/hashicorp/vault-helm/pull/441), [GH-442](https://github.com/hashicorp/vault-helm/pull/442) +* Set VAULT_DEV_LISTEN_ADDRESS in dev mode [GH-446](https://github.com/hashicorp/vault-helm/pull/446) + +## 0.9.0 (January 5th, 2021) + +Features: +* Injector now supports configurable number of replicas [GH-436](https://github.com/hashicorp/vault-helm/pull/436) +* Injector now supports auto TLS for multiple replicas using leader elections [GH-436](https://github.com/hashicorp/vault-helm/pull/436) + +Improvements: +* Dev mode now supports `server.extraArgs` [GH-421](https://github.com/hashicorp/vault-helm/pull/421) +* Dev mode root token is now configurable with `server.dev.devRootToken` [GH-415](https://github.com/hashicorp/vault-helm/pull/415) +* ClusterRoleBinding updated to `v1` [GH-395](https://github.com/hashicorp/vault-helm/pull/395) +* MutatingWebhook updated to `v1` [GH-408](https://github.com/hashicorp/vault-helm/pull/408) +* Injector service now supports `injector.service.annotations` [425](https://github.com/hashicorp/vault-helm/pull/425) +* Injector now supports `injector.extraLabels` [428](https://github.com/hashicorp/vault-helm/pull/428) +* Added `allowPrivilegeEscalation: false` to Vault and Injector containers [429](https://github.com/hashicorp/vault-helm/pull/429) +* Network Policy now supports `server.networkPolicy.egress` [389](https://github.com/hashicorp/vault-helm/pull/389) + +## 0.8.0 (October 20th, 2020) + +Improvements: +* Make server NetworkPolicy independent of OpenShift [GH-381](https://github.com/hashicorp/vault-helm/pull/381) +* Added configurables for all probe values [GH-387](https://github.com/hashicorp/vault-helm/pull/387) +* MountPath for audit and data storage is now configurable [GH-393](https://github.com/hashicorp/vault-helm/pull/393) +* Annotations can now be added to the Injector pods [GH-394](https://github.com/hashicorp/vault-helm/pull/394) +* The injector can now be configured with a failurePolicy [GH-400](https://github.com/hashicorp/vault-helm/pull/400) +* Added additional environment variables for rendering within Vault config [GH-398](https://github.com/hashicorp/vault-helm/pull/398) +* Service account for Vault K8s auth is automatically created when `injector.externalVaultAddr` is set [GH-392](https://github.com/hashicorp/vault-helm/pull/392) + +Bugs: +* Fixed install output using Helm V2 command [GH-378](https://github.com/hashicorp/vault-helm/pull/378) + +## 0.7.0 (August 24th, 2020) + +Features: +* Added `volumes` and `volumeMounts` for mounting _any_ type of volume [GH-314](https://github.com/hashicorp/vault-helm/pull/314). +* Added configurable to enable prometheus telemetery exporter for Vault Agent Injector [GH-372](https://github.com/hashicorp/vault-helm/pull/372) + +Improvements: +* Added `defaultMode` configurable to `extraVolumes`[GH-321](https://github.com/hashicorp/vault-helm/pull/321) +* Option to install and use PodSecurityPolicy's for vault server and injector [GH-177](https://github.com/hashicorp/vault-helm/pull/177) +* `VAULT_API_ADDR` is now configurable [GH-290](https://github.com/hashicorp/vault-helm/pull/290) +* Removed deprecated tolerate unready endpoint annotations [GH-363](https://github.com/hashicorp/vault-helm/pull/363) +* Add an option to set annotations on the StatefulSet [GH-199](https://github.com/hashicorp/vault-helm/pull/199) +* Make the vault server serviceAccount name a configuration option [GH-367](https://github.com/hashicorp/vault-helm/pull/367) +* Removed annotation striction from `dev` mode [GH-371](https://github.com/hashicorp/vault-helm/pull/371) +* Add an option to set annotations on PVCs [GH-364](https://github.com/hashicorp/vault-helm/pull/364) +* Added service configurables for UI [GH-285](https://github.com/hashicorp/vault-helm/pull/285) + +Bugs: +* Fix python dependency in test image [GH-337](https://github.com/hashicorp/vault-helm/pull/337) +* Fix caBundle not being quoted causing validation issues with Helm 3 [GH-352](https://github.com/hashicorp/vault-helm/pull/352) +* Fix injector network policy being rendered when injector is not enabled [GH-358](https://github.com/hashicorp/vault-helm/pull/358) + +## 0.6.0 (June 3rd, 2020) + +Features: +* Added `extraInitContainers` to define init containers for the Vault cluster [GH-258](https://github.com/hashicorp/vault-helm/pull/258) +* Added `postStart` lifecycle hook allowing users to configure commands to run on the Vault pods after they're ready [GH-315](https://github.com/hashicorp/vault-helm/pull/315) +* Beta: Added OpenShift support [GH-319](https://github.com/hashicorp/vault-helm/pull/319) + +Improvements: +* Server configs can now be defined in YAML. Multi-line string configs are still compatible [GH-213](https://github.com/hashicorp/vault-helm/pull/213) +* Removed IPC_LOCK privileges since swap is disabled on containers [[GH-198](https://github.com/hashicorp/vault-helm/pull/198)] +* Use port names that map to vault.scheme [[GH-223](https://github.com/hashicorp/vault-helm/pull/223)] +* Allow both yaml and multi-line string annotations [[GH-272](https://github.com/hashicorp/vault-helm/pull/272)] +* Added configurable to set the Raft node name to hostname [[GH-269](https://github.com/hashicorp/vault-helm/pull/269)] +* Support setting priorityClassName on pods [[GH-282](https://github.com/hashicorp/vault-helm/pull/282)] +* Added support for ingress apiVersion `networking.k8s.io/v1beta1` [[GH-310](https://github.com/hashicorp/vault-helm/pull/310)] +* Added configurable to change service type for the HA active service [GH-317](https://github.com/hashicorp/vault-helm/pull/317) + +Bugs: +* Fixed default ingress path [[GH-224](https://github.com/hashicorp/vault-helm/pull/224)] +* Fixed annotations for HA standby/active services [[GH-268](https://github.com/hashicorp/vault-helm/pull/268)] +* Updated some value defaults to match their use in templates [[GH-309](https://github.com/hashicorp/vault-helm/pull/309)] +* Use active service on ingress when ha [[GH-270](https://github.com/hashicorp/vault-helm/pull/270)] +* Fixed bug where pull secrets weren't being used for injector image [GH-298](https://github.com/hashicorp/vault-helm/pull/298) + +## 0.5.0 (April 9th, 2020) + +Features: + +* Added Raft support for HA mode [[GH-228](https://github.com/hashicorp/vault-helm/pull/229)] +* Now supports Vault Enterprise [[GH-250](https://github.com/hashicorp/vault-helm/pull/250)] +* Added K8s Service Registration for HA modes [[GH-250](https://github.com/hashicorp/vault-helm/pull/250)] + +* Option to set `AGENT_INJECT_VAULT_AUTH_PATH` for the injector [[GH-185](https://github.com/hashicorp/vault-helm/pull/185)] +* Added environment variables for logging and revocation on Vault Agent Injector [[GH-219](https://github.com/hashicorp/vault-helm/pull/219)] +* Option to set environment variables for the injector deployment [[GH-232](https://github.com/hashicorp/vault-helm/pull/232)] +* Added affinity, tolerations, and nodeSelector options for the injector deployment [[GH-234](https://github.com/hashicorp/vault-helm/pull/234)] +* Made all annotations multi-line strings [[GH-227](https://github.com/hashicorp/vault-helm/pull/227)] + +## 0.4.0 (February 21st, 2020) + +Improvements: + +* Allow process namespace sharing between Vault and sidecar containers [[GH-174](https://github.com/hashicorp/vault-helm/pull/174)] +* Added configurable to change updateStrategy [[GH-172](https://github.com/hashicorp/vault-helm/pull/172)] +* Added sleep in the preStop lifecycle step [[GH-188](https://github.com/hashicorp/vault-helm/pull/188)] +* Updated chart and tests to Helm 3 [[GH-195](https://github.com/hashicorp/vault-helm/pull/195)] +* Adds Values.injector.externalVaultAddr to use the injector with an external vault [[GH-207](https://github.com/hashicorp/vault-helm/pull/207)] + +Bugs: + +* Fix bug where Vault lifecycle was appended after extra containers. [[GH-179](https://github.com/hashicorp/vault-helm/pull/179)] + +## 0.3.3 (January 14th, 2020) + +Security: + +* Added `server.extraArgs` to allow loading of additional Vault configurations containing sensitive settings [GH-175](https://github.com/hashicorp/vault-helm/issues/175) + +Bugs: + +* Fixed injection bug where wrong environment variables were being used for manually mounted TLS files + +## 0.3.2 (January 8th, 2020) + +Bugs: + +* Fixed injection bug where TLS Skip Verify was true by default [VK8S-35] + +## 0.3.1 (January 2nd, 2020) + +Bugs: + +* Fixed injection bug causing kube-system pods to be rejected [VK8S-14] + +## 0.3.0 (December 19th, 2019) + +Features: + +* Extra containers can now be added to the Vault pods +* Added configurability of pod probes +* Added Vault Agent Injector + +Improvements: + +* Moved `global.image` to `server.image` +* Changed UI service template to route pods that aren't ready via `publishNotReadyAddresses: true` +* Added better HTTP/HTTPS scheme support to http probes +* Added configurable node port for Vault service +* `server.authDelegator` is now enabled by default + +Bugs: + +* Fixed upgrade bug by removing chart label which contained the version +* Fixed typo on `serviceAccount` (was `serviceaccount`) +* Fixed readiness/liveliness HTTP probe default to accept standbys + +## 0.2.1 (November 12th, 2019) + +Bugs: + +* Removed `readOnlyRootFilesystem` causing issues when validating deployments + +## 0.2.0 (October 29th, 2019) + +Features: + +* Added load balancer support +* Added ingress support +* Added configurable for service types (ClusterIP, NodePort, LoadBalancer, etc) +* Removed root requirements, now runs as Vault user + +Improvements: + +* Added namespace value to all rendered objects +* Made ports configurable in services +* Added the ability to add custom annotations to services +* Added docker image for running bats test in CircleCI +* Removed restrictions around `dev` mode such as annotations +* `readOnlyRootFilesystem` is now configurable +* Image Pull Policy is now configurable + +Bugs: + +* Fixed selector bugs related to Helm label updates (services, affinities, and pod disruption) +* Fixed bug where audit storage was not being mounted in HA mode +* Fixed bug where Vault pod wasn't receiving SIGTERM signals + + +## 0.1.2 (August 22nd, 2019) + +Features: + +* Added `extraSecretEnvironmentVars` to allow users to mount secrets as + environment variables +* Added `tlsDisable` configurable to change HTTP protocols from HTTP/HTTPS + depending on the value +* Added `serviceNodePort` to configure a NodePort value when setting `serviceType` + to "NodePort" + +Improvements: + +* Changed UI port to 8200 for better HTTP protocol support +* Added `path` to `extraVolumes` to define where the volume should be + mounted. Defaults to `/vault/userconfig` +* Upgraded Vault to 1.2.2 + +Bugs: + +* Fixed bug where upgrade would fail because immutable labels were being + changed (Helm Version label) +* Fixed bug where UI service used wrong selector after updating helm labels +* Added `VAULT_API_ADDR` env to Vault pod to fixed bug where Vault thinks + Consul is the active node +* Removed `step-down` preStop since it requires authentication. Shutdown signal + sent by Kube acts similar to `step-down` + + +## 0.1.1 (August 7th, 2019) + +Features: + +* Added `authDelegator` Cluster Role Binding to Vault service account for + bootstrapping Kube auth method + +Improvements: + +* Added `server.service.clusterIP` to `values.yml` so users can toggle + the Vault service to headless by using the value `None`. +* Upgraded Vault to 1.2.1 + +## 0.1.0 (August 6th, 2019) + +Initial release diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..f1c16000 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,239 @@ +# Contributing to Vault Helm + +**Please note:** We take Vault's security and our users' trust very seriously. +If you believe you have found a security issue in Vault, please responsibly +disclose by contacting us at security@hashicorp.com. + +**First:** if you're unsure or afraid of _anything_, just ask or submit the +issue or pull request anyways. You won't be yelled at for giving it your best +effort. The worst that can happen is that you'll be politely asked to change +something. We appreciate any sort of contributions, and don't want a wall of +rules to get in the way of that. + +That said, if you want to ensure that a pull request is likely to be merged, +talk to us! You can find out our thoughts and ensure that your contribution +won't clash or be obviated by Vault's normal direction. A great way to do this +is via the [Vault Google Group][2]. Sometimes Vault devs are in `#vault-tool` +on Freenode, too. + +This document will cover what we're looking for in terms of reporting issues. +By addressing all the points we're looking for, it raises the chances we can +quickly merge or address your contributions. + +## Issues + +### Reporting an Issue + +* Make sure you test against the latest released version. It is possible + we already fixed the bug you're experiencing. Even better is if you can test + against `main`, as bugs are fixed regularly but new versions are only + released every few months. + +* Provide steps to reproduce the issue, and if possible include the expected + results as well as the actual results. Please provide text, not screen shots! + +* Respond as promptly as possible to any questions made by the Vault + team to your issue. Stale issues will be closed periodically. + +### Issue Lifecycle + +1. The issue is reported. + +2. The issue is verified and categorized by a Vault Helm collaborator. + Categorization is done via tags. For example, bugs are marked as "bugs". + +3. Unless it is critical, the issue may be left for a period of time (sometimes + many weeks), giving outside contributors -- maybe you!? -- a chance to + address the issue. + +4. The issue is addressed in a pull request or commit. The issue will be + referenced in the commit message so that the code that fixes it is clearly + linked. + +5. The issue is closed. Sometimes, valid issues will be closed to keep + the issue tracker clean. The issue is still indexed and available for + future viewers, or can be re-opened if necessary. + +## Testing + +The Helm chart ships with both unit and acceptance tests. + +The unit tests don't require any active Kubernetes cluster and complete +very quickly. These should be used for fast feedback during development. +The acceptance tests require a Kubernetes cluster with a configured `kubectl`. + +### Test Using Docker Container + +The following are the instructions for running bats tests using a Docker container. + +#### Prerequisites + +* Docker installed +* `vault-helm` checked out locally + +#### Test + +**Note:** the following commands should be run from the `vault-helm` directory. + +First, build the Docker image for running the tests: + +```shell +docker build -f ${PWD}/test/docker/Test.dockerfile ${PWD}/test/docker/ -t vault-helm-test +``` +Next, execute the tests with the following commands: +```shell +docker run -it --rm -v "${PWD}:/test" vault-helm-test bats /test/test/unit +``` +It's possible to only run specific bats tests using regular expressions. +For example, the following will run only tests with "injector" in the name: +```shell +docker run -it --rm -v "${PWD}:/test" vault-helm-test bats /test/test/unit -f "injector" +``` + +### Test Manually +The following are the instructions for running bats tests on your workstation. +#### Prerequisites +* [Bats](https://github.com/bats-core/bats-core) + ```bash + brew install bats-core + ``` +* [yq](https://pypi.org/project/yq/) + ```bash + brew install python-yq + ``` +* [helm](https://helm.sh) + ```bash + brew install kubernetes-helm + ``` + +#### Test + +To run the unit tests: + + bats ./test/unit + +To run the acceptance tests: + + bats ./test/acceptance + +If the acceptance tests fail, deployed resources in the Kubernetes cluster +may not be properly cleaned up. We recommend recycling the Kubernetes cluster to +start from a clean slate. + +**Note:** There is a Terraform configuration in the +[`test/terraform/`](https://github.com/hashicorp/vault-helm/tree/main/test/terraform) directory +that can be used to quickly bring up a GKE cluster and configure +`kubectl` and `helm` locally. This can be used to quickly spin up a test +cluster for acceptance tests. Unit tests _do not_ require a running Kubernetes +cluster. + +### Writing Unit Tests + +Changes to the Helm chart should be accompanied by appropriate unit tests. + +#### Formatting + +- Put tests in the test file in the same order as the variables appear in the `values.yaml`. +- Start tests for a chart value with a header that says what is being tested, like this: + ``` + #-------------------------------------------------------------------- + # annotations + ``` + +- Name the test based on what it's testing in the following format (this will be its first line): + ``` + @test "
: " { + ``` + + When adding tests to an existing file, the first section will be the same as the other tests in the file. + +#### Test Details + +[Bats](https://github.com/bats-core/bats-core) provides a way to run commands in a shell and inspect the output in an automated way. +In all of the tests in this repo, the base command being run is [helm template](https://docs.helm.sh/helm/#helm-template) which turns the templated files into straight yaml output. +In this way, we're able to test that the various conditionals in the templates render as we would expect. + +Each test defines the files that should be rendered using the `--show-only` flag, then it might adjust chart values by adding `--set` flags as well. +The output from this `helm template` command is then piped to [yq](https://pypi.org/project/yq/). +`yq` allows us to pull out just the information we're interested in, either by referencing its position in the yaml file directly or giving information about it (like its length). +The `-r` flag can be used with `yq` to return a raw string instead of a quoted one which is especially useful when looking for an exact match. + +The test passes or fails based on the conditional at the end that is in square brackets, which is a comparison of our expected value and the output of `helm template` piped to `yq`. + +The `| tee /dev/stderr ` pieces direct any terminal output of the `helm template` and `yq` commands to stderr so that it doesn't interfere with `bats`. + +#### Test Examples + +Here are some examples of common test patterns: + +- Check that a value is disabled by default + + ``` + @test "ui/Service: no type by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "null" ] + } + ``` + + In this example, nothing is changed from the default templates (no `--set` flags), then we use `yq` to retrieve the value we're checking, `.spec.type`. + This output is then compared against our expected value (`null` in this case) in the assertion `[ "${actual}" = "null" ]`. + + +- Check that a template value is rendered to a specific value + ``` + @test "ui/Service: specified type" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.serviceType=LoadBalancer' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "LoadBalancer" ] + } + ``` + + This is very similar to the last example, except we've changed a default value with the `--set` flag and correspondingly changed the expected value. + +- Check that a template value contains several values + ``` + @test "server/standalone-StatefulSet: custom resources" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.resources.requests.memory=256Mi' \ + --set 'server.resources.requests.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.resources.limits.memory=256Mi' \ + --set 'server.resources.limits.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + ``` + + *Note:* If testing more than two conditions, it would be good to separate the `helm template` part of the command from the `yq` sections to reduce redundant work. + +- Check that an entire template file is not rendered + ``` + @test "syncCatalog/Deployment: disabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + } + ``` + Here we are check the length of the command output to see if the anything is rendered. + This style can easily be switched to check that a file is rendered instead. diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 00000000..91565e3b --- /dev/null +++ b/Chart.yaml @@ -0,0 +1,14 @@ +apiVersion: v2 +name: vault +version: 0.18.0 +appVersion: 1.9.0 +kubeVersion: ">= 1.14.0-0" +description: Official HashiCorp Vault Chart +home: https://www.vaultproject.io +icon: https://github.com/hashicorp/vault/raw/f22d202cde2018f9455dec755118a9b84586e082/Vault_PrimaryLogo_Black.png +keywords: ["vault", "security", "encryption", "secrets", "management", "automation", "infrastructure"] +sources: + - https://github.com/hashicorp/vault + - https://github.com/hashicorp/vault-helm + - https://github.com/hashicorp/vault-k8s + - https://github.com/hashicorp/vault-csi-provider diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..82b4de97 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,353 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..0ac68501 --- /dev/null +++ b/Makefile @@ -0,0 +1,101 @@ +TEST_IMAGE?=vault-helm-test +GOOGLE_CREDENTIALS?=vault-helm-test.json +CLOUDSDK_CORE_PROJECT?=vault-helm-dev-246514 +# set to run a single test - e.g acceptance/server-ha-enterprise-dr.bats +ACCEPTANCE_TESTS?=acceptance + +# filter bats unit tests to run. +UNIT_TESTS_FILTER?='.*' + +# set to 'true' to run acceptance tests locally in a kind cluster +LOCAL_ACCEPTANCE_TESTS?=false + +# kind cluster name +KIND_CLUSTER_NAME?=vault-helm + +# kind k8s version +KIND_K8S_VERSION?=v1.20.2 + +# Generate json schema for chart values. See test/README.md for more details. +values-schema: + helm schema-gen values.yaml > values.schema.json + +test-image: + @docker build --rm -t $(TEST_IMAGE) -f $(CURDIR)/test/docker/Test.dockerfile $(CURDIR) + +test-unit: + @docker run --rm -it -v ${PWD}:/helm-test $(TEST_IMAGE) bats -f $(UNIT_TESTS_FILTER) /helm-test/test/unit + +test-bats: test-unit test-acceptance + +test: test-image test-bats + +# run acceptance tests on GKE +# set google project/credential vars above +test-acceptance: +ifeq ($(LOCAL_ACCEPTANCE_TESTS),true) + make setup-kind acceptance +else + @docker run -it -v ${PWD}:/helm-test \ + -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ + -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ + -e KUBECONFIG=/helm-test/.kube/config \ + -e VAULT_LICENSE_CI=${VAULT_LICENSE_CI} \ + -w /helm-test \ + $(TEST_IMAGE) \ + make acceptance +endif + +# destroy GKE cluster using terraform +test-destroy: + @docker run -it -v ${PWD}:/helm-test \ + -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ + -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ + -w /helm-test \ + $(TEST_IMAGE) \ + make destroy-cluster + +# provision GKE cluster using terraform +test-provision: + @docker run -it -v ${PWD}:/helm-test \ + -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ + -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ + -e KUBECONFIG=/helm-test/.kube/config \ + -w /helm-test \ + $(TEST_IMAGE) \ + make provision-cluster + +# this target is for running the acceptance tests +# it is run in the docker container above when the test-acceptance target is invoked +acceptance: +ifneq ($(LOCAL_ACCEPTANCE_TESTS),true) + gcloud auth activate-service-account --key-file=${GOOGLE_CREDENTIALS} +endif + bats test/${ACCEPTANCE_TESTS} + +# this target is for provisioning the GKE cluster +# it is run in the docker container above when the test-provision target is invoked +provision-cluster: + gcloud auth activate-service-account --key-file=${GOOGLE_CREDENTIALS} + terraform init test/terraform + terraform apply -var project=${CLOUDSDK_CORE_PROJECT} -var init_cli=true -auto-approve test/terraform + +# this target is for removing the GKE cluster +# it is run in the docker container above when the test-destroy target is invoked +destroy-cluster: + terraform destroy -auto-approve + +# create a kind cluster for running the acceptance tests locally +setup-kind: + kind get clusters | grep -q "^${KIND_CLUSTER_NAME}$$" || \ + kind create cluster \ + --image kindest/node:${KIND_K8S_VERSION} \ + --name ${KIND_CLUSTER_NAME} \ + --config $(CURDIR)/test/kind/config.yaml + kubectl config use-context kind-${KIND_CLUSTER_NAME} + +# delete the kind cluster +delete-kind: + kind delete cluster --name ${KIND_CLUSTER_NAME} || : + +.PHONY: values-schema test-image test-unit test-bats test test-acceptance test-destroy test-provision acceptance provision-cluster destroy-cluster diff --git a/README.md b/README.md new file mode 100644 index 00000000..f95b26fc --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# Vault Helm Chart + +> :warning: **Please note**: We take Vault's security and our users' trust very seriously. If +you believe you have found a security issue in Vault Helm, _please responsibly disclose_ +by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). + +This repository contains the official HashiCorp Helm chart for installing +and configuring Vault on Kubernetes. This chart supports multiple use +cases of Vault on Kubernetes depending on the values provided. + +For full documentation on this Helm chart along with all the ways you can +use Vault with Kubernetes, please see the +[Vault and Kubernetes documentation](https://www.vaultproject.io/docs/platform/k8s/). + +## Prerequisites + +To use the charts here, [Helm](https://helm.sh/) must be configured for your +Kubernetes cluster. Setting up Kubernetes and Helm is outside the scope of +this README. Please refer to the Kubernetes and Helm documentation. + +The versions required are: + + * **Helm 3.0+** - This is the earliest version of Helm tested. It is possible + it works with earlier versions but this chart is untested for those versions. + * **Kubernetes 1.14+** - This is the earliest version of Kubernetes tested. + It is possible that this chart works with earlier versions but it is + untested. + +## Usage + +To install the latest version of this chart, add the Hashicorp helm repository +and run `helm install`: + +```console +$ helm repo add hashicorp https://helm.releases.hashicorp.com +"hashicorp" has been added to your repositories + +$ helm install vault hashicorp/vault +``` + +Please see the many options supported in the `values.yaml` file. These are also +fully documented directly on the [Vault +website](https://www.vaultproject.io/docs/platform/k8s/helm) along with more +detailed installation instructions. diff --git a/templates/NOTES.txt b/templates/NOTES.txt new file mode 100644 index 00000000..8e267121 --- /dev/null +++ b/templates/NOTES.txt @@ -0,0 +1,14 @@ + +Thank you for installing HashiCorp Vault! + +Now that you have deployed Vault, you should look over the docs on using +Vault with Kubernetes available here: + +https://www.vaultproject.io/docs/ + + +Your release is named {{ .Release.Name }}. To learn more about the release, try: + + $ helm status {{ .Release.Name }} + $ helm get manifest {{ .Release.Name }} + diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl new file mode 100644 index 00000000..731119a9 --- /dev/null +++ b/templates/_helpers.tpl @@ -0,0 +1,692 @@ +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to +this (by the DNS naming spec). If release name contains chart name it will +be used as a full name. +*/}} +{{- define "vault.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "vault.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Expand the name of the chart. +*/}} +{{- define "vault.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Compute the maximum number of unavailable replicas for the PodDisruptionBudget. +This defaults to (n/2)-1 where n is the number of members of the server cluster. +Add a special case for replicas=1, where it should default to 0 as well. +*/}} +{{- define "vault.pdb.maxUnavailable" -}} +{{- if eq (int .Values.server.ha.replicas) 1 -}} +{{ 0 }} +{{- else if .Values.server.ha.disruptionBudget.maxUnavailable -}} +{{ .Values.server.ha.disruptionBudget.maxUnavailable -}} +{{- else -}} +{{- div (sub (div (mul (int .Values.server.ha.replicas) 10) 2) 1) 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Set the variable 'mode' to the server mode requested by the user to simplify +template logic. +*/}} +{{- define "vault.mode" -}} + {{- if .Values.injector.externalVaultAddr -}} + {{- $_ := set . "mode" "external" -}} + {{- else if ne (.Values.server.enabled | toString) "true" -}} + {{- $_ := set . "mode" "external" -}} + {{- else if eq (.Values.server.dev.enabled | toString) "true" -}} + {{- $_ := set . "mode" "dev" -}} + {{- else if eq (.Values.server.ha.enabled | toString) "true" -}} + {{- $_ := set . "mode" "ha" -}} + {{- else if or (eq (.Values.server.standalone.enabled | toString) "true") (eq (.Values.server.standalone.enabled | toString) "-") -}} + {{- $_ := set . "mode" "standalone" -}} + {{- else -}} + {{- $_ := set . "mode" "" -}} + {{- end -}} +{{- end -}} + +{{/* +Set's the replica count based on the different modes configured by user +*/}} +{{- define "vault.replicas" -}} + {{ if eq .mode "standalone" }} + {{- default 1 -}} + {{ else if eq .mode "ha" }} + {{- .Values.server.ha.replicas | default 3 -}} + {{ else }} + {{- default 1 -}} + {{ end }} +{{- end -}} + +{{/* +Set's up configmap mounts if this isn't a dev deployment and the user +defined a custom configuration. Additionally iterates over any +extra volumes the user may have specified (such as a secret with TLS). +*/}} +{{- define "vault.volumes" -}} + {{- if and (ne .mode "dev") (or (.Values.server.standalone.config) (.Values.server.ha.config)) }} + - name: config + configMap: + name: {{ template "vault.fullname" . }}-config + {{ end }} + {{- range .Values.server.extraVolumes }} + - name: userconfig-{{ .name }} + {{ .type }}: + {{- if (eq .type "configMap") }} + name: {{ .name }} + {{- else if (eq .type "secret") }} + secretName: {{ .name }} + {{- end }} + defaultMode: {{ .defaultMode | default 420 }} + {{- end }} + {{- if .Values.server.volumes }} + {{- toYaml .Values.server.volumes | nindent 8}} + {{- end }} + {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} + - name: vault-license + secret: + secretName: {{ .Values.server.enterpriseLicense.secretName }} + defaultMode: 0440 + {{- end }} +{{- end -}} + +{{/* +Set's the args for custom command to render the Vault configuration +file with IP addresses to make the out of box experience easier +for users looking to use this chart with Consul Helm. +*/}} +{{- define "vault.args" -}} + {{ if or (eq .mode "standalone") (eq .mode "ha") }} + - | + cp /vault/config/extraconfig-from-values.hcl /tmp/storageconfig.hcl; + [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /tmp/storageconfig.hcl; + [ -n "${API_ADDR}" ] && sed -Ei "s|API_ADDR|${API_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${TRANSIT_ADDR}" ] && sed -Ei "s|TRANSIT_ADDR|${TRANSIT_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${RAFT_ADDR}" ] && sed -Ei "s|RAFT_ADDR|${RAFT_ADDR?}|g" /tmp/storageconfig.hcl; + /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl {{ .Values.server.extraArgs }} + {{ else if eq .mode "dev" }} + - | + /usr/local/bin/docker-entrypoint.sh vault server -dev {{ .Values.server.extraArgs }} + {{ end }} +{{- end -}} + +{{/* +Set's additional environment variables based on the mode. +*/}} +{{- define "vault.envs" -}} + {{ if eq .mode "dev" }} + - name: VAULT_DEV_ROOT_TOKEN_ID + value: {{ .Values.server.dev.devRootToken }} + - name: VAULT_DEV_LISTEN_ADDRESS + value: "[::]:8200" + {{ end }} +{{- end -}} + +{{/* +Set's which additional volumes should be mounted to the container +based on the mode configured. +*/}} +{{- define "vault.mounts" -}} + {{ if eq (.Values.server.auditStorage.enabled | toString) "true" }} + - name: audit + mountPath: {{ .Values.server.auditStorage.mountPath }} + {{ end }} + {{ if or (eq .mode "standalone") (and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true")) }} + {{ if eq (.Values.server.dataStorage.enabled | toString) "true" }} + - name: data + mountPath: {{ .Values.server.dataStorage.mountPath }} + {{ end }} + {{ end }} + {{ if and (ne .mode "dev") (or (.Values.server.standalone.config) (.Values.server.ha.config)) }} + - name: config + mountPath: /vault/config + {{ end }} + {{- range .Values.server.extraVolumes }} + - name: userconfig-{{ .name }} + readOnly: true + mountPath: {{ .path | default "/vault/userconfig" }}/{{ .name }} + {{- end }} + {{- if .Values.server.volumeMounts }} + {{- toYaml .Values.server.volumeMounts | nindent 12}} + {{- end }} + {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} + - name: vault-license + mountPath: /vault/license + readOnly: true + {{- end }} +{{- end -}} + +{{/* +Set's up the volumeClaimTemplates when data or audit storage is required. HA +might not use data storage since Consul is likely it's backend, however, audit +storage might be desired by the user. +*/}} +{{- define "vault.volumeclaims" -}} + {{- if and (ne .mode "dev") (or .Values.server.dataStorage.enabled .Values.server.auditStorage.enabled) }} + volumeClaimTemplates: + {{- if and (eq (.Values.server.dataStorage.enabled | toString) "true") (or (eq .mode "standalone") (eq (.Values.server.ha.raft.enabled | toString ) "true" )) }} + - metadata: + name: data + {{- include "vault.dataVolumeClaim.annotations" . | nindent 6 }} + spec: + accessModes: + - {{ .Values.server.dataStorage.accessMode | default "ReadWriteOnce" }} + resources: + requests: + storage: {{ .Values.server.dataStorage.size }} + {{- if .Values.server.dataStorage.storageClass }} + storageClassName: {{ .Values.server.dataStorage.storageClass }} + {{- end }} + {{ end }} + {{- if eq (.Values.server.auditStorage.enabled | toString) "true" }} + - metadata: + name: audit + {{- include "vault.auditVolumeClaim.annotations" . | nindent 6 }} + spec: + accessModes: + - {{ .Values.server.auditStorage.accessMode | default "ReadWriteOnce" }} + resources: + requests: + storage: {{ .Values.server.auditStorage.size }} + {{- if .Values.server.auditStorage.storageClass }} + storageClassName: {{ .Values.server.auditStorage.storageClass }} + {{- end }} + {{ end }} + {{ end }} +{{- end -}} + +{{/* +Set's the affinity for pod placement when running in standalone and HA modes. +*/}} +{{- define "vault.affinity" -}} + {{- if and (ne .mode "dev") .Values.server.affinity }} + affinity: + {{ $tp := typeOf .Values.server.affinity }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.affinity . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.affinity | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + +{{/* +Sets the injector affinity for pod placement +*/}} +{{- define "injector.affinity" -}} + {{- if .Values.injector.affinity }} + affinity: + {{ $tp := typeOf .Values.injector.affinity }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.affinity . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.affinity | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + +{{/* +Sets the toleration for pod placement when running in standalone and HA modes. +*/}} +{{- define "vault.tolerations" -}} + {{- if and (ne .mode "dev") .Values.server.tolerations }} + tolerations: + {{- $tp := typeOf .Values.server.tolerations }} + {{- if eq $tp "string" }} + {{ tpl .Values.server.tolerations . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.tolerations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the injector toleration for pod placement +*/}} +{{- define "injector.tolerations" -}} + {{- if .Values.injector.tolerations }} + tolerations: + {{- $tp := typeOf .Values.injector.tolerations }} + {{- if eq $tp "string" }} + {{ tpl .Values.injector.tolerations . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.tolerations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Set's the node selector for pod placement when running in standalone and HA modes. +*/}} +{{- define "vault.nodeselector" -}} + {{- if and (ne .mode "dev") .Values.server.nodeSelector }} + nodeSelector: + {{- $tp := typeOf .Values.server.nodeSelector }} + {{- if eq $tp "string" }} + {{ tpl .Values.server.nodeSelector . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.nodeSelector | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the injector node selector for pod placement +*/}} +{{- define "injector.nodeselector" -}} + {{- if .Values.injector.nodeSelector }} + nodeSelector: + {{- $tp := typeOf .Values.injector.nodeSelector }} + {{- if eq $tp "string" }} + {{ tpl .Values.injector.nodeSelector . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.nodeSelector | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra pod annotations +*/}} +{{- define "vault.annotations" -}} + {{- if .Values.server.annotations }} + annotations: + {{- $tp := typeOf .Values.server.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.annotations . | nindent 8 }} + {{- else }} + {{- toYaml .Values.server.annotations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra injector pod annotations +*/}} +{{- define "injector.annotations" -}} + {{- if .Values.injector.annotations }} + annotations: + {{- $tp := typeOf .Values.injector.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.annotations . | nindent 8 }} + {{- else }} + {{- toYaml .Values.injector.annotations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra injector service annotations +*/}} +{{- define "injector.service.annotations" -}} + {{- if .Values.injector.service.annotations }} + annotations: + {{- $tp := typeOf .Values.injector.service.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.service.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.injector.service.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra injector webhook annotations +*/}} +{{- define "injector.webhookAnnotations" -}} + {{- if .Values.injector.webhookAnnotations }} + annotations: + {{- $tp := typeOf .Values.injector.webhookAnnotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.webhookAnnotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.injector.webhookAnnotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra ui service annotations +*/}} +{{- define "vault.ui.annotations" -}} + {{- if .Values.ui.annotations }} + annotations: + {{- $tp := typeOf .Values.ui.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.ui.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.ui.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "vault.serviceAccount.name" -}} +{{- if .Values.server.serviceAccount.create -}} + {{ default (include "vault.fullname" .) .Values.server.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.server.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Sets extra service account annotations +*/}} +{{- define "vault.serviceAccount.annotations" -}} + {{- if and (ne .mode "dev") .Values.server.serviceAccount.annotations }} + annotations: + {{- $tp := typeOf .Values.server.serviceAccount.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.serviceAccount.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.serviceAccount.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra ingress annotations +*/}} +{{- define "vault.ingress.annotations" -}} + {{- if .Values.server.ingress.annotations }} + annotations: + {{- $tp := typeOf .Values.server.ingress.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.ingress.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.ingress.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra route annotations +*/}} +{{- define "vault.route.annotations" -}} + {{- if .Values.server.route.annotations }} + annotations: + {{- $tp := typeOf .Values.server.route.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.route.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.route.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra vault server Service annotations +*/}} +{{- define "vault.service.annotations" -}} + {{- if .Values.server.service.annotations }} + {{- $tp := typeOf .Values.server.service.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.service.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.service.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets PodSecurityPolicy annotations +*/}} +{{- define "vault.psp.annotations" -}} + {{- if .Values.global.psp.annotations }} + annotations: + {{- $tp := typeOf .Values.global.psp.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.global.psp.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.global.psp.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra statefulset annotations +*/}} +{{- define "vault.statefulSet.annotations" -}} + {{- if .Values.server.statefulSet.annotations }} + annotations: + {{- $tp := typeOf .Values.server.statefulSet.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.statefulSet.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.statefulSet.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets VolumeClaim annotations for data volume +*/}} +{{- define "vault.dataVolumeClaim.annotations" -}} + {{- if and (ne .mode "dev") (.Values.server.dataStorage.enabled) (.Values.server.dataStorage.annotations) }} + annotations: + {{- $tp := typeOf .Values.server.dataStorage.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.dataStorage.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.dataStorage.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets VolumeClaim annotations for audit volume +*/}} +{{- define "vault.auditVolumeClaim.annotations" -}} + {{- if and (ne .mode "dev") (.Values.server.auditStorage.enabled) (.Values.server.auditStorage.annotations) }} + annotations: + {{- $tp := typeOf .Values.server.auditStorage.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.auditStorage.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.auditStorage.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Set's the container resources if the user has set any. +*/}} +{{- define "vault.resources" -}} + {{- if .Values.server.resources -}} + resources: +{{ toYaml .Values.server.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets the container resources if the user has set any. +*/}} +{{- define "injector.resources" -}} + {{- if .Values.injector.resources -}} + resources: +{{ toYaml .Values.injector.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets the container resources if the user has set any. +*/}} +{{- define "csi.resources" -}} + {{- if .Values.csi.resources -}} + resources: +{{ toYaml .Values.csi.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets extra CSI daemonset annotations +*/}} +{{- define "csi.daemonSet.annotations" -}} + {{- if .Values.csi.daemonSet.annotations }} + annotations: + {{- $tp := typeOf .Values.csi.daemonSet.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.daemonSet.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.csi.daemonSet.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the injector toleration for pod placement +*/}} +{{- define "csi.pod.tolerations" -}} + {{- if .Values.csi.pod.tolerations }} + tolerations: + {{- $tp := typeOf .Values.csi.pod.tolerations }} + {{- if eq $tp "string" }} + {{ tpl .Values.csi.pod.tolerations . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.csi.pod.tolerations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra CSI provider pod annotations +*/}} +{{- define "csi.pod.annotations" -}} + {{- if .Values.csi.pod.annotations }} + annotations: + {{- $tp := typeOf .Values.csi.pod.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.pod.annotations . | nindent 8 }} + {{- else }} + {{- toYaml .Values.csi.pod.annotations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra CSI service account annotations +*/}} +{{- define "csi.serviceAccount.annotations" -}} + {{- if .Values.csi.serviceAccount.annotations }} + annotations: + {{- $tp := typeOf .Values.csi.serviceAccount.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.serviceAccount.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.csi.serviceAccount.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Inject extra environment vars in the format key:value, if populated +*/}} +{{- define "vault.extraEnvironmentVars" -}} +{{- if .extraEnvironmentVars -}} +{{- range $key, $value := .extraEnvironmentVars }} +- name: {{ printf "%s" $key | replace "." "_" | upper | quote }} + value: {{ $value | quote }} +{{- end }} +{{- end -}} +{{- end -}} + +{{/* +Inject extra environment populated by secrets, if populated +*/}} +{{- define "vault.extraSecretEnvironmentVars" -}} +{{- if .extraSecretEnvironmentVars -}} +{{- range .extraSecretEnvironmentVars }} +- name: {{ .envName }} + valueFrom: + secretKeyRef: + name: {{ .secretName }} + key: {{ .secretKey }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* Scheme for health check and local endpoint */}} +{{- define "vault.scheme" -}} +{{- if .Values.global.tlsDisable -}} +{{ "http" }} +{{- else -}} +{{ "https" }} +{{- end -}} +{{- end -}} + +{{/* +imagePullSecrets generates pull secrets from either string or map values. +A map value must be indexable by the key 'name'. +*/}} +{{- define "imagePullSecrets" -}} +{{- with .Values.global.imagePullSecrets -}} +imagePullSecrets: +{{- range . -}} +{{- if typeIs "string" . }} + - name: {{ . }} +{{- else if index . "name" }} + - name: {{ .name }} +{{- end }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +externalTrafficPolicy sets a Service's externalTrafficPolicy if applicable. +Supported inputs are Values.server.service and Values.ui +*/}} +{{- define "service.externalTrafficPolicy" -}} +{{- $type := "" -}} +{{- if .serviceType -}} +{{- $type = .serviceType -}} +{{- else if .type -}} +{{- $type = .type -}} +{{- end -}} +{{- if and .externalTrafficPolicy (or (eq $type "LoadBalancer") (eq $type "NodePort")) }} + externalTrafficPolicy: {{ .externalTrafficPolicy }} +{{- else }} +{{- end }} +{{- end -}} + +{{/* +loadBalancer configuration for the the UI service. +Supported inputs are Values.ui +*/}} +{{- define "service.loadBalancer" -}} +{{- if eq (.serviceType | toString) "LoadBalancer" }} +{{- if .loadBalancerIP }} + loadBalancerIP: {{ .loadBalancerIP }} +{{- end }} +{{- with .loadBalancerSourceRanges }} + loadBalancerSourceRanges: +{{- range . }} + - {{ . }} +{{- end }} +{{- end -}} +{{- end }} +{{- end -}} diff --git a/templates/csi-clusterrole.yaml b/templates/csi-clusterrole.yaml new file mode 100644 index 00000000..a19e520f --- /dev/null +++ b/templates/csi-clusterrole.yaml @@ -0,0 +1,17 @@ +{{- if and (eq (.Values.csi.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-clusterrole + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: + - "" + resources: + - serviceaccounts/token + verbs: + - create +{{- end }} diff --git a/templates/csi-clusterrolebinding.yaml b/templates/csi-clusterrolebinding.yaml new file mode 100644 index 00000000..63d69c7b --- /dev/null +++ b/templates/csi-clusterrolebinding.yaml @@ -0,0 +1,18 @@ +{{- if and (eq (.Values.csi.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-clusterrolebinding + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "vault.fullname" . }}-csi-provider-clusterrole +subjects: +- kind: ServiceAccount + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/templates/csi-daemonset.yaml b/templates/csi-daemonset.yaml new file mode 100644 index 00000000..a6461fbd --- /dev/null +++ b/templates/csi-daemonset.yaml @@ -0,0 +1,81 @@ +{{- if and (eq (.Values.csi.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{ template "csi.daemonSet.annotations" . }} +spec: + updateStrategy: + type: {{ .Values.csi.daemonSet.updateStrategy.type }} + {{- if .Values.csi.daemonSet.updateStrategy.maxUnavailable }} + rollingUpdate: + maxUnavailable: {{ .Values.csi.daemonSet.updateStrategy.maxUnavailable }} + {{- end }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ template "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + {{ template "csi.pod.annotations" . }} + spec: + serviceAccountName: {{ template "vault.fullname" . }}-csi-provider + {{- template "csi.pod.tolerations" . }} + containers: + - name: {{ include "vault.name" . }}-csi-provider + {{ template "csi.resources" . }} + image: "{{ .Values.csi.image.repository }}:{{ .Values.csi.image.tag }}" + imagePullPolicy: {{ .Values.csi.image.pullPolicy }} + args: + - --endpoint=/provider/vault.sock + - --debug={{ .Values.csi.debug }} + {{- if .Values.csi.extraArgs }} + {{- toYaml .Values.csi.extraArgs | nindent 12 }} + {{- end }} + volumeMounts: + - name: providervol + mountPath: "/provider" + - name: mountpoint-dir + mountPath: {{ .Values.csi.daemonSet.kubeletRootDir }}/pods + mountPropagation: HostToContainer + {{- if .Values.csi.volumeMounts }} + {{- toYaml .Values.csi.volumeMounts | nindent 12}} + {{- end }} + livenessProbe: + httpGet: + path: /health/ready + port: 8080 + failureThreshold: {{ .Values.csi.livenessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.csi.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.csi.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.csi.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.csi.livenessProbe.timeoutSeconds }} + readinessProbe: + httpGet: + path: /health/ready + port: 8080 + failureThreshold: {{ .Values.csi.readinessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.csi.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.csi.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.csi.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.csi.readinessProbe.timeoutSeconds }} + volumes: + - name: providervol + hostPath: + path: {{ .Values.csi.daemonSet.providersDir }} + - name: mountpoint-dir + hostPath: + path: {{ .Values.csi.daemonSet.kubeletRootDir }}/pods + {{- if .Values.csi.volumes }} + {{- toYaml .Values.csi.volumes | nindent 8}} + {{- end }} + {{- include "imagePullSecrets" . | nindent 6 }} +{{- end }} diff --git a/templates/csi-serviceaccount.yaml b/templates/csi-serviceaccount.yaml new file mode 100644 index 00000000..ee127481 --- /dev/null +++ b/templates/csi-serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if and (eq (.Values.csi.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{ template "csi.serviceAccount.annotations" . }} +{{- end }} diff --git a/templates/injector-certs-secret.yaml b/templates/injector-certs-secret.yaml new file mode 100644 index 00000000..78363be5 --- /dev/null +++ b/templates/injector-certs-secret.yaml @@ -0,0 +1,10 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} +apiVersion: v1 +kind: Secret +metadata: + name: vault-injector-certs + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} diff --git a/templates/injector-clusterrole.yaml b/templates/injector-clusterrole.yaml new file mode 100644 index 00000000..4ff25abe --- /dev/null +++ b/templates/injector-clusterrole.yaml @@ -0,0 +1,18 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-clusterrole + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: ["admissionregistration.k8s.io"] + resources: ["mutatingwebhookconfigurations"] + verbs: + - "get" + - "list" + - "watch" + - "patch" +{{ end }} diff --git a/templates/injector-clusterrolebinding.yaml b/templates/injector-clusterrolebinding.yaml new file mode 100644 index 00000000..35d30b39 --- /dev/null +++ b/templates/injector-clusterrolebinding.yaml @@ -0,0 +1,18 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-binding + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "vault.fullname" . }}-agent-injector-clusterrole +subjects: +- kind: ServiceAccount + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} +{{ end }} diff --git a/templates/injector-deployment.yaml b/templates/injector-deployment.yaml new file mode 100644 index 00000000..aefbf088 --- /dev/null +++ b/templates/injector-deployment.yaml @@ -0,0 +1,157 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +# Deployment for the injector +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + component: webhook +spec: + replicas: {{ .Values.injector.replicas }} + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + template: + metadata: + labels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + {{- if .Values.injector.extraLabels -}} + {{- toYaml .Values.injector.extraLabels | nindent 8 -}} + {{- end -}} + {{ template "injector.annotations" . }} + spec: + {{ template "injector.affinity" . }} + {{ template "injector.tolerations" . }} + {{ template "injector.nodeselector" . }} + {{- if .Values.injector.priorityClassName }} + priorityClassName: {{ .Values.injector.priorityClassName }} + {{- end }} + serviceAccountName: "{{ template "vault.fullname" . }}-agent-injector" + {{- if not .Values.global.openshift }} + hostNetwork: {{ .Values.injector.hostNetwork }} + securityContext: + runAsNonRoot: true + runAsGroup: {{ .Values.injector.gid | default 1000 }} + runAsUser: {{ .Values.injector.uid | default 100 }} + {{- end }} + containers: + - name: sidecar-injector + {{ template "injector.resources" . }} + image: "{{ .Values.injector.image.repository }}:{{ .Values.injector.image.tag }}" + imagePullPolicy: "{{ .Values.injector.image.pullPolicy }}" + {{- if not .Values.global.openshift }} + securityContext: + allowPrivilegeEscalation: false + {{- end }} + env: + - name: AGENT_INJECT_LISTEN + value: {{ printf ":%v" .Values.injector.port }} + - name: AGENT_INJECT_LOG_LEVEL + value: {{ .Values.injector.logLevel | default "info" }} + - name: AGENT_INJECT_VAULT_ADDR + {{- if .Values.injector.externalVaultAddr }} + value: "{{ .Values.injector.externalVaultAddr }}" + {{- else }} + value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} + {{- end }} + - name: AGENT_INJECT_VAULT_AUTH_PATH + value: {{ .Values.injector.authPath }} + - name: AGENT_INJECT_VAULT_IMAGE + value: "{{ .Values.injector.agentImage.repository }}:{{ .Values.injector.agentImage.tag }}" + {{- if .Values.injector.certs.secretName }} + - name: AGENT_INJECT_TLS_CERT_FILE + value: "/etc/webhook/certs/{{ .Values.injector.certs.certName }}" + - name: AGENT_INJECT_TLS_KEY_FILE + value: "/etc/webhook/certs/{{ .Values.injector.certs.keyName }}" + {{- else }} + - name: AGENT_INJECT_TLS_AUTO + value: {{ template "vault.fullname" . }}-agent-injector-cfg + - name: AGENT_INJECT_TLS_AUTO_HOSTS + value: {{ template "vault.fullname" . }}-agent-injector-svc,{{ template "vault.fullname" . }}-agent-injector-svc.{{ .Release.Namespace }},{{ template "vault.fullname" . }}-agent-injector-svc.{{ .Release.Namespace }}.svc + {{- end }} + - name: AGENT_INJECT_LOG_FORMAT + value: {{ .Values.injector.logFormat | default "standard" }} + - name: AGENT_INJECT_REVOKE_ON_SHUTDOWN + value: "{{ .Values.injector.revokeOnShutdown | default false }}" + {{- if .Values.global.openshift }} + - name: AGENT_INJECT_SET_SECURITY_CONTEXT + value: "false" + {{- end }} + {{- if .Values.injector.metrics.enabled }} + - name: AGENT_INJECT_TELEMETRY_PATH + value: "/metrics" + {{- end }} + {{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} + - name: AGENT_INJECT_USE_LEADER_ELECTOR + value: "true" + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- end }} + - name: AGENT_INJECT_CPU_REQUEST + value: "{{ .Values.injector.agentDefaults.cpuRequest }}" + - name: AGENT_INJECT_CPU_LIMIT + value: "{{ .Values.injector.agentDefaults.cpuLimit }}" + - name: AGENT_INJECT_MEM_REQUEST + value: "{{ .Values.injector.agentDefaults.memRequest }}" + - name: AGENT_INJECT_MEM_LIMIT + value: "{{ .Values.injector.agentDefaults.memLimit }}" + - name: AGENT_INJECT_DEFAULT_TEMPLATE + value: "{{ .Values.injector.agentDefaults.template }}" + - name: AGENT_INJECT_TEMPLATE_CONFIG_EXIT_ON_RETRY_FAILURE + value: "{{ .Values.injector.agentDefaults.templateConfig.exitOnRetryFailure }}" + {{- if .Values.injector.agentDefaults.templateConfig.staticSecretRenderInterval }} + - name: AGENT_INJECT_TEMPLATE_STATIC_SECRET_RENDER_INTERVAL + value: "{{ .Values.injector.agentDefaults.templateConfig.staticSecretRenderInterval }}" + {{- end }} + {{- include "vault.extraEnvironmentVars" .Values.injector | nindent 12 }} + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + args: + - agent-inject + - 2>&1 + livenessProbe: + httpGet: + path: /health/ready + port: {{ .Values.injector.port }} + scheme: HTTPS + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 2 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /health/ready + port: {{ .Values.injector.port }} + scheme: HTTPS + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 2 + successThreshold: 1 + timeoutSeconds: 5 +{{- if .Values.injector.certs.secretName }} + volumeMounts: + - name: webhook-certs + mountPath: /etc/webhook/certs + readOnly: true +{{- end }} +{{- if .Values.injector.certs.secretName }} + volumes: + - name: webhook-certs + secret: + secretName: "{{ .Values.injector.certs.secretName }}" +{{- end }} + {{- include "imagePullSecrets" . | nindent 6 }} +{{ end }} diff --git a/templates/injector-mutating-webhook.yaml b/templates/injector-mutating-webhook.yaml new file mode 100644 index 00000000..de7dd562 --- /dev/null +++ b/templates/injector-mutating-webhook.yaml @@ -0,0 +1,43 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +{{- if .Capabilities.APIVersions.Has "admissionregistration.k8s.io/v1" }} +apiVersion: admissionregistration.k8s.io/v1 +{{- else }} +apiVersion: admissionregistration.k8s.io/v1beta1 +{{- end }} +kind: MutatingWebhookConfiguration +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-cfg + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- template "injector.webhookAnnotations" . }} +webhooks: + - name: vault.hashicorp.com + sideEffects: None + admissionReviewVersions: + - "v1beta1" + - "v1" + clientConfig: + service: + name: {{ template "vault.fullname" . }}-agent-injector-svc + namespace: {{ .Release.Namespace }} + path: "/mutate" + caBundle: {{ .Values.injector.certs.caBundle | quote }} + rules: + - operations: ["CREATE", "UPDATE"] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods"] +{{- if .Values.injector.namespaceSelector }} + namespaceSelector: +{{ toYaml .Values.injector.namespaceSelector | indent 6}} +{{ end }} +{{- if .Values.injector.objectSelector }} + objectSelector: +{{ toYaml .Values.injector.objectSelector | indent 6}} +{{ end }} +{{- with .Values.injector.failurePolicy }} + failurePolicy: {{.}} +{{ end }} +{{ end }} diff --git a/templates/injector-network-policy.yaml b/templates/injector-network-policy.yaml new file mode 100644 index 00000000..7a399a53 --- /dev/null +++ b/templates/injector-network-policy.yaml @@ -0,0 +1,21 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.openshift | toString) "true") }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + labels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + ingress: + - from: + - namespaceSelector: {} + ports: + - port: 8080 + protocol: TCP +{{ end }} diff --git a/templates/injector-psp-role.yaml b/templates/injector-psp-role.yaml new file mode 100644 index 00000000..20c87bb2 --- /dev/null +++ b/templates/injector-psp-role.yaml @@ -0,0 +1,17 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "vault.fullname" . }}-agent-injector +{{- end }} diff --git a/templates/injector-psp-rolebinding.yaml b/templates/injector-psp-rolebinding.yaml new file mode 100644 index 00000000..d6d0d5e2 --- /dev/null +++ b/templates/injector-psp-rolebinding.yaml @@ -0,0 +1,18 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + kind: Role + name: {{ template "vault.fullname" . }}-agent-injector-psp + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ template "vault.fullname" . }}-agent-injector +{{- end }} diff --git a/templates/injector-psp.yaml b/templates/injector-psp.yaml new file mode 100644 index 00000000..c024ac10 --- /dev/null +++ b/templates/injector-psp.yaml @@ -0,0 +1,43 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- template "vault.psp.annotations" . }} +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + volumes: + - configMap + - emptyDir + - projected + - secret + - downwardAPI + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + # Require the container to run without root privileges. + rule: MustRunAsNonRoot + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: RunAsAny + supplementalGroups: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} diff --git a/templates/injector-role.yaml b/templates/injector-role.yaml new file mode 100644 index 00000000..e7e383d1 --- /dev/null +++ b/templates/injector-role.yaml @@ -0,0 +1,25 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-role + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: + - apiGroups: [""] + resources: ["secrets", "configmaps"] + verbs: + - "create" + - "get" + - "watch" + - "list" + - "update" + - apiGroups: [""] + resources: ["pods"] + verbs: + - "get" + - "patch" + - "delete" +{{- end }} diff --git a/templates/injector-rolebinding.yaml b/templates/injector-rolebinding.yaml new file mode 100644 index 00000000..aa817942 --- /dev/null +++ b/templates/injector-rolebinding.yaml @@ -0,0 +1,18 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-binding + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-role +subjects: + - kind: ServiceAccount + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/templates/injector-service.yaml b/templates/injector-service.yaml new file mode 100644 index 00000000..3138b7a5 --- /dev/null +++ b/templates/injector-service.yaml @@ -0,0 +1,21 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-svc + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{ template "injector.service.annotations" . }} +spec: + ports: + - name: https + port: 443 + targetPort: {{ .Values.injector.port }} + selector: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook +{{- end }} diff --git a/templates/injector-serviceaccount.yaml b/templates/injector-serviceaccount.yaml new file mode 100644 index 00000000..a28d38fa --- /dev/null +++ b/templates/injector-serviceaccount.yaml @@ -0,0 +1,11 @@ +{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{ end }} diff --git a/templates/server-clusterrolebinding.yaml b/templates/server-clusterrolebinding.yaml new file mode 100644 index 00000000..e5e0f5fe --- /dev/null +++ b/templates/server-clusterrolebinding.yaml @@ -0,0 +1,24 @@ +{{ template "vault.mode" . }} +{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") (eq (.Values.server.authDelegator.enabled | toString) "true") }} +{{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1" -}} +apiVersion: rbac.authorization.k8s.io/v1 +{{- else }} +apiVersion: rbac.authorization.k8s.io/v1beta1 +{{- end }} +kind: ClusterRoleBinding +metadata: + name: {{ template "vault.fullname" . }}-server-binding + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: {{ template "vault.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{ end }} diff --git a/templates/server-config-configmap.yaml b/templates/server-config-configmap.yaml new file mode 100644 index 00000000..b8093ad0 --- /dev/null +++ b/templates/server-config-configmap.yaml @@ -0,0 +1,38 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if and (eq (.Values.global.enabled | toString) "true") (ne .mode "dev") -}} +{{ if or (.Values.server.standalone.config) (.Values.server.ha.config) -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "vault.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +data: + extraconfig-from-values.hcl: |- + {{- if or (eq .mode "ha") (eq .mode "standalone") }} + {{- $type := typeOf (index .Values.server .mode).config }} + {{- if eq $type "string" }} + disable_mlock = true + {{- if eq .mode "standalone" }} + {{ tpl .Values.server.standalone.config . | nindent 4 | trim }} + {{- else if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "false") }} + {{ tpl .Values.server.ha.config . | nindent 4 | trim }} + {{- else if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true") }} + {{ tpl .Values.server.ha.raft.config . | nindent 4 | trim }} + {{ end }} + {{- else }} + {{- if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true") }} +{{ merge (dict "disable_mlock" true) (index .Values.server .mode).raft.config | toPrettyJson | indent 4 }} + {{- else }} +{{ merge (dict "disable_mlock" true) (index .Values.server .mode).config | toPrettyJson | indent 4 }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/templates/server-discovery-role.yaml b/templates/server-discovery-role.yaml new file mode 100644 index 00000000..4a39cec2 --- /dev/null +++ b/templates/server-discovery-role.yaml @@ -0,0 +1,19 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if and (eq .mode "ha" ) (eq (.Values.global.enabled | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: {{ .Release.Namespace }} + name: {{ template "vault.fullname" . }}-discovery-role + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "watch", "list", "update", "patch"] +{{ end }} +{{ end }} diff --git a/templates/server-discovery-rolebinding.yaml b/templates/server-discovery-rolebinding.yaml new file mode 100644 index 00000000..47526650 --- /dev/null +++ b/templates/server-discovery-rolebinding.yaml @@ -0,0 +1,27 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if and (eq .mode "ha" ) (eq (.Values.global.enabled | toString) "true") }} +{{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1" -}} +apiVersion: rbac.authorization.k8s.io/v1 +{{- else }} +apiVersion: rbac.authorization.k8s.io/v1beta1 +{{- end }} +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-discovery-rolebinding + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "vault.fullname" . }}-discovery-role +subjects: +- kind: ServiceAccount + name: {{ template "vault.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{ end }} +{{ end }} diff --git a/templates/server-disruptionbudget.yaml b/templates/server-disruptionbudget.yaml new file mode 100644 index 00000000..3c45cc04 --- /dev/null +++ b/templates/server-disruptionbudget.yaml @@ -0,0 +1,24 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" -}} +{{- if and (eq (.Values.global.enabled | toString) "true") (eq .mode "ha") (eq (.Values.server.ha.disruptionBudget.enabled | toString) "true") -}} +# PodDisruptionBudget to prevent degrading the server cluster through +# voluntary cluster changes. +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +spec: + maxUnavailable: {{ template "vault.pdb.maxUnavailable" . }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server +{{- end -}} +{{- end -}} diff --git a/templates/server-ha-active-service.yaml b/templates/server-ha-active-service.yaml new file mode 100644 index 00000000..c2a4f022 --- /dev/null +++ b/templates/server-ha-active-service.yaml @@ -0,0 +1,42 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if and (eq .mode "ha" ) (eq (.Values.server.service.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +# Service for active Vault pod +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-active + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: +{{ template "vault.service.annotations" .}} +spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} + {{- end }} + {{- include "service.externalTrafficPolicy" .Values.server.service }} + publishNotReadyAddresses: true + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + {{- if and (.Values.server.service.nodePort) (eq (.Values.server.service.type | toString) "NodePort") }} + nodePort: {{ .Values.server.service.nodePort }} + {{- end }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + vault-active: "true" +{{- end }} +{{- end }} diff --git a/templates/server-ha-standby-service.yaml b/templates/server-ha-standby-service.yaml new file mode 100644 index 00000000..fef92a1b --- /dev/null +++ b/templates/server-ha-standby-service.yaml @@ -0,0 +1,42 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if and (eq .mode "ha" ) (eq (.Values.server.service.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +# Service for standby Vault pod +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-standby + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: +{{ template "vault.service.annotations" .}} +spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} + {{- end }} + {{- include "service.externalTrafficPolicy" .Values.server.service }} + publishNotReadyAddresses: true + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + {{- if and (.Values.server.service.nodePort) (eq (.Values.server.service.type | toString) "NodePort") }} + nodePort: {{ .Values.server.service.nodePort }} + {{- end }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + vault-active: "false" +{{- end }} +{{- end }} diff --git a/templates/server-headless-service.yaml b/templates/server-headless-service.yaml new file mode 100644 index 00000000..a37c6395 --- /dev/null +++ b/templates/server-headless-service.yaml @@ -0,0 +1,32 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if and (eq (.Values.server.service.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-internal + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: +{{ template "vault.service.annotations" .}} +spec: + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: "{{ include "vault.scheme" . }}" + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server +{{- end }} +{{- end }} diff --git a/templates/server-ingress.yaml b/templates/server-ingress.yaml new file mode 100644 index 00000000..48c76a82 --- /dev/null +++ b/templates/server-ingress.yaml @@ -0,0 +1,74 @@ +{{- if not .Values.global.openshift }} +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if .Values.server.ingress.enabled -}} +{{- $extraPaths := .Values.server.ingress.extraPaths -}} +{{- $serviceName := include "vault.fullname" . -}} +{{- if and (eq .mode "ha" ) (eq (.Values.server.service.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.server.ingress.activeService | toString) "true") }} +{{- $serviceName = printf "%s-%s" $serviceName "active" -}} +{{- end }} +{{- $servicePort := .Values.server.service.port -}} +{{- $pathType := .Values.server.ingress.pathType -}} +{{- $kubeVersion := .Capabilities.KubeVersion.Version }} +{{ if semverCompare ">= 1.19.0-0" $kubeVersion }} +apiVersion: networking.k8s.io/v1 +{{ else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" }} +apiVersion: networking.k8s.io/v1beta1 +{{ else }} +apiVersion: extensions/v1beta1 +{{ end }} +kind: Ingress +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- with .Values.server.ingress.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- template "vault.ingress.annotations" . }} +spec: +{{- if .Values.server.ingress.tls }} + tls: + {{- range .Values.server.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} +{{- if .Values.server.ingress.ingressClassName }} + ingressClassName: {{ .Values.server.ingress.ingressClassName }} +{{- end }} + rules: + {{- range .Values.server.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: +{{ if $extraPaths }} +{{ toYaml $extraPaths | indent 10 }} +{{- end }} + {{- range (.paths | default (list "/")) }} + - path: {{ . }} + {{ if semverCompare ">= 1.19.0-0" $kubeVersion }} + pathType: {{ $pathType }} + {{ end }} + backend: + {{ if semverCompare ">= 1.19.0-0" $kubeVersion }} + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{ else }} + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{ end }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/templates/server-network-policy.yaml b/templates/server-network-policy.yaml new file mode 100644 index 00000000..5f4c21a4 --- /dev/null +++ b/templates/server-network-policy.yaml @@ -0,0 +1,26 @@ +{{- if eq (.Values.server.networkPolicy.enabled | toString) "true" }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "vault.fullname" . }} + labels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + ingress: + - from: + - namespaceSelector: {} + ports: + - port: 8200 + protocol: TCP + - port: 8201 + protocol: TCP + {{- if .Values.server.networkPolicy.egress }} + egress: + {{- toYaml .Values.server.networkPolicy.egress | nindent 4 }} + {{ end }} +{{ end }} diff --git a/templates/server-psp-role.yaml b/templates/server-psp-role.yaml new file mode 100644 index 00000000..fd12e1eb --- /dev/null +++ b/templates/server-psp-role.yaml @@ -0,0 +1,18 @@ +{{ template "vault.mode" . }} +{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "vault.fullname" . }} +{{- end }} diff --git a/templates/server-psp-rolebinding.yaml b/templates/server-psp-rolebinding.yaml new file mode 100644 index 00000000..b2a43c83 --- /dev/null +++ b/templates/server-psp-rolebinding.yaml @@ -0,0 +1,19 @@ +{{ template "vault.mode" . }} +{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + kind: Role + name: {{ template "vault.fullname" . }}-psp + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ template "vault.fullname" . }} +{{- end }} diff --git a/templates/server-psp.yaml b/templates/server-psp.yaml new file mode 100644 index 00000000..2d942681 --- /dev/null +++ b/templates/server-psp.yaml @@ -0,0 +1,47 @@ +{{ template "vault.mode" . }} +{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "vault.fullname" . }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- template "vault.psp.annotations" . }} +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + volumes: + - configMap + - emptyDir + - projected + - secret + - downwardAPI + {{- if eq (.Values.server.dataStorage.enabled | toString) "true" }} + - persistentVolumeClaim + {{- end }} + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + # Require the container to run without root privileges. + rule: MustRunAsNonRoot + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: RunAsAny + supplementalGroups: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} diff --git a/templates/server-route.yaml b/templates/server-route.yaml new file mode 100644 index 00000000..63055db3 --- /dev/null +++ b/templates/server-route.yaml @@ -0,0 +1,33 @@ +{{- if .Values.global.openshift }} +{{- if ne .mode "external" }} +{{- if .Values.server.route.enabled -}} +{{- $serviceName := include "vault.fullname" . -}} +{{- if and (eq .mode "ha" ) (eq (.Values.server.route.activeService | toString) "true") }} +{{- $serviceName = printf "%s-%s" $serviceName "active" -}} +{{- end }} +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: {{ template "vault.fullname" . }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- with .Values.server.route.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- template "vault.route.annotations" . }} +spec: + host: {{ .Values.server.route.host }} + to: + kind: Service + name: {{ $serviceName }} + weight: 100 + port: + targetPort: 8200 + tls: + termination: passthrough +{{- end }} +{{- end }} +{{- end }} diff --git a/templates/server-service.yaml b/templates/server-service.yaml new file mode 100644 index 00000000..00996aa2 --- /dev/null +++ b/templates/server-service.yaml @@ -0,0 +1,43 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if and (eq (.Values.server.service.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: +{{ template "vault.service.annotations" .}} +spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} + {{- end }} + {{- include "service.externalTrafficPolicy" .Values.server.service }} + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: true + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + {{- if and (.Values.server.service.nodePort) (eq (.Values.server.service.type | toString) "NodePort") }} + nodePort: {{ .Values.server.service.nodePort }} + {{- end }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server +{{- end }} +{{- end }} diff --git a/templates/server-serviceaccount.yaml b/templates/server-serviceaccount.yaml new file mode 100644 index 00000000..925b166b --- /dev/null +++ b/templates/server-serviceaccount.yaml @@ -0,0 +1,16 @@ +{{ template "vault.mode" . }} +{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") }} +{{- if (eq (.Values.server.serviceAccount.create | toString) "true" ) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "vault.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{ template "vault.serviceAccount.annotations" . }} +{{ end }} +{{ end }} diff --git a/templates/server-statefulset.yaml b/templates/server-statefulset.yaml new file mode 100644 index 00000000..031b1790 --- /dev/null +++ b/templates/server-statefulset.yaml @@ -0,0 +1,208 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") }} +# StatefulSet to run the actual vault server cluster. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- template "vault.statefulSet.annotations" . }} +spec: + serviceName: {{ template "vault.fullname" . }}-internal + podManagementPolicy: Parallel + replicas: {{ template "vault.replicas" . }} + updateStrategy: + type: {{ .Values.server.updateStrategyType }} + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + template: + metadata: + labels: + helm.sh/chart: {{ template "vault.chart" . }} + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + {{- if .Values.server.extraLabels -}} + {{- toYaml .Values.server.extraLabels | nindent 8 -}} + {{- end -}} + {{ template "vault.annotations" . }} + spec: + {{ template "vault.affinity" . }} + {{ template "vault.tolerations" . }} + {{ template "vault.nodeselector" . }} + {{- if .Values.server.priorityClassName }} + priorityClassName: {{ .Values.server.priorityClassName }} + {{- end }} + terminationGracePeriodSeconds: 10 + serviceAccountName: {{ template "vault.serviceAccount.name" . }} + {{ if .Values.server.shareProcessNamespace }} + shareProcessNamespace: true + {{ end }} + {{- if not .Values.global.openshift }} + securityContext: + runAsNonRoot: true + runAsGroup: {{ .Values.server.gid | default 1000 }} + runAsUser: {{ .Values.server.uid | default 100 }} + fsGroup: {{ .Values.server.gid | default 1000 }} + {{- end }} + volumes: + {{ template "vault.volumes" . }} + - name: home + emptyDir: {} + {{- if .Values.server.extraInitContainers }} + initContainers: + {{ toYaml .Values.server.extraInitContainers | nindent 8}} + {{- end }} + containers: + - name: vault + {{ template "vault.resources" . }} + image: {{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default "latest" }} + imagePullPolicy: {{ .Values.server.image.pullPolicy }} + command: + - "/bin/sh" + - "-ec" + args: {{ template "vault.args" . }} + {{- if not .Values.global.openshift }} + securityContext: + allowPrivilegeEscalation: false + {{- end }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: VAULT_K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: VAULT_ADDR + value: "{{ include "vault.scheme" . }}://127.0.0.1:8200" + - name: VAULT_API_ADDR + {{- if .Values.server.ha.apiAddr }} + value: {{ .Values.server.ha.apiAddr }} + {{- else }} + value: "{{ include "vault.scheme" . }}://$(POD_IP):8200" + {{- end }} + - name: SKIP_CHOWN + value: "true" + - name: SKIP_SETCAP + value: "true" + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_CLUSTER_ADDR + value: "https://$(HOSTNAME).{{ template "vault.fullname" . }}-internal:8201" + {{- if and (eq (.Values.server.ha.raft.enabled | toString) "true") (eq (.Values.server.ha.raft.setNodeId | toString) "true") }} + - name: VAULT_RAFT_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- end }} + - name: HOME + value: "/home/vault" + {{- if .Values.server.logLevel }} + - name: VAULT_LOG_LEVEL + value: "{{ .Values.server.logLevel }}" + {{- end }} + {{- if .Values.server.logFormat }} + - name: VAULT_LOG_FORMAT + value: "{{ .Values.server.logFormat }}" + {{- end }} + {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} + - name: VAULT_LICENSE_PATH + value: /vault/license/{{ .Values.server.enterpriseLicense.secretKey }} + {{- end }} + {{ template "vault.envs" . }} + {{- include "vault.extraEnvironmentVars" .Values.server | nindent 12 }} + {{- include "vault.extraSecretEnvironmentVars" .Values.server | nindent 12 }} + volumeMounts: + {{ template "vault.mounts" . }} + - name: home + mountPath: /home/vault + ports: + - containerPort: 8200 + name: {{ include "vault.scheme" . }} + - containerPort: 8201 + name: https-internal + - containerPort: 8202 + name: {{ include "vault.scheme" . }}-rep + {{- if .Values.server.readinessProbe.enabled }} + readinessProbe: + {{- if .Values.server.readinessProbe.path }} + httpGet: + path: {{ .Values.server.readinessProbe.path | quote }} + port: 8200 + scheme: {{ include "vault.scheme" . | upper }} + {{- else }} + # Check status; unsealed vault servers return 0 + # The exit code reflects the seal status: + # 0 - unsealed + # 1 - error + # 2 - sealed + exec: + command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] + {{- end }} + failureThreshold: {{ .Values.server.readinessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.server.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.server.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.server.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.server.readinessProbe.timeoutSeconds }} + {{- end }} + {{- if .Values.server.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.server.livenessProbe.path | quote }} + port: 8200 + scheme: {{ include "vault.scheme" . | upper }} + failureThreshold: {{ .Values.server.livenessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.server.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.server.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.server.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.server.livenessProbe.timeoutSeconds }} + {{- end }} + lifecycle: + # Vault container doesn't receive SIGTERM from Kubernetes + # and after the grace period ends, Kube sends SIGKILL. This + # causes issues with graceful shutdowns such as deregistering itself + # from Consul (zombie services). + preStop: + exec: + command: [ + "/bin/sh", "-c", + # Adding a sleep here to give the pod eviction a + # chance to propagate, so requests will not be made + # to this pod while it's terminating + "sleep {{ .Values.server.preStopSleepSeconds }} && kill -SIGTERM $(pidof vault)", + ] + {{- if .Values.server.postStart }} + postStart: + exec: + command: + {{- range (.Values.server.postStart) }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.server.extraContainers }} + {{ toYaml .Values.server.extraContainers | nindent 8}} + {{- end }} + {{- include "imagePullSecrets" . | nindent 6 }} + {{ template "vault.volumeclaims" . }} +{{ end }} +{{ end }} diff --git a/templates/tests/server-test.yaml b/templates/tests/server-test.yaml new file mode 100644 index 00000000..66aa178f --- /dev/null +++ b/templates/tests/server-test.yaml @@ -0,0 +1,40 @@ +{{- if .Values.server.enabled }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ .Release.Name }}-server-test" + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": test +spec: + {{- include "imagePullSecrets" . | nindent 2 }} + containers: + - name: {{ .Release.Name }}-server-test + image: {{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default "latest" }} + imagePullPolicy: {{ .Values.server.image.pullPolicy }} + env: + - name: VAULT_ADDR + value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} + command: + - /bin/sh + - -c + - | + echo "Checking for sealed info in 'vault status' output" + ATTEMPTS=10 + n=0 + until [ "$n" -ge $ATTEMPTS ] + do + echo "Attempt" $n... + vault status -format yaml | grep -E '^sealed: (true|false)' && break + n=$((n+1)) + sleep 5 + done + if [ $n -ge $ATTEMPTS ]; then + echo "timed out looking for sealed info in 'vault status' output" + exit 1 + fi + + exit 0 + + restartPolicy: Never +{{- end }} diff --git a/templates/ui-service.yaml b/templates/ui-service.yaml new file mode 100644 index 00000000..ea27de28 --- /dev/null +++ b/templates/ui-service.yaml @@ -0,0 +1,37 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") }} +{{- if eq (.Values.ui.enabled | toString) "true" }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-ui + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }}-ui + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- template "vault.ui.annotations" . }} +spec: + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + {{- if and (.Values.ui.activeVaultPodOnly) (eq .mode "ha") }} + vault-active: "true" + {{- end }} + publishNotReadyAddresses: {{ .Values.ui.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.ui.externalPort }} + targetPort: {{ .Values.ui.targetPort }} + {{- if .Values.ui.serviceNodePort }} + nodePort: {{ .Values.ui.serviceNodePort }} + {{- end }} + type: {{ .Values.ui.serviceType }} + {{- include "service.externalTrafficPolicy" .Values.ui }} + {{- include "service.loadBalancer" .Values.ui }} +{{- end -}} +{{- end }} +{{- end }} diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000..951a0616 --- /dev/null +++ b/test/README.md @@ -0,0 +1,55 @@ +# Vault Helm Tests + +## Running Vault Helm Acceptance tests + +The Makefile at the top level of this repo contains a few target that should help with running acceptance tests in your own GKE instance or in a kind cluster. + +Note that for the Vault Enterprise tests to pass, a `VAULT_LICENSE_CI` environment variable needs to be set to the contents of a valid Vault Enterprise license. + +### Running in a GKE cluster + +* Set the `GOOGLE_CREDENTIALS` and `CLOUDSDK_CORE_PROJECT` variables at the top of the file. `GOOGLE_CREDENTIALS` should contain the local path to your Google Cloud Platform account credentials in JSON format. `CLOUDSDK_CORE_PROJECT` should be set to the ID of your GCP project. +* Run `make test-image` to create the docker image (with dependencies installed) that will be re-used in the below steps. +* Run `make test-provision` to provision the GKE cluster using terraform. +* Run `make test-acceptance` to run the acceptance tests in this already provisioned cluster. +* You can choose to only run certain tests by setting the ACCEPTANCE_TESTS variable and re-running the above target. +* Run `make test-destroy` when you have finished testing and want to tear-down and remove the cluster. + +### Running in a kind cluster + +* Run `make test-acceptance LOCAL_ACCEPTANCE_TESTS=true` +* You can choose to only run certain tests by setting the `ACCEPTANCE_TESTS` variable and re-running the above target. +* Run `make delete-kind` when you have finished testing and want to tear-down and remove the cluster. +* You can set an alternate kind cluster name by specifying the `KIND_CLUSTER_NAME` variable for any of the above targets. +* You can set an alternate K8S version by specifying the `KIND_K8S_VERSION` variable for any of the above targets. + +See [kind-quick-start](https://kind.sigs.k8s.io/docs/user/quick-start/) if you don't have kind installed on your system. + +## Running chart verification tests + +If [chart-verifier](https://github.com/redhat-certification/chart-verifier) is built and available in your PATH, run: + + bats test/chart/verifier.bats + +Or if you'd rather use the latest chart-verifier docker container, set +USE_DOCKER: + + USE_DOCKER=true bats test/chart/verifier.bats + +## Generating the values json schema + +There is a make target for generating values.schema.json: + + make values-schema + +It relies on the helm [schema-gen plugin][schema-gen]. Note that some manual +editing will be required, since several properties accept multiple data types. + +[schema-gen]: https://github.com/karuppiah7890/helm-schema-gen + +## Helm test + +Vault Helm also contains a simple helm test under +[templates/tests/](../templates/tests/) that may be run against a helm release: + + helm test diff --git a/test/acceptance/_helpers.bash b/test/acceptance/_helpers.bash new file mode 100644 index 00000000..466a5173 --- /dev/null +++ b/test/acceptance/_helpers.bash @@ -0,0 +1,159 @@ +# name_prefix returns the prefix of the resources within Kubernetes. +name_prefix() { + printf "vault" +} + +# chart_dir returns the directory for the chart +chart_dir() { + echo ${BATS_TEST_DIRNAME}/../.. +} + +# helm_install installs the vault chart. This will source overridable +# values from the "values.yaml" file in this directory. This can be set +# by CI or other environments to do test-specific overrides. Note that its +# easily possible to break tests this way so be careful. +helm_install() { + local values="${BATS_TEST_DIRNAME}/values.yaml" + if [ ! -f "${values}" ]; then + touch $values + fi + + helm install -f ${values} \ + --name vault \ + ${BATS_TEST_DIRNAME}/../.. +} + +# helm_install_ha installs the vault chart using HA mode. This will source +# overridable values from the "values.yaml" file in this directory. This can be +# set by CI or other environments to do test-specific overrides. Note that its +# easily possible to break tests this way so be careful. +helm_install_ha() { + local values="${BATS_TEST_DIRNAME}/values.yaml" + if [ ! -f "${values}" ]; then + touch $values + fi + + helm install -f ${values} \ + --name vault \ + --set 'server.enabled=false' \ + --set 'serverHA.enabled=true' \ + ${BATS_TEST_DIRNAME}/../.. +} + +# wait for consul to be running +wait_for_running_consul() { + check() { + # This requests the pod and checks whether the status is running + # and the ready state is true. If so, it outputs the name. Otherwise + # it outputs empty. Therefore, to check for success, check for nonzero + # string length. + kubectl get pods -l component=client -o json | \ + jq -r '.items[0] | select( + .status.phase == "Running" and + ([ .status.conditions[] | select(.type == "Ready" and .status == "True") ] | length) == 1 + ) | .metadata.name' + } + + for i in $(seq 60); do + if [ -n "$(check ${POD_NAME})" ]; then + echo "consul clients are ready." + return + fi + + echo "Waiting for ${POD_NAME} to be ready..." + sleep 2 + done + + echo "consul clients never became ready." + return 1 +} + +# wait for a pod to be ready +wait_for_running() { + POD_NAME=$1 + + check() { + # This requests the pod and checks whether the status is running + # and the ready state is true. If so, it outputs the name. Otherwise + # it outputs empty. Therefore, to check for success, check for nonzero + # string length. + kubectl get pods $1 -o json | \ + jq -r 'select( + .status.phase == "Running" and + ([ .status.conditions[] | select(.type == "Ready" and .status == "False") ] | length) == 1 + ) | .metadata.namespace + "/" + .metadata.name' + } + + for i in $(seq 60); do + if [ -n "$(check ${POD_NAME})" ]; then + echo "${POD_NAME} is ready." + sleep 5 + return + fi + + echo "Waiting for ${POD_NAME} to be ready..." + sleep 2 + done + + echo "${POD_NAME} never became ready." + return 1 +} + +wait_for_ready() { + POD_NAME=$1 + + check() { + # This requests the pod and checks whether the status is running + # and the ready state is true. If so, it outputs the name. Otherwise + # it outputs empty. Therefore, to check for success, check for nonzero + # string length. + kubectl get pods $1 -o json | \ + jq -r 'select( + .status.phase == "Running" and + ([ .status.conditions[] | select(.type == "Ready" and .status == "True") ] | length) == 1 + ) | .metadata.namespace + "/" + .metadata.name' + } + + for i in $(seq 60); do + if [ -n "$(check ${POD_NAME})" ]; then + echo "${POD_NAME} is ready." + sleep 5 + return + fi + + echo "Waiting for ${POD_NAME} to be ready..." + sleep 2 + done + + echo "${POD_NAME} never became ready." + return 1 +} + +wait_for_complete_job() { + POD_NAME=$1 + + check() { + # This requests the pod and checks whether the status is running + # and the ready state is true. If so, it outputs the name. Otherwise + # it outputs empty. Therefore, to check for success, check for nonzero + # string length. + kubectl get job $1 -o json | \ + jq -r 'select( + .status.succeeded == 1 + ) | .metadata.namespace + "/" + .metadata.name' + } + + for i in $(seq 60); do + if [ -n "$(check ${POD_NAME})" ]; then + echo "${POD_NAME} is complete." + sleep 5 + return + fi + + echo "Waiting for ${POD_NAME} to be complete..." + sleep 2 + done + + echo "${POD_NAME} never completed." + return 1 +} diff --git a/test/acceptance/csi-test/nginx.yaml b/test/acceptance/csi-test/nginx.yaml new file mode 100644 index 00000000..fed1137f --- /dev/null +++ b/test/acceptance/csi-test/nginx.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: nginx +--- +kind: Pod +apiVersion: v1 +metadata: + name: nginx +spec: + terminationGracePeriodSeconds: 0 + serviceAccountName: nginx + containers: + - image: docker.mirror.hashicorp.services/nginx + name: nginx + volumeMounts: + - name: secrets-store-inline + mountPath: "/mnt/secrets-store" + readOnly: true + volumes: + - name: secrets-store-inline + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: "vault-kv" diff --git a/test/acceptance/csi-test/vault-kv-secretproviderclass.yaml b/test/acceptance/csi-test/vault-kv-secretproviderclass.yaml new file mode 100644 index 00000000..e793bde6 --- /dev/null +++ b/test/acceptance/csi-test/vault-kv-secretproviderclass.yaml @@ -0,0 +1,14 @@ +# The "Hello World" Vault SecretProviderClass +apiVersion: secrets-store.csi.x-k8s.io/v1alpha1 +kind: SecretProviderClass +metadata: + name: vault-kv +spec: + provider: vault + parameters: + roleName: "kv-role" + vaultAddress: http://vault:8200 + objects: | + - objectName: "bar" + secretPath: "secret/data/kv1" + secretKey: "bar1" diff --git a/test/acceptance/csi-test/vault-policy.hcl b/test/acceptance/csi-test/vault-policy.hcl new file mode 100644 index 00000000..48b670ea --- /dev/null +++ b/test/acceptance/csi-test/vault-policy.hcl @@ -0,0 +1,3 @@ +path "secret/data/kv1" { + capabilities = ["read"] +} \ No newline at end of file diff --git a/test/acceptance/csi.bats b/test/acceptance/csi.bats new file mode 100644 index 00000000..d222ca27 --- /dev/null +++ b/test/acceptance/csi.bats @@ -0,0 +1,60 @@ +#!/usr/bin/env bats + +load _helpers + +@test "csi: testing deployment" { + cd `chart_dir` + + kubectl delete namespace acceptance --ignore-not-found=true + kubectl create namespace acceptance + + # Install Secrets Store CSI driver + CSI_DRIVER_VERSION=0.2.0 + helm install secrets-store-csi-driver https://github.com/kubernetes-sigs/secrets-store-csi-driver/blob/v${CSI_DRIVER_VERSION}/charts/secrets-store-csi-driver-${CSI_DRIVER_VERSION}.tgz?raw=true \ + --wait --timeout=5m \ + --namespace=acceptance \ + --set linux.image.pullPolicy="IfNotPresent" + # Install Vault and Vault provider + helm install vault \ + --wait --timeout=5m \ + --namespace=acceptance \ + --set="server.dev.enabled=true" \ + --set="csi.enabled=true" \ + --set="injector.enabled=false" . + kubectl --namespace=acceptance wait --for=condition=Ready --timeout=5m pod -l app.kubernetes.io/name=vault + kubectl --namespace=acceptance wait --for=condition=Ready --timeout=5m pod -l app.kubernetes.io/name=vault-csi-provider + + # Set up k8s auth and a kv secret. + cat ./test/acceptance/csi-test/vault-policy.hcl | kubectl --namespace=acceptance exec -i vault-0 -- vault policy write kv-policy - + kubectl --namespace=acceptance exec vault-0 -- vault auth enable kubernetes + kubectl --namespace=acceptance exec vault-0 -- sh -c 'vault write auth/kubernetes/config \ + token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ + kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \ + kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ + disable_iss_validation=true' + kubectl --namespace=acceptance exec vault-0 -- vault write auth/kubernetes/role/kv-role \ + bound_service_account_names=nginx \ + bound_service_account_namespaces=acceptance \ + policies=kv-policy \ + ttl=20m + kubectl --namespace=acceptance exec vault-0 -- vault kv put secret/kv1 bar1=hello1 + + kubectl --namespace=acceptance apply -f ./test/acceptance/csi-test/vault-kv-secretproviderclass.yaml + kubectl --namespace=acceptance apply -f ./test/acceptance/csi-test/nginx.yaml + kubectl --namespace=acceptance wait --for=condition=Ready --timeout=5m pod nginx + + result=$(kubectl --namespace=acceptance exec nginx -- cat /mnt/secrets-store/bar) + [[ "$result" == "hello1" ]] +} + +# Clean up +teardown() { + if [[ ${CLEANUP:-true} == "true" ]] + then + echo "helm/pvc teardown" + helm --namespace=acceptance delete vault + helm --namespace=acceptance delete secrets-store-csi-driver + kubectl delete --all pvc + kubectl delete namespace acceptance + fi +} diff --git a/test/acceptance/helm-test.bats b/test/acceptance/helm-test.bats new file mode 100644 index 00000000..c5f9553d --- /dev/null +++ b/test/acceptance/helm-test.bats @@ -0,0 +1,27 @@ +#!/usr/bin/env bats + +load _helpers + +@test "helm/test: running helm test" { + cd `chart_dir` + + kubectl delete namespace acceptance --ignore-not-found=true + kubectl create namespace acceptance + kubectl config set-context --current --namespace=acceptance + + helm install "$(name_prefix)" . + wait_for_running $(name_prefix)-0 + + helm test "$(name_prefix)" +} + +# Clean up +teardown() { + if [[ ${CLEANUP:-true} == "true" ]] + then + echo "helm/pvc teardown" + helm delete vault + kubectl delete --all pvc + kubectl delete namespace acceptance --ignore-not-found=true + fi +} diff --git a/test/acceptance/injector-leader-elector.bats b/test/acceptance/injector-leader-elector.bats new file mode 100644 index 00000000..0f91e02a --- /dev/null +++ b/test/acceptance/injector-leader-elector.bats @@ -0,0 +1,52 @@ +#!/usr/bin/env bats + +load _helpers + +@test "injector: testing leader elector" { + cd `chart_dir` + + kubectl delete namespace acceptance --ignore-not-found=true + kubectl create namespace acceptance + kubectl config set-context --current --namespace=acceptance + + helm install "$(name_prefix)" \ + --wait \ + --timeout=5m \ + --set="injector.replicas=3" . + kubectl wait --for condition=Ready pod -l app.kubernetes.io/name=vault-agent-injector --timeout=5m + + pods=($(kubectl get pods -l app.kubernetes.io/name=vault-agent-injector -o json | jq -r '.items[] | .metadata.name')) + [ "${#pods[@]}" == 3 ] + + leader='' + tries=0 + until [ $tries -ge 60 ] + do + owner=$(kubectl get configmaps vault-k8s-leader -o json | jq -r .metadata.ownerReferences\[0\].name) + leader=$(kubectl get pods $owner -o json | jq -r .metadata.name) + [ -n "${leader}" ] && [ "${leader}" != "null" ] && break + ((++tries)) + sleep .5 + done + + # Check the leader name is valid - i.e. one of the 3 pods + [[ " ${pods[@]} " =~ " ${leader} " ]] + +} + +setup() { + kubectl delete namespace acceptance --ignore-not-found=true + kubectl create namespace acceptance + kubectl config set-context --current --namespace=acceptance +} + +# Clean up +teardown() { + if [[ ${CLEANUP:-true} == "true" ]] + then + echo "helm/pvc teardown" + helm delete vault + kubectl delete --all pvc + kubectl delete namespace acceptance + fi +} \ No newline at end of file diff --git a/test/acceptance/injector-test/bootstrap.sh b/test/acceptance/injector-test/bootstrap.sh new file mode 100755 index 00000000..d738fd28 --- /dev/null +++ b/test/acceptance/injector-test/bootstrap.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +OUTPUT=/tmp/output.txt + +vault operator init -n 1 -t 1 >> ${OUTPUT?} + +unseal=$(cat ${OUTPUT?} | grep "Unseal Key 1:" | sed -e "s/Unseal Key 1: //g") +root=$(cat ${OUTPUT?} | grep "Initial Root Token:" | sed -e "s/Initial Root Token: //g") + +vault operator unseal ${unseal?} + +vault login -no-print ${root?} + +vault policy write db-backup /vault/userconfig/test/pgdump-policy.hcl + +vault auth enable kubernetes + +vault write auth/kubernetes/config \ + token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ + kubernetes_host=https://${KUBERNETES_PORT_443_TCP_ADDR}:443 \ + kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + +vault write auth/kubernetes/role/db-backup \ + bound_service_account_names=pgdump \ + bound_service_account_namespaces=acceptance \ + policies=db-backup \ + ttl=1h + +vault secrets enable database + +vault write database/config/postgresql \ + plugin_name=postgresql-database-plugin \ + allowed_roles="db-backup" \ + connection_url="postgresql://{{username}}:{{password}}@postgres:5432/mydb?sslmode=disable" \ + username="vault" \ + password="vault" + +vault write database/roles/db-backup \ + db_name=postgresql \ + creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \ + GRANT CONNECT ON DATABASE mydb TO \"{{name}}\"; \ + GRANT USAGE ON SCHEMA app TO \"{{name}}\"; \ + GRANT SELECT ON ALL TABLES IN SCHEMA app TO \"{{name}}\";" \ + revocation_statements="ALTER ROLE \"{{name}}\" NOLOGIN;"\ + default_ttl="1h" \ + max_ttl="24h" diff --git a/test/acceptance/injector-test/job.yaml b/test/acceptance/injector-test/job.yaml new file mode 100644 index 00000000..d665383c --- /dev/null +++ b/test/acceptance/injector-test/job.yaml @@ -0,0 +1,39 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: pgdump + labels: + app: pgdump +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: pgdump +spec: + backoffLimit: 0 + template: + metadata: + name: pgdump + labels: + app: pgdump + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/db-backup" + vault.hashicorp.com/agent-inject-template-db-creds: | + {{- with secret "database/creds/db-backup" -}} + postgresql://{{ .Data.username }}:{{ .Data.password }}@postgres.acceptance.svc.cluster.local:5432/mydb + {{- end }} + vault.hashicorp.com/role: "db-backup" + vault.hashicorp.com/agent-pre-populate-only: "true" + spec: + serviceAccountName: pgdump + containers: + - name: pgdump + image: postgres:11.5 + command: + - "/bin/sh" + - "-ec" + args: + - "/usr/bin/pg_dump $(cat /vault/secrets/db-creds) --no-owner > /dev/stdout" + restartPolicy: Never diff --git a/test/acceptance/injector-test/pg-deployment.yaml b/test/acceptance/injector-test/pg-deployment.yaml new file mode 100644 index 00000000..caf8605d --- /dev/null +++ b/test/acceptance/injector-test/pg-deployment.yaml @@ -0,0 +1,69 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres + labels: + app: postgres +spec: + type: ClusterIP + ports: + - port: 5432 + targetPort: 5432 + selector: + app: postgres +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres +spec: + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + service: postgres + app: postgres + spec: + containers: + - name: postgres + image: postgres:11.5 + ports: + - containerPort: 5432 + env: + - name: POSTGRES_DB + value: mydb + - name: POSTGRES_USER + value: postgres + - name: POSTGRES_PASSWORD + value: password + volumeMounts: + - mountPath: "/var/lib/postgresql" + name: "pgdata" + - mountPath: "/docker-entrypoint-initdb.d" + name: "pgconf" + volumes: + - name: pgdata + emptyDir: {} + - name: pgconf + configMap: + name: "pg-init" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: pg-init + labels: + app: postgres +data: + setup.sql: | + CREATE ROLE vault; + ALTER ROLE vault WITH SUPERUSER LOGIN PASSWORD 'vault'; + + \c mydb + CREATE SCHEMA app; + CREATE TABLE app.inventory(id int); + INSERT INTO app.inventory(id) VALUES (0); diff --git a/test/acceptance/injector-test/pgdump-policy.hcl b/test/acceptance/injector-test/pgdump-policy.hcl new file mode 100644 index 00000000..88a6cd66 --- /dev/null +++ b/test/acceptance/injector-test/pgdump-policy.hcl @@ -0,0 +1,3 @@ +path "database/creds/db-backup" { + capabilities = ["read"] +} diff --git a/test/acceptance/injector.bats b/test/acceptance/injector.bats new file mode 100644 index 00000000..e7fb393a --- /dev/null +++ b/test/acceptance/injector.bats @@ -0,0 +1,58 @@ +#!/usr/bin/env bats + +load _helpers + +@test "injector: testing deployment" { + cd `chart_dir` + + kubectl delete namespace acceptance --ignore-not-found=true + kubectl create namespace acceptance + kubectl config set-context --current --namespace=acceptance + + kubectl create -f ./test/acceptance/injector-test/pg-deployment.yaml + sleep 5 + wait_for_ready $(kubectl get pod -l app=postgres -o jsonpath="{.items[0].metadata.name}") + + kubectl create secret generic test \ + --from-file ./test/acceptance/injector-test/pgdump-policy.hcl \ + --from-file ./test/acceptance/injector-test/bootstrap.sh + + kubectl label secret test app=vault-agent-demo + + helm install "$(name_prefix)" \ + --set="server.extraVolumes[0].type=secret" \ + --set="server.extraVolumes[0].name=test" . + wait_for_running $(name_prefix)-0 + + wait_for_ready $(kubectl get pod -l component=webhook -o jsonpath="{.items[0].metadata.name}") + + kubectl exec -ti "$(name_prefix)-0" -- /bin/sh -c "cp /vault/userconfig/test/bootstrap.sh /tmp/bootstrap.sh && chmod +x /tmp/bootstrap.sh && /tmp/bootstrap.sh" + sleep 5 + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "false" ] + + local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "true" ] + + + kubectl create -f ./test/acceptance/injector-test/job.yaml + wait_for_complete_job "pgdump" +} + +# Clean up +teardown() { + if [[ ${CLEANUP:-true} == "true" ]] + then + echo "helm/pvc teardown" + helm delete vault + kubectl delete --all pvc + kubectl delete secret test + kubectl delete job pgdump + kubectl delete deployment postgres + kubectl delete namespace acceptance + fi +} diff --git a/test/acceptance/server-annotations.bats b/test/acceptance/server-annotations.bats new file mode 100644 index 00000000..d382788a --- /dev/null +++ b/test/acceptance/server-annotations.bats @@ -0,0 +1,46 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/annotations: testing yaml and yaml-formatted string formats" { + cd `chart_dir` + kubectl delete namespace acceptance --ignore-not-found=true + kubectl create namespace acceptance + kubectl config set-context --current --namespace=acceptance + + helm install "$(name_prefix)" -f ./test/acceptance/server-test/annotations-overrides.yaml . + wait_for_running $(name_prefix)-0 + + # service annotations + local awesome=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.metadata.annotations.active') + [ "${awesome}" == "sometimes" ] + + local pickMe=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.metadata.annotations.pickMe') + [ "${pickMe}" == "please" ] + + local environment=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.metadata.annotations.environment') + [ "${environment}" == "production" ] + + local milk=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.metadata.annotations.milk') + [ "${milk}" == "oat" ] + + local myName=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.metadata.annotations.myName') + [ "${myName}" == "$(name_prefix)" ] + +} + +# Clean up +teardown() { + if [[ ${CLEANUP:-true} == "true" ]] + then + echo "helm/pvc teardown" + helm delete $(name_prefix) + kubectl delete --all pvc + kubectl delete namespace acceptance --ignore-not-found=true + fi +} diff --git a/test/acceptance/server-dev.bats b/test/acceptance/server-dev.bats new file mode 100644 index 00000000..0619c289 --- /dev/null +++ b/test/acceptance/server-dev.bats @@ -0,0 +1,64 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/dev: testing deployment" { + cd `chart_dir` + kubectl delete namespace acceptance --ignore-not-found=true + kubectl create namespace acceptance + kubectl config set-context --current --namespace=acceptance + + helm install "$(name_prefix)" --set='server.dev.enabled=true' . + wait_for_running $(name_prefix)-0 + + # Replicas + local replicas=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.replicas') + [ "${replicas}" == "1" ] + + # Volume Mounts + local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.containers[0].volumeMounts | length') + [ "${volumeCount}" == "1" ] + + # Service + local service=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.clusterIP') + [ "${service}" != "None" ] + + local service=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.type') + [ "${service}" == "ClusterIP" ] + + local ports=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.ports | length') + [ "${ports}" == "2" ] + + local ports=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.ports[0].port') + [ "${ports}" == "8200" ] + + local ports=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.ports[1].port') + [ "${ports}" == "8201" ] + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "false" ] + + local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "true" ] +} + +# Clean up +teardown() { + if [[ ${CLEANUP:-true} == "true" ]] + then + echo "helm/pvc teardown" + helm delete vault + kubectl delete --all pvc + kubectl delete namespace acceptance --ignore-not-found=true + fi +} diff --git a/test/acceptance/server-ha-enterprise-dr.bats b/test/acceptance/server-ha-enterprise-dr.bats new file mode 100644 index 00000000..ee27518f --- /dev/null +++ b/test/acceptance/server-ha-enterprise-dr.bats @@ -0,0 +1,170 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/ha-enterprise-raft: testing DR deployment" { + cd `chart_dir` + + helm install "$(name_prefix)-east" \ + --set='server.image.repository=hashicorp/vault-enterprise' \ + --set='server.image.tag=1.9.0_ent' \ + --set='injector.enabled=false' \ + --set='server.ha.enabled=true' \ + --set='server.ha.raft.enabled=true' \ + --set='server.enterpriseLicense.secretName=vault-license' . + wait_for_running "$(name_prefix)-east-0" + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "true" ] + + local init_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "false" ] + + # Vault Init + local init=$(kubectl exec -ti "$(name_prefix)-east-0" -- \ + vault operator init -format=json -n 1 -t 1) + + local primary_token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') + [ "${primary_token}" != "" ] + + local primary_root=$(echo ${init} | jq -r '.root_token') + [ "${primary_root}" != "" ] + + kubectl exec -ti "$(name_prefix)-east-0" -- vault operator unseal ${primary_token} + wait_for_ready "$(name_prefix)-east-0" + + sleep 10 + + # Vault Unseal + local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) + for pod in "${pods[@]}" + do + if [[ ${pod?} != "$(name_prefix)-east-0" ]] + then + kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-east-0.$(name_prefix)-east-internal:8200 + kubectl exec -ti ${pod} -- vault operator unseal ${primary_token} + wait_for_ready "${pod}" + fi + done + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "false" ] + + local init_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "true" ] + + kubectl exec "$(name_prefix)-east-0" -- vault login ${primary_root} + + local raft_status=$(kubectl exec "$(name_prefix)-east-0" -- vault operator raft list-peers -format=json | + jq -r '.data.config.servers | length') + [ "${raft_status}" == "3" ] + + kubectl exec -ti $(name_prefix)-east-0 -- vault write -f sys/replication/dr/primary/enable primary_cluster_addr=https://$(name_prefix)-east-active:8201 + + local secondary=$(kubectl exec -ti "$(name_prefix)-east-0" -- vault write sys/replication/dr/primary/secondary-token id=secondary -format=json) + [ "${secondary}" != "" ] + + local secondary_replica_token=$(echo ${secondary} | jq -r '.wrap_info.token') + [ "${secondary_replica_token}" != "" ] + + # Install vault-west + helm install "$(name_prefix)-west" \ + --set='injector.enabled=false' \ + --set='server.image.repository=hashicorp/vault-enterprise' \ + --set='server.image.tag=1.9.0_ent' \ + --set='server.ha.enabled=true' \ + --set='server.ha.raft.enabled=true' \ + --set='server.enterpriseLicense.secretName=vault-license' . + wait_for_running "$(name_prefix)-west-0" + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "true" ] + + local init_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "false" ] + + # Vault Init + local init=$(kubectl exec -ti "$(name_prefix)-west-0" -- \ + vault operator init -format=json -n 1 -t 1) + + local secondary_token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') + [ "${secondary_token}" != "" ] + + local secondary_root=$(echo ${init} | jq -r '.root_token') + [ "${secondary_root}" != "" ] + + kubectl exec -ti "$(name_prefix)-west-0" -- vault operator unseal ${secondary_token} + wait_for_ready "$(name_prefix)-west-0" + + sleep 10 + + # Vault Unseal + local pods=($(kubectl get pods --selector='app.kubernetes.io/instance=vault-west' -o json | jq -r '.items[].metadata.name')) + for pod in "${pods[@]}" + do + if [[ ${pod?} != "$(name_prefix)-west-0" ]] + then + kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-west-0.$(name_prefix)-west-internal:8200 + kubectl exec -ti ${pod} -- vault operator unseal ${secondary_token} + wait_for_ready "${pod}" + fi + done + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "false" ] + + local init_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "true" ] + + kubectl exec "$(name_prefix)-west-0" -- vault login ${secondary_root} + + local raft_status=$(kubectl exec "$(name_prefix)-west-0" -- vault operator raft list-peers -format=json | + jq -r '.data.config.servers | length') + [ "${raft_status}" == "3" ] + + kubectl exec -ti "$(name_prefix)-west-0" -- vault write sys/replication/dr/secondary/enable token=${secondary_replica_token} + + sleep 10 + + local pods=($(kubectl get pods --selector='app.kubernetes.io/instance=vault-west' -o json | jq -r '.items[].metadata.name')) + for pod in "${pods[@]}" + do + if [[ ${pod?} != "$(name_prefix)-west-0" ]] + then + kubectl delete pod "${pod?}" + wait_for_running "${pod?}" + kubectl exec -ti ${pod} -- vault operator unseal ${primary_token} + wait_for_ready "${pod}" + fi + done +} + +setup() { + kubectl delete namespace acceptance --ignore-not-found=true + kubectl create namespace acceptance + kubectl config set-context --current --namespace=acceptance + kubectl create secret generic vault-license --from-literal license=$VAULT_LICENSE_CI +} + +#cleanup +teardown() { + if [[ ${CLEANUP:-true} == "true" ]] + then + helm delete vault-east + helm delete vault-west + kubectl delete --all pvc + kubectl delete namespace acceptance --ignore-not-found=true + fi +} diff --git a/test/acceptance/server-ha-enterprise-perf.bats b/test/acceptance/server-ha-enterprise-perf.bats new file mode 100644 index 00000000..c359c1c2 --- /dev/null +++ b/test/acceptance/server-ha-enterprise-perf.bats @@ -0,0 +1,168 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/ha-enterprise-raft: testing performance replica deployment" { + cd `chart_dir` + + helm install "$(name_prefix)-east" \ + --set='injector.enabled=false' \ + --set='server.image.repository=hashicorp/vault-enterprise' \ + --set='server.image.tag=1.9.0_ent' \ + --set='server.ha.enabled=true' \ + --set='server.ha.raft.enabled=true' \ + --set='server.enterpriseLicense.secretName=vault-license' . + wait_for_running "$(name_prefix)-east-0" + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "true" ] + + local init_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "false" ] + + # Vault Init + local init=$(kubectl exec -ti "$(name_prefix)-east-0" -- \ + vault operator init -format=json -n 1 -t 1) + + local primary_token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') + [ "${primary_token}" != "" ] + + local primary_root=$(echo ${init} | jq -r '.root_token') + [ "${primary_root}" != "" ] + + kubectl exec -ti "$(name_prefix)-east-0" -- vault operator unseal ${primary_token} + wait_for_ready "$(name_prefix)-east-0" + + sleep 30 + + # Vault Unseal + local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) + for pod in "${pods[@]}" + do + if [[ ${pod?} != "$(name_prefix)-east-0" ]] + then + kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-east-0.$(name_prefix)-east-internal:8200 + kubectl exec -ti ${pod} -- vault operator unseal ${primary_token} + wait_for_ready "${pod}" + fi + done + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "false" ] + + local init_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "true" ] + + kubectl exec "$(name_prefix)-east-0" -- vault login ${primary_root} + + local raft_status=$(kubectl exec "$(name_prefix)-east-0" -- vault operator raft list-peers -format=json | + jq -r '.data.config.servers | length') + [ "${raft_status}" == "3" ] + + kubectl exec -ti $(name_prefix)-east-0 -- vault write -f sys/replication/performance/primary/enable primary_cluster_addr=https://$(name_prefix)-east-active:8201 + + local secondary=$(kubectl exec -ti "$(name_prefix)-east-0" -- vault write sys/replication/performance/primary/secondary-token id=secondary -format=json) + [ "${secondary}" != "" ] + + local secondary_replica_token=$(echo ${secondary} | jq -r '.wrap_info.token') + [ "${secondary_replica_token}" != "" ] + + # Install vault-west + helm install "$(name_prefix)-west" \ + --set='injector.enabled=false' \ + --set='server.image.repository=hashicorp/vault-enterprise' \ + --set='server.image.tag=1.9.0_ent' \ + --set='server.ha.enabled=true' \ + --set='server.ha.raft.enabled=true' \ + --set='server.enterpriseLicense.secretName=vault-license' . + wait_for_running "$(name_prefix)-west-0" + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "true" ] + + local init_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "false" ] + + # Vault Init + local init=$(kubectl exec -ti "$(name_prefix)-west-0" -- \ + vault operator init -format=json -n 1 -t 1) + + local secondary_token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') + [ "${secondary_token}" != "" ] + + local secondary_root=$(echo ${init} | jq -r '.root_token') + [ "${secondary_root}" != "" ] + + kubectl exec -ti "$(name_prefix)-west-0" -- vault operator unseal ${secondary_token} + wait_for_ready "$(name_prefix)-west-0" + + sleep 30 + + # Vault Unseal + local pods=($(kubectl get pods --selector='app.kubernetes.io/instance=vault-west' -o json | jq -r '.items[].metadata.name')) + for pod in "${pods[@]}" + do + if [[ ${pod?} != "$(name_prefix)-west-0" ]] + then + kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-west-0.$(name_prefix)-west-internal:8200 + kubectl exec -ti ${pod} -- vault operator unseal ${secondary_token} + wait_for_ready "${pod}" + fi + done + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "false" ] + + local init_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "true" ] + + kubectl exec "$(name_prefix)-west-0" -- vault login ${secondary_root} + + local raft_status=$(kubectl exec "$(name_prefix)-west-0" -- vault operator raft list-peers -format=json | + jq -r '.data.config.servers | length') + [ "${raft_status}" == "3" ] + + kubectl exec -ti "$(name_prefix)-west-0" -- vault write sys/replication/performance/secondary/enable token=${secondary_replica_token} + + sleep 30 + + local pods=($(kubectl get pods --selector='app.kubernetes.io/instance=vault-west' -o json | jq -r '.items[].metadata.name')) + for pod in "${pods[@]}" + do + if [[ ${pod?} != "$(name_prefix)-west-0" ]] + then + kubectl exec -ti ${pod} -- vault operator unseal ${primary_token} + wait_for_ready "${pod}" + fi + done +} + +setup() { + kubectl delete namespace acceptance --ignore-not-found=true + kubectl create namespace acceptance + kubectl config set-context --current --namespace=acceptance + kubectl create secret generic vault-license --from-literal license=$VAULT_LICENSE_CI +} + +#cleanup +teardown() { + if [[ ${CLEANUP:-true} == "true" ]] + then + helm delete vault-east + helm delete vault-west + kubectl delete --all pvc + kubectl delete namespace acceptance --ignore-not-found=true + fi +} diff --git a/test/acceptance/server-ha-raft.bats b/test/acceptance/server-ha-raft.bats new file mode 100644 index 00000000..9f9f3dec --- /dev/null +++ b/test/acceptance/server-ha-raft.bats @@ -0,0 +1,119 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/ha-raft: testing deployment" { + cd `chart_dir` + + helm install "$(name_prefix)" \ + --set='server.ha.enabled=true' \ + --set='server.ha.raft.enabled=true' . + wait_for_running $(name_prefix)-0 + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "true" ] + + local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "false" ] + + # Replicas + local replicas=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.replicas') + [ "${replicas}" == "3" ] + + # Volume Mounts + local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.containers[0].volumeMounts | length') + [ "${volumeCount}" == "3" ] + + # Volumes + local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.volumes | length') + [ "${volumeCount}" == "2" ] + + local volume=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.volumes[0].configMap.name') + [ "${volume}" == "$(name_prefix)-config" ] + + # Service + local service=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.clusterIP') + [ "${service}" != "None" ] + + local service=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.type') + [ "${service}" == "ClusterIP" ] + + local ports=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.ports | length') + [ "${ports}" == "2" ] + + local ports=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.ports[0].port') + [ "${ports}" == "8200" ] + + local ports=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.ports[1].port') + [ "${ports}" == "8201" ] + + # Vault Init + local init=$(kubectl exec -ti "$(name_prefix)-0" -- \ + vault operator init -format=json -n 1 -t 1) + + local token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') + [ "${token}" != "" ] + + local root=$(echo ${init} | jq -r '.root_token') + [ "${root}" != "" ] + + kubectl exec -ti vault-0 -- vault operator unseal ${token} + wait_for_ready "$(name_prefix)-0" + + sleep 5 + + # Vault Unseal + local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) + for pod in "${pods[@]}" + do + if [[ ${pod?} != "$(name_prefix)-0" ]] + then + kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-0.$(name_prefix)-internal:8200 + kubectl exec -ti ${pod} -- vault operator unseal ${token} + wait_for_ready "${pod}" + fi + done + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "false" ] + + local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "true" ] + + kubectl exec "$(name_prefix)-0" -- vault login ${root} + + local raft_status=$(kubectl exec "$(name_prefix)-0" -- vault operator raft list-peers -format=json | + jq -r '.data.config.servers | length') + [ "${raft_status}" == "3" ] +} + +setup() { + kubectl delete namespace acceptance --ignore-not-found=true + kubectl create namespace acceptance + kubectl config set-context --current --namespace=acceptance +} + +#cleanup +teardown() { + if [[ ${CLEANUP:-true} == "true" ]] + then + helm delete vault + kubectl delete --all pvc + kubectl delete namespace acceptance --ignore-not-found=true + fi +} diff --git a/test/acceptance/server-ha.bats b/test/acceptance/server-ha.bats new file mode 100644 index 00000000..3d629598 --- /dev/null +++ b/test/acceptance/server-ha.bats @@ -0,0 +1,108 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/ha: testing deployment" { + cd `chart_dir` + + helm install "$(name_prefix)" \ + --set='server.ha.enabled=true' . + wait_for_running $(name_prefix)-0 + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "true" ] + + local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "false" ] + + # Replicas + local replicas=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.replicas') + [ "${replicas}" == "3" ] + + # Volume Mounts + local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.containers[0].volumeMounts | length') + [ "${volumeCount}" == "2" ] + + # Volumes + local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.volumes | length') + [ "${volumeCount}" == "2" ] + + local volume=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.volumes[0].configMap.name') + [ "${volume}" == "$(name_prefix)-config" ] + + # Service + local service=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.clusterIP') + [ "${service}" != "None" ] + + local service=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.type') + [ "${service}" == "ClusterIP" ] + + local ports=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.ports | length') + [ "${ports}" == "2" ] + + local ports=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.ports[0].port') + [ "${ports}" == "8200" ] + + local ports=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.ports[1].port') + [ "${ports}" == "8201" ] + + # Vault Init + local token=$(kubectl exec -ti "$(name_prefix)-0" -- \ + vault operator init -format=json -n 1 -t 1 | \ + jq -r '.unseal_keys_b64[0]') + [ "${token}" != "" ] + + # Vault Unseal + local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) + for pod in "${pods[@]}" + do + kubectl exec -ti ${pod} -- vault operator unseal ${token} + done + + wait_for_ready "$(name_prefix)-0" + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "false" ] + + local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "true" ] +} + +# setup a consul env +setup() { + kubectl delete namespace acceptance --ignore-not-found=true + kubectl create namespace acceptance + kubectl config set-context --current --namespace=acceptance + + helm install consul \ + https://github.com/hashicorp/consul-helm/archive/v0.28.0.tar.gz \ + --set 'ui.enabled=false' \ + + wait_for_running_consul +} + +#cleanup +teardown() { + if [[ ${CLEANUP:-true} == "true" ]] + then + helm delete vault + helm delete consul + kubectl delete --all pvc + kubectl delete namespace acceptance --ignore-not-found=true + fi +} diff --git a/test/acceptance/server-test/annotations-overrides.yaml b/test/acceptance/server-test/annotations-overrides.yaml new file mode 100644 index 00000000..459576a9 --- /dev/null +++ b/test/acceptance/server-test/annotations-overrides.yaml @@ -0,0 +1,9 @@ +server: + annotations: | + environment: production + milk: oat + myName: "{{ .Release.Name }}" + service: + annotations: + active: sometimes + pickMe: please diff --git a/test/acceptance/server.bats b/test/acceptance/server.bats new file mode 100644 index 00000000..84a4e7d9 --- /dev/null +++ b/test/acceptance/server.bats @@ -0,0 +1,111 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/standalone: testing deployment" { + cd `chart_dir` + + kubectl delete namespace acceptance --ignore-not-found=true + kubectl create namespace acceptance + kubectl config set-context --current --namespace=acceptance + + helm install "$(name_prefix)" . + wait_for_running $(name_prefix)-0 + + # Sealed, not initialized + local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "true" ] + + local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "false" ] + + # Replicas + local replicas=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.replicas') + [ "${replicas}" == "1" ] + + # Affinity + local affinity=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.affinity') + [ "${affinity}" != "null" ] + + # Volume Mounts + local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.containers[0].volumeMounts | length') + [ "${volumeCount}" == "3" ] + + local mountName=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.containers[0].volumeMounts[0].name') + [ "${mountName}" == "data" ] + + local mountPath=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.containers[0].volumeMounts[0].mountPath') + [ "${mountPath}" == "/vault/data" ] + + # Volumes + local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.volumes | length') + [ "${volumeCount}" == "2" ] + + local volume=$(kubectl get statefulset "$(name_prefix)" --output json | + jq -r '.spec.template.spec.volumes[0].configMap.name') + [ "${volume}" == "$(name_prefix)-config" ] + + # Service + local service=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.clusterIP') + [ "${service}" != "None" ] + + local service=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.type') + [ "${service}" == "ClusterIP" ] + + local ports=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.ports | length') + [ "${ports}" == "2" ] + + local ports=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.ports[0].port') + [ "${ports}" == "8200" ] + + local ports=$(kubectl get service "$(name_prefix)" --output json | + jq -r '.spec.ports[1].port') + [ "${ports}" == "8201" ] + + # Vault Init + local token=$(kubectl exec -ti "$(name_prefix)-0" -- \ + vault operator init -format=json -n 1 -t 1 | \ + jq -r '.unseal_keys_b64[0]') + [ "${token}" != "" ] + + # Vault Unseal + local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) + for pod in "${pods[@]}" + do + kubectl exec -ti ${pod} -- vault operator unseal ${token} + done + + wait_for_ready "$(name_prefix)-0" + + # Unsealed, initialized + local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.sealed' ) + [ "${sealed_status}" == "false" ] + + local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | + jq -r '.initialized') + [ "${init_status}" == "true" ] +} + +# Clean up +teardown() { + if [[ ${CLEANUP:-true} == "true" ]] + then + echo "helm/pvc teardown" + helm delete vault + kubectl delete --all pvc + kubectl delete namespace acceptance --ignore-not-found=true + fi +} diff --git a/test/chart/_helpers.bash b/test/chart/_helpers.bash new file mode 100644 index 00000000..fb9db31d --- /dev/null +++ b/test/chart/_helpers.bash @@ -0,0 +1,18 @@ +# chart_dir returns the directory for the chart +chart_dir() { + echo ${BATS_TEST_DIRNAME}/../.. +} + +# check_result checks if the specified test passed +# results schema example: +# { +# "check": "has-minkubeversion", +# "type": "Mandatory", +# "outcome": "PASS", +# "reason": "Minimum Kubernetes version specified" +# } +check_result() { + local -r var="$1" + local check=$(cat $VERIFY_OUTPUT | jq -r ".results[] | select(.check==\"${var}\").outcome") + [ "$check" = "PASS" ] +} diff --git a/test/chart/verifier.bats b/test/chart/verifier.bats new file mode 100644 index 00000000..63c79395 --- /dev/null +++ b/test/chart/verifier.bats @@ -0,0 +1,85 @@ +#!/usr/bin/env bats + +load _helpers + +setup_file() { + cd `chart_dir` + export VERIFY_OUTPUT="/$BATS_RUN_TMPDIR/verify.json" + export CHART_VOLUME=vault-helm-chart-src + local IMAGE="quay.io/redhat-certification/chart-verifier:1.2.1" + # chart-verifier requires an openshift version if a cluster isn't available + local OPENSHIFT_VERSION="4.8" + local DISABLED_TESTS="chart-testing" + + local run_cmd="chart-verifier" + local chart_src="." + + if [ ! -e $USE_DOCKER ]; then + chart_src="/chart" + # Create a dummy container which will hold a volume with chart source + docker create -v $chart_src --name $CHART_VOLUME alpine:3 /bin/true + # Copy the chart source into this volume + docker cp . $CHART_VOLUME:$chart_src + # Make sure we have the latest version of chart-verifier + docker pull $IMAGE + # Start chart-verifier using this volume + run_cmd="docker run --rm --volumes-from $CHART_VOLUME -w $chart_src $IMAGE" + fi + + $run_cmd verify $chart_src \ + --output json \ + --openshift-version $OPENSHIFT_VERSION \ + --disable $DISABLED_TESTS \ + --chart-values values.openshift.yaml 2>&1 | tee $VERIFY_OUTPUT +} + +teardown_file() { + if [ ! -e $USE_DOCKER ]; then + docker rm $CHART_VOLUME + fi +} + +@test "has-kubeversion" { + check_result v1.0/has-kubeversion +} + +@test "is-helm-v3" { + check_result v1.0/is-helm-v3 +} + +@test "not-contains-crds" { + check_result v1.0/not-contains-crds +} + +@test "helm-lint" { + check_result v1.0/helm-lint +} + +@test "not-contain-csi-objects" { + check_result v1.0/not-contain-csi-objects +} + +@test "has-readme" { + check_result v1.0/has-readme +} + +@test "contains-values" { + check_result v1.0/contains-values +} + +@test "contains-values-schema" { + check_result v1.0/contains-values-schema +} + +@test "contains-test" { + check_result v1.0/contains-test +} + +@test "images-are-certified" { + check_result v1.0/images-are-certified +} + +@test "chart-testing" { + skip "Skipping since this test requires a kubernetes/openshift cluster" + check_result v1.0/chart-testing +} diff --git a/test/docker/Test.dockerfile b/test/docker/Test.dockerfile new file mode 100644 index 00000000..98afeace --- /dev/null +++ b/test/docker/Test.dockerfile @@ -0,0 +1,51 @@ +# This Dockerfile installs all the dependencies necessary to run the unit and +# acceptance tests. This image also contains gcloud so you can run tests +# against a GKE cluster easily. +# +# This image has no automatic entrypoint. It is expected that you'll run +# a script to configure kubectl, potentially install Helm, and run the tests +# manually. This image only has the dependencies pre-installed. + +FROM docker.mirror.hashicorp.services/alpine:latest +WORKDIR /root + +ENV BATS_VERSION "1.3.0" +ENV TERRAFORM_VERSION "0.12.10" + +# base packages +RUN apk update && apk add --no-cache --virtual .build-deps \ + ca-certificates \ + curl \ + tar \ + bash \ + openssl \ + py-pip \ + git \ + make \ + jq + +# yq +RUN pip install yq + +# gcloud +RUN curl -OL https://dl.google.com/dl/cloudsdk/channels/rapid/install_google_cloud_sdk.bash && \ + bash install_google_cloud_sdk.bash --disable-prompts --install-dir='/root/' && \ + ln -s /root/google-cloud-sdk/bin/gcloud /usr/local/bin/gcloud + +# terraform +RUN curl -sSL https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip -o /tmp/tf.zip \ + && unzip /tmp/tf.zip \ + && ln -s /root/terraform /usr/local/bin/terraform + +# kubectl +RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && \ + chmod +x ./kubectl && \ + mv ./kubectl /usr/local/bin/kubectl + +# helm +RUN curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + +# bats +RUN curl -sSL https://github.com/bats-core/bats-core/archive/v${BATS_VERSION}.tar.gz -o /tmp/bats.tgz \ + && tar -zxf /tmp/bats.tgz -C /tmp \ + && /bin/bash /tmp/bats-core-$BATS_VERSION/install.sh /usr/local diff --git a/test/kind/config.yaml b/test/kind/config.yaml new file mode 100644 index 00000000..25096640 --- /dev/null +++ b/test/kind/config.yaml @@ -0,0 +1,7 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: +- role: control-plane +- role: worker +- role: worker +- role: worker diff --git a/test/terraform/.gitignore b/test/terraform/.gitignore new file mode 100644 index 00000000..d6800621 --- /dev/null +++ b/test/terraform/.gitignore @@ -0,0 +1 @@ +vault-helm-dev-creds.json diff --git a/test/terraform/main.tf b/test/terraform/main.tf new file mode 100644 index 00000000..5c3570f2 --- /dev/null +++ b/test/terraform/main.tf @@ -0,0 +1,72 @@ +provider "google" { + project = "${var.project}" +} + +resource "random_id" "suffix" { + byte_length = 4 +} + +data "google_container_engine_versions" "main" { + location = "${var.zone}" + version_prefix = "1.19." +} + +data "google_service_account" "gcpapi" { + account_id = "${var.gcp_service_account}" +} + +resource "google_container_cluster" "cluster" { + name = "vault-helm-dev-${random_id.suffix.dec}" + project = "${var.project}" + enable_legacy_abac = true + initial_node_count = 3 + location = "${var.zone}" + min_master_version = "${data.google_container_engine_versions.main.latest_master_version}" + node_version = "${data.google_container_engine_versions.main.latest_node_version}" + + node_config { + #service account for nodes to use + oauth_scopes = [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/compute", + "https://www.googleapis.com/auth/devstorage.read_write", + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring", + "https://www.googleapis.com/auth/service.management.readonly", + "https://www.googleapis.com/auth/servicecontrol", + "https://www.googleapis.com/auth/trace.append", + ] + + service_account = "${data.google_service_account.gcpapi.email}" + } +} + +resource "null_resource" "kubectl" { + count = "${var.init_cli ? 1 : 0 }" + + triggers = { + cluster = "${google_container_cluster.cluster.id}" + } + + # On creation, we want to setup the kubectl credentials. The easiest way + # to do this is to shell out to gcloud. + provisioner "local-exec" { + command = "gcloud container clusters get-credentials --zone=${var.zone} ${google_container_cluster.cluster.name}" + } + + # On destroy we want to try to clean up the kubectl credentials. This + # might fail if the credentials are already cleaned up or something so we + # want this to continue on failure. Generally, this works just fine since + # it only operates on local data. + provisioner "local-exec" { + when = "destroy" + on_failure = "continue" + command = "kubectl config get-clusters | grep ${google_container_cluster.cluster.name} | xargs -n1 kubectl config delete-cluster" + } + + provisioner "local-exec" { + when = "destroy" + on_failure = "continue" + command = "kubectl config get-contexts | grep ${google_container_cluster.cluster.name} | xargs -n1 kubectl config delete-context" + } +} diff --git a/test/terraform/outputs.tf b/test/terraform/outputs.tf new file mode 100644 index 00000000..6435d2b7 --- /dev/null +++ b/test/terraform/outputs.tf @@ -0,0 +1,7 @@ +output "cluster_id" { + value = "${google_container_cluster.cluster.id}" +} + +output "cluster_name" { + value = "${google_container_cluster.cluster.name}" +} diff --git a/test/terraform/variables.tf b/test/terraform/variables.tf new file mode 100644 index 00000000..971af4e5 --- /dev/null +++ b/test/terraform/variables.tf @@ -0,0 +1,28 @@ +variable "project" { + default = "vault-helm-dev-246514" + + description = < 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "csi/ClusterRole: enabled with csi.enabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-clusterrole.yaml \ + --set 'csi.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +# ClusterRole name +@test "csi/ClusterRole: name" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-clusterrole.yaml \ + --set "csi.enabled=true" \ + . | tee /dev/stderr | + yq -r '.metadata.name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault-csi-provider-clusterrole" ] +} \ No newline at end of file diff --git a/test/unit/csi-clusterrolebinding.bats b/test/unit/csi-clusterrolebinding.bats new file mode 100644 index 00000000..cff3a369 --- /dev/null +++ b/test/unit/csi-clusterrolebinding.bats @@ -0,0 +1,44 @@ +#!/usr/bin/env bats + +load _helpers + +@test "csi/ClusterRoleBinding: disabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/csi-clusterrolebinding.yaml \ + . || echo "---")| tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "csi/ClusterRoleBinding: enabled with csi.enabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-clusterrolebinding.yaml \ + --set 'csi.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +# ClusterRoleBinding cluster role ref name +@test "csi/ClusterRoleBinding: cluster role ref name" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-clusterrolebinding.yaml \ + --set "csi.enabled=true" \ + . | tee /dev/stderr | + yq -r '.roleRef.name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault-csi-provider-clusterrole" ] +} + +# ClusterRoleBinding service account name +@test "csi/ClusterRoleBinding: service account name" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-clusterrolebinding.yaml \ + --set "csi.enabled=true" \ + . | tee /dev/stderr | + yq -r '.subjects[0].name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault-csi-provider" ] +} \ No newline at end of file diff --git a/test/unit/csi-daemonset.bats b/test/unit/csi-daemonset.bats new file mode 100644 index 00000000..5cfd8a7e --- /dev/null +++ b/test/unit/csi-daemonset.bats @@ -0,0 +1,516 @@ +#!/usr/bin/env bats + +load _helpers + +#-------------------------------------------------------------------- +# Daemonset + +# Enabled +@test "csi/daemonset: created only when enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/csi-daemonset.yaml \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$( (helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + --set "global.enabled=false" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +# serviceAccountName reference name +@test "csi/daemonset: serviceAccountName reference name" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.serviceAccountName' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault-csi-provider" ] +} + +# Image +@test "csi/daemonset: image is configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + --set "csi.image.repository=SomeOtherImage" \ + --set "csi.image.tag=0.0.1" \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "SomeOtherImage:0.0.1" ] + + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + --set "csi.image.pullPolicy=SomePullPolicy" \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].imagePullPolicy' | tee /dev/stderr) + [ "${actual}" = "SomePullPolicy" ] +} + +@test "csi/daemonset: Custom imagePullSecrets" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + --set 'global.imagePullSecrets[0].name=foo' \ + --set 'global.imagePullSecrets[1].name=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.imagePullSecrets' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '. | length' | tee /dev/stderr) + [ "${actual}" = "2" ] + + local actual=$(echo $object | + yq -r '.[0].name' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + local actual=$(echo $object | + yq -r '.[1].name' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +@test "csi/daemonset: Custom imagePullSecrets - string array" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + --set 'global.imagePullSecrets[0]=foo' \ + --set 'global.imagePullSecrets[1]=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.imagePullSecrets' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '. | length' | tee /dev/stderr) + [ "${actual}" = "2" ] + + local actual=$(echo $object | + yq -r '.[0].name' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + local actual=$(echo $object | + yq -r '.[1].name' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +@test "csi/daemonset: default imagePullSecrets" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.imagePullSecrets' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +# Debug arg +@test "csi/daemonset: debug arg is configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args[1]' | tee /dev/stderr) + [ "${actual}" = "--debug=false" ] + + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + --set "csi.debug=true" \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args[1]' | tee /dev/stderr) + [ "${actual}" = "--debug=true" ] +} + +# Extra args +@test "csi/daemonset: extra args can be passed" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args | length' | tee /dev/stderr) + [ "${actual}" = "2" ] + + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + --set "csi.extraArgs={--foo=bar,--bar baz,first}" \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0]') + local actual=$(echo $object | + yq -r '.args | length' | tee /dev/stderr) + [ "${actual}" = "5" ] + local actual=$(echo $object | + yq -r '.args[2]' | tee /dev/stderr) + [ "${actual}" = "--foo=bar" ] + local actual=$(echo $object | + yq -r '.args[3]' | tee /dev/stderr) + [ "${actual}" = "--bar baz" ] + local actual=$(echo $object | + yq -r '.args[4]' | tee /dev/stderr) + [ "${actual}" = "first" ] +} + +# updateStrategy +@test "csi/daemonset: updateStrategy is configurable" { + cd `chart_dir` + # Default + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + . | tee /dev/stderr | + yq -r '.spec.updateStrategy.type' | tee /dev/stderr) + [ "${actual}" = "RollingUpdate" ] + + # OnDelete + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + --set "csi.daemonSet.updateStrategy.type=OnDelete" \ + . | tee /dev/stderr | + yq -r '.spec.updateStrategy.type' | tee /dev/stderr) + [ "${actual}" = "OnDelete" ] + + # Max unavailable + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set "csi.enabled=true" \ + --set "csi.daemonSet.updateStrategy.maxUnavailable=25%" \ + . | tee /dev/stderr | + yq -r '.spec.updateStrategy.rollingUpdate.maxUnavailable' | tee /dev/stderr) + [ "${actual}" = "25%" ] +} + +#-------------------------------------------------------------------- +# Extra annotations +@test "csi/daemonset: default csi.daemonSet.annotations" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "csi/daemonset: specify csi.daemonSet.annotations yaml" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + --set 'csi.daemonSet.annotations.foo=bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +@test "csi/daemonset: specify csi.daemonSet.annotations yaml string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + --set 'csi.daemonSet.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +@test "csi/daemonset: default csi.pod.annotations" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "csi/daemonset: specify csi.pod.annotations yaml" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + --set 'csi.pod.annotations.foo=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +@test "csi/daemonset: specify csi.pod.annotations yaml string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + --set 'csi.pod.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +@test "csi/daemonset: tolerations not set by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec | .tolerations? == null' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "csi/daemonset: tolerations can be set as string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + --set 'csi.pod.tolerations=foobar' \ + . | tee /dev/stderr | + yq '.spec.template.spec.tolerations == "foobar"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "csi/daemonset: tolerations can be set as YAML" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + --set "csi.pod.tolerations[0].foo=bar,csi.pod.tolerations[1].baz=qux" \ + . | tee /dev/stderr | + yq '.spec.template.spec.tolerations == [{"foo": "bar"}, {"baz": "qux"}]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# volumes + +@test "csi/daemonset: csi.volumes adds volume" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + --set 'csi.volumes[0].name=plugins' \ + --set 'csi.volumes[0].emptyDir=\{\}' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "plugins")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.emptyDir' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "csi/daemonset: csi providersDir default" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "providervol")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.hostPath.path' | tee /dev/stderr) + [ "${actual}" = "/etc/kubernetes/secrets-store-csi-providers" ] +} + +@test "csi/daemonset: csi kubeletRootDir default" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "mountpoint-dir")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.hostPath.path' | tee /dev/stderr) + [ "${actual}" = "/var/lib/kubelet/pods" ] +} + +@test "csi/daemonset: csi providersDir override " { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + --set 'csi.daemonSet.providersDir=/alt/csi-prov-dir' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "providervol")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.hostPath.path' | tee /dev/stderr) + [ "${actual}" = "/alt/csi-prov-dir" ] +} + +@test "csi/daemonset: csi kubeletRootDir override" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + --set 'csi.daemonSet.kubeletRootDir=/alt/kubelet-root' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "mountpoint-dir")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.hostPath.path' | tee /dev/stderr) + [ "${actual}" = "/alt/kubelet-root/pods" ] +} + +#-------------------------------------------------------------------- +# volumeMounts + +@test "csi/daemonset: csi.volumeMounts adds volume mounts" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + --set 'csi.volumeMounts[0].name=plugins' \ + --set 'csi.volumeMounts[0].mountPath=/usr/local/libexec/vault' \ + --set 'csi.volumeMounts[0].readOnly=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "plugins")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/usr/local/libexec/vault" ] + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# Readiness/liveness probes + +@test "csi/daemonset: csi.livenessProbe is configurable" { + cd `chart_dir` + + # Test the defaults + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.failureThreshold' | tee /dev/stderr) + [ "${actual}" = "2" ] + local actual=$(echo $object | + yq -r '.initialDelaySeconds' | tee /dev/stderr) + [ "${actual}" = "5" ] + local actual=$(echo $object | + yq -r '.periodSeconds' | tee /dev/stderr) + [ "${actual}" = "5" ] + local actual=$(echo $object | + yq -r '.successThreshold' | tee /dev/stderr) + [ "${actual}" = "1" ] + local actual=$(echo $object | + yq -r '.timeoutSeconds' | tee /dev/stderr) + [ "${actual}" = "3" ] + + # Test it is configurable + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + --set 'csi.livenessProbe.failureThreshold=10' \ + --set 'csi.livenessProbe.initialDelaySeconds=11' \ + --set 'csi.livenessProbe.periodSeconds=12' \ + --set 'csi.livenessProbe.successThreshold=13' \ + --set 'csi.livenessProbe.timeoutSeconds=14' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.failureThreshold' | tee /dev/stderr) + [ "${actual}" = "10" ] + local actual=$(echo $object | + yq -r '.initialDelaySeconds' | tee /dev/stderr) + [ "${actual}" = "11" ] + local actual=$(echo $object | + yq -r '.periodSeconds' | tee /dev/stderr) + [ "${actual}" = "12" ] + local actual=$(echo $object | + yq -r '.successThreshold' | tee /dev/stderr) + [ "${actual}" = "13" ] + local actual=$(echo $object | + yq -r '.timeoutSeconds' | tee /dev/stderr) + [ "${actual}" = "14" ] +} + +@test "csi/daemonset: csi.readinessProbe is configurable" { + cd `chart_dir` + + # Test the defaults + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.failureThreshold' | tee /dev/stderr) + [ "${actual}" = "2" ] + local actual=$(echo $object | + yq -r '.initialDelaySeconds' | tee /dev/stderr) + [ "${actual}" = "5" ] + local actual=$(echo $object | + yq -r '.periodSeconds' | tee /dev/stderr) + [ "${actual}" = "5" ] + local actual=$(echo $object | + yq -r '.successThreshold' | tee /dev/stderr) + [ "${actual}" = "1" ] + local actual=$(echo $object | + yq -r '.timeoutSeconds' | tee /dev/stderr) + [ "${actual}" = "3" ] + + # Test it is configurable + local object=$(helm template \ + --show-only templates/csi-daemonset.yaml \ + --set 'csi.enabled=true' \ + --set 'csi.readinessProbe.failureThreshold=10' \ + --set 'csi.readinessProbe.initialDelaySeconds=11' \ + --set 'csi.readinessProbe.periodSeconds=12' \ + --set 'csi.readinessProbe.successThreshold=13' \ + --set 'csi.readinessProbe.timeoutSeconds=14' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.failureThreshold' | tee /dev/stderr) + [ "${actual}" = "10" ] + local actual=$(echo $object | + yq -r '.initialDelaySeconds' | tee /dev/stderr) + [ "${actual}" = "11" ] + local actual=$(echo $object | + yq -r '.periodSeconds' | tee /dev/stderr) + [ "${actual}" = "12" ] + local actual=$(echo $object | + yq -r '.successThreshold' | tee /dev/stderr) + [ "${actual}" = "13" ] + local actual=$(echo $object | + yq -r '.timeoutSeconds' | tee /dev/stderr) + [ "${actual}" = "14" ] +} diff --git a/test/unit/csi-serviceaccount.bats b/test/unit/csi-serviceaccount.bats new file mode 100644 index 00000000..22ba06df --- /dev/null +++ b/test/unit/csi-serviceaccount.bats @@ -0,0 +1,59 @@ +#!/usr/bin/env bats + +load _helpers + +@test "csi/ServiceAccount: disabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/csi-serviceaccount.yaml \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "csi/ServiceAccount: enable with csi.enabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-serviceaccount.yaml \ + --set 'csi.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +# serviceAccountName reference name +@test "csi/daemonset: serviceAccountName name" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/csi-serviceaccount.yaml \ + --set "csi.enabled=true" \ + . | tee /dev/stderr | + yq -r '.metadata.name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault-csi-provider" ] +} + +@test "csi/serviceAccount: specify annotations" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-serviceaccount.yaml \ + --set 'csi.enabled=true' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(helm template \ + --show-only templates/server-serviceaccount.yaml \ + --set 'csi.enabled=true' \ + --set 'csi.serviceAccount.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(helm template \ + --show-only templates/server-serviceaccount.yaml \ + --set 'csi.enabled=true' \ + --set 'server.serviceAccount.annotations.foo=bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} \ No newline at end of file diff --git a/test/unit/injector-clusterrole.bats b/test/unit/injector-clusterrole.bats new file mode 100755 index 00000000..7c25f39d --- /dev/null +++ b/test/unit/injector-clusterrole.bats @@ -0,0 +1,22 @@ +#!/usr/bin/env bats + +load _helpers + +@test "injector/ClusterRole: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-clusterrole.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/ClusterRole: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-clusterrole.yaml \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} diff --git a/test/unit/injector-clusterrolebinding.bats b/test/unit/injector-clusterrolebinding.bats new file mode 100755 index 00000000..6e217878 --- /dev/null +++ b/test/unit/injector-clusterrolebinding.bats @@ -0,0 +1,22 @@ +#!/usr/bin/env bats + +load _helpers + +@test "injector/ClusterRoleBinding: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-clusterrolebinding.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/ClusterRoleBinding: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-clusterrolebinding.yaml \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} diff --git a/test/unit/injector-deployment.bats b/test/unit/injector-deployment.bats new file mode 100755 index 00000000..3bae2af7 --- /dev/null +++ b/test/unit/injector-deployment.bats @@ -0,0 +1,723 @@ +#!/usr/bin/env bats + +load _helpers + +@test "injector/deployment: default injector.enabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/deployment: enable with injector.enabled true" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/deployment: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'global.enabled=false' \ + --set 'injector.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "injector/deployment: image defaults to injector.image" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.image.repository=foo' \ + --set 'injector.image.tag=1.2.3' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "foo:1.2.3" ] + + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.image.repository=foo' \ + --set 'injector.image.tag=1.2.3' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "foo:1.2.3" ] +} + +@test "injector/deployment: default imagePullPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].imagePullPolicy' | tee /dev/stderr) + [ "${actual}" = "IfNotPresent" ] +} + +@test "injector/deployment: default resources" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "injector/deployment: custom resources" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.enabled=true' \ + --set 'injector.resources.requests.memory=256Mi' \ + --set 'injector.resources.requests.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.enabled=true' \ + --set 'injector.resources.limits.memory=256Mi' \ + --set 'injector.resources.limits.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.enabled=true' \ + --set 'injector.resources.requests.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.requests.cpu' | tee /dev/stderr) + [ "${actual}" = "250m" ] + + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.enabled=true' \ + --set 'injector.resources.limits.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.limits.cpu' | tee /dev/stderr) + [ "${actual}" = "250m" ] +} + +@test "injector/deployment: enable metrics" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.metrics.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.[9].name' | tee /dev/stderr) + [ "${actual}" = "AGENT_INJECT_TELEMETRY_PATH" ] + + local actual=$(echo $object | + yq -r '.[9].value' | tee /dev/stderr) + [ "${actual}" = "/metrics" ] +} + +@test "injector/deployment: manual TLS environment vars" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.certs.secretName=foobar' \ + --set 'injector.certs.certName=test.crt' \ + --set 'injector.certs.keyName=test.key' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_TLS_CERT_FILE")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "/etc/webhook/certs/test.crt" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_TLS_KEY_FILE")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "/etc/webhook/certs/test.key" ] +} + +@test "injector/deployment: auto TLS by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts | length' | tee /dev/stderr) + [ "${actual}" = "0" ] + + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_TLS_AUTO")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "RELEASE-NAME-vault-agent-injector-cfg" ] + + # helm template does uses current context namespace and ignores namespace flags, so + # discover the targeted namespace so we can check the rendered value correctly. + local namespace=$(kubectl config view --minify --output 'jsonpath={..namespace}') + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_TLS_AUTO_HOSTS")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "RELEASE-NAME-vault-agent-injector-svc,RELEASE-NAME-vault-agent-injector-svc.${namespace:-default},RELEASE-NAME-vault-agent-injector-svc.${namespace:-default}.svc" ] +} + +@test "injector/deployment: manual TLS adds volume mount" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.enabled=true' \ + --set 'injector.certs.secretName=vault-tls' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "webhook-certs")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/etc/webhook/certs" ] + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/deployment: with externalVaultAddr" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_VAULT_ADDR")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "http://vault-outside" ] +} + +@test "injector/deployment: without externalVaultAddr" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --release-name not-external-test \ + --namespace default \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_VAULT_ADDR")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "http://not-external-test-vault.default.svc:8200" ] +} + +@test "injector/deployment: default authPath" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_VAULT_AUTH_PATH")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "auth/kubernetes" ] +} + +@test "injector/deployment: custom authPath" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.authPath=auth/k8s' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_VAULT_AUTH_PATH")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "auth/k8s" ] +} + +@test "injector/deployment: default logLevel" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_LOG_LEVEL")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "info" ] +} + +@test "injector/deployment: custom logLevel" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.logLevel=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_LOG_LEVEL")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "foo" ] +} + +@test "injector/deployment: default logFormat" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_LOG_FORMAT")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "standard" ] +} + +@test "injector/deployment: custom logFormat" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.logFormat=json' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_LOG_FORMAT")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "json" ] +} + +@test "injector/deployment: default revoke on shutdown" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_REVOKE_ON_SHUTDOWN")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "false" ] +} + +@test "injector/deployment: custom revoke on shutdown" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.revokeOnShutdown=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_REVOKE_ON_SHUTDOWN")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "true" ] +} + +@test "injector/deployment: disable security context when openshift enabled" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'global.openshift=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_SET_SECURITY_CONTEXT")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "false" ] +} + +#-------------------------------------------------------------------- +# extraEnvironmentVars + +@test "injector/deployment: set extraEnvironmentVars" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.extraEnvironmentVars.FOO=bar' \ + --set 'injector.extraEnvironmentVars.FOOBAR=foobar' \ + --set 'injector.extraEnvironmentVars.lower\.case=sanitized' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="FOO")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "bar" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="FOOBAR")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "foobar" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="LOWER_CASE")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "sanitized" ] +} + +#-------------------------------------------------------------------- +# extra annotations + +@test "injector/deployment: default annotations" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "injector/deployment: specify annotations yaml" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.annotations.foo=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +@test "injector/deployment: specify annotations yaml string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +#-------------------------------------------------------------------- +# agent port + +@test "injector/deployment: default agentPort" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.[0].name' | tee /dev/stderr) + [ "${actual}" = "AGENT_INJECT_LISTEN" ] + + local actual=$(echo $object | + yq -r '.[0].value' | tee /dev/stderr) + [ "${actual}" = ":8080" ] +} + +@test "injector/deployment: custom agentPort" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.port=8443' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.[0].name' | tee /dev/stderr) + [ "${actual}" = "AGENT_INJECT_LISTEN" ] + + local actual=$(echo $object | + yq -r '.[0].value' | tee /dev/stderr) + [ "${actual}" = ":8443" ] +} + +#-------------------------------------------------------------------- +# affinity + +@test "injector/deployment: affinity set by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq '.spec.template.spec | .affinity? == null' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "injector/deployment: affinity can be set as string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.affinity=foobar' \ + . | tee /dev/stderr | + yq '.spec.template.spec.affinity == "foobar"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/deployment: affinity can be set as YAML" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.affinity.podAntiAffinity=foobar' \ + . | tee /dev/stderr | + yq '.spec.template.spec.affinity.podAntiAffinity == "foobar"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# tolerations + +@test "injector/deployment: tolerations not set by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq '.spec.template.spec | .tolerations? == null' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/deployment: tolerations can be set as string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.tolerations=foobar' \ + . | tee /dev/stderr | + yq '.spec.template.spec.tolerations == "foobar"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/deployment: tolerations can be set as YAML" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set "injector.tolerations[0].foo=bar,injector.tolerations[1].baz=qux" \ + . | tee /dev/stderr | + yq '.spec.template.spec.tolerations == [{"foo": "bar"}, {"baz": "qux"}]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# nodeSelector + +@test "injector/deployment: nodeSelector is not set by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq '.spec.template.spec.nodeSelector' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "injector/deployment: nodeSelector can be set as string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.nodeSelector=testing' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.nodeSelector' | tee /dev/stderr) + [ "${actual}" = "testing" ] +} + +@test "injector/deployment: nodeSelector can be set as YAML" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set "injector.nodeSelector.beta\.kubernetes\.io/arch=amd64" \ + . | tee /dev/stderr | + yq '.spec.template.spec.nodeSelector == {"beta.kubernetes.io/arch": "amd64"}' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + + +#-------------------------------------------------------------------- +# priorityClassName + +@test "injector/deployment: priorityClassName not set by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq '.spec.template.spec | .priorityClassName? == null' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/deployment: priorityClassName can be set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.priorityClassName=armaggeddon' \ + . | tee /dev/stderr | + yq '.spec.template.spec | .priorityClassName == "armaggeddon"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} +#-------------------------------------------------------------------- +# OpenShift + +@test "injector/deployment: OpenShift - runAsUser disabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'global.openshift=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.securityContext.runAsUser | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "injector/deployment: OpenShift - runAsGroup disabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'global.openshift=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.securityContext.runAsGroup | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} +#-------------------------------------------------------------------- +# extra labels + +@test "injector/deployment: specify extraLabels" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.extraLabels.foo=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +#-------------------------------------------------------------------- +# hostNetwork + +@test "injector/deployment: injector.hostNetwork not set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.hostNetwork' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "injector/deployment: injector.hostNetwork is set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.hostNetwork=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.hostNetwork' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/deployment: agent default resources" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_CPU_LIMIT")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "500m" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_CPU_REQUEST")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "250m" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_MEM_LIMIT")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "128Mi" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_MEM_REQUEST")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "64Mi" ] +} + +@test "injector/deployment: can set agent default resources" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set 'injector.agentDefaults.cpuLimit=cpuLimit' \ + --set 'injector.agentDefaults.cpuRequest=cpuRequest' \ + --set 'injector.agentDefaults.memLimit=memLimit' \ + --set 'injector.agentDefaults.memRequest=memRequest' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_CPU_LIMIT")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "cpuLimit" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_CPU_REQUEST")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "cpuRequest" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_MEM_LIMIT")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "memLimit" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_MEM_REQUEST")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "memRequest" ] +} + +@test "injector/deployment: agent default template" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_DEFAULT_TEMPLATE")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "map" ] +} + +@test "injector/deployment: can set agent default template" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set='injector.agentDefaults.template=json' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_DEFAULT_TEMPLATE")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "json" ] +} + +@test "injector/deployment: agent default template_config.exit_on_retry_failure" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_TEMPLATE_CONFIG_EXIT_ON_RETRY_FAILURE")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "true" ] +} + +@test "injector/deployment: can set agent template_config.exit_on_retry_failure" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set='injector.agentDefaults.templateConfig.exitOnRetryFailure=false' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_TEMPLATE_CONFIG_EXIT_ON_RETRY_FAILURE")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "false" ] +} + +@test "injector/deployment: agent default template_config.static_secret_render_interval" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_TEMPLATE_STATIC_SECRET_RENDER_INTERVAL")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "" ] +} + +@test "injector/deployment: can set agent template_config.static_secret_render_interval" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set='injector.agentDefaults.templateConfig.staticSecretRenderInterval=1m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="AGENT_INJECT_TEMPLATE_STATIC_SECRET_RENDER_INTERVAL")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "1m" ] +} diff --git a/test/unit/injector-leader-elector.bats b/test/unit/injector-leader-elector.bats new file mode 100644 index 00000000..b6fa4ae6 --- /dev/null +++ b/test/unit/injector-leader-elector.bats @@ -0,0 +1,168 @@ +#!/usr/bin/env bats + +load _helpers + +#-------------------------------------------------------------------- +# Deployment + +@test "injector/deployment: replica count" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set "injector.replicas=2" \ + . | tee /dev/stderr | + yq '.spec.replicas' | tee /dev/stderr) + [ "${actual}" = "2" ] +} + +@test "injector/deployment: leader elector configuration for sidecar-injector" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env[] | select(.name == "AGENT_INJECT_USE_LEADER_ELECTOR") | .value' | tee /dev/stderr) + [ "${actual}" = "" ] + + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set "injector.replicas=2" \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env[] | select(.name == "AGENT_INJECT_USE_LEADER_ELECTOR") | .value' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env[] | select(.name == "NAMESPACE") | .valueFrom.fieldRef.fieldPath' | tee /dev/stderr) + [ "${actual}" = "" ] + + local actual=$(helm template \ + --show-only templates/injector-deployment.yaml \ + --set "injector.replicas=2" \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env[] | select(.name == "NAMESPACE") | .valueFrom.fieldRef.fieldPath' | tee /dev/stderr) + [ "${actual}" = "metadata.namespace" ] +} + +#-------------------------------------------------------------------- +# Resource creation + +@test "injector/certs-secret: created/skipped as appropriate" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-certs-secret.yaml \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-certs-secret.yaml \ + --set "injector.replicas=2" \ + --set "global.enabled=false" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-certs-secret.yaml \ + --set "injector.replicas=2" \ + --set "injector.enabled=false" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-certs-secret.yaml \ + --set "injector.replicas=2" \ + --set "injector.leaderElector.enabled=false" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-certs-secret.yaml \ + --set "injector.replicas=2" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/role: created/skipped as appropriate" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-role.yaml \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-role.yaml \ + --set "injector.replicas=2" \ + --set "global.enabled=false" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-role.yaml \ + --set "injector.replicas=2" \ + --set "injector.enabled=false" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-role.yaml \ + --set "injector.replicas=2" \ + --set "injector.leaderElector.enabled=false" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-role.yaml \ + --set "injector.replicas=2" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/rolebinding: created/skipped as appropriate" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-rolebinding.yaml \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-rolebinding.yaml \ + --set "injector.replicas=2" \ + --set "global.enabled=false" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-rolebinding.yaml \ + --set "injector.replicas=2" \ + --set "injector.enabled=false" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-rolebinding.yaml \ + --set "injector.replicas=2" \ + --set "injector.leaderElector.enabled=false" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-rolebinding.yaml \ + --set "injector.replicas=2" \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/test/unit/injector-mutating-webhook.bats b/test/unit/injector-mutating-webhook.bats new file mode 100755 index 00000000..1e6e150d --- /dev/null +++ b/test/unit/injector-mutating-webhook.bats @@ -0,0 +1,155 @@ +#!/usr/bin/env bats + +load _helpers + +@test "injector/MutatingWebhookConfiguration: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/MutatingWebhookConfiguration: disable with global.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "injector/MutatingWebhookConfiguration: disable with injector.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + --set 'injector.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "injector/MutatingWebhookConfiguration: namespace is set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + --set 'injector.enabled=true' \ + --namespace foo \ + . | tee /dev/stderr | + yq '.webhooks[0].clientConfig.service.namespace' | tee /dev/stderr) + [ "${actual}" = "\"foo\"" ] +} + +@test "injector/MutatingWebhookConfiguration: caBundle is empty string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + --set 'injector.enabled=true' \ + --namespace foo \ + . | tee /dev/stderr | + yq '.webhooks[0].clientConfig.caBundle' | tee /dev/stderr) + [ "${actual}" = "\"\"" ] +} + +@test "injector/MutatingWebhookConfiguration: namespaceSelector empty by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + --set 'injector.enabled=true' \ + --namespace foo \ + . | tee /dev/stderr | + yq '.webhooks[0].namespaceSelector' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "injector/MutatingWebhookConfiguration: can set namespaceSelector" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + --set 'injector.enabled=true' \ + --set 'injector.namespaceSelector.matchLabels.injector=true' \ + . | tee /dev/stderr | + yq '.webhooks[0].namespaceSelector.matchLabels.injector' | tee /dev/stderr) + + [ "${actual}" = "true" ] +} + +@test "injector/MutatingWebhookConfiguration: objectSelector empty by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + --set 'injector.enabled=true' \ + --namespace foo \ + . | tee /dev/stderr | + yq '.webhooks[0].objectSelector' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "injector/MutatingWebhookConfiguration: can set objectSelector" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + --set 'injector.enabled=true' \ + --set 'injector.objectSelector.matchLabels.injector=true' \ + . | tee /dev/stderr | + yq '.webhooks[0].objectSelector.matchLabels.injector' | tee /dev/stderr) + + [ "${actual}" = "true" ] +} + +@test "injector/MutatingWebhookConfiguration: failurePolicy 'Ignore' by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + --set 'injector.enabled=true' \ + --namespace foo \ + . | tee /dev/stderr | + yq '.webhooks[0].failurePolicy' | tee /dev/stderr) + [ "${actual}" = "\"Ignore\"" ] +} + +@test "injector/MutatingWebhookConfiguration: can set failurePolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + --set 'injector.enabled=true' \ + --set 'injector.failurePolicy=Fail' \ + . | tee /dev/stderr | + yq '.webhooks[0].failurePolicy' | tee /dev/stderr) + + [ "${actual}" = "\"Fail\"" ] +} + +#-------------------------------------------------------------------- +# annotations + +@test "injector/MutatingWebhookConfiguration: default annotations" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + . | tee /dev/stderr | + yq -r '.metadata.annotations' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "injector/MutatingWebhookConfiguration: specify annotations yaml" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + --set 'injector.webhookAnnotations.foo=bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +@test "injector/MutatingWebhookConfiguration: specify annotations yaml string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-mutating-webhook.yaml \ + --set 'injector.webhookAnnotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/test/unit/injector-psp-role.bats b/test/unit/injector-psp-role.bats new file mode 100644 index 00000000..c6dc522a --- /dev/null +++ b/test/unit/injector-psp-role.bats @@ -0,0 +1,35 @@ +#!/usr/bin/env bats + +load _helpers + +@test "injector/PodSecurityPolicy-Role: PodSecurityPolicy-Role not enabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-psp-role.yaml \ + . || echo "---" ) | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "injector/PodSecurityPolicy-Role: enable with injector.enabled and global.psp.enable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-psp-role.yaml \ + --set 'injector.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/PodSecurityPolicy-Role: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-psp-role.yaml \ + --set 'global.enabled=false' \ + --set 'injector.enabled=true' \ + --set 'global.psp.enable=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} diff --git a/test/unit/injector-psp-rolebinding.bats b/test/unit/injector-psp-rolebinding.bats new file mode 100644 index 00000000..f8a8255f --- /dev/null +++ b/test/unit/injector-psp-rolebinding.bats @@ -0,0 +1,35 @@ +#!/usr/bin/env bats + +load _helpers + +@test "injector/PodSecurityPolicy-RoleBinding: PodSecurityPolicy-RoleBinding not enabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-psp-rolebinding.yaml \ + . || echo "---" ) | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "injector/PodSecurityPolicy-RoleBinding: enable with injector.enabled and global.psp.enable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-psp-rolebinding.yaml \ + --set 'injector.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/PodSecurityPolicy-RoleBinding: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-psp-rolebinding.yaml \ + --set 'global.enabled=false' \ + --set 'injector.enabled=true' \ + --set 'global.psp.enable=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} diff --git a/test/unit/injector-psp.bats b/test/unit/injector-psp.bats new file mode 100644 index 00000000..fa14b0f1 --- /dev/null +++ b/test/unit/injector-psp.bats @@ -0,0 +1,70 @@ +#!/usr/bin/env bats + +load _helpers + +@test "injector/PodSecurityPolicy: PodSecurityPolicy not enabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-psp.yaml \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "injector/PodSecurityPolicy: enable with injector.enabled and global.psp.enable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-psp.yaml \ + --set 'injector.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/PodSecurityPolicy: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-psp.yaml \ + --set 'global.enabled=false' \ + --set 'injector.enabled=true' \ + --set 'global.psp.enable=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "injector/PodSecurityPolicy: annotations are templated correctly by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-psp.yaml \ + --set 'injector.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq '.metadata.annotations | length == 4' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/PodSecurityPolicy: annotations are added - string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-psp.yaml \ + --set 'injector.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'global.psp.annotations=vault-is: amazing' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) + [ "${actual}" = "amazing" ] +} + +@test "injector/PodSecurityPolicy: annotations are added - object" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-psp.yaml \ + --set 'injector.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'global.psp.annotations.vault-is=amazing' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) + [ "${actual}" = "amazing" ] +} diff --git a/test/unit/injector-service.bats b/test/unit/injector-service.bats new file mode 100755 index 00000000..ad480099 --- /dev/null +++ b/test/unit/injector-service.bats @@ -0,0 +1,66 @@ +#!/usr/bin/env bats + +load _helpers + +@test "injector/Service: service enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-service.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/injector-service.yaml \ + --set 'injector.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/Service: service with default port" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-service.yaml \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) + [ "${actual}" = "8080" ] +} + +@test "injector/Service: service with custom port" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-service.yaml \ + --set 'injector.port=8443' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) + [ "${actual}" = "8443" ] +} + +@test "injector/Service: disable with global.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-service.yaml \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/injector-service.yaml \ + --set 'global.enabled=false' \ + --set 'injector.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "injector/Service: generic annotations" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-service.yaml \ + --set 'injector.service.annotations=vaultIsAwesome: true' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/test/unit/injector-serviceaccount.bats b/test/unit/injector-serviceaccount.bats new file mode 100755 index 00000000..1055d906 --- /dev/null +++ b/test/unit/injector-serviceaccount.bats @@ -0,0 +1,22 @@ +#!/usr/bin/env bats + +load _helpers + +@test "injector/ServiceAccount: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/injector-serviceaccount.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "injector/ServiceAccount: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/injector-serviceaccount.yaml \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} diff --git a/test/unit/schema.bats b/test/unit/schema.bats new file mode 100644 index 00000000..a42614bf --- /dev/null +++ b/test/unit/schema.bats @@ -0,0 +1,46 @@ +#!/usr/bin/env bats + +load _helpers + +# These tests are just to verify there is a schema file used in the chart. Since +# .enabled is defined as a boolean type for each of the top-level blocks in the +# schema, setting it as a string fails 'helm template'. +@test "schema: csi enabled datatype" { + cd `chart_dir` + run helm template . --set csi.enabled="nope" + [ "$status" -eq 1 ] + [ "${lines[2]}" = "- csi.enabled: Invalid type. Expected: boolean, given: string" ] + + run helm template . --set csi.enabled=true + [ "$status" -eq 0 ] +} + +@test "schema: injector enabled datatype" { + cd `chart_dir` + run helm template . --set injector.enabled="nope" + [ "$status" -eq 1 ] + [ "${lines[2]}" = "- injector.enabled: Invalid type. Expected: boolean, given: string" ] + + run helm template . --set injector.enabled=true + [ "$status" -eq 0 ] +} + +@test "schema: server enabled datatype" { + cd `chart_dir` + run helm template . --set server.enabled="nope" + [ "$status" -eq 1 ] + [ "${lines[2]}" = "- server.enabled: Invalid type. Expected: boolean, given: string" ] + + run helm template . --set server.enabled=true + [ "$status" -eq 0 ] +} + +@test "schema: ui enabled datatype" { + cd `chart_dir` + run helm template . --set ui.enabled="nope" + [ "$status" -eq 1 ] + [ "${lines[2]}" = "- ui.enabled: Invalid type. Expected: boolean, given: string" ] + + run helm template . --set ui.enabled=true + [ "$status" -eq 0 ] +} diff --git a/test/unit/server-clusterrolebinding.bats b/test/unit/server-clusterrolebinding.bats new file mode 100755 index 00000000..bf0a4d85 --- /dev/null +++ b/test/unit/server-clusterrolebinding.bats @@ -0,0 +1,72 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/ClusterRoleBinding: enabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-clusterrolebinding.yaml \ + --set 'server.dev.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$( (helm template \ + --show-only templates/server-clusterrolebinding.yaml \ + --set 'server.ha.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$( (helm template \ + --show-only templates/server-clusterrolebinding.yaml \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/ClusterRoleBinding: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-clusterrolebinding.yaml \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ClusterRoleBinding: can disable with server.authDelegator" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-clusterrolebinding.yaml \ + --set 'server.authDelegator.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-clusterrolebinding.yaml \ + --set 'server.authDelegator.enabled=false' \ + --set 'server.ha.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-clusterrolebinding.yaml \ + --set 'server.authDelegator.enabled=false' \ + --set 'server.dev.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ClusterRoleBinding: also deploy with injector.externalVaultAddr" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-clusterrolebinding.yaml \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/test/unit/server-configmap.bats b/test/unit/server-configmap.bats new file mode 100755 index 00000000..fe2ac125 --- /dev/null +++ b/test/unit/server-configmap.bats @@ -0,0 +1,124 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/ConfigMap: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-config-configmap.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-config-configmap.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-config-configmap.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.ha.raft.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-config-configmap.yaml \ + --set 'server.standalone.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/ConfigMap: raft config disabled by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-config-configmap.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + grep "raft" | yq 'length > 0' | tee /dev/stderr) + [ "${actual}" != "true" ] +} + +@test "server/ConfigMap: raft config can be enabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-config-configmap.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.ha.raft.enabled=true' \ + . | tee /dev/stderr | + grep "raft" | yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + + +@test "server/ConfigMap: disabled by server.dev.enabled true" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-config-configmap.yaml \ + --set 'server.dev.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-config-configmap.yaml \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: standalone extraConfig is set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-config-configmap.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.standalone.config="{\"hello\": \"world\"}"' \ + . | tee /dev/stderr | + yq '.data["extraconfig-from-values.hcl"] | match("world") | length' | tee /dev/stderr) + [ ! -z "${actual}" ] + + local actual=$(helm template \ + --show-only templates/server-config-configmap.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.standalone.config="{\"foo\": \"bar\"}"' \ + . | tee /dev/stderr | + yq '.data["extraconfig-from-values.hcl"] | match("bar") | length' | tee /dev/stderr) + [ ! -z "${actual}" ] +} + +@test "server/ConfigMap: ha extraConfig is set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-config-configmap.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.ha.config="{\"hello\": \"world\"}"' \ + . | tee /dev/stderr | + yq '.data["extraconfig-from-values.hcl"] | match("world") | length' | tee /dev/stderr) + [ ! -z "${actual}" ] + + local actual=$(helm template \ + --show-only templates/server-config-configmap.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.ha.config="{\"foo\": \"bar\"}"' \ + . | tee /dev/stderr | + yq '.data["extraconfig-from-values.hcl"] | match("bar") | length' | tee /dev/stderr) + [ ! -z "${actual}" ] +} + +@test "server/ConfigMap: disabled by injector.externalVaultAddr" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-config-configmap.yaml \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} diff --git a/test/unit/server-dev-statefulset.bats b/test/unit/server-dev-statefulset.bats new file mode 100755 index 00000000..3c5f9d8f --- /dev/null +++ b/test/unit/server-dev-statefulset.bats @@ -0,0 +1,461 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/dev-StatefulSet: enable with server.dev.enabled true" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/dev-StatefulSet: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.enabled=false' \ + --set 'server.dev.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/dev-StatefulSet: disable with injector.externalVaultAddr" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + --set 'server.dev.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/dev-StatefulSet: image defaults to server.image.repository:tag" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.image.repository=foo' \ + --set 'server.image.tag=1.2.3' \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "foo:1.2.3" ] +} + +@test "server/ha-StatefulSet: image tag defaults to latest" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.image.repository=foo' \ + --set 'server.image.tag=' \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "foo:latest" ] +} + +#-------------------------------------------------------------------- +# replicas + +@test "server/dev-StatefulSet: default replicas" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.replicas' | tee /dev/stderr) + [ "${actual}" = "1" ] +} + +@test "server/dev-StatefulSet: cant set replicas" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.dev.replicas=100' \ + . | tee /dev/stderr | + yq -r '.spec.replicas' | tee /dev/stderr) + [ "${actual}" = "1" ] +} + +#-------------------------------------------------------------------- +# updateStrategy + +@test "server/dev-StatefulSet: updateStrategy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.updateStrategy.type' | tee /dev/stderr) + [ "${actual}" = "OnDelete" ] +} + +#-------------------------------------------------------------------- +# resources + +@test "server/dev-StatefulSet: default resources" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/dev-StatefulSet: custom resources" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.resources.requests.memory=256Mi' \ + --set 'server.resources.requests.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.resources.limits.memory=256Mi' \ + --set 'server.resources.limits.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.resources.requests.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.requests.cpu' | tee /dev/stderr) + [ "${actual}" = "250m" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.resources.limits.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.limits.cpu' | tee /dev/stderr) + [ "${actual}" = "250m" ] +} + +#-------------------------------------------------------------------- +# extraVolumes + +@test "server/dev-StatefulSet: adds extra volume" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.configMap.name' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + local actual=$(echo $object | + yq -r '.configMap.secretName' | tee /dev/stderr) + [ "${actual}" = "null" ] + + # Test that it mounts it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/vault/userconfig/foo" ] +} + +@test "server/dev-StatefulSet: adds extra secret volume" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.extraVolumes[0].type=secret' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.secret.name' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(echo $object | + yq -r '.secret.secretName' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + # Test that it mounts it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/vault/userconfig/foo" ] +} + +@test "server/dev-StatefulSet: no storageClass on claim by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates[0].spec.storageClassName' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +#-------------------------------------------------------------------- +# devRootToken + +@test "server/dev-StatefulSet: set default devRootToken" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local name=$(echo $object | + yq -r 'map(select(.name=="VAULT_DEV_ROOT_TOKEN_ID")) | .[] .value' | tee /dev/stderr) + [ "${name}" = "root" ] +} + +@test "server/dev-StatefulSet: set custom devRootToken" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.dev.devRootToken=customtoken' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local name=$(echo $object | + yq -r 'map(select(.name=="VAULT_DEV_ROOT_TOKEN_ID")) | .[] .value' | tee /dev/stderr) + [ "${name}" = "customtoken" ] +} + +#-------------------------------------------------------------------- +# dev listen address + +@test "server/dev-StatefulSet: set dev listen address in dev mode" { + cd `chart_dir` + local objects=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $objects | + yq -r 'map(select(.name=="VAULT_DEV_LISTEN_ADDRESS")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "[::]:8200" ] +} + +@test "server/dev-StatefulSet: dev listen address isn't set in non-dev mode" { + cd `chart_dir` + local objects=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=false' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local name=$(echo $objects | + yq -r 'map(select(.name=="VAULT_DEV_LISTEN_ADDRESS")) | .[] .name' | tee /dev/stderr) + [ "${name}" = "" ] +} + +#-------------------------------------------------------------------- +# extraEnvironmentVars + +@test "server/dev-StatefulSet: set extraEnvironmentVars" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.extraEnvironmentVars.FOO=bar' \ + --set 'server.extraEnvironmentVars.FOOBAR=foobar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="FOO")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "bar" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="FOOBAR")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "foobar" ] +} + +#-------------------------------------------------------------------- +# extraSecretEnvironmentVars + +@test "server/dev-StatefulSet: set extraSecretEnvironmentVars" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.extraSecretEnvironmentVars[0].envName=ENV_FOO_0' \ + --set 'server.extraSecretEnvironmentVars[0].secretName=secret_name_0' \ + --set 'server.extraSecretEnvironmentVars[0].secretKey=secret_key_0' \ + --set 'server.extraSecretEnvironmentVars[1].envName=ENV_FOO_1' \ + --set 'server.extraSecretEnvironmentVars[1].secretName=secret_name_1' \ + --set 'server.extraSecretEnvironmentVars[1].secretKey=secret_key_1' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="ENV_FOO_0")) | .[] .valueFrom.secretKeyRef.name' | tee /dev/stderr) + [ "${value}" = "secret_name_0" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="ENV_FOO_0")) | .[] .valueFrom.secretKeyRef.key' | tee /dev/stderr) + [ "${value}" = "secret_key_0" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="ENV_FOO_1")) | .[] .valueFrom.secretKeyRef.name' | tee /dev/stderr) + [ "${value}" = "secret_name_1" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="ENV_FOO_1")) | .[] .valueFrom.secretKeyRef.key' | tee /dev/stderr) + [ "${value}" = "secret_key_1" ] +} + +#-------------------------------------------------------------------- +# storage class + +@test "server/dev-StatefulSet: can't set storageClass" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.dataStorage.enabled=true' \ + --set 'server.dataStorage.storageClass=foo' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.auditStorage.storageClass=foo' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.auditStorage.storageClass=foo' \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.auditStorage.storageClass=foo' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +#-------------------------------------------------------------------- +# Security Contexts +@test "server/dev-StatefulSet: uid default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +@test "server/dev-StatefulSet: uid configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.uid=2000' \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + [ "${actual}" = "2000" ] +} + +@test "server/dev-StatefulSet: gid default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsGroup' | tee /dev/stderr) + [ "${actual}" = "1000" ] +} + +@test "server/dev-StatefulSet: gid configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.gid=2000' \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsGroup' | tee /dev/stderr) + [ "${actual}" = "2000" ] +} + +@test "server/dev-StatefulSet: fsgroup default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.fsGroup' | tee /dev/stderr) + [ "${actual}" = "1000" ] +} + +@test "server/dev-StatefulSet: fsgroup configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.gid=2000' \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.fsGroup' | tee /dev/stderr) + [ "${actual}" = "2000" ] +} + +@test "server/dev-StatefulSet: add extraArgs" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.extraArgs=foobar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args[0]' | tee /dev/stderr) + [[ "${actual}" = *"foobar"* ]] +} diff --git a/test/unit/server-ha-active-service.bats b/test/unit/server-ha-active-service.bats new file mode 100755 index 00000000..a835c9d9 --- /dev/null +++ b/test/unit/server-ha-active-service.bats @@ -0,0 +1,199 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/ha-active-Service: generic annotations" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.annotations=vaultIsAwesome: true' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/ha-active-Service: disable with ha.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=false' \ + --set 'server.service.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ha-active-Service: disable with server.service.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ha-active-Service: type empty by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/ha-active-Service: type can set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=NodePort' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "NodePort" ] +} + +@test "server/ha-active-Service: clusterIP empty by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.clusterIP' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/ha-active-Service: clusterIP can set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.clusterIP=None' \ + . | tee /dev/stderr | + yq -r '.spec.clusterIP' | tee /dev/stderr) + [ "${actual}" = "None" ] +} + +@test "server/ha-active-Service: port and targetPort will be 8200 by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].port' | tee /dev/stderr) + [ "${actual}" = "8200" ] + + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) + [ "${actual}" = "8200" ] +} + +@test "server/ha-active-Service: port and targetPort can be set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.port=8000' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].port' | tee /dev/stderr) + [ "${actual}" = "8000" ] + + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.targetPort=80' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) + [ "${actual}" = "80" ] +} + +@test "server/ha-active-Service: nodeport can set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=NodePort' \ + --set 'server.service.nodePort=30009' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) + [ "${actual}" = "30009" ] +} + +@test "server/ha-active-Service: nodeport can't set when type isn't NodePort" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.nodePort=30009' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/ha-active-Service: vault port name is http, when tlsDisable is true" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.tlsDisable=true' \ + . | tee /dev/stderr | + yq -r '.spec.ports | map(select(.port==8200)) | .[] .name' | tee /dev/stderr) + [ "${actual}" = "http" ] +} + +@test "server/ha-active-Service: vault port name is https, when tlsDisable is false" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.tlsDisable=false' \ + . | tee /dev/stderr | + yq -r '.spec.ports | map(select(.port==8200)) | .[] .name' | tee /dev/stderr) + [ "${actual}" = "https" ] +} + +# duplicated in server-service.bats +@test "server/ha-active-Service: NodePort assert externalTrafficPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=NodePort' \ + --set 'server.service.externalTrafficPolicy=Foo' \ + . | tee /dev/stderr | + yq -r '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "Foo" ] +} + +# duplicated in server-service.bats +@test "server/ha-active-Service: NodePort assert no externalTrafficPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=NodePort' \ + --set 'server.service.externalTrafficPolicy=' \ + . | tee /dev/stderr | + yq '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +# duplicated in server-service.bats +@test "server/ha-active-Service: ClusterIP assert no externalTrafficPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-active-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=ClusterIP' \ + --set 'server.service.externalTrafficPolicy=Foo' \ + . | tee /dev/stderr | + yq '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + diff --git a/test/unit/server-ha-disruptionbudget.bats b/test/unit/server-ha-disruptionbudget.bats new file mode 100755 index 00000000..6cf21f20 --- /dev/null +++ b/test/unit/server-ha-disruptionbudget.bats @@ -0,0 +1,99 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/DisruptionBudget: enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-disruptionbudget.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/DisruptionBudget: disable with server.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-disruptionbudget.yaml \ + --set 'globa.enabled=false' \ + --set 'server.ha.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/DisruptionBudget: disable with server.disruptionBudget.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-disruptionbudget.yaml \ + --set 'server.ha.disruptionBudget.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/DisruptionBudget: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-disruptionbudget.yaml \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/DisruptionBudget: disable with injector.exernalVaultAddr" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-disruptionbudget.yaml \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/DisruptionBudget: correct maxUnavailable with n=1" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-disruptionbudget.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.ha.replicas=1' \ + . | tee /dev/stderr | + yq '.spec.maxUnavailable' | tee /dev/stderr) + [ "${actual}" = "0" ] +} + +@test "server/DisruptionBudget: correct maxUnavailable with n=3" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-disruptionbudget.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.ha.replicas=3' \ + . | tee /dev/stderr | + yq '.spec.maxUnavailable' | tee /dev/stderr) + [ "${actual}" = "1" ] +} + +@test "server/DisruptionBudget: correct maxUnavailable with n=5" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-disruptionbudget.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.ha.replicas=5' \ + . | tee /dev/stderr | + yq '.spec.maxUnavailable' | tee /dev/stderr) + [ "${actual}" = "2" ] +} + +@test "server/DisruptionBudget: correct maxUnavailable with custom value" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-disruptionbudget.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.ha.replicas=3' \ + --set 'server.ha.disruptionBudget.maxUnavailable=2' \ + . | tee /dev/stderr | + yq '.spec.maxUnavailable' | tee /dev/stderr) + [ "${actual}" = "2" ] +} \ No newline at end of file diff --git a/test/unit/server-ha-standby-service.bats b/test/unit/server-ha-standby-service.bats new file mode 100755 index 00000000..7dfd5d7f --- /dev/null +++ b/test/unit/server-ha-standby-service.bats @@ -0,0 +1,210 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/ha-standby-Service: generic annotations string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.annotations=vaultIsAwesome: true' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/ha-standby-Service: generic annotations yaml" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.annotations.vaultIsAwesome=true' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/ha-standby-Service: disable with ha.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=false' \ + --set 'server.service.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ha-standby-Service: disable with server.service.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ha-standby-Service: type empty by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/ha-standby-Service: type can set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=NodePort' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "NodePort" ] +} + +@test "server/ha-standby-Service: clusterIP empty by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.clusterIP' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/ha-standby-Service: clusterIP can set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.clusterIP=None' \ + . | tee /dev/stderr | + yq -r '.spec.clusterIP' | tee /dev/stderr) + [ "${actual}" = "None" ] +} + +@test "server/ha-standby-Service: port and targetPort will be 8200 by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].port' | tee /dev/stderr) + [ "${actual}" = "8200" ] + + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) + [ "${actual}" = "8200" ] +} + +@test "server/ha-standby-Service: port and targetPort can be set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.port=8000' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].port' | tee /dev/stderr) + [ "${actual}" = "8000" ] + + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.targetPort=80' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) + [ "${actual}" = "80" ] +} + +@test "server/ha-standby-Service: nodeport can set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=NodePort' \ + --set 'server.service.nodePort=30009' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) + [ "${actual}" = "30009" ] +} + +@test "server/ha-standby-Service: nodeport can't set when type isn't NodePort" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.nodePort=30009' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/ha-standby-Service: vault port name is http, when tlsDisable is true" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.tlsDisable=true' \ + . | tee /dev/stderr | + yq -r '.spec.ports | map(select(.port==8200)) | .[] .name' | tee /dev/stderr) + [ "${actual}" = "http" ] +} + +@test "server/ha-standby-Service: vault port name is https, when tlsDisable is false" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.tlsDisable=false' \ + . | tee /dev/stderr | + yq -r '.spec.ports | map(select(.port==8200)) | .[] .name' | tee /dev/stderr) + [ "${actual}" = "https" ] +} + +# duplicated in server-service.bats +@test "server/ha-standby-Service: NodePort assert externalTrafficPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=NodePort' \ + --set 'server.service.externalTrafficPolicy=Foo' \ + . | tee /dev/stderr | + yq -r '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "Foo" ] +} + +# duplicated in server-service.bats +@test "server/ha-standby-Service: NodePort assert no externalTrafficPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=NodePort' \ + --set 'server.service.externalTrafficPolicy=' \ + . | tee /dev/stderr | + yq '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +# duplicated in server-service.bats +@test "server/ha-standby-Service: ClusterIP assert no externalTrafficPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ha-standby-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=ClusterIP' \ + --set 'server.service.externalTrafficPolicy=Foo' \ + . | tee /dev/stderr | + yq '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + diff --git a/test/unit/server-ha-statefulset.bats b/test/unit/server-ha-statefulset.bats new file mode 100755 index 00000000..cc77e7e3 --- /dev/null +++ b/test/unit/server-ha-statefulset.bats @@ -0,0 +1,682 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/ha-StatefulSet: enable with server.ha.enabled true" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/ha-StatefulSet: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.enabled=false' \ + --set 'server.ha.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ha-StatefulSet: disable with injector.externalVaultAddr" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + --set 'server.ha.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ha-StatefulSet: image defaults to server.image.repository:tag" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.image.repository=foo' \ + --set 'server.image.tag=1.2.3' \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "foo:1.2.3" ] +} + +@test "server/ha-StatefulSet: image tag defaults to latest" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.image.repository=foo' \ + --set 'server.image.tag=' \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "foo:latest" ] +} + +#-------------------------------------------------------------------- +# TLS + +@test "server/ha-StatefulSet: tls disabled" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.tlsDisable=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="VAULT_ADDR")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "http://127.0.0.1:8200" ] +} + +@test "server/ha-StatefulSet: tls enabled" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.tlsDisable=false' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="VAULT_ADDR")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "https://127.0.0.1:8200" ] +} + +#-------------------------------------------------------------------- +# updateStrategy + +@test "server/ha-StatefulSet: OnDelete updateStrategy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.updateStrategy.type' | tee /dev/stderr) + [ "${actual}" = "OnDelete" ] +} + +@test "server/ha-StatefulSet: RollingUpdate updateStrategy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.updateStrategyType="RollingUpdate"' \ + . | tee /dev/stderr | + yq -r '.spec.updateStrategy.type' | tee /dev/stderr) + [ "${actual}" = "RollingUpdate" ] +} + +#-------------------------------------------------------------------- +# affinity + +@test "server/ha-StatefulSet: default affinity" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.affinity' | tee /dev/stderr) + [ "${actual}" != "null" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.affinity=' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.affinity' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +#-------------------------------------------------------------------- +# replicas + +@test "server/ha-StatefulSet: default replicas" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.replicas' | tee /dev/stderr) + [ "${actual}" = "3" ] +} + +@test "server/ha-StatefulSet: custom replicas" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.ha.replicas=10' \ + . | tee /dev/stderr | + yq -r '.spec.replicas' | tee /dev/stderr) + [ "${actual}" = "10" ] +} + +#-------------------------------------------------------------------- +# resources + +@test "server/ha-StatefulSet: default resources" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/ha-StatefulSet: custom resources" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.resources.requests.memory=256Mi' \ + --set 'server.resources.requests.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.resources.limits.memory=256Mi' \ + --set 'server.resources.limits.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.resources.requests.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.requests.cpu' | tee /dev/stderr) + [ "${actual}" = "250m" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.resources.limits.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.limits.cpu' | tee /dev/stderr) + [ "${actual}" = "250m" ] +} + +#-------------------------------------------------------------------- +# extraVolumes + +@test "server/ha-StatefulSet: adds extra volume" { + cd `chart_dir` + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.configMap.name' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + local actual=$(echo $object | + yq -r '.configMap.secretName' | tee /dev/stderr) + [ "${actual}" = "null" ] + + # Test that it mounts it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/vault/userconfig/foo" ] +} + +@test "server/ha-StatefulSet: adds extra volume custom mount path" { + cd `chart_dir` + # Test that it mounts it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + --set 'server.extraVolumes[0].path=/custom/path' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/custom/path/foo" ] +} + +@test "server/ha-StatefulSet: adds extra secret volume custom mount path" { + cd `chart_dir` + + # Test that it mounts it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + --set 'server.extraVolumes[0].path=/custom/path' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/custom/path/foo" ] +} + +@test "server/ha-StatefulSet: adds extra secret volume" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.extraVolumes[0].type=secret' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.secret.name' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(echo $object | + yq -r '.secret.secretName' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + # Test that it mounts it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/vault/userconfig/foo" ] +} + +#-------------------------------------------------------------------- +# extraEnvironmentVars + +@test "server/ha-StatefulSet: set extraEnvironmentVars" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.extraEnvironmentVars.FOO=bar' \ + --set 'server.extraEnvironmentVars.FOOBAR=foobar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="FOO")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "bar" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="FOOBAR")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "foobar" ] +} + +#-------------------------------------------------------------------- +# extraSecretEnvironmentVars + +@test "server/ha-StatefulSet: set extraSecretEnvironmentVars" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.extraSecretEnvironmentVars[0].envName=ENV_FOO_0' \ + --set 'server.extraSecretEnvironmentVars[0].secretName=secret_name_0' \ + --set 'server.extraSecretEnvironmentVars[0].secretKey=secret_key_0' \ + --set 'server.extraSecretEnvironmentVars[1].envName=ENV_FOO_1' \ + --set 'server.extraSecretEnvironmentVars[1].secretName=secret_name_1' \ + --set 'server.extraSecretEnvironmentVars[1].secretKey=secret_key_1' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="ENV_FOO_0")) | .[] .valueFrom.secretKeyRef.name' | tee /dev/stderr) + [ "${value}" = "secret_name_0" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="ENV_FOO_0")) | .[] .valueFrom.secretKeyRef.key' | tee /dev/stderr) + [ "${value}" = "secret_key_0" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="ENV_FOO_1")) | .[] .valueFrom.secretKeyRef.name' | tee /dev/stderr) + [ "${value}" = "secret_name_1" ] + + local value=$(echo $object | + yq -r 'map(select(.name=="ENV_FOO_1")) | .[] .valueFrom.secretKeyRef.key' | tee /dev/stderr) + [ "${value}" = "secret_key_1" ] +} + +#-------------------------------------------------------------------- +# VAULT_API_ADDR renders + +@test "server/ha-StatefulSet: api addr renders to Pod IP by default" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="VAULT_API_ADDR")) | .[] .value' | tee /dev/stderr) + [ "${value}" = 'http://$(POD_IP):8200' ] +} + +@test "server/ha-StatefulSet: api addr is configurable" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.ha.apiAddr="https://example.com:8200"' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="VAULT_API_ADDR")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "https://example.com:8200" ] +} + +#-------------------------------------------------------------------- +# VAULT_CLUSTER_ADDR renders + +@test "server/ha-StatefulSet: cluster addr renders" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.ha.raft.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="VAULT_CLUSTER_ADDR")) | .[] .value' | tee /dev/stderr) + [ "${value}" = 'https://$(HOSTNAME).RELEASE-NAME-vault-internal:8201' ] +} + +#-------------------------------------------------------------------- +# VAULT_RAFT_NODE_ID renders + +@test "server/ha-StatefulSet: raft node ID renders" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.ha.raft.enabled=true' \ + --set 'server.ha.raft.setNodeId=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $object | + yq -r 'map(select(.name=="VAULT_RAFT_NODE_ID")) | .[] .valueFrom.fieldRef.fieldPath' | tee /dev/stderr) + [ "${value}" = "metadata.name" ] +} + +#-------------------------------------------------------------------- +# storage class + +@test "server/ha-StatefulSet: no storage by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "0" ] +} + + +@test "server/ha-StatefulSet: cant set data storage" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.dataStorage.enabled=true' \ + --set 'server.dataStorage.storageClass=foo' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/ha-StatefulSet: can set storageClass" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.dataStorage.enabled=false' \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.auditStorage.storageClass=foo' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates[0].spec.storageClassName' | tee /dev/stderr) + [ "${actual}" = "foo" ] +} + +@test "server/ha-StatefulSet: can disable storage" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.auditStorage.enabled=false' \ + --set 'server.dataStorage.enabled=false' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "0" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.dataStorage.enabled=false' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "1" ] +} + +@test "server/ha-StatefulSet: can mount audit" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.auditStorage.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "audit")' | tee /dev/stderr) +} + +@test "server/ha-StatefulSet: no data storage" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.auditStorage.enabled=false' \ + --set 'server.dataStorage.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "0" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.dataStorage.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "1" ] +} + +@test "server/ha-StatefulSet: tolerations not set by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec | .tolerations? == null' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/ha-StatefulSet: tolerations can be set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.tolerations=foobar' \ + . | tee /dev/stderr | + yq '.spec.template.spec.tolerations == "foobar"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/ha-StatefulSet: nodeSelector is not set by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.nodeSelector' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/ha-StatefulSet: specified nodeSelector as string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.nodeSelector=testing' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.nodeSelector' | tee /dev/stderr) + [ "${actual}" = "testing" ] +} + +@test "server/ha-StatefulSet: nodeSelector can be set as YAML" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + --set "server.nodeSelector.beta\.kubernetes\.io/arch=amd64" \ + . | tee /dev/stderr | + yq '.spec.template.spec.nodeSelector == {"beta.kubernetes.io/arch": "amd64"}' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# Security Contexts +@test "server/ha-StatefulSet: uid default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +@test "server/ha-StatefulSet: uid configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.uid=2000' \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + [ "${actual}" = "2000" ] +} + +@test "server/ha-StatefulSet: gid default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsGroup' | tee /dev/stderr) + [ "${actual}" = "1000" ] +} + +@test "server/ha-StatefulSet: gid configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.gid=2000' \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsGroup' | tee /dev/stderr) + [ "${actual}" = "2000" ] +} + +@test "server/ha-StatefulSet: fsgroup default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.fsGroup' | tee /dev/stderr) + [ "${actual}" = "1000" ] +} + +@test "server/ha-StatefulSet: fsgroup configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.gid=2000' \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.fsGroup' | tee /dev/stderr) + [ "${actual}" = "2000" ] +} + +#-------------------------------------------------------------------- +# OpenShift + +@test "server/ha-statefulset: OpenShift - runAsUser disabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.openshift=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.securityContext.runAsUser | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ha-statefulset: OpenShift - runAsGroup disabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.openshift=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.securityContext.runAsGroup | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} diff --git a/test/unit/server-ingress.bats b/test/unit/server-ingress.bats new file mode 100755 index 00000000..4132c16a --- /dev/null +++ b/test/unit/server-ingress.bats @@ -0,0 +1,267 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/ingress: disabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-ingress.yaml \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ingress: disable by injector.externalVaultAddr" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ingress: checking host entry gets added and path is /" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.ingress.hosts[0].host=test.com' \ + --set 'server.ingress.hosts[0].paths[0]=/' \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].host' | tee /dev/stderr) + [ "${actual}" = 'test.com' ] + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.ingress.hosts[0].host=test.com' \ + --set 'server.ingress.hosts[0].paths[0]=/' \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].path' | tee /dev/stderr) + [ "${actual}" = '/' ] +} + +@test "server/ingress: vault backend should be added when I specify a path" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.ingress.hosts[0].host=test.com' \ + --set 'server.ingress.hosts[0].paths[0]=/' \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].backend.service.name | length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + +} + +@test "server/ingress: extra paths prepend host configuration" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.ingress.hosts[0].host=test.com' \ + --set 'server.ingress.hosts[0].paths[0]=/' \ + --set 'server.ingress.extraPaths[0].path=/annotation-service' \ + --set 'server.ingress.extraPaths[0].backend.service.name=ssl-redirect' \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].backend.service.name' | tee /dev/stderr) + [ "${actual}" = 'ssl-redirect' ] + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.ingress.hosts[0].host=test.com' \ + --set 'server.ingress.hosts[0].paths[0]=/' \ + --set 'server.ingress.extraPaths[0].path=/annotation-service' \ + --set 'server.ingress.extraPaths[0].backend.service.name=ssl-redirect' \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].path' | tee /dev/stderr) + [ "${actual}" = '/annotation-service' ] + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.ingress.hosts[0].host=test.com' \ + --set 'server.ingress.hosts[0].paths[0]=/' \ + --set 'server.ingress.extraPaths[0].path=/annotation-service' \ + --set 'server.ingress.extraPaths[0].backend.service.name=ssl-redirect' \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[1].path' | tee /dev/stderr) + [ "${actual}" = '/' ] +} + +@test "server/ingress: labels gets added to object" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.ingress.labels.traffic=external' \ + --set 'server.ingress.labels.team=dev' \ + . | tee /dev/stderr | + yq -r '.metadata.labels.traffic' | tee /dev/stderr) + [ "${actual}" = "external" ] +} + +@test "server/ingress: annotations added to object - string" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.ingress.annotations=kubernetes.io/ingress.class: nginx' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["kubernetes.io/ingress.class"]' | tee /dev/stderr) + [ "${actual}" = "nginx" ] +} + +@test "server/ingress: annotations added to object - yaml" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set server.ingress.annotations."kubernetes\.io/ingress\.class"=nginx \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["kubernetes.io/ingress.class"]' | tee /dev/stderr) + [ "${actual}" = "nginx" ] +} + +@test "server/ingress: ingressClassName added to object spec - string" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set server.ingress.ingressClassName=nginx \ + . | tee /dev/stderr | + yq -r '.spec.ingressClassName' | tee /dev/stderr) + [ "${actual}" = "nginx" ] +} + +@test "server/ingress: ingressClassName is not added by default" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.ingressClassName' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/ingress: uses active service when ha by default - yaml" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.dev.enabled=false' \ + --set 'server.ha.enabled=true' \ + --set 'server.service.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].backend.service.name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault-active" ] +} + +@test "server/ingress: uses regular service when configured with ha - yaml" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.ingress.activeService=false' \ + --set 'server.dev.enabled=false' \ + --set 'server.ha.enabled=true' \ + --set 'server.service.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].backend.service.name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault" ] +} + +@test "server/ingress: uses regular service when not ha - yaml" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.dev.enabled=false' \ + --set 'server.ha.enabled=false' \ + --set 'server.service.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].backend.service.name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault" ] +} + +@test "server/ingress: k8s 1.18.3 uses regular service when not ha - yaml" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.dev.enabled=false' \ + --set 'server.ha.enabled=false' \ + --set 'server.service.enabled=true' \ + --kube-version 1.18.3 \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].backend.serviceName' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault" ] +} + +@test "server/ingress: uses regular service when not ha and activeService is true - yaml" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set 'server.ingress.activeService=true' \ + --set 'server.dev.enabled=false' \ + --set 'server.ha.enabled=false' \ + --set 'server.service.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].backend.service.name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault" ] +} + +@test "server/ingress: pathType is added to Kubernetes version == 1.19.0" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set server.ingress.pathType=ImplementationSpecific \ + --kube-version 1.19.0 \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].pathType' | tee /dev/stderr) + [ "${actual}" = "ImplementationSpecific" ] +} + +@test "server/ingress: pathType is not added to Kubernetes versions < 1.19" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set server.ingress.pathType=ImplementationSpecific \ + --kube-version 1.18.3 \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].pathType' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/ingress: pathType is added to Kubernetes versions > 1.19" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-ingress.yaml \ + --set 'server.ingress.enabled=true' \ + --set server.ingress.pathType=Prefix \ + --kube-version 1.20.0 \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].pathType' | tee /dev/stderr) + [ "${actual}" = "Prefix" ] +} diff --git a/test/unit/server-network-policy.bats b/test/unit/server-network-policy.bats new file mode 100755 index 00000000..1364321d --- /dev/null +++ b/test/unit/server-network-policy.bats @@ -0,0 +1,35 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/network-policy: disabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-network-policy.yaml \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/network-policy: enabled by server.networkPolicy.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --set 'server.networkPolicy.enabled=true' \ + --show-only templates/server-network-policy.yaml \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/network-policy: egress enabled by server.networkPolicy.egress" { + cd `chart_dir` + local actual=$(helm template \ + --set 'server.networkPolicy.enabled=true' \ + --set 'server.networkPolicy.egress[0].to[0].ipBlock.cidr=10.0.0.0/24' \ + --set 'server.networkPolicy.egress[0].ports[0].protocol=TCP' \ + --set 'server.networkPolicy.egress[0].ports[0].port=443' \ + --show-only templates/server-network-policy.yaml \ + . | tee /dev/stderr | + yq -r '.spec.egress[0].to[0].ipBlock.cidr' | tee /dev/stderr) + [ "${actual}" = "10.0.0.0/24" ] +} diff --git a/test/unit/server-psp-role.bats b/test/unit/server-psp-role.bats new file mode 100644 index 00000000..1d3e62c4 --- /dev/null +++ b/test/unit/server-psp-role.bats @@ -0,0 +1,111 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/PSP-Role: PSP-Role not enabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-psp-role.yaml \ + --set 'server.dev.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp-role.yaml \ + --set 'server.ha.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp-role.yaml \ + --set 'server.standalone.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/PSP-Role: PSP-Role can be enabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-psp-role.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-psp-role.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-psp-role.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/PSP-Role: disable with global.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-psp-role.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.enabled=false' \ + --set 'global.psp.enable=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp-role.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.enabled=false' \ + --set 'global.psp.enable=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp-role.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.enabled=false' \ + --set 'global.psp.enable=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/PSP-Role: disable with global.psp.enable false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-psp-role.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.psp.enable=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp-role.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.psp.enable=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp-role.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.psp.enable=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} diff --git a/test/unit/server-psp-rolebinding.bats b/test/unit/server-psp-rolebinding.bats new file mode 100644 index 00000000..4171219f --- /dev/null +++ b/test/unit/server-psp-rolebinding.bats @@ -0,0 +1,111 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/PSP-RoleBinding: PSP-RoleBinding not enabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-psp-rolebinding.yaml \ + --set 'server.dev.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp-rolebinding.yaml \ + --set 'server.ha.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp-rolebinding.yaml \ + --set 'server.standalone.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/PSP-RoleBinding: PSP-RoleBinding can be enabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-psp-rolebinding.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-psp-rolebinding.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-psp-rolebinding.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/PSP-RoleBinding: disable with global.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-psp-rolebinding.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.enabled=false' \ + --set 'global.psp.enable=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp-rolebinding.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.enabled=false' \ + --set 'global.psp.enable=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp-rolebinding.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.enabled=false' \ + --set 'global.psp.enable=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/PSP-RoleBinding: disable with global.psp.enable false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-psp-rolebinding.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.psp.enable=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp-rolebinding.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.psp.enable=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp-rolebinding.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.psp.enable=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} diff --git a/test/unit/server-psp.bats b/test/unit/server-psp.bats new file mode 100644 index 00000000..400e76d1 --- /dev/null +++ b/test/unit/server-psp.bats @@ -0,0 +1,285 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/PodSecurityPolicy: PodSecurityPolicy not enabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.dev.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.ha.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.standalone.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/PodSecurityPolicy: PodSecurityPolicy can be enabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/PodSecurityPolicy: PodSecurityPolicy annotations are templated correctly" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq '.metadata.annotations | length == 4' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq '.metadata.annotations | length == 4' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq '.metadata.annotations | length == 4' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/PodSecurityPolicy: annotations are added - string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'global.psp.annotations=vault-is: amazing' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) + [ "${actual}" = "amazing" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'global.psp.annotations=vault-is: amazing' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) + [ "${actual}" = "amazing" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'global.psp.annotations=vault-is: amazing' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) + [ "${actual}" = "amazing" ] +} + +@test "server/PodSecurityPolicy: annotations are added - object" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'global.psp.annotations.vault-is=amazing' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) + [ "${actual}" = "amazing" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'global.psp.annotations.vault-is=amazing' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) + [ "${actual}" = "amazing" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'global.psp.annotations.vault-is=amazing' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) + [ "${actual}" = "amazing" ] +} + +@test "server/PodSecurityPolicy: disable with global.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.enabled=false' \ + --set 'global.psp.enable=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.enabled=false' \ + --set 'global.psp.enable=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.enabled=false' \ + --set 'global.psp.enable=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/PodSecurityPolicy: disable with global.psp.enable false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.psp.enable=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.psp.enable=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.psp.enable=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/PodSecurityPolicy: PodSecurityPolicy allows PVC by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.psp.enable=true' \ + . | tee /dev/stderr | + yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/PodSecurityPolicy: PodSecurityPolicy allows PVC with dataStorage" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'server.dataStorage.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'server.dataStorage.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'server.dataStorage.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/PodSecurityPolicy: PodSecurityPolicy does not allow PVC without dataStorage" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'server.dataStorage.enabled=false' \ + . | tee /dev/stderr | + yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'server.dataStorage.enabled=false' \ + . | tee /dev/stderr | + yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(helm template \ + --show-only templates/server-psp.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.psp.enable=true' \ + --set 'server.dataStorage.enabled=false' \ + . | tee /dev/stderr | + yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) + [ "${actual}" = "false" ] +} diff --git a/test/unit/server-route.bats b/test/unit/server-route.bats new file mode 100755 index 00000000..d141fb63 --- /dev/null +++ b/test/unit/server-route.bats @@ -0,0 +1,143 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/route: OpenShift - disabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --set 'global.openshift=true' \ + --show-only templates/server-route.yaml \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/route: OpenShift -disable by injector.externalVaultAddr" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-route.yaml \ + --set 'global.openshift=true' \ + --set 'server.route.enabled=true' \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/route: OpenShift - checking host entry gets added and path is /" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-route.yaml \ + --set 'global.openshift=true' \ + --set 'server.route.enabled=true' \ + --set 'server.route.host=test.com' \ + . | tee /dev/stderr | + yq -r '.spec.host' | tee /dev/stderr) + [ "${actual}" = 'test.com' ] +} + +@test "server/route: OpenShift - vault backend should be added when I specify a path" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-route.yaml \ + --set 'global.openshift=true' \ + --set 'server.route.enabled=true' \ + --set 'server.route.host=test.com' \ + . | tee /dev/stderr | + yq -r '.spec.to.name | length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + +} + +@test "server/route: OpenShift - labels gets added to object" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-route.yaml \ + --set 'global.openshift=true' \ + --set 'server.route.enabled=true' \ + --set 'server.route.labels.traffic=external' \ + --set 'server.route.labels.team=dev' \ + . | tee /dev/stderr | + yq -r '.metadata.labels.traffic' | tee /dev/stderr) + [ "${actual}" = "external" ] +} + +@test "server/route: OpenShift - annotations added to object - string" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-route.yaml \ + --set 'global.openshift=true' \ + --set 'server.route.enabled=true' \ + --set 'server.route.annotations=kubernetes.io/route.class: haproxy' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["kubernetes.io/route.class"]' | tee /dev/stderr) + [ "${actual}" = "haproxy" ] +} + +@test "server/route: OpenShift - annotations added to object - yaml" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-route.yaml \ + --set 'global.openshift=true' \ + --set 'server.route.enabled=true' \ + --set server.route.annotations."kubernetes\.io/route\.class"=haproxy \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["kubernetes.io/route.class"]' | tee /dev/stderr) + [ "${actual}" = "haproxy" ] +} + +@test "server/route: OpenShift - route points to main service by default" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-route.yaml \ + --set 'global.openshift=true' \ + --set 'server.route.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.to.name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault" ] +} + +@test "server/route: OpenShift - route points to main service when not ha and activeService is true" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-route.yaml \ + --set 'global.openshift=true' \ + --set 'server.route.enabled=true' \ + --set 'server.route.activeService=true' \ + . | tee /dev/stderr | + yq -r '.spec.to.name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault" ] +} + +@test "server/route: OpenShift - route points to active service by when HA by default" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-route.yaml \ + --set 'global.openshift=true' \ + --set 'server.route.enabled=true' \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.to.name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault-active" ] +} + +@test "server/route: OpenShift - route points to general service by when HA when configured" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-route.yaml \ + --set 'global.openshift=true' \ + --set 'server.route.enabled=true' \ + --set 'server.route.activeService=false' \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.to.name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault" ] +} diff --git a/test/unit/server-service.bats b/test/unit/server-service.bats new file mode 100755 index 00000000..4695f2ff --- /dev/null +++ b/test/unit/server-service.bats @@ -0,0 +1,426 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/Service: service enabled by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.standalone.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/Service: disable with global.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.enabled=false' \ + --set 'server.service.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.enabled=false' \ + --set 'server.service.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.enabled=false' \ + --set 'server.service.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/Service: disable with server.service.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.service.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.service.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/Service: disable with global.enabled false server.service.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.enabled=false' \ + --set 'server.service.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.enabled=false' \ + --set 'server.service.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.enabled=false' \ + --set 'server.service.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/Service: disable with injector.externalVaultAddr" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + --set 'server.service.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + --set 'server.service.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + --set 'server.service.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/Service: generic annotations" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.service.annotations=vaultIsAwesome: true' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/Service: publish not ready" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.publishNotReadyAddresses' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.publishNotReadyAddresses' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.standalone.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.publishNotReadyAddresses' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/Service: type empty by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/Service: type can set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.service.type=NodePort' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "NodePort" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=NodePort' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "NodePort" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.service.type=NodePort' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "NodePort" ] +} + +@test "server/Service: clusterIP empty by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.clusterIP' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.clusterIP' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + . | tee /dev/stderr | + yq -r '.spec.clusterIP' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/Service: clusterIP can set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.service.clusterIP=None' \ + . | tee /dev/stderr | + yq -r '.spec.clusterIP' | tee /dev/stderr) + [ "${actual}" = "None" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.clusterIP=None' \ + . | tee /dev/stderr | + yq -r '.spec.clusterIP' | tee /dev/stderr) + [ "${actual}" = "None" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.service.clusterIP=None' \ + . | tee /dev/stderr | + yq -r '.spec.clusterIP' | tee /dev/stderr) + [ "${actual}" = "None" ] +} + +@test "server/Service: port and targetPort will be 8200 by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].port' | tee /dev/stderr) + [ "${actual}" = "8200" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) + [ "${actual}" = "8200" ] +} + +@test "server/Service: port and targetPort can be set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.service.port=8000' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].port' | tee /dev/stderr) + [ "${actual}" = "8000" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.service.targetPort=80' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) + [ "${actual}" = "80" ] +} + +@test "server/Service: nodeport can set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.service.type=NodePort' \ + --set 'server.service.nodePort=30008' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) + [ "${actual}" = "30008" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=NodePort' \ + --set 'server.service.nodePort=30009' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) + [ "${actual}" = "30009" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.service.type=NodePort' \ + --set 'server.service.nodePort=30010' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) + [ "${actual}" = "30010" ] +} + +@test "server/Service: nodeport can't set when type isn't NodePort" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.service.nodePort=30008' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.nodePort=30009' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.service.nodePort=30010' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/Service: vault port name is http, when tlsDisable is true" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'global.tlsDisable=true' \ + . | tee /dev/stderr | + yq -r '.spec.ports | map(select(.port==8200)) | .[] .name' | tee /dev/stderr) + [ "${actual}" = "http" ] +} + +@test "server/Service: vault port name is https, when tlsDisable is false" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'global.tlsDisable=false' \ + . | tee /dev/stderr | + yq -r '.spec.ports | map(select(.port==8200)) | .[] .name' | tee /dev/stderr) + [ "${actual}" = "https" ] +} + +# duplicated in server-ha-active-service.bats +@test "server/Service: NodePort assert externalTrafficPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=NodePort' \ + --set 'server.service.externalTrafficPolicy=Foo' \ + . | tee /dev/stderr | + yq -r '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "Foo" ] +} + +# duplicated in server-ha-active-service.bats +@test "server/ha-active-Service: NodePort assert no externalTrafficPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=NodePort' \ + --set 'server.service.externalTrafficPolicy=' \ + . | tee /dev/stderr | + yq '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +# duplicated in server-ha-active-service.bats +@test "server/Service: ClusterIP assert no externalTrafficPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.service.type=ClusterIP' \ + --set 'server.service.externalTrafficPolicy=Foo' \ + . | tee /dev/stderr | + yq '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + diff --git a/test/unit/server-serviceaccount.bats b/test/unit/server-serviceaccount.bats new file mode 100755 index 00000000..29e18b56 --- /dev/null +++ b/test/unit/server-serviceaccount.bats @@ -0,0 +1,119 @@ +#!/usr/bin/env bats + +load _helpers + +@test "server/ServiceAccount: specify service account name" { + cd `chart_dir` + + local actual=$( (helm template \ + --show-only templates/server-serviceaccount.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.serviceAccount.create=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(helm template \ + --show-only templates/server-serviceaccount.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.serviceAccount.name=user-defined-ksa' \ + . | tee /dev/stderr | + yq -r '.metadata.name' | tee /dev/stderr) + [ "${actual}" = "user-defined-ksa" ] + + local actual=$(helm template \ + --show-only templates/server-serviceaccount.yaml \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.metadata.name' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault" ] + +} + +@test "server/ServiceAccount: specify annotations" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-serviceaccount.yaml \ + --set 'server.dev.enabled=true' \ + --set 'server.serviceAccount.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(helm template \ + --show-only templates/server-serviceaccount.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.serviceAccount.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) + [ "${actual}" = "bar" ] + + local actual=$(helm template \ + --show-only templates/server-serviceaccount.yaml \ + --set 'server.ha.enabled=true' \ + --set 'server.serviceAccount.annotations.foo=bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) + [ "${actual}" = "bar" ] + + local actual=$(helm template \ + --show-only templates/server-serviceaccount.yaml \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/ServiceAccount: disable with global.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ServiceAccount: disable by injector.externalVaultAddr" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/server-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} diff --git a/test/unit/server-statefulset.bats b/test/unit/server-statefulset.bats new file mode 100755 index 00000000..b9390518 --- /dev/null +++ b/test/unit/server-statefulset.bats @@ -0,0 +1,1681 @@ +#!/usr/bin/env bats + +load _helpers + +#-------------------------------------------------------------------- +# disable / enable server deployment + +@test "server/StatefulSet: disabled server.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/StatefulSet: disabled server.enabled random string" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.enabled=blabla' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/StatefulSet: enabled server.enabled explicit true" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- + +@test "server/standalone-StatefulSet: default server.standalone.enabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/standalone-StatefulSet: enable with server.standalone.enabled true" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/standalone-StatefulSet: disable with global.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.enabled=false' \ + --set 'server.standalone.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/standalone-StatefulSet: disable with injector.externalVaultAddr" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + --set 'server.standalone.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/standalone-StatefulSet: image defaults to server.image.repository:tag" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.image.repository=foo' \ + --set 'server.image.tag=1.2.3' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "foo:1.2.3" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.image.repository=foo' \ + --set 'server.image.tag=1.2.3' \ + --set 'server.standalone.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "foo:1.2.3" ] +} + +@test "server/standalone-StatefulSet: image tag defaults to latest" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.image.repository=foo' \ + --set 'server.image.tag=' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "foo:latest" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.image.repository=foo' \ + --set 'server.image.tag=' \ + --set 'server.standalone.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "foo:latest" ] +} + +@test "server/standalone-StatefulSet: default imagePullPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].imagePullPolicy' | tee /dev/stderr) + [ "${actual}" = "IfNotPresent" ] +} + +@test "server/standalone-StatefulSet: Custom imagePullPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.image.pullPolicy=Always' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].imagePullPolicy' | tee /dev/stderr) + [ "${actual}" = "Always" ] +} + +@test "server/standalone-StatefulSet: Custom imagePullSecrets" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.imagePullSecrets[0].name=foo' \ + --set 'global.imagePullSecrets[1].name=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.imagePullSecrets' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '. | length' | tee /dev/stderr) + [ "${actual}" = "2" ] + + local actual=$(echo $object | + yq -r '.[0].name' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + local actual=$(echo $object | + yq -r '.[1].name' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +@test "server/standalone-StatefulSet: Custom imagePullSecrets - string array" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.imagePullSecrets[0]=foo' \ + --set 'global.imagePullSecrets[1]=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.imagePullSecrets' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '. | length' | tee /dev/stderr) + [ "${actual}" = "2" ] + + local actual=$(echo $object | + yq -r '.[0].name' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + local actual=$(echo $object | + yq -r '.[1].name' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +@test "server/standalone-StatefulSet: default imagePullSecrets" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.imagePullSecrets' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +#-------------------------------------------------------------------- +# updateStrategy + +@test "server/standalone-StatefulSet: OnDelete updateStrategy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.updateStrategy.type' | tee /dev/stderr) + [ "${actual}" = "OnDelete" ] +} + +#-------------------------------------------------------------------- +# replicas + +@test "server/standalone-StatefulSet: default replicas" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.replicas' | tee /dev/stderr) + [ "${actual}" = "1" ] +} + +@test "server/standalone-StatefulSet: custom replicas" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.replicas=100' \ + . | tee /dev/stderr | + yq -r '.spec.replicas' | tee /dev/stderr) + [ "${actual}" = "1" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.standalone.replicas=100' \ + . | tee /dev/stderr | + yq -r '.spec.replicas' | tee /dev/stderr) + [ "${actual}" = "1" ] +} + +#-------------------------------------------------------------------- +# resources + +@test "server/standalone-StatefulSet: default resources" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/standalone-StatefulSet: custom resources" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.resources.requests.memory=256Mi' \ + --set 'server.resources.requests.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.resources.limits.memory=256Mi' \ + --set 'server.resources.limits.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.resources.requests.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.requests.cpu' | tee /dev/stderr) + [ "${actual}" = "250m" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.resources.limits.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.limits.cpu' | tee /dev/stderr) + [ "${actual}" = "250m" ] +} + +#-------------------------------------------------------------------- +# extraVolumes + +@test "server/standalone-StatefulSet: server.extraVolumes adds extra volume" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.configMap.name' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + local actual=$(echo $object | + yq -r '.configMap.secretName' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.configMap.name' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + local actual=$(echo $object | + yq -r '.configMap.secretName' | tee /dev/stderr) + [ "${actual}" = "null" ] + + # Test that it mounts it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/vault/userconfig/foo" ] + + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/vault/userconfig/foo" ] +} + +@test "server/standalone-StatefulSet: server.extraVolumes adds extra secret volume" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.extraVolumes[0].type=secret' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.secret.name' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(echo $object | + yq -r '.secret.secretName' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.extraVolumes[0].type=secret' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.secret.name' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(echo $object | + yq -r '.secret.secretName' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + # Test that it mounts it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/vault/userconfig/foo" ] + + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.extraVolumes[0].type=configMap' \ + --set 'server.extraVolumes[0].name=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/vault/userconfig/foo" ] +} + +@test "server/standalone-StatefulSet: can mount audit" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.auditStorage.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "audit")' | tee /dev/stderr) +} + +#-------------------------------------------------------------------- +# volumes + +@test "server/standalone-StatefulSet: server.volumes adds volume" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.volumes[0].name=plugins' \ + --set 'server.volumes[0].emptyDir=\{\}' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.volumes[] | select(.name == "plugins")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.emptyDir' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +#-------------------------------------------------------------------- +# volumeMounts + +@test "server/standalone-StatefulSet: server.volumeMounts adds volumeMount" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.volumeMounts[0].name=plugins' \ + --set 'server.volumeMounts[0].mountPath=/usr/local/libexec/vault' \ + --set 'server.volumeMounts[0].readOnly=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "plugins")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/usr/local/libexec/vault" ] + + local actual=$(echo $object | + yq -r '.readOnly' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# log level + +@test "server/standalone-StatefulSet: default log level to empty" { + cd `chart_dir` + local objects=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $objects | + yq -r 'map(select(.name=="VAULT_LOG_LEVEL")) | .[] .name' | tee /dev/stderr) + [ "${value}" = "" ] +} + +@test "server/standalone-StatefulSet: log level can be changed" { + cd `chart_dir` + local objects=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set='server.logLevel=debug' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $objects | + yq -r 'map(select(.name=="VAULT_LOG_LEVEL")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "debug" ] +} + +#-------------------------------------------------------------------- +# log format + +@test "server/standalone-StatefulSet: default log format to empty" { + cd `chart_dir` + local objects=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $objects | + yq -r 'map(select(.name=="VAULT_LOG_FORMAT")) | .[] .name' | tee /dev/stderr) + [ "${value}" = "" ] +} + +@test "server/standalone-StatefulSet: can set log format" { + cd `chart_dir` + local objects=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set='server.logFormat=json' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local value=$(echo $objects | + yq -r 'map(select(.name=="VAULT_LOG_FORMAT")) | .[] .value' | tee /dev/stderr) + [ "${value}" = "json" ] +} + +#-------------------------------------------------------------------- +# extraEnvironmentVars + +@test "server/standalone-StatefulSet: set extraEnvironmentVars" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.stanadlone.enabled=true' \ + --set 'server.extraEnvironmentVars.FOO=bar' \ + --set 'server.extraEnvironmentVars.FOOBAR=foobar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local name=$(echo $object | + yq -r 'map(select(.name=="FOO")) | .[] .value' | tee /dev/stderr) + [ "${name}" = "bar" ] + + local name=$(echo $object | + yq -r 'map(select(.name=="FOOBAR")) | .[] .value' | tee /dev/stderr) + [ "${name}" = "foobar" ] + + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.extraEnvironmentVars.FOO=bar' \ + --set 'server.extraEnvironmentVars.FOOBAR=foobar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local name=$(echo $object | + yq -r 'map(select(.name=="FOO")) | .[] .value' | tee /dev/stderr) + [ "${name}" = "bar" ] + + local name=$(echo $object | + yq -r 'map(select(.name=="FOOBAR")) | .[] .value' | tee /dev/stderr) + [ "${name}" = "foobar" ] +} + +#-------------------------------------------------------------------- +# storage class + +@test "server/standalone-StatefulSet: storageClass on claim by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates[0].spec.storageClassName' | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates[0].spec.storageClassName' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + + +@test "server/standalone-StatefulSet: can set storageClass" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dataStorage.enabled=true' \ + --set 'server.dataStorage.storageClass=foo' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates[0].spec.storageClassName' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.dataStorage.enabled=false' \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.auditStorage.storageClass=foo' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates[0].spec.storageClassName' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.auditStorage.storageClass=foo' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates[1].spec.storageClassName' | tee /dev/stderr) + [ "${actual}" = "foo" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.dataStorage.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "2" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.dataStorage.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "2" ] +} + +@test "server/standalone-StatefulSet: can disable storage" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.auditStorage.enabled=false' \ + --set 'server.dataStorage.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "1" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.dataStorage.enabled=false' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "1" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.auditStorage.enabled=false' \ + --set 'server.dataStorage.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "1" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.dataStorage.enabled=false' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "1" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.dataStorage.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "2" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.dataStorage.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "2" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.auditStorage.enabled=fa;se' \ + --set 'server.dataStorage.enabled=false' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "0" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.auditStorage.enabled=false' \ + --set 'server.dataStorage.enabled=false' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) + [ "${actual}" = "0" ] +} + +@test "server/standalone-StatefulSet: default audit storage mount path" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.auditStorage.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "audit")' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/vault/audit" ] +} + +@test "server/standalone-StatefulSet: can configure audit storage mount path" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.auditStorage.mountPath=/var/log/vault' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "audit")' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/var/log/vault" ] +} + +@test "server/standalone-StatefulSet: default data storage mount path" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dataStorage.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "data")' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/vault/data" ] +} + +@test "server/standalone-StatefulSet: can configure data storage mount path" { + cd `chart_dir` + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dataStorage.enabled=true' \ + --set 'server.dataStorage.mountPath=/opt/vault' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "data")' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.mountPath' | tee /dev/stderr) + [ "${actual}" = "/opt/vault" ] +} + +@test "server/standalone-StatefulSet: affinity is set by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq '.spec.template.spec.affinity["podAntiAffinity"]? != null' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/standalone-StatefulSet: affinity can be set as string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.affinity=foobar' \ + . | tee /dev/stderr | + yq '.spec.template.spec.affinity == "foobar"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/standalone-StatefulSet: affinity can be set as YAML" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.affinity.podAntiAffinity=foobar' \ + . | tee /dev/stderr | + yq '.spec.template.spec.affinity.podAntiAffinity == "foobar"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + + +@test "server/standalone-StatefulSet: tolerations not set by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq '.spec.template.spec | .tolerations? == null' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/standalone-StatefulSet: tolerations can be set as string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.tolerations=foobar' \ + . | tee /dev/stderr | + yq '.spec.template.spec.tolerations == "foobar"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/standalone-StatefulSet: tolerations can be set as YAML" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set "server.tolerations[0].foo=bar,server.tolerations[1].baz=qux" \ + . | tee /dev/stderr | + yq '.spec.template.spec.tolerations == [{"foo": "bar"}, {"baz": "qux"}]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/standalone-StatefulSet: nodeSelector is not set by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq '.spec.template.spec.nodeSelector' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/standalone-StatefulSet: specified nodeSelector as string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.nodeSelector=testing' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.nodeSelector' | tee /dev/stderr) + [ "${actual}" = "testing" ] +} + +@test "server/standalone-StatefulSet: nodeSelector can be set as YAML" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set "server.nodeSelector.beta\.kubernetes\.io/arch=amd64" \ + . | tee /dev/stderr | + yq '.spec.template.spec.nodeSelector == {"beta.kubernetes.io/arch": "amd64"}' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# extraInitContainers + +@test "server/standalone-StatefulSet: adds extra init containers" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.extraInitContainers[0].image=test-image' \ + --set 'server.extraInitContainers[0].name=test-container' \ + --set 'server.extraInitContainers[0].ports[0].name=test-port' \ + --set 'server.extraInitContainers[0].ports[0].containerPort=9410' \ + --set 'server.extraInitContainers[0].ports[0].protocol=TCP' \ + --set 'server.extraInitContainers[0].env[0].name=TEST_ENV' \ + --set 'server.extraInitContainers[0].env[0].value=test_env_value' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[] | select(.name == "test-container")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.name' | tee /dev/stderr) + [ "${actual}" = "test-container" ] + + local actual=$(echo $object | + yq -r '.image' | tee /dev/stderr) + [ "${actual}" = "test-image" ] + + local actual=$(echo $object | + yq -r '.ports[0].name' | tee /dev/stderr) + [ "${actual}" = "test-port" ] + + local actual=$(echo $object | + yq -r '.ports[0].containerPort' | tee /dev/stderr) + [ "${actual}" = "9410" ] + + local actual=$(echo $object | + yq -r '.ports[0].protocol' | tee /dev/stderr) + [ "${actual}" = "TCP" ] + + local actual=$(echo $object | + yq -r '.env[0].name' | tee /dev/stderr) + [ "${actual}" = "TEST_ENV" ] + + local actual=$(echo $object | + yq -r '.env[0].value' | tee /dev/stderr) + [ "${actual}" = "test_env_value" ] + +} + +@test "server/standalone-StatefulSet: add two extra init containers" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.extraInitContainers[0].image=test-image' \ + --set 'server.extraInitContainers[0].name=test-container' \ + --set 'server.extraInitContainers[1].image=test-image' \ + --set 'server.extraInitContainers[1].name=test-container-2' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers' | tee /dev/stderr) + + local containers_count=$(echo $object | + yq -r 'length' | tee /dev/stderr) + [ "${containers_count}" = 2 ] + +} + +#-------------------------------------------------------------------- +# extraContainers + +@test "server/standalone-StatefulSet: adds extra containers" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.extraContainers[0].image=test-image' \ + --set 'server.extraContainers[0].name=test-container' \ + --set 'server.extraContainers[0].ports[0].name=test-port' \ + --set 'server.extraContainers[0].ports[0].containerPort=9410' \ + --set 'server.extraContainers[0].ports[0].protocol=TCP' \ + --set 'server.extraContainers[0].env[0].name=TEST_ENV' \ + --set 'server.extraContainers[0].env[0].value=test_env_value' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[] | select(.name == "test-container")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.name' | tee /dev/stderr) + [ "${actual}" = "test-container" ] + + local actual=$(echo $object | + yq -r '.image' | tee /dev/stderr) + [ "${actual}" = "test-image" ] + + local actual=$(echo $object | + yq -r '.ports[0].name' | tee /dev/stderr) + [ "${actual}" = "test-port" ] + + local actual=$(echo $object | + yq -r '.ports[0].containerPort' | tee /dev/stderr) + [ "${actual}" = "9410" ] + + local actual=$(echo $object | + yq -r '.ports[0].protocol' | tee /dev/stderr) + [ "${actual}" = "TCP" ] + + local actual=$(echo $object | + yq -r '.env[0].name' | tee /dev/stderr) + [ "${actual}" = "TEST_ENV" ] + + local actual=$(echo $object | + yq -r '.env[0].value' | tee /dev/stderr) + [ "${actual}" = "test_env_value" ] + +} + +@test "server/standalone-StatefulSet: add two extra containers" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.extraContainers[0].image=test-image' \ + --set 'server.extraContainers[0].name=test-container' \ + --set 'server.extraContainers[1].image=test-image' \ + --set 'server.extraContainers[1].name=test-container-2' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers' | tee /dev/stderr) + + local containers_count=$(echo $object | + yq -r 'length' | tee /dev/stderr) + [ "${containers_count}" = 3 ] + +} + +@test "server/standalone-StatefulSet: no extra containers added" { + cd `chart_dir` + + # Test that it defines it + local object=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers' | tee /dev/stderr) + + local containers_count=$(echo $object | + yq -r 'length' | tee /dev/stderr) + [ "${containers_count}" = 1 ] +} + +# sharedProcessNamespace + +@test "server/standalone-StatefulSet: shareProcessNamespace disabled by default" { + cd `chart_dir` + + # Test that it defines it + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.shareProcessNamespace' | tee /dev/stderr) + + [ "${actual}" = "null" ] +} + +@test "server/standalone-StatefulSet: shareProcessNamespace enabled" { + cd `chart_dir` + + # Test that it defines it + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.shareProcessNamespace=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.shareProcessNamespace' | tee /dev/stderr) + + [ "${actual}" = "true" ] +} + +# extra labels + +@test "server/standalone-StatefulSet: specify extraLabels" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.extraLabels.foo=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +# extra annotations + +@test "server/standalone-StatefulSet: default statefulSet.annotations" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.metadata.annotations' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/standalone-StatefulSet: specify statefulSet.annotations yaml" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.statefulSet.annotations.foo=bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +@test "server/standalone-StatefulSet: specify statefulSet.annotations yaml string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.statefulSet.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +#-------------------------------------------------------------------- +# Security Contexts +@test "server/standalone-StatefulSet: uid default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +@test "server/standalone-StatefulSet: uid configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.uid=2000' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + [ "${actual}" = "2000" ] +} + +@test "server/standalone-StatefulSet: gid default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsGroup' | tee /dev/stderr) + [ "${actual}" = "1000" ] +} + +@test "server/standalone-StatefulSet: gid configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.gid=2000' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsGroup' | tee /dev/stderr) + [ "${actual}" = "2000" ] +} + +@test "server/standalone-StatefulSet: fsgroup default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.fsGroup' | tee /dev/stderr) + [ "${actual}" = "1000" ] +} + +@test "server/standalone-StatefulSet: fsgroup configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.gid=2000' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.fsGroup' | tee /dev/stderr) + [ "${actual}" = "2000" ] +} + +#-------------------------------------------------------------------- +# health checks + +@test "server/standalone-StatefulSet: readinessProbe default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe.exec.command[2]' | tee /dev/stderr) + [ "${actual}" = "vault status -tls-skip-verify" ] +} + +@test "server/standalone-StatefulSet: readinessProbe configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.readinessProbe.enabled=false' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/standalone-StatefulSet: readiness failureThreshold default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.readinessProbe.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe.failureThreshold' | tee /dev/stderr) + [ "${actual}" = "2" ] +} + +@test "server/standalone-StatefulSet: readiness failureThreshold configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.readinessProbe.enabled=true' \ + --set 'server.readinessProbe.failureThreshold=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe.failureThreshold' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +@test "server/standalone-StatefulSet: readiness initialDelaySeconds default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.readinessProbe.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe.initialDelaySeconds' | tee /dev/stderr) + [ "${actual}" = "5" ] +} + +@test "server/standalone-StatefulSet: readiness initialDelaySeconds configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.readinessProbe.enabled=true' \ + --set 'server.readinessProbe.initialDelaySeconds=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe.initialDelaySeconds' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +@test "server/standalone-StatefulSet: readiness periodSeconds default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.readinessProbe.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe.periodSeconds' | tee /dev/stderr) + [ "${actual}" = "5" ] +} + +@test "server/standalone-StatefulSet: readiness periodSeconds configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.readinessProbe.enabled=true' \ + --set 'server.readinessProbe.periodSeconds=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe.periodSeconds' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +@test "server/standalone-StatefulSet: readiness successThreshold default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.readinessProbe.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe.successThreshold' | tee /dev/stderr) + [ "${actual}" = "1" ] +} + +@test "server/standalone-StatefulSet: readiness successThreshold configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.readinessProbe.enabled=true' \ + --set 'server.readinessProbe.successThreshold=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe.successThreshold' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +@test "server/standalone-StatefulSet: readiness timeoutSeconds default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.readinessProbe.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe.timeoutSeconds' | tee /dev/stderr) + [ "${actual}" = "3" ] +} + +@test "server/standalone-StatefulSet: readiness timeoutSeconds configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.readinessProbe.enabled=true' \ + --set 'server.readinessProbe.timeoutSeconds=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].readinessProbe.timeoutSeconds' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +@test "server/standalone-StatefulSet: livenessProbe default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/standalone-StatefulSet: livenessProbe configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.livenessProbe.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe.httpGet.path' | tee /dev/stderr) + [ "${actual}" = "/v1/sys/health?standbyok=true" ] +} + +@test "server/standalone-StatefulSet: liveness failureThreshold default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.livenessProbe.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe.failureThreshold' | tee /dev/stderr) + [ "${actual}" = "2" ] +} + +@test "server/standalone-StatefulSet: liveness failureThreshold configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.livenessProbe.enabled=true' \ + --set 'server.livenessProbe.failureThreshold=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe.failureThreshold' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +@test "server/standalone-StatefulSet: liveness initialDelaySeconds default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.livenessProbe.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe.initialDelaySeconds' | tee /dev/stderr) + [ "${actual}" = "60" ] +} + +@test "server/standalone-StatefulSet: liveness initialDelaySeconds configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.livenessProbe.enabled=true' \ + --set 'server.livenessProbe.initialDelaySeconds=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe.initialDelaySeconds' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +@test "server/standalone-StatefulSet: liveness periodSeconds default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.livenessProbe.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe.periodSeconds' | tee /dev/stderr) + [ "${actual}" = "5" ] +} + +@test "server/standalone-StatefulSet: liveness periodSeconds configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.livenessProbe.enabled=true' \ + --set 'server.livenessProbe.periodSeconds=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe.periodSeconds' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +@test "server/standalone-StatefulSet: liveness successThreshold default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.livenessProbe.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe.successThreshold' | tee /dev/stderr) + [ "${actual}" = "1" ] +} + +@test "server/standalone-StatefulSet: liveness successThreshold configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.livenessProbe.enabled=true' \ + --set 'server.livenessProbe.successThreshold=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe.successThreshold' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +@test "server/standalone-StatefulSet: liveness timeoutSeconds default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.livenessProbe.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe.timeoutSeconds' | tee /dev/stderr) + [ "${actual}" = "3" ] +} + +@test "server/standalone-StatefulSet: liveness timeoutSeconds configurable" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.livenessProbe.enabled=true' \ + --set 'server.livenessProbe.timeoutSeconds=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].livenessProbe.timeoutSeconds' | tee /dev/stderr) + [ "${actual}" = "100" ] +} + +#-------------------------------------------------------------------- +# args +@test "server/standalone-StatefulSet: add extraArgs" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.extraArgs=foobar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args[0]' | tee /dev/stderr) + [[ "${actual}" = *"foobar"* ]] +} + +#-------------------------------------------------------------------- +# preStop +@test "server/standalone-StatefulSet: preStop sleep duration default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].lifecycle.preStop.exec.command[2]' | tee /dev/stderr) + [[ "${actual}" = "sleep 5 &&"* ]] +} + +@test "server/standalone-StatefulSet: preStop sleep duration 10" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.preStopSleepSeconds=10' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].lifecycle.preStop.exec.command[2]' | tee /dev/stderr) + [[ "${actual}" = "sleep 10 &&"* ]] +} + +@test "server/standalone-StatefulSet: vault port name is http, when tlsDisable is true" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.tlsDisable=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].ports | map(select(.containerPort==8200)) | .[] .name' | tee /dev/stderr) + [ "${actual}" = "http" ] +} + +@test "server/standalone-StatefulSet: vault replication port name is http-rep, when tlsDisable is true" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.tlsDisable=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].ports | map(select(.containerPort==8202)) | .[] .name' | tee /dev/stderr) + [ "${actual}" = "http-rep" ] +} + +@test "server/standalone-StatefulSet: vault port name is https, when tlsDisable is false" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.tlsDisable=false' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].ports | map(select(.containerPort==8200)) | .[] .name' | tee /dev/stderr) + [ "${actual}" = "https" ] +} + +@test "server/standalone-StatefulSet: vault replication port name is https-rep, when tlsDisable is false" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.tlsDisable=false' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].ports | map(select(.containerPort==8202)) | .[] .name' | tee /dev/stderr) + [ "${actual}" = "https-rep" ] +} + +#-------------------------------------------------------------------- +# annotations +@test "server/standalone-StatefulSet: generic annotations string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.annotations=vaultIsAwesome: true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/standalone-StatefulSet: auditStorage volumeClaim annotations string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.auditStorage.annotations=vaultIsAwesome: true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates[1].metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/standalone-StatefulSet: dataStorage volumeClaim annotations string" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dataStorage.enabled=true' \ + --set 'server.dataStorage.annotations=vaultIsAwesome: true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates[0].metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/standalone-StatefulSet: auditStorage volumeClaim annotations yaml" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.auditStorage.enabled=true' \ + --set 'server.auditStorage.annotations.vaultIsAwesome=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates[1].metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/standalone-StatefulSet: dataStorage volumeClaim annotations yaml" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.dataStorage.enabled=true' \ + --set 'server.dataStorage.annotations.vaultIsAwesome=true' \ + . | tee /dev/stderr | + yq -r '.spec.volumeClaimTemplates[0].metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/ha-standby-Service: generic annotations yaml" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.annotations.vaultIsAwesome=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# priorityClassName + +@test "server/standalone-StatefulSet: priorityClassName not set by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq '.spec.template.spec | .priorityClassName? == null' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/standalone-StatefulSet: priorityClassName can be set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.priorityClassName=armaggeddon' \ + . | tee /dev/stderr | + yq '.spec.template.spec | .priorityClassName == "armaggeddon"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +# postStart +@test "server/standalone-StatefulSet: postStart disabled by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].lifecycle.postStart' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "server/standalone-StatefulSet: postStart can be set" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set='server.postStart={/bin/sh,-c,sleep}' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].lifecycle.postStart.exec.command[0]' | tee /dev/stderr) + [ "${actual}" = "/bin/sh" ] +} + +#-------------------------------------------------------------------- +# OpenShift + +@test "server/standalone-StatefulSet: OpenShift - runAsUser disabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.openshift=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.securityContext.runAsUser | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/standalone-StatefulSet: OpenShift - runAsGroup disabled" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.openshift=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.securityContext.runAsGroup | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +#-------------------------------------------------------------------- +# serviceAccount + +@test "server/standalone-StatefulSet: serviceAccount.name is set" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.serviceAccount.create=false' \ + --set 'server.serviceAccount.name=user-defined-ksa' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.serviceAccountName' | tee /dev/stderr) + [ "${actual}" = "user-defined-ksa" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.serviceAccount.create=true' \ + --set 'server.serviceAccount.name=user-defined-ksa' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.serviceAccountName' | tee /dev/stderr) + [ "${actual}" = "user-defined-ksa" ] +} + +@test "server/standalone-StatefulSet: serviceAccount.name is not set" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.serviceAccount.create=false' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.serviceAccountName' | tee /dev/stderr) + [ "${actual}" = "default" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.serviceAccount.create=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.serviceAccountName' | tee /dev/stderr) + [ "${actual}" = "RELEASE-NAME-vault" ] + + +} + +#-------------------------------------------------------------------- +# enterprise license autoload support +@test "server/StatefulSet: adds volume for license secret when enterprise license secret name and key are provided" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'server.enterpriseLicense.secretName=foo' \ + --set 'server.enterpriseLicense.secretKey=bar' \ + . | tee /dev/stderr | + yq -r -c '.spec.template.spec.volumes[] | select(.name == "vault-license")' | tee /dev/stderr) + [ "${actual}" = '{"name":"vault-license","secret":{"secretName":"foo","defaultMode":288}}' ] +} + +@test "server/StatefulSet: adds volume mount for license secret when enterprise license secret name and key are provided" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'server.enterpriseLicense.secretName=foo' \ + --set 'server.enterpriseLicense.secretKey=bar' \ + . | tee /dev/stderr | + yq -r -c '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "vault-license")' | tee /dev/stderr) + [ "${actual}" = '{"name":"vault-license","mountPath":"/vault/license","readOnly":true}' ] +} + +@test "server/StatefulSet: adds env var for license path when enterprise license secret name and key are provided" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'server.enterpriseLicense.secretName=foo' \ + --set 'server.enterpriseLicense.secretKey=bar' \ + . | tee /dev/stderr | + yq -r -c '.spec.template.spec.containers[0].env[] | select(.name == "VAULT_LICENSE_PATH")' | tee /dev/stderr) + [ "${actual}" = '{"name":"VAULT_LICENSE_PATH","value":"/vault/license/bar"}' ] +} + +@test "server/StatefulSet: blank secretName does not set env var" { + cd `chart_dir` + + # setting secretName=null + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'server.enterpriseLicense.secretName=null' \ + --set 'server.enterpriseLicense.secretKey=bar' \ + . | tee /dev/stderr | + yq -r -c '.spec.template.spec.containers[0].env[] | select(.name == "VAULT_LICENSE_PATH")' | tee /dev/stderr) + [ "${actual}" = '' ] + + # omitting secretName + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'server.enterpriseLicense.secretKey=bar' \ + . | tee /dev/stderr | + yq -r -c '.spec.template.spec.containers[0].env[] | select(.name == "VAULT_LICENSE_PATH")' | tee /dev/stderr) + [ "${actual}" = '' ] +} diff --git a/test/unit/ui-service.bats b/test/unit/ui-service.bats new file mode 100755 index 00000000..0603303c --- /dev/null +++ b/test/unit/ui-service.bats @@ -0,0 +1,375 @@ +#!/usr/bin/env bats + +load _helpers + +@test "ui/Service: disabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.dev.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.ha.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.standalone.enabled=true' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "ui/Service: disable with ui.enabled" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'ui.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'ui.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'ui.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "ui/Service: disable with injector.externalVaultAddr" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$( (helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'injector.externalVaultAddr=http://vault-outside' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "ui/Service: ClusterIP type by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "ClusterIP" ] + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "ClusterIP" ] + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "ClusterIP" ] +} + +@test "ui/Service: specified type" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'ui.serviceType=LoadBalancer' \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "LoadBalancer" ] + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'ui.serviceType=LoadBalancer' \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "LoadBalancer" ] + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'ui.serviceType=LoadBalancer' \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "LoadBalancer" ] + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'ui.serviceType=LoadBalancer' \ + --set 'ui.externalTrafficPolicy=Local' \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "Local" ] +} + +@test "ui/Service: LoadBalancerIP set if specified and serviceType == LoadBalancer" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'ui.serviceType=LoadBalancer' \ + --set 'ui.enabled=true' \ + --set 'ui.loadBalancerIP=123.123.123.123' \ + . | tee /dev/stderr | + yq -r '.spec.loadBalancerIP' | tee /dev/stderr) + [ "${actual}" = "123.123.123.123" ] + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'ui.serviceType=ClusterIP' \ + --set 'ui.enabled=true' \ + --set 'ui.loadBalancerIP=123.123.123.123' \ + . | tee /dev/stderr | + yq -r '.spec.loadBalancerIP' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "ui/Service: set loadBalancerSourceRanges when LoadBalancer is configured as serviceType" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'ui.serviceType=LoadBalancer' \ + --set 'ui.enabled=true' \ + --set 'ui.loadBalancerSourceRanges={"123.123.123.123"}' \ + . | tee /dev/stderr | + yq -r '.spec.loadBalancerSourceRanges[0]' | tee /dev/stderr) + [ "${actual}" = "123.123.123.123" ] + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'ui.serviceType=ClusterIP' \ + --set 'ui.enabled=true' \ + --set 'ui.loadBalancerSourceRanges={"123.123.123.123"}' \ + . | tee /dev/stderr | + yq -r '.spec.loadBalancerSourceRanges[0]' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "ui/Service: ClusterIP assert no externalTrafficPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'ui.serviceType=ClusterIP' \ + --set 'ui.externalTrafficPolicy=Foo' \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "ui/Service: specify annotations" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.dev.enabled=true' \ + --set 'ui.serviceType=LoadBalancer' \ + --set 'ui.enabled=true' \ + --set 'ui.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) + [ "${actual}" = "bar" ] + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'ui.serviceType=LoadBalancer' \ + --set 'ui.enabled=true' \ + --set 'ui.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) + [ "${actual}" = "bar" ] + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'ui.serviceType=LoadBalancer' \ + --set 'ui.enabled=true' \ + --set 'ui.annotations.foo=bar' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) + [ "${actual}" = "bar" ] + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'server.ha.enabled=true' \ + --set 'ui.serviceType=LoadBalancer' \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "ui/Service: port name is http, when tlsDisable is true" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'global.tlsDisable=true' \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].name' | tee /dev/stderr) + [ "${actual}" = "http" ] +} + +@test "ui/Service: port name is https, when tlsDisable is false" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'global.tlsDisable=false' \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].name' | tee /dev/stderr) + [ "${actual}" = "https" ] +} + +@test "ui/Service: publishNotReadyAddresses set true by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.publishNotReadyAddresses' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "ui/Service: publishNotReadyAddresses can be set to false" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.enabled=true' \ + --set 'ui.publishNotReadyAddresses=false' \ + . | tee /dev/stderr | + yq -r '.spec.publishNotReadyAddresses' | tee /dev/stderr) + [ "${actual}" = 'false' ] +} + +@test "ui/Service: active pod only selector not set by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.selector["vault-active"]' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "ui/Service: active pod only selector can be set on HA" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.enabled=true' \ + --set 'ui.activeVaultPodOnly=true' \ + --set 'server.dev.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.selector["vault-active"]' | tee /dev/stderr) + [ "${actual}" = 'null' ] + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.enabled=true' \ + --set 'ui.activeVaultPodOnly=true' \ + --set 'server.ha.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.selector["vault-active"]' | tee /dev/stderr) + [ "${actual}" = 'true' ] +} + +@test "ui/Service: default is no nodePort" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "ui/Service: can set nodePort" { + cd `chart_dir` + + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.enabled=true' \ + --set 'ui.serviceNodePort=123' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) + [ "${actual}" = "123" ] +} + +@test "ui/Service: LoadBalancer assert externalTrafficPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.enabled=true' \ + --set 'server.standalone.enabled=true' \ + --set 'ui.serviceType=LoadBalancer' \ + --set 'ui.externalTrafficPolicy=Foo' \ + . | tee /dev/stderr | + yq -r '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "Foo" ] +} + +@test "ui/Service: LoadBalancer assert no externalTrafficPolicy" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.enabled=true' \ + --set 'server.standalone.enabled=true' \ + --set 'ui.serviceType=LoadBalancer' \ + --set 'ui.externalTrafficPolicy=' \ + . | tee /dev/stderr | + yq '.spec.externalTrafficPolicy' | tee /dev/stderr) + [ "${actual}" = "null" ] + +} diff --git a/values.openshift.yaml b/values.openshift.yaml new file mode 100644 index 00000000..afbe1f98 --- /dev/null +++ b/values.openshift.yaml @@ -0,0 +1,18 @@ +# These overrides are appropriate defaults for deploying this chart on OpenShift + +global: + openshift: true + +injector: + image: + repository: "registry.connect.redhat.com/hashicorp/vault-k8s" + tag: "0.14.1-ubi" + + agentImage: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.9.0-ubi" + +server: + image: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.9.0-ubi" diff --git a/values.schema.json b/values.schema.json new file mode 100644 index 00000000..26f13674 --- /dev/null +++ b/values.schema.json @@ -0,0 +1,841 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "csi": { + "type": "object", + "properties": { + "daemonSet": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "updateStrategy": { + "type": "object", + "properties": { + "maxUnavailable": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "providersDir": { + "type": "string" + }, + "kubeletRootDir": { + "type": "string" + } + } + }, + "debug": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "extraArgs": { + "type": "array" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "pod": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "tolerations": { + "type": [ + "null", + "array", + "string" + ] + } + } + }, + "readinessProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "resources": { + "type": "object" + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "volumeMounts": { + "type": [ + "null", + "array" + ] + }, + "volumes": { + "type": [ + "null", + "array" + ] + } + } + }, + "global": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "imagePullSecrets": { + "type": "array" + }, + "openshift": { + "type": "boolean" + }, + "psp": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enable": { + "type": "boolean" + } + } + }, + "tlsDisable": { + "type": "boolean" + } + } + }, + "injector": { + "type": "object", + "properties": { + "affinity": { + "type": [ + "object", + "string" + ] + }, + "agentDefaults": { + "type": "object", + "properties": { + "cpuLimit": { + "type": "string" + }, + "cpuRequest": { + "type": "string" + }, + "memLimit": { + "type": "string" + }, + "memRequest": { + "type": "string" + }, + "template": { + "type": "string" + }, + "templateConfig": { + "type": "object", + "properties": { + "exitOnRetryFailure": { + "type": "boolean" + }, + "staticSecretRenderInterval": { + "type": "string" + } + } + } + } + }, + "agentImage": { + "type": "object", + "properties": { + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "authPath": { + "type": "string" + }, + "certs": { + "type": "object", + "properties": { + "caBundle": { + "type": "string" + }, + "certName": { + "type": "string" + }, + "keyName": { + "type": "string" + }, + "secretName": { + "type": [ + "null", + "string" + ] + } + } + }, + "enabled": { + "type": "boolean" + }, + "externalVaultAddr": { + "type": "string" + }, + "extraEnvironmentVars": { + "type": "object" + }, + "extraLabels": { + "type": "object" + }, + "failurePolicy": { + "type": "string" + }, + "hostNetwork": { + "type": "boolean" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "leaderElector": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "logFormat": { + "type": "string" + }, + "logLevel": { + "type": "string" + }, + "metrics": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "namespaceSelector": { + "type": "object" + }, + "nodeSelector": { + "type": [ + "null", + "object", + "string" + ] + }, + "objectSelector": { + "type": "object" + }, + "port": { + "type": "integer" + }, + "priorityClassName": { + "type": "string" + }, + "replicas": { + "type": "integer" + }, + "resources": { + "type": "object" + }, + "revokeOnShutdown": { + "type": "boolean" + }, + "service": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "tolerations": { + "type": [ + "null", + "array", + "string" + ] + }, + "webhookAnnotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "server": { + "type": "object", + "properties": { + "affinity": { + "type": [ + "object", + "string" + ] + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "auditStorage": { + "type": "object", + "properties": { + "accessMode": { + "type": "string" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "mountPath": { + "type": "string" + }, + "size": { + "type": "string" + }, + "storageClass": { + "type": [ + "null", + "string" + ] + } + } + }, + "authDelegator": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "dataStorage": { + "type": "object", + "properties": { + "accessMode": { + "type": "string" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "mountPath": { + "type": "string" + }, + "size": { + "type": "string" + }, + "storageClass": { + "type": [ + "null", + "string" + ] + } + } + }, + "dev": { + "type": "object", + "properties": { + "devRootToken": { + "type": "string" + }, + "enabled": { + "type": "boolean" + } + } + }, + "enabled": { + "type": "boolean" + }, + "enterpriseLicense": { + "type": "object", + "properties": { + "secretKey": { + "type": "string" + }, + "secretName": { + "type": "string" + } + } + }, + "extraArgs": { + "type": "string" + }, + "extraContainers": { + "type": [ + "null", + "array" + ] + }, + "extraEnvironmentVars": { + "type": "object" + }, + "extraInitContainers": { + "type": [ + "null", + "array" + ] + }, + "extraLabels": { + "type": "object" + }, + "extraSecretEnvironmentVars": { + "type": "array" + }, + "extraVolumes": { + "type": "array" + }, + "ha": { + "type": "object", + "properties": { + "apiAddr": { + "type": [ + "null", + "string" + ] + }, + "config": { + "type": "string" + }, + "disruptionBudget": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "maxUnavailable": { + "type": [ + "null", + "integer" + ] + } + } + }, + "enabled": { + "type": "boolean" + }, + "raft": { + "type": "object", + "properties": { + "config": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "setNodeId": { + "type": "boolean" + } + } + }, + "replicas": { + "type": "integer" + } + } + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "ingress": { + "type": "object", + "properties": { + "activeService": { + "type": "boolean" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "extraPaths": { + "type": "array" + }, + "hosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "paths": { + "type": "array" + } + } + } + }, + "ingressClassName": { + "type": "string" + }, + "labels": { + "type": "object" + }, + "tls": { + "type": "array" + } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "path": { + "type": "string" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "logFormat": { + "type": "string" + }, + "logLevel": { + "type": "string" + }, + "networkPolicy": { + "type": "object", + "properties": { + "egress": { + "type": "array" + }, + "enabled": { + "type": "boolean" + } + } + }, + "nodeSelector": { + "type": [ + "null", + "object", + "string" + ] + }, + "postStart": { + "type": "array" + }, + "preStopSleepSeconds": { + "type": "integer" + }, + "priorityClassName": { + "type": "string" + }, + "readinessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "resources": { + "type": "object" + }, + "route": { + "type": "object", + "properties": { + "activeService": { + "type": "boolean" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "labels": { + "type": "object" + } + } + }, + "service": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "port": { + "type": "integer" + }, + "targetPort": { + "type": "integer" + } + } + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "shareProcessNamespace": { + "type": "boolean" + }, + "standalone": { + "type": "object", + "properties": { + "config": { + "type": "string" + }, + "enabled": { + "type": [ + "string", + "boolean" + ] + } + } + }, + "statefulSet": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "tolerations": { + "type": [ + "null", + "array", + "string" + ] + }, + "updateStrategyType": { + "type": "string" + }, + "volumeMounts": { + "type": [ + "null", + "array" + ] + }, + "volumes": { + "type": [ + "null", + "array" + ] + } + } + }, + "ui": { + "type": "object", + "properties": { + "activeVaultPodOnly": { + "type": "boolean" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "externalPort": { + "type": "integer" + }, + "publishNotReadyAddresses": { + "type": "boolean" + }, + "serviceNodePort": { + "type": [ + "null", + "integer" + ] + }, + "serviceType": { + "type": "string" + }, + "targetPort": { + "type": "integer" + } + } + } + } +} diff --git a/values.yaml b/values.yaml new file mode 100644 index 00000000..5ba57d41 --- /dev/null +++ b/values.yaml @@ -0,0 +1,831 @@ +# Available parameters and their default values for the Vault chart. + +global: + # enabled is the master enabled switch. Setting this to true or false + # will enable or disable all the components within this chart by default. + enabled: true + # Image pull secret to use for registry authentication. + # Alternatively, the value may be specified as an array of strings. + imagePullSecrets: [] + # imagePullSecrets: + # - name: image-pull-secret + # TLS for end-to-end encrypted transport + tlsDisable: true + # If deploying to OpenShift + openshift: false + # Create PodSecurityPolicy for pods + psp: + enable: false + # Annotation for PodSecurityPolicy. + # This is a multi-line templated string map, and can also be set as YAML. + annotations: | + seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default,runtime/default + apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default + seccomp.security.alpha.kubernetes.io/defaultProfileName: runtime/default + apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default + +injector: + # True if you want to enable vault agent injection. + enabled: true + + replicas: 1 + + # Configures the port the injector should listen on + port: 8080 + + # If multiple replicas are specified, by default a leader will be determined + # so that only one injector attempts to create TLS certificates. + leaderElector: + enabled: true + + # If true, will enable a node exporter metrics endpoint at /metrics. + metrics: + enabled: false + + # External vault server address for the injector to use. Setting this will + # disable deployment of a vault server along with the injector. + externalVaultAddr: "" + + # image sets the repo and tag of the vault-k8s image to use for the injector. + image: + repository: "hashicorp/vault-k8s" + tag: "0.14.1" + pullPolicy: IfNotPresent + + # agentImage sets the repo and tag of the Vault image to use for the Vault Agent + # containers. This should be set to the official Vault image. Vault 1.3.1+ is + # required. + agentImage: + repository: "hashicorp/vault" + tag: "1.9.0" + + # The default values for the injected Vault Agent containers. + agentDefaults: + # For more information on configuring resources, see the K8s documentation: + # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + cpuLimit: "500m" + cpuRequest: "250m" + memLimit: "128Mi" + memRequest: "64Mi" + + # Default template type for secrets when no custom template is specified. + # Possible values include: "json" and "map". + template: "map" + + # Default values within Agent's template_config stanza. + templateConfig: + exitOnRetryFailure: true + staticSecretRenderInterval: "" + + # Mount Path of the Vault Kubernetes Auth Method. + authPath: "auth/kubernetes" + + # Configures the log verbosity of the injector. + # Supported log levels include: trace, debug, info, warn, error + logLevel: "info" + + # Configures the log format of the injector. Supported log formats: "standard", "json". + logFormat: "standard" + + # Configures all Vault Agent sidecars to revoke their token when shutting down + revokeOnShutdown: false + + # namespaceSelector is the selector for restricting the webhook to only + # specific namespaces. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector + # for more details. + # Example: + # namespaceSelector: + # matchLabels: + # sidecar-injector: enabled + namespaceSelector: {} + # objectSelector is the selector for restricting the webhook to only + # specific labels. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector + # for more details. + # Example: + # objectSelector: + # matchLabels: + # vault-sidecar-injector: enabled + objectSelector: {} + + # Configures failurePolicy of the webhook. The "unspecified" default behaviour deoends on the + # API Version of the WebHook. + # To block pod creation while webhook is unavailable, set the policy to `Fail` below. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy + # + failurePolicy: Ignore + + # Extra annotations to attach to the webhook + webhookAnnotations: {} + + certs: + # secretName is the name of the secret that has the TLS certificate and + # private key to serve the injector webhook. If this is null, then the + # injector will default to its automatic management mode that will assign + # a service account to the injector to generate its own certificates. + secretName: null + + # caBundle is a base64-encoded PEM-encoded certificate bundle for the CA + # that signed the TLS certificate that the webhook serves. This must be set + # if secretName is non-null, unless an external service like cert-manager is + # keeping the caBundle updated. + caBundle: "" + + # certName and keyName are the names of the files within the secret for + # the TLS cert and private key, respectively. These have reasonable + # defaults but can be customized if necessary. + certName: tls.crt + keyName: tls.key + + resources: {} + # resources: + # requests: + # memory: 256Mi + # cpu: 250m + # limits: + # memory: 256Mi + # cpu: 250m + + # extraEnvironmentVars is a list of extra environment variables to set in the + # injector deployment. + extraEnvironmentVars: {} + # KUBERNETES_SERVICE_HOST: kubernetes.default.svc + + # Affinity Settings for injector pods + # This can either be multi-line string or YAML matching the PodSpec's affinity field. + # Commenting out or setting as empty the affinity variable, will allow + # deployment of multiple replicas to single node services such as Minikube. + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: "{{ .Release.Name }}" + component: webhook + topologyKey: kubernetes.io/hostname + + # Toleration Settings for injector pods + # This should be either a multi-line string or YAML matching the Toleration array + # in a PodSpec. + tolerations: [] + + # nodeSelector labels for server pod assignment, formatted as a multi-line string or YAML map. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # Example: + # nodeSelector: + # beta.kubernetes.io/arch: amd64 + nodeSelector: {} + + # Priority class for injector pods + priorityClassName: "" + + # Extra annotations to attach to the injector pods + # This can either be YAML or a YAML-formatted multi-line templated string map + # of the annotations to apply to the injector pods + annotations: {} + + # Extra labels to attach to the agent-injector + # This should be a YAML map of the labels to apply to the injector + extraLabels: {} + + # Should the injector pods run on the host network (useful when using + # an alternate CNI in EKS) + hostNetwork: false + + # Injector service specific config + service: + # Extra annotations to attach to the injector service + annotations: {} + +server: + # If not set to true, Vault server will not be installed. See vault.mode in _helpers.tpl for implementation details + enabled: true + + # [Enterprise Only] This value refers to a Kubernetes secret that you have + # created that contains your enterprise license. If you are not using an + # enterprise image or if you plan to introduce the license key via another + # route, then leave secretName blank ("") or set it to null. + # Requires Vault Enterprise 1.8 or later. + enterpriseLicense: + # The name of the Kubernetes secret that holds the enterprise license. The + # secret must be in the same namespace that Vault is installed into. + secretName: "" + # The key within the Kubernetes secret that holds the enterprise license. + secretKey: "license" + + # Resource requests, limits, etc. for the server cluster placement. This + # should map directly to the value of the resources field for a PodSpec. + # By default no direct resource request is made. + + image: + repository: "hashicorp/vault" + tag: "1.9.0" + # Overrides the default Image Pull Policy + pullPolicy: IfNotPresent + + # Configure the Update Strategy Type for the StatefulSet + # See https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + updateStrategyType: "OnDelete" + + # Configure the logging verbosity for the Vault server. + # Supported log levels include: trace, debug, info, warn, error + logLevel: "" + + # Configure the logging format for the Vault server. + # Supported log formats include: standard, json + logFormat: "" + + resources: {} + # resources: + # requests: + # memory: 256Mi + # cpu: 250m + # limits: + # memory: 256Mi + # cpu: 250m + + # Ingress allows ingress services to be created to allow external access + # from Kubernetes to access Vault pods. + # If deployment is on OpenShift, the following block is ignored. + # In order to expose the service, use the route section below + ingress: + enabled: false + labels: {} + # traffic: external + annotations: {} + # | + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # or + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + + # Optionally use ingressClassName instead of deprecated annotation. + # See: https://kubernetes.io/docs/concepts/services-networking/ingress/#deprecated-annotation + ingressClassName: "" + + # As of Kubernetes 1.19, all Ingress Paths must have a pathType configured. The default value below should be sufficient in most cases. + # See: https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types for other possible values. + pathType: Prefix + + # When HA mode is enabled and K8s service registration is being used, + # configure the ingress to point to the Vault active service. + activeService: true + hosts: + - host: chart-example.local + paths: [] + ## Extra paths to prepend to the host configuration. This is useful when working with annotation based services. + extraPaths: [] + # - path: /* + # backend: + # service: + # name: ssl-redirect + # port: + # number: use-annotation + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + + # OpenShift only - create a route to expose the service + # The created route will be of type passthrough + route: + enabled: false + + # When HA mode is enabled and K8s service registration is being used, + # configure the route to point to the Vault active service. + activeService: true + + labels: {} + annotations: {} + host: chart-example.local + + # authDelegator enables a cluster role binding to be attached to the service + # account. This cluster role binding can be used to setup Kubernetes auth + # method. https://www.vaultproject.io/docs/auth/kubernetes.html + authDelegator: + enabled: true + + # extraInitContainers is a list of init containers. Specified as a YAML list. + # This is useful if you need to run a script to provision TLS certificates or + # write out configuration files in a dynamic way. + extraInitContainers: null + # # This example installs a plugin pulled from github into the /usr/local/libexec/vault/oauthapp folder, + # # which is defined in the volumes value. + # - name: oauthapp + # image: "alpine" + # command: [sh, -c] + # args: + # - cd /tmp && + # wget https://github.com/puppetlabs/vault-plugin-secrets-oauthapp/releases/download/v1.2.0/vault-plugin-secrets-oauthapp-v1.2.0-linux-amd64.tar.xz -O oauthapp.xz && + # tar -xf oauthapp.xz && + # mv vault-plugin-secrets-oauthapp-v1.2.0-linux-amd64 /usr/local/libexec/vault/oauthapp && + # chmod +x /usr/local/libexec/vault/oauthapp + # volumeMounts: + # - name: plugins + # mountPath: /usr/local/libexec/vault + + # extraContainers is a list of sidecar containers. Specified as a YAML list. + extraContainers: null + + # shareProcessNamespace enables process namespace sharing between Vault and the extraContainers + # This is useful if Vault must be signaled, e.g. to send a SIGHUP for log rotation + shareProcessNamespace: false + + # extraArgs is a string containing additional Vault server arguments. + extraArgs: "" + + # Used to define custom readinessProbe settings + readinessProbe: + enabled: true + # If you need to use a http path instead of the default exec + # path: /v1/sys/health?standbyok=true + + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + # Used to enable a livenessProbe for the pods + livenessProbe: + enabled: false + path: "/v1/sys/health?standbyok=true" + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 60 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + + # Used to set the sleep time during the preStop step + preStopSleepSeconds: 5 + + # Used to define commands to run after the pod is ready. + # This can be used to automate processes such as initialization + # or boostrapping auth methods. + postStart: [] + # - /bin/sh + # - -c + # - /vault/userconfig/myscript/run.sh + + # extraEnvironmentVars is a list of extra environment variables to set with the stateful set. These could be + # used to include variables required for auto-unseal. + extraEnvironmentVars: {} + # GOOGLE_REGION: global + # GOOGLE_PROJECT: myproject + # GOOGLE_APPLICATION_CREDENTIALS: /vault/userconfig/myproject/myproject-creds.json + + # extraSecretEnvironmentVars is a list of extra environment variables to set with the stateful set. + # These variables take value from existing Secret objects. + extraSecretEnvironmentVars: [] + # - envName: AWS_SECRET_ACCESS_KEY + # secretName: vault + # secretKey: AWS_SECRET_ACCESS_KEY + + # Deprecated: please use 'volumes' instead. + # extraVolumes is a list of extra volumes to mount. These will be exposed + # to Vault in the path `/vault/userconfig//`. The value below is + # an array of objects, examples are shown below. + extraVolumes: [] + # - type: secret (or "configMap") + # name: my-secret + # path: null # default is `/vault/userconfig` + + # volumes is a list of volumes made available to all containers. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumes: null + # - name: plugins + # emptyDir: {} + + # volumeMounts is a list of volumeMounts for the main server container. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumeMounts: null + # - mountPath: /usr/local/libexec/vault + # name: plugins + # readOnly: true + + # Affinity Settings + # Commenting out or setting as empty the affinity variable, will allow + # deployment to single node services such as Minikube + # This should be either a multi-line string or YAML matching the PodSpec's affinity field. + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: "{{ .Release.Name }}" + component: server + topologyKey: kubernetes.io/hostname + + # Toleration Settings for server pods + # This should be either a multi-line string or YAML matching the Toleration array + # in a PodSpec. + tolerations: [] + + # nodeSelector labels for server pod assignment, formatted as a multi-line string or YAML map. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # Example: + # nodeSelector: + # beta.kubernetes.io/arch: amd64 + nodeSelector: {} + + # Enables network policy for server pods + networkPolicy: + enabled: false + egress: [] + # egress: + # - to: + # - ipBlock: + # cidr: 10.0.0.0/24 + # ports: + # - protocol: TCP + # port: 443 + + # Priority class for server pods + priorityClassName: "" + + # Extra labels to attach to the server pods + # This should be a YAML map of the labels to apply to the server pods + extraLabels: {} + + # Extra annotations to attach to the server pods + # This can either be YAML or a YAML-formatted multi-line templated string map + # of the annotations to apply to the server pods + annotations: {} + + # Enables a headless service to be used by the Vault Statefulset + service: + enabled: true + # clusterIP controls whether a Cluster IP address is attached to the + # Vault service within Kubernetes. By default the Vault service will + # be given a Cluster IP address, set to None to disable. When disabled + # Kubernetes will create a "headless" service. Headless services can be + # used to communicate with pods directly through DNS instead of a round robin + # load balancer. + # clusterIP: None + + # Configures the service type for the main Vault service. Can be ClusterIP + # or NodePort. + #type: ClusterIP + + # The externalTrafficPolicy can be set to either Cluster or Local + # and is only valid for LoadBalancer and NodePort service types. + # The default value is Cluster. + # ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-traffic-policy + externalTrafficPolicy: Cluster + + # If type is set to "NodePort", a specific nodePort value can be configured, + # will be random if left blank. + #nodePort: 30000 + + # Port on which Vault server is listening + port: 8200 + # Target port to which the service should be mapped to + targetPort: 8200 + # Extra annotations for the service definition. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the service. + annotations: {} + + # This configures the Vault Statefulset to create a PVC for data + # storage when using the file or raft backend storage engines. + # See https://www.vaultproject.io/docs/configuration/storage/index.html to know more + dataStorage: + enabled: true + # Size of the PVC created + size: 10Gi + # Location where the PVC will be mounted. + mountPath: "/vault/data" + # Name of the storage class to use. If null it will use the + # configured default Storage Class. + storageClass: null + # Access Mode of the storage device being used for the PVC + accessMode: ReadWriteOnce + # Annotations to apply to the PVC + annotations: {} + + # This configures the Vault Statefulset to create a PVC for audit + # logs. Once Vault is deployed, initialized and unseal, Vault must + # be configured to use this for audit logs. This will be mounted to + # /vault/audit + # See https://www.vaultproject.io/docs/audit/index.html to know more + auditStorage: + enabled: false + # Size of the PVC created + size: 10Gi + # Location where the PVC will be mounted. + mountPath: "/vault/audit" + # Name of the storage class to use. If null it will use the + # configured default Storage Class. + storageClass: null + # Access Mode of the storage device being used for the PVC + accessMode: ReadWriteOnce + # Annotations to apply to the PVC + annotations: {} + + # Run Vault in "dev" mode. This requires no further setup, no state management, + # and no initialization. This is useful for experimenting with Vault without + # needing to unseal, store keys, et. al. All data is lost on restart - do not + # use dev mode for anything other than experimenting. + # See https://www.vaultproject.io/docs/concepts/dev-server.html to know more + dev: + enabled: false + + # Set VAULT_DEV_ROOT_TOKEN_ID value + devRootToken: "root" + + # Run Vault in "standalone" mode. This is the default mode that will deploy if + # no arguments are given to helm. This requires a PVC for data storage to use + # the "file" backend. This mode is not highly available and should not be scaled + # past a single replica. + standalone: + enabled: "-" + + # config is a raw string of default configuration when using a Stateful + # deployment. Default is to use a PersistentVolumeClaim mounted at /vault/data + # and store data there. This is only used when using a Replica count of 1, and + # using a stateful set. This should be HCL. + + # Note: Configuration files are stored in ConfigMaps so sensitive data + # such as passwords should be either mounted through extraSecretEnvironmentVars + # or through a Kube secret. For more information see: + # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + } + storage "file" { + path = "/vault/data" + } + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} + + # Run Vault in "HA" mode. There are no storage requirements unless audit log + # persistence is required. In HA mode Vault will configure itself to use Consul + # for its storage backend. The default configuration provided will work the Consul + # Helm project by default. It is possible to manually configure Vault to use a + # different HA backend. + ha: + enabled: false + replicas: 3 + + # Set the api_addr configuration for Vault HA + # See https://www.vaultproject.io/docs/configuration#api_addr + # If set to null, this will be set to the Pod IP Address + apiAddr: null + + # Enables Vault's integrated Raft storage. Unlike the typical HA modes where + # Vault's persistence is external (such as Consul), enabling Raft mode will create + # persistent volumes for Vault to store data according to the configuration under server.dataStorage. + # The Vault cluster will coordinate leader elections and failovers internally. + raft: + + # Enables Raft integrated storage + enabled: false + # Set the Node Raft ID to the name of the pod + setNodeId: false + + # Note: Configuration files are stored in ConfigMaps so sensitive data + # such as passwords should be either mounted through extraSecretEnvironmentVars + # or through a Kube secret. For more information see: + # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + } + + storage "raft" { + path = "/vault/data" + } + + service_registration "kubernetes" {} + + # config is a raw string of default configuration when using a Stateful + # deployment. Default is to use a Consul for its HA storage backend. + # This should be HCL. + + # Note: Configuration files are stored in ConfigMaps so sensitive data + # such as passwords should be either mounted through extraSecretEnvironmentVars + # or through a Kube secret. For more information see: + # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + } + storage "consul" { + path = "vault" + address = "HOST_IP:8500" + } + + service_registration "kubernetes" {} + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev-246514" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} + + # A disruption budget limits the number of pods of a replicated application + # that are down simultaneously from voluntary disruptions + disruptionBudget: + enabled: true + + # maxUnavailable will default to (n/2)-1 where n is the number of + # replicas. If you'd like a custom value, you can specify an override here. + maxUnavailable: null + + # Definition of the serviceAccount used to run Vault. + # These options are also used when using an external Vault server to validate + # Kubernetes tokens. + serviceAccount: + # Specifies whether a service account should be created + create: true + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + # Extra annotations for the serviceAccount definition. This can either be + # YAML or a YAML-formatted multi-line templated string map of the + # annotations to apply to the serviceAccount. + annotations: {} + + # Settings for the statefulSet used to run Vault. + statefulSet: + # Extra annotations for the statefulSet. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the statefulSet. + annotations: {} + +# Vault UI +ui: + # True if you want to create a Service entry for the Vault UI. + # + # serviceType can be used to control the type of service created. For + # example, setting this to "LoadBalancer" will create an external load + # balancer (for supported K8S installations) to access the UI. + enabled: false + publishNotReadyAddresses: true + # The service should only contain selectors for active Vault pod + activeVaultPodOnly: false + serviceType: "ClusterIP" + serviceNodePort: null + externalPort: 8200 + targetPort: 8200 + + # The externalTrafficPolicy can be set to either Cluster or Local + # and is only valid for LoadBalancer and NodePort service types. + # The default value is Cluster. + # ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-traffic-policy + externalTrafficPolicy: Cluster + + #loadBalancerSourceRanges: + # - 10.0.0.0/16 + # - 1.78.23.3/32 + + # loadBalancerIP: + + # Extra annotations to attach to the ui service + # This can either be YAML or a YAML-formatted multi-line templated string map + # of the annotations to apply to the ui service + annotations: {} + +# secrets-store-csi-driver-provider-vault +csi: + # True if you want to install a secrets-store-csi-driver-provider-vault daemonset. + # + # Requires installing the secrets-store-csi-driver separately, see: + # https://github.com/kubernetes-sigs/secrets-store-csi-driver#install-the-secrets-store-csi-driver + # + # With the driver and provider installed, you can mount Vault secrets into volumes + # similar to the Vault Agent injector, and you can also sync those secrets into + # Kubernetes secrets. + enabled: false + + image: + repository: "hashicorp/vault-csi-provider" + tag: "0.3.0" + pullPolicy: IfNotPresent + + # volumes is a list of volumes made available to all containers. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumes: null + # - name: tls + # secret: + # secretName: vault-tls + + # volumeMounts is a list of volumeMounts for the main server container. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumeMounts: null + # - name: tls + # mountPath: "/vault/tls" + # readOnly: true + + resources: {} + # resources: + # requests: + # cpu: 50m + # memory: 128Mi + # limits: + # cpu: 50m + # memory: 128Mi + + # Settings for the daemonSet used to run the provider. + daemonSet: + updateStrategy: + type: RollingUpdate + maxUnavailable: "" + # Extra annotations for the daemonSet. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the daemonSet. + annotations: {} + # Provider host path (must match the CSI provider's path) + providersDir: "/etc/kubernetes/secrets-store-csi-providers" + # Kubelet host path + kubeletRootDir: "/var/lib/kubelet" + + pod: + # Extra annotations for the provider pods. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the pod. + annotations: {} + + # Toleration Settings for provider pods + # This should be either a multi-line string or YAML matching the Toleration array + # in a PodSpec. + tolerations: [] + + serviceAccount: + # Extra annotations for the serviceAccount definition. This can either be + # YAML or a YAML-formatted multi-line templated string map of the + # annotations to apply to the serviceAccount. + annotations: {} + + # Used to configure readinessProbe for the pods. + readinessProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + # Used to configure livenessProbe for the pods. + livenessProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + + # Enables debug logging. + debug: false + + # Pass arbitrary additional arguments to vault-csi-provider. + extraArgs: [] From 12575ecf2ca0871326a30db814db6d445160549a Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 9 Dec 2021 14:59:10 -0600 Subject: [PATCH 0169/1263] Change site to clustername to allow for multiple clusters in a config group --- Makefile | 2 +- .../policies/application-policies.yaml | 10 +++--- acm/values.yaml | 4 +-- {site => clustergroup}/.helmignore | 0 clustergroup/Chart.yaml | 7 ++++ .../templates/applications.yaml | 6 ++-- .../templates/argocd-super-role.yaml | 10 +++--- {site => clustergroup}/templates/argocd.yaml | 4 +-- .../templates/gitops-namespace.yaml | 6 ++-- .../templates/namespaces.yaml | 4 +-- .../templates/operatorgroup.yaml | 8 ++--- .../templates/projects.yaml | 4 +-- .../templates/subsciptions.yaml | 2 +- .../templates/subscriptions.yaml | 2 +- {site => clustergroup}/values.yaml | 6 ++-- examples/values-example.yaml | 10 +++--- install/templates/argocd/application.yaml | 8 ++--- install/values.yaml | 2 +- reference-output.yaml | 2 +- site/Chart.yaml | 7 ---- tests/acm-normal.expected.yaml | 10 +++--- ....yaml => clustergroup-naked.expected.yaml} | 10 +++--- ...yaml => clustergroup-normal.expected.yaml} | 32 +++++++++---------- tests/install-naked.expected.yaml | 2 +- tests/install-normal.expected.yaml | 2 +- values-global.yaml | 2 +- 26 files changed, 81 insertions(+), 81 deletions(-) rename {site => clustergroup}/.helmignore (100%) create mode 100644 clustergroup/Chart.yaml rename {site => clustergroup}/templates/applications.yaml (89%) rename {site => clustergroup}/templates/argocd-super-role.yaml (72%) rename {site => clustergroup}/templates/argocd.yaml (97%) rename {site => clustergroup}/templates/gitops-namespace.yaml (54%) rename {site => clustergroup}/templates/namespaces.yaml (71%) rename {site => clustergroup}/templates/operatorgroup.yaml (63%) rename {site => clustergroup}/templates/projects.yaml (73%) rename {site => clustergroup}/templates/subsciptions.yaml (96%) rename {site => clustergroup}/templates/subscriptions.yaml (91%) rename {site => clustergroup}/values.yaml (93%) delete mode 100644 site/Chart.yaml rename tests/{site-naked.expected.yaml => clustergroup-naked.expected.yaml} (93%) rename tests/{site-normal.expected.yaml => clustergroup-normal.expected.yaml} (90%) diff --git a/Makefile b/Makefile index d1c2fa33..9dcb8307 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ argosecret: show: helm template common/install/ --name-template $(NAME) $(HELM_OPTS) -CHARTS=install site acm +CHARTS=install clustergroup acm test: # Test that all values used by the chart are in values.yaml with the same defaults as the pattern diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 669a3f51..070b0eb0 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -1,9 +1,9 @@ # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io -{{- range .Values.site.managedSites }} +{{- range .Values.clusterGroup.managedClusterGroups }} apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: - name: {{ .name }}-site-policy + name: {{ .name }}-clustergroup-policy annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true argocd.argoproj.io/compare-options: IgnoreExtraneous @@ -15,7 +15,7 @@ spec: apiVersion: policy.open-cluster-management.io/v1 kind: ConfigurationPolicy metadata: - name: {{ .name }}-site-config + name: {{ .name }}-clustergroup-config spec: remediationAction: enforce severity: med @@ -39,7 +39,7 @@ spec: source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} - path: {{ default "common/site" .path }} + path: {{ default "common/clustergroup" .path }} helm: valueFiles: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" @@ -87,7 +87,7 @@ placementRef: kind: PlacementRule apiGroup: apps.open-cluster-management.io subjects: - - name: {{ .name }}-site-policy + - name: {{ .name }}-clustergroup-policy kind: Policy apiGroup: policy.open-cluster-management.io --- diff --git a/acm/values.yaml b/acm/values.yaml index 51d3e936..ae8c0a86 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -5,6 +5,6 @@ global: targetRevision: main -site: - managedSites: +clusterGroup: + managedClusterGroups: diff --git a/site/.helmignore b/clustergroup/.helmignore similarity index 100% rename from site/.helmignore rename to clustergroup/.helmignore diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml new file mode 100644 index 00000000..771b7960 --- /dev/null +++ b/clustergroup/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +description: A Helm chart to create per-clustergroup ArgoCD applications and any required namespaces or subscriptions +keywords: +- manuela +- pattern +name: pattern-clustergroup +version: 0.0.1 diff --git a/site/templates/applications.yaml b/clustergroup/templates/applications.yaml similarity index 89% rename from site/templates/applications.yaml rename to clustergroup/templates/applications.yaml index 4f2c345e..7f1dd647 100644 --- a/site/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -1,5 +1,5 @@ -{{- $namespace := cat $.Values.global.pattern $.Values.site.name | replace " " "-" }} -{{- range .Values.site.applications }} +{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} +{{- range .Values.clusterGroup.applications }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -24,7 +24,7 @@ spec: helm: valueFiles: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" - - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Values.site.name }}.yaml" + - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Values.clusterGroup.name }}.yaml" # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL diff --git a/site/templates/argocd-super-role.yaml b/clustergroup/templates/argocd-super-role.yaml similarity index 72% rename from site/templates/argocd-super-role.yaml rename to clustergroup/templates/argocd-super-role.yaml index f4744b2d..f3c885f5 100644 --- a/site/templates/argocd-super-role.yaml +++ b/clustergroup/templates/argocd-super-role.yaml @@ -20,7 +20,7 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: {{ $.Values.global.pattern }}-{{ .Values.site.name }}-cluster-admin-rolebinding + name: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }}-cluster-admin-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -28,10 +28,10 @@ roleRef: subjects: - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-application-controller - name: {{ .Values.site.name }}-gitops-argocd-application-controller - namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }} + name: {{ .Values.clusterGroup.name }}-gitops-argocd-application-controller + namespace: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-server - name: {{ .Values.site.name }}-gitops-argocd-server - namespace: {{ $.Values.global.pattern }}-{{ .Values.site.name }} + name: {{ .Values.clusterGroup.name }}-gitops-argocd-server + namespace: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} diff --git a/site/templates/argocd.yaml b/clustergroup/templates/argocd.yaml similarity index 97% rename from site/templates/argocd.yaml rename to clustergroup/templates/argocd.yaml index 44a0edba..230f7da5 100644 --- a/site/templates/argocd.yaml +++ b/clustergroup/templates/argocd.yaml @@ -5,7 +5,7 @@ metadata: - argoproj.io/finalizer # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations - name: {{ .Values.site.name }}-gitops + name: {{ .Values.clusterGroup.name }}-gitops annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: @@ -19,7 +19,7 @@ spec: helm-with-kustomize\n init:\n command: [\"/bin/sh\", \"-c\"]\n args: [\"helm dependency build\"]\n generate:\n command: [/bin/bash, -c]\n args: [\"helm template . --name-template ${ARGOCD_APP_NAME:0:52} -f {{ .Values.global.valuesDirectoryURL }}/values-global.yaml - -f {{ .Values.global.valuesDirectoryURL }}/values-{{ .Values.site.name }}.yaml + -f {{ .Values.global.valuesDirectoryURL }}/values-{{ .Values.clusterGroup.name }}.yaml --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE diff --git a/site/templates/gitops-namespace.yaml b/clustergroup/templates/gitops-namespace.yaml similarity index 54% rename from site/templates/gitops-namespace.yaml rename to clustergroup/templates/gitops-namespace.yaml index b6af2263..785ef75f 100644 --- a/site/templates/gitops-namespace.yaml +++ b/clustergroup/templates/gitops-namespace.yaml @@ -2,10 +2,10 @@ apiVersion: v1 kind: Namespace metadata: labels: - name: {{ $.Values.global.pattern }}-{{ .Values.site.name }} + name: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} # The name here needs to be consistent with # - acm/templates/policies/application-policies.yaml - # - site/templates/applications.yaml + # - clustergroup/templates/applications.yaml # - any references to secrets and route URLs in documentation - name: {{ $.Values.global.pattern }}-{{ .Values.site.name }} + name: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} spec: {} diff --git a/site/templates/namespaces.yaml b/clustergroup/templates/namespaces.yaml similarity index 71% rename from site/templates/namespaces.yaml rename to clustergroup/templates/namespaces.yaml index 5e5353bf..b3bc86a5 100644 --- a/site/templates/namespaces.yaml +++ b/clustergroup/templates/namespaces.yaml @@ -1,10 +1,10 @@ -{{- range .Values.site.namespaces }} +{{- range .Values.clusterGroup.namespaces }} apiVersion: v1 kind: Namespace metadata: labels: name: {{ default "pattern" $.Release.name }} - argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.site.name }} + argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} name: {{ . }} spec: --- diff --git a/site/templates/operatorgroup.yaml b/clustergroup/templates/operatorgroup.yaml similarity index 63% rename from site/templates/operatorgroup.yaml rename to clustergroup/templates/operatorgroup.yaml index d22b6299..0180a912 100644 --- a/site/templates/operatorgroup.yaml +++ b/clustergroup/templates/operatorgroup.yaml @@ -1,6 +1,6 @@ -{{- range .Values.site.namespaces }} +{{- range .Values.clusterGroup.namespaces }} -{{- if empty $.Values.site.operatorgroupExcludes }} +{{- if empty $.Values.clusterGroup.operatorgroupExcludes }} apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -10,7 +10,7 @@ spec: targetNamespaces: - {{ . }} --- -{{- else if not (has . $.Values.site.operatorgroupExcludes) }} +{{- else if not (has . $.Values.clusterGroup.operatorgroupExcludes) }} apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -22,4 +22,4 @@ spec: --- {{- end }} -{{- end }} \ No newline at end of file +{{- end }} diff --git a/site/templates/projects.yaml b/clustergroup/templates/projects.yaml similarity index 73% rename from site/templates/projects.yaml rename to clustergroup/templates/projects.yaml index 43242026..d74e2cba 100644 --- a/site/templates/projects.yaml +++ b/clustergroup/templates/projects.yaml @@ -1,9 +1,9 @@ -{{- range .Values.site.projects }} +{{- range .Values.clusterGroup.projects }} apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: {{ . }} - namespace: {{ $.Values.global.pattern }}-{{ $.Values.site.name }} + namespace: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} spec: description: "Pattern {{ . }}" destinations: diff --git a/site/templates/subsciptions.yaml b/clustergroup/templates/subsciptions.yaml similarity index 96% rename from site/templates/subsciptions.yaml rename to clustergroup/templates/subsciptions.yaml index c1cbcd49..cf295bb2 100644 --- a/site/templates/subsciptions.yaml +++ b/clustergroup/templates/subsciptions.yaml @@ -1,4 +1,4 @@ -{{- range .Values.site.subscriptions }} +{{- range .Values.clusterGroup.subscriptions }} {{- $subs := . }} {{- $installPlanValue := .installPlanApproval }} diff --git a/site/templates/subscriptions.yaml b/clustergroup/templates/subscriptions.yaml similarity index 91% rename from site/templates/subscriptions.yaml rename to clustergroup/templates/subscriptions.yaml index 65ec71f2..e3b43ac3 100644 --- a/site/templates/subscriptions.yaml +++ b/clustergroup/templates/subscriptions.yaml @@ -1,4 +1,4 @@ -{{- range .Values.site.subscriptions }} +{{- range .Values.clusterGroup.subscriptions }} apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: diff --git a/site/values.yaml b/clustergroup/values.yaml similarity index 93% rename from site/values.yaml rename to clustergroup/values.yaml index d02a9c82..aec0fcd4 100644 --- a/site/values.yaml +++ b/clustergroup/values.yaml @@ -6,14 +6,14 @@ global: syncPolicy: Automatic installPlanApproval: Automatic -site: +clusterGroup: name: example proposedOptions: manageGitops: True isHubCluster: True -# managedSites: +# managedClusterGroups: # - name: factory # # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git # # Location of values-global.yaml, values-{name}.yaml, values-{app}.yaml @@ -21,7 +21,7 @@ site: # targetRevision: main # path: applications/factory # helmOverrides: -# - name: site.isHubCluster +# - name: clusterGroup.isHubCluster # value: false # clusterSelector: # matchExpressions: diff --git a/examples/values-example.yaml b/examples/values-example.yaml index fac57e81..47d79e34 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -4,7 +4,7 @@ global: syncPolicy: Automatic installPlanApproval: Automatic -site: +clusterGroup: name: example namespaces: @@ -39,19 +39,19 @@ site: project: datacenter path: charts/datacenter/pipelines - managedSites: + managedClusterGroups: - name: edge # Optional - Point to a different repo # repoURL: https://github.com/dagger-refuse-cool/mySite.git - # Must contain values-{sitename}.yaml at the top level + # Must contain values-{clustergroupname}.yaml at the top level targetRevision: main helmOverrides: # Values must be strings! - - name: site.isHubCluster + - name: clusterGroup.isHubCluster value: "false" clusterSelector: # matchLabels: -# site: factory +# clusterGroup: factory matchExpressions: - key: vendor operator: In diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 4da1b595..54c6a629 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -3,21 +3,21 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: {{ .Release.Name }}-{{ .Values.main.siteName }} + name: {{ .Release.Name }}-{{ .Values.main.clusterGroupName }} namespace: openshift-gitops spec: destination: name: in-cluster - namespace: {{ .Release.Name }}-{{ .Values.main.siteName }} + namespace: {{ .Release.Name }}-{{ .Values.main.clusterGroupName }} project: default source: repoURL: {{ .Values.main.git.repoURL }} targetRevision: {{ .Values.main.git.revision }} - path: common/site + path: common/clustergroup helm: valueFiles: - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-global.yaml" - - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-{{ .Values.main.siteName }}.yaml" + - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-{{ .Values.main.clusterGroupName }}.yaml" # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 parameters: - name: global.repoURL diff --git a/install/values.yaml b/install/values.yaml index 859324d7..05d57466 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -15,7 +15,7 @@ main: source: redhat-operators csv: v1.3.0 - siteName: default + clusterGroupName: default global: imageregistry: diff --git a/reference-output.yaml b/reference-output.yaml index 5294bfcc..aa0e27e7 100644 --- a/reference-output.yaml +++ b/reference-output.yaml @@ -83,7 +83,7 @@ spec: source: repoURL: https://github.com/beekhof/common.git targetRevision: main - path: common/site + path: common/clustergroup helm: valueFiles: - "https://github.com/beekhof/patterns/raw/main/values-global.yaml" diff --git a/site/Chart.yaml b/site/Chart.yaml deleted file mode 100644 index 4216d6be..00000000 --- a/site/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v2 -description: A Helm chart to create per-site ArgoCD applications and any required namespaces or subscriptions -keywords: -- manuela -- pattern -name: pattern-site -version: 0.0.1 diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 495b80b4..a2856da1 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -81,7 +81,7 @@ placementRef: kind: PlacementRule apiGroup: apps.open-cluster-management.io subjects: - - name: edge-site-policy + - name: edge-clustergroup-policy kind: Policy apiGroup: policy.open-cluster-management.io --- @@ -143,7 +143,7 @@ spec: apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: - name: edge-site-policy + name: edge-clustergroup-policy annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true argocd.argoproj.io/compare-options: IgnoreExtraneous @@ -155,7 +155,7 @@ spec: apiVersion: policy.open-cluster-management.io/v1 kind: ConfigurationPolicy metadata: - name: edge-site-config + name: edge-clustergroup-config spec: remediationAction: enforce severity: med @@ -179,7 +179,7 @@ spec: source: repoURL: https://github.com/pattern-clone/mypattern targetRevision: main - path: common/site + path: common/clustergroup helm: valueFiles: - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" @@ -195,7 +195,7 @@ spec: value: mypattern - name: global.valuesDirectoryURL value: https://github.com/pattern-clone/mypattern/raw/main - - name: site.isHubCluster + - name: clusterGroup.isHubCluster value: "false" destination: server: https://kubernetes.default.svc diff --git a/tests/site-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml similarity index 93% rename from tests/site-naked.expected.yaml rename to tests/clustergroup-naked.expected.yaml index e0d32ae0..f48965df 100644 --- a/tests/site-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -1,5 +1,5 @@ --- -# Source: pattern-site/templates/gitops-namespace.yaml +# Source: pattern-clustergroup/templates/gitops-namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -7,12 +7,12 @@ metadata: name: common-example # The name here needs to be consistent with # - acm/templates/policies/application-policies.yaml - # - site/templates/applications.yaml + # - clustergroup/templates/applications.yaml # - any references to secrets and route URLs in documentation name: common-example spec: {} --- -# Source: pattern-site/templates/argocd-super-role.yaml +# Source: pattern-clustergroup/templates/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -31,7 +31,7 @@ subjects: name: openshift-gitops-argocd-server namespace: openshift-gitops --- -# Source: pattern-site/templates/argocd-super-role.yaml +# Source: pattern-clustergroup/templates/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -52,7 +52,7 @@ subjects: name: example-gitops-argocd-server namespace: common-example --- -# Source: pattern-site/templates/argocd.yaml +# Source: pattern-clustergroup/templates/argocd.yaml apiVersion: argoproj.io/v1alpha1 kind: ArgoCD metadata: diff --git a/tests/site-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml similarity index 90% rename from tests/site-normal.expected.yaml rename to tests/clustergroup-normal.expected.yaml index 31607a8a..2580b372 100644 --- a/tests/site-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -1,5 +1,5 @@ --- -# Source: pattern-site/templates/gitops-namespace.yaml +# Source: pattern-clustergroup/templates/gitops-namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -7,12 +7,12 @@ metadata: name: mypattern-example # The name here needs to be consistent with # - acm/templates/policies/application-policies.yaml - # - site/templates/applications.yaml + # - clustergroup/templates/applications.yaml # - any references to secrets and route URLs in documentation name: mypattern-example spec: {} --- -# Source: pattern-site/templates/namespaces.yaml +# Source: pattern-clustergroup/templates/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -22,7 +22,7 @@ metadata: name: open-cluster-management spec: --- -# Source: pattern-site/templates/namespaces.yaml +# Source: pattern-clustergroup/templates/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -32,7 +32,7 @@ metadata: name: application-ci spec: --- -# Source: pattern-site/templates/argocd-super-role.yaml +# Source: pattern-clustergroup/templates/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -51,7 +51,7 @@ subjects: name: openshift-gitops-argocd-server namespace: openshift-gitops --- -# Source: pattern-site/templates/argocd-super-role.yaml +# Source: pattern-clustergroup/templates/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -72,7 +72,7 @@ subjects: name: example-gitops-argocd-server namespace: mypattern-example --- -# Source: pattern-site/templates/projects.yaml +# Source: pattern-clustergroup/templates/projects.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -93,7 +93,7 @@ spec: - '*' status: {} --- -# Source: pattern-site/templates/applications.yaml +# Source: pattern-clustergroup/templates/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -137,7 +137,7 @@ spec: automated: {} # selfHeal: true --- -# Source: pattern-site/templates/applications.yaml +# Source: pattern-clustergroup/templates/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -172,7 +172,7 @@ spec: automated: {} # selfHeal: true --- -# Source: pattern-site/templates/argocd.yaml +# Source: pattern-clustergroup/templates/argocd.yaml apiVersion: argoproj.io/v1alpha1 kind: ArgoCD metadata: @@ -267,7 +267,7 @@ spec: ca: {} status: --- -# Source: pattern-site/templates/operatorgroup.yaml +# Source: pattern-clustergroup/templates/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -277,7 +277,7 @@ spec: targetNamespaces: - open-cluster-management --- -# Source: pattern-site/templates/operatorgroup.yaml +# Source: pattern-clustergroup/templates/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -287,7 +287,7 @@ spec: targetNamespaces: - application-ci --- -# Source: pattern-site/templates/subsciptions.yaml +# Source: pattern-clustergroup/templates/subsciptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -300,7 +300,7 @@ spec: channel: release-2.3 installPlanApproval: Automatic --- -# Source: pattern-site/templates/subsciptions.yaml +# Source: pattern-clustergroup/templates/subsciptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -313,7 +313,7 @@ spec: channel: stable installPlanApproval: Automatic --- -# Source: pattern-site/templates/subscriptions.yaml +# Source: pattern-clustergroup/templates/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -326,7 +326,7 @@ spec: channel: release-2.3 installPlanApproval: Automatic --- -# Source: pattern-site/templates/subscriptions.yaml +# Source: pattern-clustergroup/templates/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: diff --git a/tests/install-naked.expected.yaml b/tests/install-naked.expected.yaml index af97dbcc..8ec1e715 100644 --- a/tests/install-naked.expected.yaml +++ b/tests/install-naked.expected.yaml @@ -21,7 +21,7 @@ spec: source: repoURL: https://github.com/pattern-clone/mypattern targetRevision: main - path: common/site + path: common/clustergroup helm: valueFiles: - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yaml index 3a7704ff..6f2fdc06 100644 --- a/tests/install-normal.expected.yaml +++ b/tests/install-normal.expected.yaml @@ -21,7 +21,7 @@ spec: source: repoURL: https://github.com/pattern-clone/mypattern targetRevision: main - path: common/site + path: common/clustergroup helm: valueFiles: - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" diff --git a/values-global.yaml b/values-global.yaml index c273d217..dcda0607 100644 --- a/values-global.yaml +++ b/values-global.yaml @@ -12,5 +12,5 @@ global: dev_revision: main main: - siteName: example + clusterGroupName: example From 9f486189ab675eb3b73f08d34a2814718b2d4796 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 9 Dec 2021 15:36:22 -0600 Subject: [PATCH 0170/1263] Remove staging and adjust tests to reflect that --- acm/templates/managed-clusters/staging.yaml | 56 ------------------- tests/acm-naked.expected.yaml | 60 --------------------- tests/acm-normal.expected.yaml | 60 --------------------- 3 files changed, 176 deletions(-) delete mode 100644 acm/templates/managed-clusters/staging.yaml diff --git a/acm/templates/managed-clusters/staging.yaml b/acm/templates/managed-clusters/staging.yaml deleted file mode 100644 index 1c6fd818..00000000 --- a/acm/templates/managed-clusters/staging.yaml +++ /dev/null @@ -1,56 +0,0 @@ -apiVersion: cluster.open-cluster-management.io/v1 -kind: ManagedCluster -metadata: - labels: - cloud: auto-detect - vendor: auto-detect - name: staging - gitops-mgmt: argocd - manuela: factory - name: staging - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -spec: - hubAcceptsClient: true ---- -apiVersion: agent.open-cluster-management.io/v1 -kind: KlusterletAddonConfig -metadata: - name: staging - namespace: staging - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -spec: - clusterName: staging - clusterNamespace: staging - clusterLabels: - cloud: auto-detect - vendor: auto-detect - applicationManager: - enabled: true - policyController: - enabled: true - searchCollector: - enabled: true - certPolicyController: - enabled: true - iamPolicyController: - enabled: true - version: "2.0" ---- -apiVersion: internal.open-cluster-management.io/v1beta1 -kind: ManagedClusterInfo -metadata: - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - labels: - argocd.argoproj.io/instance: manuela-acm - cloud: auto-detect - gitops-mgmt: argocd - manuela: factory - name: staging - vendor: auto-detect - name: staging - namespace: staging -spec: - loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index ed109ec5..313235c5 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -2,66 +2,6 @@ # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io --- -# Source: acm/templates/managed-clusters/staging.yaml -apiVersion: agent.open-cluster-management.io/v1 -kind: KlusterletAddonConfig -metadata: - name: staging - namespace: staging - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -spec: - clusterName: staging - clusterNamespace: staging - clusterLabels: - cloud: auto-detect - vendor: auto-detect - applicationManager: - enabled: true - policyController: - enabled: true - searchCollector: - enabled: true - certPolicyController: - enabled: true - iamPolicyController: - enabled: true - version: "2.0" ---- -# Source: acm/templates/managed-clusters/staging.yaml -apiVersion: cluster.open-cluster-management.io/v1 -kind: ManagedCluster -metadata: - labels: - cloud: auto-detect - vendor: auto-detect - name: staging - gitops-mgmt: argocd - manuela: factory - name: staging - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -spec: - hubAcceptsClient: true ---- -# Source: acm/templates/managed-clusters/staging.yaml -apiVersion: internal.open-cluster-management.io/v1beta1 -kind: ManagedClusterInfo -metadata: - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - labels: - argocd.argoproj.io/instance: manuela-acm - cloud: auto-detect - gitops-mgmt: argocd - manuela: factory - name: staging - vendor: auto-detect - name: staging - namespace: staging -spec: - loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K ---- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index a2856da1..8b3be6f1 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -1,64 +1,4 @@ --- -# Source: acm/templates/managed-clusters/staging.yaml -apiVersion: agent.open-cluster-management.io/v1 -kind: KlusterletAddonConfig -metadata: - name: staging - namespace: staging - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -spec: - clusterName: staging - clusterNamespace: staging - clusterLabels: - cloud: auto-detect - vendor: auto-detect - applicationManager: - enabled: true - policyController: - enabled: true - searchCollector: - enabled: true - certPolicyController: - enabled: true - iamPolicyController: - enabled: true - version: "2.0" ---- -# Source: acm/templates/managed-clusters/staging.yaml -apiVersion: cluster.open-cluster-management.io/v1 -kind: ManagedCluster -metadata: - labels: - cloud: auto-detect - vendor: auto-detect - name: staging - gitops-mgmt: argocd - manuela: factory - name: staging - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -spec: - hubAcceptsClient: true ---- -# Source: acm/templates/managed-clusters/staging.yaml -apiVersion: internal.open-cluster-management.io/v1beta1 -kind: ManagedClusterInfo -metadata: - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - labels: - argocd.argoproj.io/instance: manuela-acm - cloud: auto-detect - gitops-mgmt: argocd - manuela: factory - name: staging - vendor: auto-detect - name: staging - namespace: staging -spec: - loggingCA: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGekNDQWYrZ0F3SUJBZ0lSQVBDcVNwN3YyQ1hiVVJSNXl6TmRXcDB3RFFZSktvWklodmNOQVFFTEJRQXcKSlRFak1DRUdBMVVFQXhNYWJYVnNkR2xqYkhWemRHVnlhSFZpTFd0c2RYTjBaWEpzWlhRd0hoY05NakV3T0RJdwpNREUxT0RNM1doY05Nakl3T0RJd01ERTFPRE0zV2pBbE1TTXdJUVlEVlFRREV4cHRkV3gwYVdOc2RYTjBaWEpvCmRXSXRhMngxYzNSbGNteGxkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNNFAKQXcvbmc3RERiYWxvakhwQXU3U1hnNWl2TEcvUGluMTJkK0oycVhnUHI0Y0MzVEsxRzdoT3dLSldoRzVLU3RZRQpnT0cvTzhkd1REUTE2aVYrYThEc2FGcGZNR0ZNMXBQVjA1TXJiMENlNUJiR3hLblVEVHNjY04zWE1FendWQk1ECjljUThRZzVVYjdIZklzS2FCVTFVaUtML01ySE1VYkNablBHQ1JkSGpBUmR3ejF5Z0NuRlExdTJubEg3WFNYdDgKdEhwYkVENkFsWUdMWFhFeklNQXUrSyt4WElPOFlqTUZwM3RYVkdhNWdYdzFjdktpR2M2WFRoS1BHWHowVW12egpHa2V3T21lZFFCRUNjTDlwUFNtdVdCeHJMZ3p4SDVaTjRGWVA1dWR3bzJTS2t5MDhqVHVPSnZtVlp2ajJsVnlRCkQ2NHBWVENKRlNNM3l1L1ZscEVDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnS2tNQjBHQTFVZEpRUVcKTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUNLMVN6bHJydVJXbzBBdUlHYnJLUS9HQUFhRnNMdEwxUFJMNUU1bFZmS1FTbjdmd1lWCmZ5VDh2NTZlOVAycE9admRIUjYwRms4OG02dGM0Z2E5LzlYelBFM0xZVHpERXlWNElSQkNqbTF0TklTMzhndkcKdE9tamxkWjZpUFUzQTd4K2FLdWhmWUJLcjBTZmpwZlp5dG1JTm9UUjFJbExnczFQaXQwMkxSdWM1a3hxTlF6RwpuQ0d6YkpLUE51OS90U3RsMmM1cVRZOHRJUEdwdmNYbEpaZFkycm9GVmREUlRhd1JQekx2SWlIUUJLYnZHSFEvCm9XZEp4ZGJvZU04bXRRNTNIMkNRQUFFaEc0TllJZFFnbkhhY1crdXJhRlFKVk5vU1V0VlRBa1ROV1FOZHN2cDEKNlNzaXZzd1NKc0dpMGh2MXo4eEV1NlFuejdSdGdsbVdYanRECi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K ---- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub From 87ad473bea253580eeb5a755357264f03d5eddfa Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 2 Dec 2021 12:49:51 +1100 Subject: [PATCH 0171/1263] Update examples for recent cleanups --- examples/kustomize-renderer/environment.yaml | 4 ++-- examples/kustomize-renderer/templates/environment.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/kustomize-renderer/environment.yaml b/examples/kustomize-renderer/environment.yaml index b3ad1ea2..de4c48a9 100644 --- a/examples/kustomize-renderer/environment.yaml +++ b/examples/kustomize-renderer/environment.yaml @@ -3,8 +3,8 @@ kind: ConfigMap metadata: name: environment data: - IMAGE_PROVIDER: {{ .Values.global.quay.provider }} - IMAGE_ACCOUNT: {{ .Values.global.quay.account }} + IMAGE_PROVIDER: {{ .Values.global.imageregistry.hostname }} + IMAGE_ACCOUNT: {{ .Values.global.imageregistry.account }} GIT_EMAIL: {{ .Values.global.git.email }} GIT_DEV_REPO_URL: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }}/manuela-dev.git GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} diff --git a/examples/kustomize-renderer/templates/environment.yaml b/examples/kustomize-renderer/templates/environment.yaml index b3ad1ea2..de4c48a9 100644 --- a/examples/kustomize-renderer/templates/environment.yaml +++ b/examples/kustomize-renderer/templates/environment.yaml @@ -3,8 +3,8 @@ kind: ConfigMap metadata: name: environment data: - IMAGE_PROVIDER: {{ .Values.global.quay.provider }} - IMAGE_ACCOUNT: {{ .Values.global.quay.account }} + IMAGE_PROVIDER: {{ .Values.global.imageregistry.hostname }} + IMAGE_ACCOUNT: {{ .Values.global.imageregistry.account }} GIT_EMAIL: {{ .Values.global.git.email }} GIT_DEV_REPO_URL: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }}/manuela-dev.git GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} From d0409b8493040148b59021e51e99a7282550980d Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 10 Dec 2021 12:58:51 +1100 Subject: [PATCH 0172/1263] Support ocp authentication for namespaced argos --- site/templates/argocd.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/site/templates/argocd.yaml b/site/templates/argocd.yaml index 44a0edba..baed2d7d 100644 --- a/site/templates/argocd.yaml +++ b/site/templates/argocd.yaml @@ -45,6 +45,7 @@ spec: cpu: 500m memory: 2Gi dex: + openShiftOAuth: true resources: limits: cpu: 500m From e8e64f9efa58bf8f0e6162e1c1a30c6b8a76471f Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 2 Dec 2021 12:49:51 +1100 Subject: [PATCH 0173/1263] Update examples for recent cleanups --- examples/kustomize-renderer/environment.yaml | 4 ++-- examples/kustomize-renderer/templates/environment.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/kustomize-renderer/environment.yaml b/examples/kustomize-renderer/environment.yaml index b3ad1ea2..de4c48a9 100644 --- a/examples/kustomize-renderer/environment.yaml +++ b/examples/kustomize-renderer/environment.yaml @@ -3,8 +3,8 @@ kind: ConfigMap metadata: name: environment data: - IMAGE_PROVIDER: {{ .Values.global.quay.provider }} - IMAGE_ACCOUNT: {{ .Values.global.quay.account }} + IMAGE_PROVIDER: {{ .Values.global.imageregistry.hostname }} + IMAGE_ACCOUNT: {{ .Values.global.imageregistry.account }} GIT_EMAIL: {{ .Values.global.git.email }} GIT_DEV_REPO_URL: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }}/manuela-dev.git GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} diff --git a/examples/kustomize-renderer/templates/environment.yaml b/examples/kustomize-renderer/templates/environment.yaml index b3ad1ea2..de4c48a9 100644 --- a/examples/kustomize-renderer/templates/environment.yaml +++ b/examples/kustomize-renderer/templates/environment.yaml @@ -3,8 +3,8 @@ kind: ConfigMap metadata: name: environment data: - IMAGE_PROVIDER: {{ .Values.global.quay.provider }} - IMAGE_ACCOUNT: {{ .Values.global.quay.account }} + IMAGE_PROVIDER: {{ .Values.global.imageregistry.hostname }} + IMAGE_ACCOUNT: {{ .Values.global.imageregistry.account }} GIT_EMAIL: {{ .Values.global.git.email }} GIT_DEV_REPO_URL: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }}/manuela-dev.git GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} From 671bd09bb95e67dccc3c6ad442888f00b4367c13 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 10 Dec 2021 12:58:51 +1100 Subject: [PATCH 0174/1263] Support ocp authentication for namespaced argos --- clustergroup/templates/argocd.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/clustergroup/templates/argocd.yaml b/clustergroup/templates/argocd.yaml index 230f7da5..8abcb3f3 100644 --- a/clustergroup/templates/argocd.yaml +++ b/clustergroup/templates/argocd.yaml @@ -45,6 +45,7 @@ spec: cpu: 500m memory: 2Gi dex: + openShiftOAuth: true resources: limits: cpu: 500m From cc3e4cddc49cfd0b14d580b0307172e59150fb1b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 13 Dec 2021 15:22:13 +0100 Subject: [PATCH 0175/1263] Make sure that argo can manage cluster-wide resources from the relevant namespaces Otherwise we can error out with: Cluster level ClusterRoleBinding vault-server-binding cannot be managed when in namespaced mode By setting ARGOCD_CLUSTER_CONFIG_NAMESPACES to the namespaces where argocd instance is installed we allow it to create cluster wide resources for all namespaces. Tested this and now I am correctly able to invoke the vault helm chart from argo without the above error. References: - https://github.com/argoproj/argo-cd/issues/5886 - https://github.com/argoproj-labs/argocd-operator/issues/385 --- install/templates/argocd/subscription.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/install/templates/argocd/subscription.yaml b/install/templates/argocd/subscription.yaml index 1f40e25d..c1fb9a4e 100644 --- a/install/templates/argocd/subscription.yaml +++ b/install/templates/argocd/subscription.yaml @@ -12,6 +12,10 @@ spec: name: openshift-gitops-operator source: redhat-operators sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: {{ .Release.Name }}-{{ .Values.main.clusterGroupName }},openshift-gitops {{- if .Values.main.options.useCSV }} startingCSV: openshift-gitops-operator.{{ .Values.main.gitops.csv }} {{- end }} From d1c3e32d78c9fa06db16dd456da78d30850ec8b6 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 16 Dec 2021 16:02:43 -0600 Subject: [PATCH 0176/1263] Add some vault utilities and add a gitignore entry --- .gitignore | 1 + scripts/vault-utils.sh | 65 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100755 scripts/vault-utils.sh diff --git a/.gitignore b/.gitignore index 0dc4d5a1..dc540072 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ *.swo values-secret.yaml .*-expected.yaml +.vault.init diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh new file mode 100755 index 00000000..8c386beb --- /dev/null +++ b/scripts/vault-utils.sh @@ -0,0 +1,65 @@ +#!/bin/sh + +# Assumptions - vault in the demo will be running in non-HA mode so there will only be a vault-0 +# vault will be running in the "vault" namespace + +vault_delete() +{ + oc -n vault delete pod vault-0 & + oc -n vault delete pvc data-vault-0 & +} + +vault_ready_check() +{ +#NAME READY STATUS RESTARTS AGE +#vault-0 1/1 Running 0 44m +#vault-agent-injector-8d9cfcd47-skklw 1/1 Running 0 121m + oc get po -n vault | grep vault-0 | awk '{ print $2, $3 }' 2>/dev/null +} + +get_vault_ready() +{ + rdy_output=`vault_ready_check` + + # Things we may have to wait for: + # being assigned to a host + if [ "$?" ]; then + sleep 5 + until [ "$rdy_output" ] + do + sleep 5 + rdy_output=`vault_ready_check` + done + fi + + echo $rdy_output +} + +vault_unseal() +{ + for unseal in `cat $1 | grep "Unseal Key" | awk '{ print $4 }'` + do + oc -n vault exec vault-0 -- vault operator unseal $unseal + done +} + +vault_init() +{ + if [ -n "$1" ]; then + file=$1 + else + file=common/vault.init + fi + + rdy_check=`get_vault_ready` + until [ "$rdy_check" = "0/1 Running" ] + do + echo $rdy_check + rdy_check=`get_vault_ready` + done + + oc -n vault exec vault-0 -- vault operator init | tee $file + vault_unseal $file +} + +$@ From 63d090c537901d0f56736d8137fbb89ea6763a73 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 16 Dec 2021 16:07:50 -0600 Subject: [PATCH 0177/1263] Add Makefile target to init and unseal vault --- .gitignore | 2 +- Makefile | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index dc540072..2f656a0a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ *.swo values-secret.yaml .*-expected.yaml -.vault.init +pattern-vault.init diff --git a/Makefile b/Makefile index 9dcb8307..c621f227 100644 --- a/Makefile +++ b/Makefile @@ -44,4 +44,7 @@ upgrade: uninstall: helm uninstall $(NAME) +vault-init: + common/scripts/vault-utils.sh vault_init common/pattern-vault.init + .phony: install test From acd0f3a3a33a6018d2fcf1e1d791b06de0ac45ac Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 17 Dec 2021 14:37:08 -0600 Subject: [PATCH 0178/1263] Add an unseal target and provide it a default in the script --- Makefile | 3 +++ scripts/vault-utils.sh | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/Makefile b/Makefile index c621f227..0c28de8d 100644 --- a/Makefile +++ b/Makefile @@ -47,4 +47,7 @@ uninstall: vault-init: common/scripts/vault-utils.sh vault_init common/pattern-vault.init +vault-unseal: + common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init + .phony: install test diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 8c386beb..2a203d16 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -37,6 +37,14 @@ get_vault_ready() vault_unseal() { + # Argument is expected to be the text output of the vault operator init command which includes Unseal Keys + # (5 by default) and a root token. + if [ -n "$1" ]; then + file=$1 + else + file=common/vault.init + fi + for unseal in `cat $1 | grep "Unseal Key" | awk '{ print $4 }'` do oc -n vault exec vault-0 -- vault operator unseal $unseal @@ -45,12 +53,15 @@ vault_unseal() vault_init() { + # Argument is expected to be the text output of the vault operator init command which includes Unseal Keys + # (5 by default) and a root token. if [ -n "$1" ]; then file=$1 else file=common/vault.init fi + # The vault is ready to be initialized when it is "Running" but not "ready". Unsealing it makes it ready rdy_check=`get_vault_ready` until [ "$rdy_check" = "0/1 Running" ] do From e5e7894a6caec6275d272d0f4f0f8fe28fa8bf38 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 20 Dec 2021 11:16:26 -0600 Subject: [PATCH 0179/1263] Initial import of chart from 9348383 of https://github.com/external-secrets/external-secrets --- external-secrets/.helmignore | 26 +++ external-secrets/Chart.yaml | 15 ++ external-secrets/README.md | 64 +++++++ external-secrets/README.md.gotmpl | 36 ++++ external-secrets/ci/main-values.yaml | 2 + external-secrets/templates/NOTES.txt | 7 + external-secrets/templates/_helpers.tpl | 62 ++++++ external-secrets/templates/crds/README.md | 4 + external-secrets/templates/deployment.yaml | 87 +++++++++ external-secrets/templates/rbac.yaml | 177 ++++++++++++++++++ external-secrets/templates/service.yaml | 20 ++ .../templates/serviceaccount.yaml | 13 ++ external-secrets/values.yaml | 80 ++++++++ 13 files changed, 593 insertions(+) create mode 100644 external-secrets/.helmignore create mode 100644 external-secrets/Chart.yaml create mode 100644 external-secrets/README.md create mode 100644 external-secrets/README.md.gotmpl create mode 100644 external-secrets/ci/main-values.yaml create mode 100644 external-secrets/templates/NOTES.txt create mode 100644 external-secrets/templates/_helpers.tpl create mode 100644 external-secrets/templates/crds/README.md create mode 100644 external-secrets/templates/deployment.yaml create mode 100644 external-secrets/templates/rbac.yaml create mode 100644 external-secrets/templates/service.yaml create mode 100644 external-secrets/templates/serviceaccount.yaml create mode 100644 external-secrets/values.yaml diff --git a/external-secrets/.helmignore b/external-secrets/.helmignore new file mode 100644 index 00000000..855edc3f --- /dev/null +++ b/external-secrets/.helmignore @@ -0,0 +1,26 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ + +# CRD README.md +templates/crds/README.md diff --git a/external-secrets/Chart.yaml b/external-secrets/Chart.yaml new file mode 100644 index 00000000..bb22d33c --- /dev/null +++ b/external-secrets/Chart.yaml @@ -0,0 +1,15 @@ +apiVersion: v2 +name: external-secrets +description: External secret management for Kubernetes +type: application +version: "0.3.10" +appVersion: "v0.3.10" +kubeVersion: ">= 1.11.0-0" +keywords: + - kubernetes-external-secrets + - secrets +home: https://github.com/external-secrets/external-secrets +icon: https://raw.githubusercontent.com/external-secrets/external-secrets/main/assets/round_eso_logo.png +maintainers: + - name: mcavoyk + email: kellinmcavoy@gmail.com diff --git a/external-secrets/README.md b/external-secrets/README.md new file mode 100644 index 00000000..39c91023 --- /dev/null +++ b/external-secrets/README.md @@ -0,0 +1,64 @@ +# External Secrets + +

+ +[//]: # (README.md generated by gotmpl. DO NOT EDIT.) + +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![Version: 0.3.10](https://img.shields.io/badge/Version-0.3.10-informational?style=flat-square) + +External secret management for Kubernetes + +## TL;DR +```bash +helm repo add external-secrets https://charts.external-secrets.io +helm install external-secrets/external-secrets +``` + +## Installing the Chart +To install the chart with the release name `external-secrets`: +```bash +helm install external-secrets external-secrets/external-secrets +``` + +### Custom Resources +By default, the chart will install external-secrets CRDs, this can be controlled with `installCRDs` value. + +## Uninstalling the Chart +To uninstall the `external-secrets` deployment: +```bash +helm uninstall external-secrets +``` +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | | +| deploymentAnnotations | object | `{}` | Annotations to add to Deployment | +| extraArgs | object | `{}` | | +| extraEnv | list | `[]` | | +| fullnameOverride | string | `""` | | +| image.pullPolicy | string | `"IfNotPresent"` | | +| image.repository | string | `"ghcr.io/external-secrets/external-secrets"` | | +| image.tag | string | `""` | The image tag to use. The default is the chart appVersion. | +| imagePullSecrets | list | `[]` | | +| installCRDs | bool | `true` | If set, install and upgrade CRDs through helm chart. | +| leaderElect | bool | `false` | If true, external-secrets will perform leader election between instances to ensure no more than one instance of external-secrets operates at a time. | +| nameOverride | string | `""` | | +| nodeSelector | object | `{}` | | +| podAnnotations | object | `{}` | Annotations to add to Pod | +| podLabels | object | `{}` | | +| podSecurityContext | object | `{}` | | +| priorityClassName | string | `""` | Pod priority class name. | +| prometheus.enabled | bool | `false` | Specifies whether to expose Service resource for collecting Prometheus metrics | +| prometheus.service.port | int | `8080` | | +| rbac.create | bool | `true` | Specifies whether role and rolebinding resources should be created. | +| replicaCount | int | `1` | | +| resources | object | `{}` | | +| scopedNamespace | string | `""` | If set external secrets are only reconciled in the provided namespace | +| securityContext | object | `{}` | | +| serviceAccount.annotations | object | `{}` | Annotations to add to the service account. | +| serviceAccount.create | bool | `true` | Specifies whether a service account should be created. | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template. | +| tolerations | list | `[]` | | diff --git a/external-secrets/README.md.gotmpl b/external-secrets/README.md.gotmpl new file mode 100644 index 00000000..1f309c30 --- /dev/null +++ b/external-secrets/README.md.gotmpl @@ -0,0 +1,36 @@ +{{- $valuesYAML := "https://github.com/external-secrets/external-secrets/blob/master/deploy/charts/external-secrets/values.yaml" -}} +{{- $chartRepo := "https://charts.external-secrets.io" -}} +{{- $org := "external-secrets" -}} +# External Secrets + +

+ +[//]: # (README.md generated by gotmpl. DO NOT EDIT.) + +{{ template "chart.typeBadge" . }}{{ template "chart.versionBadge" . }} + +{{ template "chart.description" . }} + +## TL;DR +```bash +helm repo add {{ $org }} {{ $chartRepo }} +helm install {{ $org }}/{{ template "chart.name" . }} +``` + +## Installing the Chart +To install the chart with the release name `{{ template "chart.name" . }}`: +```bash +helm install {{ template "chart.name" . }} {{ $org }}/{{ template "chart.name" . }} +``` + +### Custom Resources +By default, the chart will install external-secrets CRDs, this can be controlled with `installCRDs` value. + +## Uninstalling the Chart +To uninstall the `{{ template "chart.name" . }}` deployment: +```bash +helm uninstall {{ template "chart.name" . }} +``` +The command removes all the Kubernetes components associated with the chart and deletes the release. + +{{ template "chart.valuesSection" . }} diff --git a/external-secrets/ci/main-values.yaml b/external-secrets/ci/main-values.yaml new file mode 100644 index 00000000..75eb234e --- /dev/null +++ b/external-secrets/ci/main-values.yaml @@ -0,0 +1,2 @@ +image: + tag: main diff --git a/external-secrets/templates/NOTES.txt b/external-secrets/templates/NOTES.txt new file mode 100644 index 00000000..4fd71699 --- /dev/null +++ b/external-secrets/templates/NOTES.txt @@ -0,0 +1,7 @@ +external-secrets has been deployed successfully! + +In order to begin using ExternalSecrets, you will need to set up a SecretStore +or ClusterSecretStore resource (for example, by creating a 'vault' SecretStore). + +More information on the different types of SecretStores and how to configure them +can be found in our Github: {{ .Chart.Home }} diff --git a/external-secrets/templates/_helpers.tpl b/external-secrets/templates/_helpers.tpl new file mode 100644 index 00000000..23c759fe --- /dev/null +++ b/external-secrets/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "external-secrets.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "external-secrets.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "external-secrets.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "external-secrets.labels" -}} +helm.sh/chart: {{ include "external-secrets.chart" . }} +{{ include "external-secrets.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "external-secrets.selectorLabels" -}} +app.kubernetes.io/name: {{ include "external-secrets.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "external-secrets.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "external-secrets.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/external-secrets/templates/crds/README.md b/external-secrets/templates/crds/README.md new file mode 100644 index 00000000..6d30dc18 --- /dev/null +++ b/external-secrets/templates/crds/README.md @@ -0,0 +1,4 @@ +# CRD Template Directory +CRDs are autogenerated during helm packaging. To install the CRDs set `installCRDS: true` during helm install or upgrade. + +The latest CRDs in the repository are located [here](../../../../crds). diff --git a/external-secrets/templates/deployment.yaml b/external-secrets/templates/deployment.yaml new file mode 100644 index 00000000..68cc7d0a --- /dev/null +++ b/external-secrets/templates/deployment.yaml @@ -0,0 +1,87 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "external-secrets.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + {{- include "external-secrets.labels" . | nindent 4 }} + {{- with .Values.deploymentAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "external-secrets.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "external-secrets.selectorLabels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "external-secrets.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- if or (.Values.leaderElect) (.Values.scopedNamespace) (.Values.extraArgs) }} + args: + {{- if .Values.leaderElect }} + - --enable-leader-election=true + {{- end }} + {{- if .Values.scopedNamespace }} + - --namespace={{ .Values.scopedNamespace }} + {{- end }} + {{- range $key, $value := .Values.extraArgs }} + {{- if $value }} + - --{{ $key }}={{ $value }} + {{- else }} + - --{{ $key }} + {{- end }} + {{- end }} + {{- end }} + ports: + - containerPort: {{ .Values.prometheus.service.port }} + protocol: TCP + {{- with .Values.extraEnv }} + env: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} diff --git a/external-secrets/templates/rbac.yaml b/external-secrets/templates/rbac.yaml new file mode 100644 index 00000000..40326782 --- /dev/null +++ b/external-secrets/templates/rbac.yaml @@ -0,0 +1,177 @@ +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "external-secrets.fullname" . }}-controller + labels: + {{- include "external-secrets.labels" . | nindent 4 }} +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "secretstores" + - "clustersecretstores" + - "externalsecrets" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "externalsecrets/status" + - "externalsecrets/finalizers" + verbs: + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "serviceaccounts" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "list" + - "watch" + - "create" + - "update" + - "delete" + - "patch" + - apiGroups: + - "" + resources: + - "serviceaccounts/token" + verbs: + - "create" + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "external-secrets.fullname" . }}-view + labels: + {{- include "external-secrets.labels" . | nindent 4 }} + rbac.authorization.k8s.io/aggregate-to-view: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "secretstores" + - "clustersecretstores" + verbs: + - "get" + - "watch" + - "list" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "external-secrets.fullname" . }}-edit + labels: + {{- include "external-secrets.labels" . | nindent 4 }} + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "secretstores" + - "clustersecretstores" + verbs: + - "create" + - "delete" + - "deletecollection" + - "patch" + - "update" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "external-secrets.fullname" . }}-controller + labels: + {{- include "external-secrets.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "external-secrets.fullname" . }}-controller +subjects: + - name: {{ include "external-secrets.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} + kind: ServiceAccount +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "external-secrets.fullname" . }}-leaderelection + namespace: {{ .Release.Namespace | quote }} + labels: + {{- include "external-secrets.labels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - "configmaps" + resourceNames: + - "external-secrets-controller" + verbs: + - "get" + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "create" + - apiGroups: + - "coordination.k8s.io" + resources: + - "leases" + verbs: + - "get" + - "create" + - "update" + - "patch" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "external-secrets.fullname" . }}-leaderelection + namespace: {{ .Release.Namespace | quote }} + labels: + {{- include "external-secrets.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "external-secrets.fullname" . }}-leaderelection +subjects: + - kind: ServiceAccount + name: {{ include "external-secrets.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/external-secrets/templates/service.yaml b/external-secrets/templates/service.yaml new file mode 100644 index 00000000..6c95edd0 --- /dev/null +++ b/external-secrets/templates/service.yaml @@ -0,0 +1,20 @@ +{{- if .Values.prometheus.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "external-secrets.fullname" . }}-metrics + labels: + {{- include "external-secrets.labels" . | nindent 4 }} + annotations: + prometheus.io/path: "/metrics" + prometheus.io/scrape: "true" + prometheus.io/port: {{ .Values.prometheus.service.port | quote }} +spec: + type: ClusterIP + ports: + - port: {{ .Values.prometheus.service.port }} + targetPort: {{ .Values.prometheus.service.port }} + protocol: TCP + selector: + {{- include "external-secrets.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/external-secrets/templates/serviceaccount.yaml b/external-secrets/templates/serviceaccount.yaml new file mode 100644 index 00000000..d3e58f78 --- /dev/null +++ b/external-secrets/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "external-secrets.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + {{- include "external-secrets.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/external-secrets/values.yaml b/external-secrets/values.yaml new file mode 100644 index 00000000..b22dad64 --- /dev/null +++ b/external-secrets/values.yaml @@ -0,0 +1,80 @@ +replicaCount: 1 + +image: + repository: ghcr.io/external-secrets/external-secrets + pullPolicy: IfNotPresent + # -- The image tag to use. The default is the chart appVersion. + tag: "" + +# -- If set, install and upgrade CRDs through helm chart. +installCRDs: true + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +# -- If true, external-secrets will perform leader election between instances to ensure no more +# than one instance of external-secrets operates at a time. +leaderElect: false + +# -- If set external secrets are only reconciled in the +# provided namespace +scopedNamespace: "" + +serviceAccount: + # -- Specifies whether a service account should be created. + create: true + # -- Annotations to add to the service account. + annotations: {} + # -- The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template. + name: "" + +rbac: + # -- Specifies whether role and rolebinding resources should be created. + create: true + +## -- Extra environment variables to add to container. +extraEnv: [] + +## -- Map of extra arguments to pass to container. +extraArgs: {} + +# -- Annotations to add to Deployment +deploymentAnnotations: {} + +# -- Annotations to add to Pod +podAnnotations: {} + +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +resources: {} + # requests: + # cpu: 10m + # memory: 32Mi + +prometheus: + # -- Specifies whether to expose Service resource for collecting Prometheus metrics + enabled: false + service: + port: 8080 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +# -- Pod priority class name. +priorityClassName: "" From a2b7fe8bf7892449d55d3b9d9faf505f4645ac9c Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 21 Dec 2021 13:48:46 -0600 Subject: [PATCH 0180/1263] Remove vault and external secrets - we can install from helm directly --- external-secrets/.helmignore | 26 - external-secrets/Chart.yaml | 15 - external-secrets/README.md | 64 - external-secrets/README.md.gotmpl | 36 - external-secrets/ci/main-values.yaml | 2 - external-secrets/templates/NOTES.txt | 7 - external-secrets/templates/_helpers.tpl | 62 - external-secrets/templates/crds/README.md | 4 - external-secrets/templates/deployment.yaml | 87 - external-secrets/templates/rbac.yaml | 177 -- external-secrets/templates/service.yaml | 20 - .../templates/serviceaccount.yaml | 13 - external-secrets/values.yaml | 80 - vault/.circleci/config.yml | 106 -- vault/.github/ISSUE_TEMPLATE/bug_report.md | 46 - vault/.github/ISSUE_TEMPLATE/config.yml | 4 - .../.github/ISSUE_TEMPLATE/feature_request.md | 20 - vault/.github/workflows/jira.yaml | 72 - vault/.gitignore | 13 - vault/.helmignore | 4 - vault/CHANGELOG.md | 350 ---- vault/CONTRIBUTING.md | 239 --- vault/Chart.yaml | 14 - vault/LICENSE.md | 353 ---- vault/Makefile | 101 - vault/README.md | 44 - vault/templates/NOTES.txt | 14 - vault/templates/_helpers.tpl | 692 ------- vault/templates/csi-clusterrole.yaml | 17 - vault/templates/csi-clusterrolebinding.yaml | 18 - vault/templates/csi-daemonset.yaml | 81 - vault/templates/csi-serviceaccount.yaml | 12 - vault/templates/injector-certs-secret.yaml | 10 - vault/templates/injector-clusterrole.yaml | 18 - .../injector-clusterrolebinding.yaml | 18 - vault/templates/injector-deployment.yaml | 157 -- .../templates/injector-mutating-webhook.yaml | 43 - vault/templates/injector-network-policy.yaml | 21 - vault/templates/injector-psp-role.yaml | 17 - vault/templates/injector-psp-rolebinding.yaml | 18 - vault/templates/injector-psp.yaml | 43 - vault/templates/injector-role.yaml | 25 - vault/templates/injector-rolebinding.yaml | 18 - vault/templates/injector-service.yaml | 21 - vault/templates/injector-serviceaccount.yaml | 11 - .../templates/server-clusterrolebinding.yaml | 24 - vault/templates/server-config-configmap.yaml | 38 - vault/templates/server-discovery-role.yaml | 19 - .../server-discovery-rolebinding.yaml | 27 - vault/templates/server-disruptionbudget.yaml | 24 - vault/templates/server-ha-active-service.yaml | 42 - .../templates/server-ha-standby-service.yaml | 42 - vault/templates/server-headless-service.yaml | 32 - vault/templates/server-ingress.yaml | 74 - vault/templates/server-network-policy.yaml | 26 - vault/templates/server-psp-role.yaml | 18 - vault/templates/server-psp-rolebinding.yaml | 19 - vault/templates/server-psp.yaml | 47 - vault/templates/server-route.yaml | 33 - vault/templates/server-service.yaml | 43 - vault/templates/server-serviceaccount.yaml | 16 - vault/templates/server-statefulset.yaml | 208 -- vault/templates/tests/server-test.yaml | 40 - vault/templates/ui-service.yaml | 37 - vault/test/README.md | 55 - vault/test/acceptance/_helpers.bash | 159 -- vault/test/acceptance/csi-test/nginx.yaml | 27 - .../vault-kv-secretproviderclass.yaml | 14 - .../test/acceptance/csi-test/vault-policy.hcl | 3 - vault/test/acceptance/csi.bats | 60 - vault/test/acceptance/helm-test.bats | 27 - .../acceptance/injector-leader-elector.bats | 52 - .../acceptance/injector-test/bootstrap.sh | 46 - vault/test/acceptance/injector-test/job.yaml | 39 - .../injector-test/pg-deployment.yaml | 69 - .../injector-test/pgdump-policy.hcl | 3 - vault/test/acceptance/injector.bats | 58 - vault/test/acceptance/server-annotations.bats | 46 - vault/test/acceptance/server-dev.bats | 64 - .../acceptance/server-ha-enterprise-dr.bats | 170 -- .../acceptance/server-ha-enterprise-perf.bats | 168 -- vault/test/acceptance/server-ha-raft.bats | 119 -- vault/test/acceptance/server-ha.bats | 108 -- .../server-test/annotations-overrides.yaml | 9 - vault/test/acceptance/server.bats | 111 -- vault/test/chart/_helpers.bash | 18 - vault/test/chart/verifier.bats | 85 - vault/test/docker/Test.dockerfile | 51 - vault/test/kind/config.yaml | 7 - vault/test/terraform/.gitignore | 1 - vault/test/terraform/main.tf | 72 - vault/test/terraform/outputs.tf | 7 - vault/test/terraform/variables.tf | 28 - vault/test/unit/_helpers.bash | 4 - vault/test/unit/csi-clusterrole.bats | 33 - vault/test/unit/csi-clusterrolebinding.bats | 44 - vault/test/unit/csi-daemonset.bats | 516 ----- vault/test/unit/csi-serviceaccount.bats | 59 - vault/test/unit/injector-clusterrole.bats | 22 - .../unit/injector-clusterrolebinding.bats | 22 - vault/test/unit/injector-deployment.bats | 723 ------- vault/test/unit/injector-leader-elector.bats | 168 -- .../test/unit/injector-mutating-webhook.bats | 155 -- vault/test/unit/injector-psp-role.bats | 35 - vault/test/unit/injector-psp-rolebinding.bats | 35 - vault/test/unit/injector-psp.bats | 70 - vault/test/unit/injector-service.bats | 66 - vault/test/unit/injector-serviceaccount.bats | 22 - vault/test/unit/schema.bats | 46 - .../test/unit/server-clusterrolebinding.bats | 72 - vault/test/unit/server-configmap.bats | 124 -- vault/test/unit/server-dev-statefulset.bats | 461 ----- vault/test/unit/server-ha-active-service.bats | 199 -- .../test/unit/server-ha-disruptionbudget.bats | 99 - .../test/unit/server-ha-standby-service.bats | 210 -- vault/test/unit/server-ha-statefulset.bats | 682 ------- vault/test/unit/server-ingress.bats | 267 --- vault/test/unit/server-network-policy.bats | 35 - vault/test/unit/server-psp-role.bats | 111 -- vault/test/unit/server-psp-rolebinding.bats | 111 -- vault/test/unit/server-psp.bats | 285 --- vault/test/unit/server-route.bats | 143 -- vault/test/unit/server-service.bats | 426 ----- vault/test/unit/server-serviceaccount.bats | 119 -- vault/test/unit/server-statefulset.bats | 1681 ----------------- vault/test/unit/ui-service.bats | 375 ---- vault/values.openshift.yaml | 18 - vault/values.schema.json | 841 --------- vault/values.yaml | 831 -------- 129 files changed, 14808 deletions(-) delete mode 100644 external-secrets/.helmignore delete mode 100644 external-secrets/Chart.yaml delete mode 100644 external-secrets/README.md delete mode 100644 external-secrets/README.md.gotmpl delete mode 100644 external-secrets/ci/main-values.yaml delete mode 100644 external-secrets/templates/NOTES.txt delete mode 100644 external-secrets/templates/_helpers.tpl delete mode 100644 external-secrets/templates/crds/README.md delete mode 100644 external-secrets/templates/deployment.yaml delete mode 100644 external-secrets/templates/rbac.yaml delete mode 100644 external-secrets/templates/service.yaml delete mode 100644 external-secrets/templates/serviceaccount.yaml delete mode 100644 external-secrets/values.yaml delete mode 100644 vault/.circleci/config.yml delete mode 100644 vault/.github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 vault/.github/ISSUE_TEMPLATE/config.yml delete mode 100644 vault/.github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 vault/.github/workflows/jira.yaml delete mode 100644 vault/.gitignore delete mode 100644 vault/.helmignore delete mode 100644 vault/CHANGELOG.md delete mode 100644 vault/CONTRIBUTING.md delete mode 100644 vault/Chart.yaml delete mode 100644 vault/LICENSE.md delete mode 100644 vault/Makefile delete mode 100644 vault/README.md delete mode 100644 vault/templates/NOTES.txt delete mode 100644 vault/templates/_helpers.tpl delete mode 100644 vault/templates/csi-clusterrole.yaml delete mode 100644 vault/templates/csi-clusterrolebinding.yaml delete mode 100644 vault/templates/csi-daemonset.yaml delete mode 100644 vault/templates/csi-serviceaccount.yaml delete mode 100644 vault/templates/injector-certs-secret.yaml delete mode 100644 vault/templates/injector-clusterrole.yaml delete mode 100644 vault/templates/injector-clusterrolebinding.yaml delete mode 100644 vault/templates/injector-deployment.yaml delete mode 100644 vault/templates/injector-mutating-webhook.yaml delete mode 100644 vault/templates/injector-network-policy.yaml delete mode 100644 vault/templates/injector-psp-role.yaml delete mode 100644 vault/templates/injector-psp-rolebinding.yaml delete mode 100644 vault/templates/injector-psp.yaml delete mode 100644 vault/templates/injector-role.yaml delete mode 100644 vault/templates/injector-rolebinding.yaml delete mode 100644 vault/templates/injector-service.yaml delete mode 100644 vault/templates/injector-serviceaccount.yaml delete mode 100644 vault/templates/server-clusterrolebinding.yaml delete mode 100644 vault/templates/server-config-configmap.yaml delete mode 100644 vault/templates/server-discovery-role.yaml delete mode 100644 vault/templates/server-discovery-rolebinding.yaml delete mode 100644 vault/templates/server-disruptionbudget.yaml delete mode 100644 vault/templates/server-ha-active-service.yaml delete mode 100644 vault/templates/server-ha-standby-service.yaml delete mode 100644 vault/templates/server-headless-service.yaml delete mode 100644 vault/templates/server-ingress.yaml delete mode 100644 vault/templates/server-network-policy.yaml delete mode 100644 vault/templates/server-psp-role.yaml delete mode 100644 vault/templates/server-psp-rolebinding.yaml delete mode 100644 vault/templates/server-psp.yaml delete mode 100644 vault/templates/server-route.yaml delete mode 100644 vault/templates/server-service.yaml delete mode 100644 vault/templates/server-serviceaccount.yaml delete mode 100644 vault/templates/server-statefulset.yaml delete mode 100644 vault/templates/tests/server-test.yaml delete mode 100644 vault/templates/ui-service.yaml delete mode 100644 vault/test/README.md delete mode 100644 vault/test/acceptance/_helpers.bash delete mode 100644 vault/test/acceptance/csi-test/nginx.yaml delete mode 100644 vault/test/acceptance/csi-test/vault-kv-secretproviderclass.yaml delete mode 100644 vault/test/acceptance/csi-test/vault-policy.hcl delete mode 100644 vault/test/acceptance/csi.bats delete mode 100644 vault/test/acceptance/helm-test.bats delete mode 100644 vault/test/acceptance/injector-leader-elector.bats delete mode 100755 vault/test/acceptance/injector-test/bootstrap.sh delete mode 100644 vault/test/acceptance/injector-test/job.yaml delete mode 100644 vault/test/acceptance/injector-test/pg-deployment.yaml delete mode 100644 vault/test/acceptance/injector-test/pgdump-policy.hcl delete mode 100644 vault/test/acceptance/injector.bats delete mode 100644 vault/test/acceptance/server-annotations.bats delete mode 100644 vault/test/acceptance/server-dev.bats delete mode 100644 vault/test/acceptance/server-ha-enterprise-dr.bats delete mode 100644 vault/test/acceptance/server-ha-enterprise-perf.bats delete mode 100644 vault/test/acceptance/server-ha-raft.bats delete mode 100644 vault/test/acceptance/server-ha.bats delete mode 100644 vault/test/acceptance/server-test/annotations-overrides.yaml delete mode 100644 vault/test/acceptance/server.bats delete mode 100644 vault/test/chart/_helpers.bash delete mode 100644 vault/test/chart/verifier.bats delete mode 100644 vault/test/docker/Test.dockerfile delete mode 100644 vault/test/kind/config.yaml delete mode 100644 vault/test/terraform/.gitignore delete mode 100644 vault/test/terraform/main.tf delete mode 100644 vault/test/terraform/outputs.tf delete mode 100644 vault/test/terraform/variables.tf delete mode 100644 vault/test/unit/_helpers.bash delete mode 100644 vault/test/unit/csi-clusterrole.bats delete mode 100644 vault/test/unit/csi-clusterrolebinding.bats delete mode 100644 vault/test/unit/csi-daemonset.bats delete mode 100644 vault/test/unit/csi-serviceaccount.bats delete mode 100755 vault/test/unit/injector-clusterrole.bats delete mode 100755 vault/test/unit/injector-clusterrolebinding.bats delete mode 100755 vault/test/unit/injector-deployment.bats delete mode 100644 vault/test/unit/injector-leader-elector.bats delete mode 100755 vault/test/unit/injector-mutating-webhook.bats delete mode 100644 vault/test/unit/injector-psp-role.bats delete mode 100644 vault/test/unit/injector-psp-rolebinding.bats delete mode 100644 vault/test/unit/injector-psp.bats delete mode 100755 vault/test/unit/injector-service.bats delete mode 100755 vault/test/unit/injector-serviceaccount.bats delete mode 100644 vault/test/unit/schema.bats delete mode 100755 vault/test/unit/server-clusterrolebinding.bats delete mode 100755 vault/test/unit/server-configmap.bats delete mode 100755 vault/test/unit/server-dev-statefulset.bats delete mode 100755 vault/test/unit/server-ha-active-service.bats delete mode 100755 vault/test/unit/server-ha-disruptionbudget.bats delete mode 100755 vault/test/unit/server-ha-standby-service.bats delete mode 100755 vault/test/unit/server-ha-statefulset.bats delete mode 100755 vault/test/unit/server-ingress.bats delete mode 100755 vault/test/unit/server-network-policy.bats delete mode 100644 vault/test/unit/server-psp-role.bats delete mode 100644 vault/test/unit/server-psp-rolebinding.bats delete mode 100644 vault/test/unit/server-psp.bats delete mode 100755 vault/test/unit/server-route.bats delete mode 100755 vault/test/unit/server-service.bats delete mode 100755 vault/test/unit/server-serviceaccount.bats delete mode 100755 vault/test/unit/server-statefulset.bats delete mode 100755 vault/test/unit/ui-service.bats delete mode 100644 vault/values.openshift.yaml delete mode 100644 vault/values.schema.json delete mode 100644 vault/values.yaml diff --git a/external-secrets/.helmignore b/external-secrets/.helmignore deleted file mode 100644 index 855edc3f..00000000 --- a/external-secrets/.helmignore +++ /dev/null @@ -1,26 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ - -# CRD README.md -templates/crds/README.md diff --git a/external-secrets/Chart.yaml b/external-secrets/Chart.yaml deleted file mode 100644 index bb22d33c..00000000 --- a/external-secrets/Chart.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v2 -name: external-secrets -description: External secret management for Kubernetes -type: application -version: "0.3.10" -appVersion: "v0.3.10" -kubeVersion: ">= 1.11.0-0" -keywords: - - kubernetes-external-secrets - - secrets -home: https://github.com/external-secrets/external-secrets -icon: https://raw.githubusercontent.com/external-secrets/external-secrets/main/assets/round_eso_logo.png -maintainers: - - name: mcavoyk - email: kellinmcavoy@gmail.com diff --git a/external-secrets/README.md b/external-secrets/README.md deleted file mode 100644 index 39c91023..00000000 --- a/external-secrets/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# External Secrets - -

- -[//]: # (README.md generated by gotmpl. DO NOT EDIT.) - -![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![Version: 0.3.10](https://img.shields.io/badge/Version-0.3.10-informational?style=flat-square) - -External secret management for Kubernetes - -## TL;DR -```bash -helm repo add external-secrets https://charts.external-secrets.io -helm install external-secrets/external-secrets -``` - -## Installing the Chart -To install the chart with the release name `external-secrets`: -```bash -helm install external-secrets external-secrets/external-secrets -``` - -### Custom Resources -By default, the chart will install external-secrets CRDs, this can be controlled with `installCRDs` value. - -## Uninstalling the Chart -To uninstall the `external-secrets` deployment: -```bash -helm uninstall external-secrets -``` -The command removes all the Kubernetes components associated with the chart and deletes the release. - -## Values - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| affinity | object | `{}` | | -| deploymentAnnotations | object | `{}` | Annotations to add to Deployment | -| extraArgs | object | `{}` | | -| extraEnv | list | `[]` | | -| fullnameOverride | string | `""` | | -| image.pullPolicy | string | `"IfNotPresent"` | | -| image.repository | string | `"ghcr.io/external-secrets/external-secrets"` | | -| image.tag | string | `""` | The image tag to use. The default is the chart appVersion. | -| imagePullSecrets | list | `[]` | | -| installCRDs | bool | `true` | If set, install and upgrade CRDs through helm chart. | -| leaderElect | bool | `false` | If true, external-secrets will perform leader election between instances to ensure no more than one instance of external-secrets operates at a time. | -| nameOverride | string | `""` | | -| nodeSelector | object | `{}` | | -| podAnnotations | object | `{}` | Annotations to add to Pod | -| podLabels | object | `{}` | | -| podSecurityContext | object | `{}` | | -| priorityClassName | string | `""` | Pod priority class name. | -| prometheus.enabled | bool | `false` | Specifies whether to expose Service resource for collecting Prometheus metrics | -| prometheus.service.port | int | `8080` | | -| rbac.create | bool | `true` | Specifies whether role and rolebinding resources should be created. | -| replicaCount | int | `1` | | -| resources | object | `{}` | | -| scopedNamespace | string | `""` | If set external secrets are only reconciled in the provided namespace | -| securityContext | object | `{}` | | -| serviceAccount.annotations | object | `{}` | Annotations to add to the service account. | -| serviceAccount.create | bool | `true` | Specifies whether a service account should be created. | -| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template. | -| tolerations | list | `[]` | | diff --git a/external-secrets/README.md.gotmpl b/external-secrets/README.md.gotmpl deleted file mode 100644 index 1f309c30..00000000 --- a/external-secrets/README.md.gotmpl +++ /dev/null @@ -1,36 +0,0 @@ -{{- $valuesYAML := "https://github.com/external-secrets/external-secrets/blob/master/deploy/charts/external-secrets/values.yaml" -}} -{{- $chartRepo := "https://charts.external-secrets.io" -}} -{{- $org := "external-secrets" -}} -# External Secrets - -

- -[//]: # (README.md generated by gotmpl. DO NOT EDIT.) - -{{ template "chart.typeBadge" . }}{{ template "chart.versionBadge" . }} - -{{ template "chart.description" . }} - -## TL;DR -```bash -helm repo add {{ $org }} {{ $chartRepo }} -helm install {{ $org }}/{{ template "chart.name" . }} -``` - -## Installing the Chart -To install the chart with the release name `{{ template "chart.name" . }}`: -```bash -helm install {{ template "chart.name" . }} {{ $org }}/{{ template "chart.name" . }} -``` - -### Custom Resources -By default, the chart will install external-secrets CRDs, this can be controlled with `installCRDs` value. - -## Uninstalling the Chart -To uninstall the `{{ template "chart.name" . }}` deployment: -```bash -helm uninstall {{ template "chart.name" . }} -``` -The command removes all the Kubernetes components associated with the chart and deletes the release. - -{{ template "chart.valuesSection" . }} diff --git a/external-secrets/ci/main-values.yaml b/external-secrets/ci/main-values.yaml deleted file mode 100644 index 75eb234e..00000000 --- a/external-secrets/ci/main-values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -image: - tag: main diff --git a/external-secrets/templates/NOTES.txt b/external-secrets/templates/NOTES.txt deleted file mode 100644 index 4fd71699..00000000 --- a/external-secrets/templates/NOTES.txt +++ /dev/null @@ -1,7 +0,0 @@ -external-secrets has been deployed successfully! - -In order to begin using ExternalSecrets, you will need to set up a SecretStore -or ClusterSecretStore resource (for example, by creating a 'vault' SecretStore). - -More information on the different types of SecretStores and how to configure them -can be found in our Github: {{ .Chart.Home }} diff --git a/external-secrets/templates/_helpers.tpl b/external-secrets/templates/_helpers.tpl deleted file mode 100644 index 23c759fe..00000000 --- a/external-secrets/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "external-secrets.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "external-secrets.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "external-secrets.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "external-secrets.labels" -}} -helm.sh/chart: {{ include "external-secrets.chart" . }} -{{ include "external-secrets.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "external-secrets.selectorLabels" -}} -app.kubernetes.io/name: {{ include "external-secrets.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "external-secrets.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "external-secrets.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/external-secrets/templates/crds/README.md b/external-secrets/templates/crds/README.md deleted file mode 100644 index 6d30dc18..00000000 --- a/external-secrets/templates/crds/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# CRD Template Directory -CRDs are autogenerated during helm packaging. To install the CRDs set `installCRDS: true` during helm install or upgrade. - -The latest CRDs in the repository are located [here](../../../../crds). diff --git a/external-secrets/templates/deployment.yaml b/external-secrets/templates/deployment.yaml deleted file mode 100644 index 68cc7d0a..00000000 --- a/external-secrets/templates/deployment.yaml +++ /dev/null @@ -1,87 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "external-secrets.fullname" . }} - namespace: {{ .Release.Namespace | quote }} - labels: - {{- include "external-secrets.labels" . | nindent 4 }} - {{- with .Values.deploymentAnnotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - {{- include "external-secrets.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "external-secrets.selectorLabels" . | nindent 8 }} - {{- with .Values.podLabels }} - {{- toYaml . | nindent 8 }} - {{- end }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "external-secrets.serviceAccountName" . }} - {{- with .Values.podSecurityContext }} - securityContext: - {{- toYaml . | nindent 8 }} - {{- end }} - containers: - - name: {{ .Chart.Name }} - {{- with .Values.securityContext }} - securityContext: - {{- toYaml . | nindent 12 }} - {{- end }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - {{- if or (.Values.leaderElect) (.Values.scopedNamespace) (.Values.extraArgs) }} - args: - {{- if .Values.leaderElect }} - - --enable-leader-election=true - {{- end }} - {{- if .Values.scopedNamespace }} - - --namespace={{ .Values.scopedNamespace }} - {{- end }} - {{- range $key, $value := .Values.extraArgs }} - {{- if $value }} - - --{{ $key }}={{ $value }} - {{- else }} - - --{{ $key }} - {{- end }} - {{- end }} - {{- end }} - ports: - - containerPort: {{ .Values.prometheus.service.port }} - protocol: TCP - {{- with .Values.extraEnv }} - env: - {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.resources }} - resources: - {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- if .Values.priorityClassName }} - priorityClassName: {{ .Values.priorityClassName }} - {{- end }} diff --git a/external-secrets/templates/rbac.yaml b/external-secrets/templates/rbac.yaml deleted file mode 100644 index 40326782..00000000 --- a/external-secrets/templates/rbac.yaml +++ /dev/null @@ -1,177 +0,0 @@ -{{- if .Values.rbac.create -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "external-secrets.fullname" . }}-controller - labels: - {{- include "external-secrets.labels" . | nindent 4 }} -rules: - - apiGroups: - - "external-secrets.io" - resources: - - "secretstores" - - "clustersecretstores" - - "externalsecrets" - verbs: - - "get" - - "list" - - "watch" - - apiGroups: - - "external-secrets.io" - resources: - - "externalsecrets" - - "externalsecrets/status" - - "externalsecrets/finalizers" - verbs: - - "update" - - "patch" - - apiGroups: - - "" - resources: - - "serviceaccounts" - verbs: - - "get" - - "list" - - "watch" - - apiGroups: - - "" - resources: - - "configmaps" - verbs: - - "get" - - "list" - - "watch" - - apiGroups: - - "" - resources: - - "secrets" - verbs: - - "get" - - "list" - - "watch" - - "create" - - "update" - - "delete" - - "patch" - - apiGroups: - - "" - resources: - - "serviceaccounts/token" - verbs: - - "create" - - apiGroups: - - "" - resources: - - "events" - verbs: - - "create" - - "patch" ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "external-secrets.fullname" . }}-view - labels: - {{- include "external-secrets.labels" . | nindent 4 }} - rbac.authorization.k8s.io/aggregate-to-view: "true" - rbac.authorization.k8s.io/aggregate-to-edit: "true" - rbac.authorization.k8s.io/aggregate-to-admin: "true" -rules: - - apiGroups: - - "external-secrets.io" - resources: - - "externalsecrets" - - "secretstores" - - "clustersecretstores" - verbs: - - "get" - - "watch" - - "list" ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "external-secrets.fullname" . }}-edit - labels: - {{- include "external-secrets.labels" . | nindent 4 }} - rbac.authorization.k8s.io/aggregate-to-edit: "true" - rbac.authorization.k8s.io/aggregate-to-admin: "true" -rules: - - apiGroups: - - "external-secrets.io" - resources: - - "externalsecrets" - - "secretstores" - - "clustersecretstores" - verbs: - - "create" - - "delete" - - "deletecollection" - - "patch" - - "update" ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ include "external-secrets.fullname" . }}-controller - labels: - {{- include "external-secrets.labels" . | nindent 4 }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ include "external-secrets.fullname" . }}-controller -subjects: - - name: {{ include "external-secrets.serviceAccountName" . }} - namespace: {{ .Release.Namespace | quote }} - kind: ServiceAccount ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ include "external-secrets.fullname" . }}-leaderelection - namespace: {{ .Release.Namespace | quote }} - labels: - {{- include "external-secrets.labels" . | nindent 4 }} -rules: - - apiGroups: - - "" - resources: - - "configmaps" - resourceNames: - - "external-secrets-controller" - verbs: - - "get" - - "update" - - "patch" - - apiGroups: - - "" - resources: - - "configmaps" - verbs: - - "create" - - apiGroups: - - "coordination.k8s.io" - resources: - - "leases" - verbs: - - "get" - - "create" - - "update" - - "patch" ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ include "external-secrets.fullname" . }}-leaderelection - namespace: {{ .Release.Namespace | quote }} - labels: - {{- include "external-secrets.labels" . | nindent 4 }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ include "external-secrets.fullname" . }}-leaderelection -subjects: - - kind: ServiceAccount - name: {{ include "external-secrets.serviceAccountName" . }} - namespace: {{ .Release.Namespace | quote }} -{{- end }} diff --git a/external-secrets/templates/service.yaml b/external-secrets/templates/service.yaml deleted file mode 100644 index 6c95edd0..00000000 --- a/external-secrets/templates/service.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if .Values.prometheus.enabled }} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "external-secrets.fullname" . }}-metrics - labels: - {{- include "external-secrets.labels" . | nindent 4 }} - annotations: - prometheus.io/path: "/metrics" - prometheus.io/scrape: "true" - prometheus.io/port: {{ .Values.prometheus.service.port | quote }} -spec: - type: ClusterIP - ports: - - port: {{ .Values.prometheus.service.port }} - targetPort: {{ .Values.prometheus.service.port }} - protocol: TCP - selector: - {{- include "external-secrets.selectorLabels" . | nindent 4 }} -{{- end }} diff --git a/external-secrets/templates/serviceaccount.yaml b/external-secrets/templates/serviceaccount.yaml deleted file mode 100644 index d3e58f78..00000000 --- a/external-secrets/templates/serviceaccount.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "external-secrets.serviceAccountName" . }} - namespace: {{ .Release.Namespace | quote }} - labels: - {{- include "external-secrets.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/external-secrets/values.yaml b/external-secrets/values.yaml deleted file mode 100644 index b22dad64..00000000 --- a/external-secrets/values.yaml +++ /dev/null @@ -1,80 +0,0 @@ -replicaCount: 1 - -image: - repository: ghcr.io/external-secrets/external-secrets - pullPolicy: IfNotPresent - # -- The image tag to use. The default is the chart appVersion. - tag: "" - -# -- If set, install and upgrade CRDs through helm chart. -installCRDs: true - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -# -- If true, external-secrets will perform leader election between instances to ensure no more -# than one instance of external-secrets operates at a time. -leaderElect: false - -# -- If set external secrets are only reconciled in the -# provided namespace -scopedNamespace: "" - -serviceAccount: - # -- Specifies whether a service account should be created. - create: true - # -- Annotations to add to the service account. - annotations: {} - # -- The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template. - name: "" - -rbac: - # -- Specifies whether role and rolebinding resources should be created. - create: true - -## -- Extra environment variables to add to container. -extraEnv: [] - -## -- Map of extra arguments to pass to container. -extraArgs: {} - -# -- Annotations to add to Deployment -deploymentAnnotations: {} - -# -- Annotations to add to Pod -podAnnotations: {} - -podLabels: {} - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -resources: {} - # requests: - # cpu: 10m - # memory: 32Mi - -prometheus: - # -- Specifies whether to expose Service resource for collecting Prometheus metrics - enabled: false - service: - port: 8080 - -nodeSelector: {} - -tolerations: [] - -affinity: {} - -# -- Pod priority class name. -priorityClassName: "" diff --git a/vault/.circleci/config.yml b/vault/.circleci/config.yml deleted file mode 100644 index 8de4c83c..00000000 --- a/vault/.circleci/config.yml +++ /dev/null @@ -1,106 +0,0 @@ -version: 2.1 -orbs: - slack: circleci/slack@3.4.2 - -jobs: - bats-unit-test: - docker: - # This image is built from test/docker/Test.dockerfile - - image: docker.mirror.hashicorp.services/hashicorpdev/vault-helm-test:0.2.0 - steps: - - checkout - - run: bats ./test/unit -t - - chart-verifier: - docker: - - image: docker.mirror.hashicorp.services/cimg/go:1.16 - environment: - BATS_VERSION: "1.3.0" - CHART_VERIFIER_VERSION: "1.2.1" - steps: - - checkout - - run: - name: install chart-verifier - command: go get github.com/redhat-certification/chart-verifier@${CHART_VERIFIER_VERSION} - - run: - name: install bats - command: | - curl -sSL https://github.com/bats-core/bats-core/archive/v${BATS_VERSION}.tar.gz -o /tmp/bats.tgz - tar -zxf /tmp/bats.tgz -C /tmp - sudo /bin/bash /tmp/bats-core-${BATS_VERSION}/install.sh /usr/local - - run: - name: run chart-verifier tests - command: bats ./test/chart -t - - acceptance: - docker: - # This image is build from test/docker/Test.dockerfile - - image: docker.mirror.hashicorp.services/hashicorpdev/vault-helm-test:0.2.0 - - steps: - - checkout - - run: - name: terraform init & apply - command: | - echo -e "${GOOGLE_APP_CREDS}" | base64 -d > vault-helm-test.json - export GOOGLE_CREDENTIALS=vault-helm-test.json - make provision-cluster - - run: - name: Run acceptance tests - command: bats ./test/acceptance -t - - - run: - name: terraform destroy - command: | - export GOOGLE_CREDENTIALS=vault-helm-test.json - make destroy-cluster - when: always - update-helm-charts-index: - docker: - - image: docker.mirror.hashicorp.services/circleci/golang:1.15.3 - steps: - - checkout - - run: - name: verify Chart version matches tag version - command: | - GO111MODULE=on go get github.com/mikefarah/yq/v2 - git_tag=$(echo "${CIRCLE_TAG#v}") - chart_tag=$(yq r Chart.yaml version) - if [ "${git_tag}" != "${chart_tag}" ]; then - echo "chart version (${chart_tag}) did not match git version (${git_tag})" - exit 1 - fi - - run: - name: update helm-charts index - command: | - curl --show-error --silent --fail --user "${CIRCLE_TOKEN}:" \ - -X POST \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -d "{\"branch\": \"master\",\"parameters\":{\"SOURCE_REPO\": \"${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}\",\"SOURCE_TAG\": \"${CIRCLE_TAG}\"}}" \ - "${CIRCLE_ENDPOINT}/${CIRCLE_PROJECT}/pipeline" - - slack/status: - fail_only: true - failure_message: "Failed to trigger an update to the helm charts index. Check the logs at: ${CIRCLE_BUILD_URL}" - -workflows: - version: 2 - build_and_test: - jobs: - - bats-unit-test - - chart-verifier - - acceptance: - requires: - - bats-unit-test - filters: - branches: - only: main - update-helm-charts-index: - jobs: - - update-helm-charts-index: - context: helm-charts-trigger-vault - filters: - tags: - only: /^v.*/ - branches: - ignore: /.*/ diff --git a/vault/.github/ISSUE_TEMPLATE/bug_report.md b/vault/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index cb69c513..00000000 --- a/vault/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: Bug report -about: Let us know about a bug! -title: '' -labels: bug -assignees: '' - ---- - - - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Install chart -2. Run vault command -3. See error (vault logs, etc.) - -Other useful info to include: vault pod logs, `kubectl describe statefulset vault` and `kubectl get statefulset vault -o yaml` output - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Environment** -* Kubernetes version: - * Distribution or cloud vendor (OpenShift, EKS, GKE, AKS, etc.): - * Other configuration options or runtime services (istio, etc.): -* vault-helm version: - -Chart values: - -```yaml -# Paste your user-supplied values here (`helm get values `). -# Be sure to scrub any sensitive values! -``` - -**Additional context** -Add any other context about the problem here. diff --git a/vault/.github/ISSUE_TEMPLATE/config.yml b/vault/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index b24b36b2..00000000 --- a/vault/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,4 +0,0 @@ -contact_links: - - name: Ask a question - url: https://discuss.hashicorp.com/c/vault - about: For increased visibility, please post questions on the discussion forum, and tag with `k8s` diff --git a/vault/.github/ISSUE_TEMPLATE/feature_request.md b/vault/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 11fc491e..00000000 --- a/vault/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/vault/.github/workflows/jira.yaml b/vault/.github/workflows/jira.yaml deleted file mode 100644 index eb369f33..00000000 --- a/vault/.github/workflows/jira.yaml +++ /dev/null @@ -1,72 +0,0 @@ -on: - issues: - types: [opened, closed, deleted, reopened] - pull_request_target: - types: [opened, closed, reopened] - issue_comment: # Also triggers when commenting on a PR from the conversation view - types: [created] - -name: Jira Sync - -jobs: - sync: - runs-on: ubuntu-latest - name: Jira sync - steps: - - name: Login - uses: atlassian/gajira-login@v2.0.0 - env: - JIRA_BASE_URL: ${{ secrets.JIRA_SYNC_BASE_URL }} - JIRA_USER_EMAIL: ${{ secrets.JIRA_SYNC_USER_EMAIL }} - JIRA_API_TOKEN: ${{ secrets.JIRA_SYNC_API_TOKEN }} - - - name: Preprocess - if: github.event.action == 'opened' || github.event.action == 'created' - id: preprocess - run: | - if [[ "${{ github.event_name }}" == "pull_request_target" ]]; then - echo "::set-output name=type::PR" - else - echo "::set-output name=type::ISS" - fi - - - name: Create ticket - if: github.event.action == 'opened' - uses: tomhjp/gh-action-jira-create@v0.2.0 - with: - project: VAULT - issuetype: "GH Issue" - summary: "${{ github.event.repository.name }} [${{ steps.preprocess.outputs.type }} #${{ github.event.issue.number || github.event.pull_request.number }}]: ${{ github.event.issue.title || github.event.pull_request.title }}" - description: "${{ github.event.issue.body || github.event.pull_request.body }}\n\n_Created from GitHub Action for ${{ github.event.issue.html_url || github.event.pull_request.html_url }} from ${{ github.actor }}_" - # customfield_10089 is Issue Link custom field - # customfield_10091 is team custom field - extraFields: '{"fixVersions": [{"name": "TBD"}], "customfield_10091": ["ecosystem", "runtime"], "customfield_10089": "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"}' - - - name: Search - if: github.event.action != 'opened' - id: search - uses: tomhjp/gh-action-jira-search@v0.2.1 - with: - # cf[10089] is Issue Link custom field - jql: 'project = "VAULT" and cf[10089]="${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' - - - name: Sync comment - if: github.event.action == 'created' && steps.search.outputs.issue - uses: tomhjp/gh-action-jira-comment@v0.2.0 - with: - issue: ${{ steps.search.outputs.issue }} - comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}" - - - name: Close ticket - if: (github.event.action == 'closed' || github.event.action == 'deleted') && steps.search.outputs.issue - uses: atlassian/gajira-transition@v2.0.1 - with: - issue: ${{ steps.search.outputs.issue }} - transition: Close - - - name: Reopen ticket - if: github.event.action == 'reopened' && steps.search.outputs.issue - uses: atlassian/gajira-transition@v2.0.1 - with: - issue: ${{ steps.search.outputs.issue }} - transition: "Pending Triage" diff --git a/vault/.gitignore b/vault/.gitignore deleted file mode 100644 index 2e23aca2..00000000 --- a/vault/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -.DS_Store -.terraform/ -.terraform.tfstate* -terraform.tfstate* -terraform.tfvars -values.dev.yaml -vaul-helm-dev-creds.json -./test/acceptance/vaul-helm-dev-creds.json -./test/terraform/vaul-helm-dev-creds.json -./test/unit/vaul-helm-dev-creds.json -./test/acceptance/values.yaml -./test/acceptance/values.yml -.idea diff --git a/vault/.helmignore b/vault/.helmignore deleted file mode 100644 index d1180d2f..00000000 --- a/vault/.helmignore +++ /dev/null @@ -1,4 +0,0 @@ -.git/ -.terraform/ -bin/ -test/ diff --git a/vault/CHANGELOG.md b/vault/CHANGELOG.md deleted file mode 100644 index c596d515..00000000 --- a/vault/CHANGELOG.md +++ /dev/null @@ -1,350 +0,0 @@ -## Unreleased - -## 0.18.0 (November 17th, 2021) - -CHANGES: -* Removed support for deploying a leader-elector container with the [vault-k8s injector](https://github.com/hashicorp/vault-k8s) injector since vault-k8s now uses an internal mechanism to determine leadership [GH-649](https://github.com/hashicorp/vault-helm/pull/649) -* Vault image default 1.9.0 -* Vault K8s image default 0.14.1 - -Improvements: -* Added templateConfig.staticSecretRenderInterval chart option for the injector [GH-621](https://github.com/hashicorp/vault-helm/pull/621) - -## 0.17.1 (October 25th, 2021) - -Improvements: - * Add option for Ingress PathType [GH-634](https://github.com/hashicorp/vault-helm/pull/634) - -## 0.17.0 (October 21st, 2021) - -KNOWN ISSUES: -* The chart will fail to deploy on Kubernetes 1.19+ with `server.ingress.enabled=true` because no `pathType` is set - -CHANGES: -* Vault image default 1.8.4 -* Vault K8s image default 0.14.0 - -Improvements: -* Support Ingress stable networking API [GH-590](https://github.com/hashicorp/vault-helm/pull/590) -* Support setting the `externalTrafficPolicy` for `LoadBalancer` and `NodePort` service types [GH-626](https://github.com/hashicorp/vault-helm/pull/626) -* Support setting ingressClassName on server Ingress [GH-630](https://github.com/hashicorp/vault-helm/pull/630) - -Bugs: -* Ensure `kubeletRootDir` volume path and mounts are the same when `csi.daemonSet.kubeletRootDir` is overridden [GH-628](https://github.com/hashicorp/vault-helm/pull/628) - -## 0.16.1 (September 29th, 2021) - -CHANGES: -* Vault image default 1.8.3 -* Vault K8s image default 0.13.1 - -## 0.16.0 (September 16th, 2021) - -CHANGES: -* Support for deploying a leader-elector container with the [vault-k8s injector](https://github.com/hashicorp/vault-k8s) injector will be removed in version 0.18.0 of this chart since vault-k8s now uses an internal mechanism to determine leadership. To enable the deployment of the leader-elector container for use with vault-k8s 0.12.0 and earlier, set `useContainer=true`. - -Improvements: - * Make CSI provider `hostPaths` configurable via `csi.daemonSet.providersDir` and `csi.daemonSet.kubeletRootDir` [GH-603](https://github.com/hashicorp/vault-helm/pull/603) - * Support vault-k8s internal leader election [GH-568](https://github.com/hashicorp/vault-helm/pull/568) [GH-607](https://github.com/hashicorp/vault-helm/pull/607) - -## 0.15.0 (August 23rd, 2021) - -Improvements: -* Add imagePullSecrets on server test [GH-572](https://github.com/hashicorp/vault-helm/pull/572) -* Add injector.webhookAnnotations chart option [GH-584](https://github.com/hashicorp/vault-helm/pull/584) - -## 0.14.0 (July 28th, 2021) - -Features: -* Added templateConfig.exitOnRetryFailure chart option for the injector [GH-560](https://github.com/hashicorp/vault-helm/pull/560) - -Improvements: -* Support configuring pod tolerations, pod affinity, and node selectors as YAML [GH-565](https://github.com/hashicorp/vault-helm/pull/565) -* Set the default vault image to come from the hashicorp organization [GH-567](https://github.com/hashicorp/vault-helm/pull/567) -* Add support for running the acceptance tests against a local `kind` cluster [GH-567](https://github.com/hashicorp/vault-helm/pull/567) -* Add `server.ingress.activeService` to configure if the ingress should use the active service [GH-570](https://github.com/hashicorp/vault-helm/pull/570) -* Add `server.route.activeService` to configure if the route should use the active service [GH-570](https://github.com/hashicorp/vault-helm/pull/570) -* Support configuring `global.imagePullSecrets` from a string array [GH-576](https://github.com/hashicorp/vault-helm/pull/576) - - -## 0.13.0 (June 17th, 2021) - -Improvements: -* Added a helm test for vault server [GH-531](https://github.com/hashicorp/vault-helm/pull/531) -* Added server.enterpriseLicense option [GH-547](https://github.com/hashicorp/vault-helm/pull/547) -* Added OpenShift overrides [GH-549](https://github.com/hashicorp/vault-helm/pull/549) - -Bugs: -* Fix ui.serviceNodePort schema [GH-537](https://github.com/hashicorp/vault-helm/pull/537) -* Fix server.ha.disruptionBudget.maxUnavailable schema [GH-535](https://github.com/hashicorp/vault-helm/pull/535) -* Added webhook-certs volume mount to sidecar injector [GH-545](https://github.com/hashicorp/vault-helm/pull/545) - -## 0.12.0 (May 25th, 2021) - -Features: -* Pass additional arguments to `vault-csi-provider` using `csi.extraArgs` [GH-526](https://github.com/hashicorp/vault-helm/pull/526) - -Improvements: -* Set chart kubeVersion and added chart-verifier tests [GH-510](https://github.com/hashicorp/vault-helm/pull/510) -* Added values json schema [GH-513](https://github.com/hashicorp/vault-helm/pull/513) -* Ability to set tolerations for CSI daemonset pods [GH-521](https://github.com/hashicorp/vault-helm/pull/521) -* UI target port is now configurable [GH-437](https://github.com/hashicorp/vault-helm/pull/437) - -Bugs: -* CSI: `global.imagePullSecrets` are now also used for CSI daemonset [GH-519](https://github.com/hashicorp/vault-helm/pull/519) - -## 0.11.0 (April 14th, 2021) - -Features: -* Added `server.enabled` to explicitly skip installing a Vault server [GH-486](https://github.com/hashicorp/vault-helm/pull/486) -* Injector now supports enabling host network [GH-471](https://github.com/hashicorp/vault-helm/pull/471) -* Injector port is now configurable [GH-489](https://github.com/hashicorp/vault-helm/pull/489) -* Injector Vault Agent resource defaults are now configurable [GH-493](https://github.com/hashicorp/vault-helm/pull/493) -* Extra paths can now be added to the Vault ingress service [GH-460](https://github.com/hashicorp/vault-helm/pull/460) -* Log level and format can now be set directly using `server.logFormat` and `server.logLevel` [GH-488](https://github.com/hashicorp/vault-helm/pull/488) - -Improvements: -* Added `https` name to injector service port [GH-495](https://github.com/hashicorp/vault-helm/pull/495) - -Bugs: -* CSI: Fix ClusterRole name and DaemonSet's service account to properly match deployment name [GH-486](https://github.com/hashicorp/vault-helm/pull/486) - -## 0.10.0 (March 25th, 2021) - -Features: -* Add support for [Vault CSI provider](https://github.com/hashicorp/vault-csi-provider) [GH-461](https://github.com/hashicorp/vault-helm/pull/461) - -Improvements: -* `objectSelector` can now be set on the mutating admission webhook [GH-456](https://github.com/hashicorp/vault-helm/pull/456) - -## 0.9.1 (February 2nd, 2021) - -Bugs: -* Injector: fix labels for default anti-affinity rule [GH-441](https://github.com/hashicorp/vault-helm/pull/441), [GH-442](https://github.com/hashicorp/vault-helm/pull/442) -* Set VAULT_DEV_LISTEN_ADDRESS in dev mode [GH-446](https://github.com/hashicorp/vault-helm/pull/446) - -## 0.9.0 (January 5th, 2021) - -Features: -* Injector now supports configurable number of replicas [GH-436](https://github.com/hashicorp/vault-helm/pull/436) -* Injector now supports auto TLS for multiple replicas using leader elections [GH-436](https://github.com/hashicorp/vault-helm/pull/436) - -Improvements: -* Dev mode now supports `server.extraArgs` [GH-421](https://github.com/hashicorp/vault-helm/pull/421) -* Dev mode root token is now configurable with `server.dev.devRootToken` [GH-415](https://github.com/hashicorp/vault-helm/pull/415) -* ClusterRoleBinding updated to `v1` [GH-395](https://github.com/hashicorp/vault-helm/pull/395) -* MutatingWebhook updated to `v1` [GH-408](https://github.com/hashicorp/vault-helm/pull/408) -* Injector service now supports `injector.service.annotations` [425](https://github.com/hashicorp/vault-helm/pull/425) -* Injector now supports `injector.extraLabels` [428](https://github.com/hashicorp/vault-helm/pull/428) -* Added `allowPrivilegeEscalation: false` to Vault and Injector containers [429](https://github.com/hashicorp/vault-helm/pull/429) -* Network Policy now supports `server.networkPolicy.egress` [389](https://github.com/hashicorp/vault-helm/pull/389) - -## 0.8.0 (October 20th, 2020) - -Improvements: -* Make server NetworkPolicy independent of OpenShift [GH-381](https://github.com/hashicorp/vault-helm/pull/381) -* Added configurables for all probe values [GH-387](https://github.com/hashicorp/vault-helm/pull/387) -* MountPath for audit and data storage is now configurable [GH-393](https://github.com/hashicorp/vault-helm/pull/393) -* Annotations can now be added to the Injector pods [GH-394](https://github.com/hashicorp/vault-helm/pull/394) -* The injector can now be configured with a failurePolicy [GH-400](https://github.com/hashicorp/vault-helm/pull/400) -* Added additional environment variables for rendering within Vault config [GH-398](https://github.com/hashicorp/vault-helm/pull/398) -* Service account for Vault K8s auth is automatically created when `injector.externalVaultAddr` is set [GH-392](https://github.com/hashicorp/vault-helm/pull/392) - -Bugs: -* Fixed install output using Helm V2 command [GH-378](https://github.com/hashicorp/vault-helm/pull/378) - -## 0.7.0 (August 24th, 2020) - -Features: -* Added `volumes` and `volumeMounts` for mounting _any_ type of volume [GH-314](https://github.com/hashicorp/vault-helm/pull/314). -* Added configurable to enable prometheus telemetery exporter for Vault Agent Injector [GH-372](https://github.com/hashicorp/vault-helm/pull/372) - -Improvements: -* Added `defaultMode` configurable to `extraVolumes`[GH-321](https://github.com/hashicorp/vault-helm/pull/321) -* Option to install and use PodSecurityPolicy's for vault server and injector [GH-177](https://github.com/hashicorp/vault-helm/pull/177) -* `VAULT_API_ADDR` is now configurable [GH-290](https://github.com/hashicorp/vault-helm/pull/290) -* Removed deprecated tolerate unready endpoint annotations [GH-363](https://github.com/hashicorp/vault-helm/pull/363) -* Add an option to set annotations on the StatefulSet [GH-199](https://github.com/hashicorp/vault-helm/pull/199) -* Make the vault server serviceAccount name a configuration option [GH-367](https://github.com/hashicorp/vault-helm/pull/367) -* Removed annotation striction from `dev` mode [GH-371](https://github.com/hashicorp/vault-helm/pull/371) -* Add an option to set annotations on PVCs [GH-364](https://github.com/hashicorp/vault-helm/pull/364) -* Added service configurables for UI [GH-285](https://github.com/hashicorp/vault-helm/pull/285) - -Bugs: -* Fix python dependency in test image [GH-337](https://github.com/hashicorp/vault-helm/pull/337) -* Fix caBundle not being quoted causing validation issues with Helm 3 [GH-352](https://github.com/hashicorp/vault-helm/pull/352) -* Fix injector network policy being rendered when injector is not enabled [GH-358](https://github.com/hashicorp/vault-helm/pull/358) - -## 0.6.0 (June 3rd, 2020) - -Features: -* Added `extraInitContainers` to define init containers for the Vault cluster [GH-258](https://github.com/hashicorp/vault-helm/pull/258) -* Added `postStart` lifecycle hook allowing users to configure commands to run on the Vault pods after they're ready [GH-315](https://github.com/hashicorp/vault-helm/pull/315) -* Beta: Added OpenShift support [GH-319](https://github.com/hashicorp/vault-helm/pull/319) - -Improvements: -* Server configs can now be defined in YAML. Multi-line string configs are still compatible [GH-213](https://github.com/hashicorp/vault-helm/pull/213) -* Removed IPC_LOCK privileges since swap is disabled on containers [[GH-198](https://github.com/hashicorp/vault-helm/pull/198)] -* Use port names that map to vault.scheme [[GH-223](https://github.com/hashicorp/vault-helm/pull/223)] -* Allow both yaml and multi-line string annotations [[GH-272](https://github.com/hashicorp/vault-helm/pull/272)] -* Added configurable to set the Raft node name to hostname [[GH-269](https://github.com/hashicorp/vault-helm/pull/269)] -* Support setting priorityClassName on pods [[GH-282](https://github.com/hashicorp/vault-helm/pull/282)] -* Added support for ingress apiVersion `networking.k8s.io/v1beta1` [[GH-310](https://github.com/hashicorp/vault-helm/pull/310)] -* Added configurable to change service type for the HA active service [GH-317](https://github.com/hashicorp/vault-helm/pull/317) - -Bugs: -* Fixed default ingress path [[GH-224](https://github.com/hashicorp/vault-helm/pull/224)] -* Fixed annotations for HA standby/active services [[GH-268](https://github.com/hashicorp/vault-helm/pull/268)] -* Updated some value defaults to match their use in templates [[GH-309](https://github.com/hashicorp/vault-helm/pull/309)] -* Use active service on ingress when ha [[GH-270](https://github.com/hashicorp/vault-helm/pull/270)] -* Fixed bug where pull secrets weren't being used for injector image [GH-298](https://github.com/hashicorp/vault-helm/pull/298) - -## 0.5.0 (April 9th, 2020) - -Features: - -* Added Raft support for HA mode [[GH-228](https://github.com/hashicorp/vault-helm/pull/229)] -* Now supports Vault Enterprise [[GH-250](https://github.com/hashicorp/vault-helm/pull/250)] -* Added K8s Service Registration for HA modes [[GH-250](https://github.com/hashicorp/vault-helm/pull/250)] - -* Option to set `AGENT_INJECT_VAULT_AUTH_PATH` for the injector [[GH-185](https://github.com/hashicorp/vault-helm/pull/185)] -* Added environment variables for logging and revocation on Vault Agent Injector [[GH-219](https://github.com/hashicorp/vault-helm/pull/219)] -* Option to set environment variables for the injector deployment [[GH-232](https://github.com/hashicorp/vault-helm/pull/232)] -* Added affinity, tolerations, and nodeSelector options for the injector deployment [[GH-234](https://github.com/hashicorp/vault-helm/pull/234)] -* Made all annotations multi-line strings [[GH-227](https://github.com/hashicorp/vault-helm/pull/227)] - -## 0.4.0 (February 21st, 2020) - -Improvements: - -* Allow process namespace sharing between Vault and sidecar containers [[GH-174](https://github.com/hashicorp/vault-helm/pull/174)] -* Added configurable to change updateStrategy [[GH-172](https://github.com/hashicorp/vault-helm/pull/172)] -* Added sleep in the preStop lifecycle step [[GH-188](https://github.com/hashicorp/vault-helm/pull/188)] -* Updated chart and tests to Helm 3 [[GH-195](https://github.com/hashicorp/vault-helm/pull/195)] -* Adds Values.injector.externalVaultAddr to use the injector with an external vault [[GH-207](https://github.com/hashicorp/vault-helm/pull/207)] - -Bugs: - -* Fix bug where Vault lifecycle was appended after extra containers. [[GH-179](https://github.com/hashicorp/vault-helm/pull/179)] - -## 0.3.3 (January 14th, 2020) - -Security: - -* Added `server.extraArgs` to allow loading of additional Vault configurations containing sensitive settings [GH-175](https://github.com/hashicorp/vault-helm/issues/175) - -Bugs: - -* Fixed injection bug where wrong environment variables were being used for manually mounted TLS files - -## 0.3.2 (January 8th, 2020) - -Bugs: - -* Fixed injection bug where TLS Skip Verify was true by default [VK8S-35] - -## 0.3.1 (January 2nd, 2020) - -Bugs: - -* Fixed injection bug causing kube-system pods to be rejected [VK8S-14] - -## 0.3.0 (December 19th, 2019) - -Features: - -* Extra containers can now be added to the Vault pods -* Added configurability of pod probes -* Added Vault Agent Injector - -Improvements: - -* Moved `global.image` to `server.image` -* Changed UI service template to route pods that aren't ready via `publishNotReadyAddresses: true` -* Added better HTTP/HTTPS scheme support to http probes -* Added configurable node port for Vault service -* `server.authDelegator` is now enabled by default - -Bugs: - -* Fixed upgrade bug by removing chart label which contained the version -* Fixed typo on `serviceAccount` (was `serviceaccount`) -* Fixed readiness/liveliness HTTP probe default to accept standbys - -## 0.2.1 (November 12th, 2019) - -Bugs: - -* Removed `readOnlyRootFilesystem` causing issues when validating deployments - -## 0.2.0 (October 29th, 2019) - -Features: - -* Added load balancer support -* Added ingress support -* Added configurable for service types (ClusterIP, NodePort, LoadBalancer, etc) -* Removed root requirements, now runs as Vault user - -Improvements: - -* Added namespace value to all rendered objects -* Made ports configurable in services -* Added the ability to add custom annotations to services -* Added docker image for running bats test in CircleCI -* Removed restrictions around `dev` mode such as annotations -* `readOnlyRootFilesystem` is now configurable -* Image Pull Policy is now configurable - -Bugs: - -* Fixed selector bugs related to Helm label updates (services, affinities, and pod disruption) -* Fixed bug where audit storage was not being mounted in HA mode -* Fixed bug where Vault pod wasn't receiving SIGTERM signals - - -## 0.1.2 (August 22nd, 2019) - -Features: - -* Added `extraSecretEnvironmentVars` to allow users to mount secrets as - environment variables -* Added `tlsDisable` configurable to change HTTP protocols from HTTP/HTTPS - depending on the value -* Added `serviceNodePort` to configure a NodePort value when setting `serviceType` - to "NodePort" - -Improvements: - -* Changed UI port to 8200 for better HTTP protocol support -* Added `path` to `extraVolumes` to define where the volume should be - mounted. Defaults to `/vault/userconfig` -* Upgraded Vault to 1.2.2 - -Bugs: - -* Fixed bug where upgrade would fail because immutable labels were being - changed (Helm Version label) -* Fixed bug where UI service used wrong selector after updating helm labels -* Added `VAULT_API_ADDR` env to Vault pod to fixed bug where Vault thinks - Consul is the active node -* Removed `step-down` preStop since it requires authentication. Shutdown signal - sent by Kube acts similar to `step-down` - - -## 0.1.1 (August 7th, 2019) - -Features: - -* Added `authDelegator` Cluster Role Binding to Vault service account for - bootstrapping Kube auth method - -Improvements: - -* Added `server.service.clusterIP` to `values.yml` so users can toggle - the Vault service to headless by using the value `None`. -* Upgraded Vault to 1.2.1 - -## 0.1.0 (August 6th, 2019) - -Initial release diff --git a/vault/CONTRIBUTING.md b/vault/CONTRIBUTING.md deleted file mode 100644 index f1c16000..00000000 --- a/vault/CONTRIBUTING.md +++ /dev/null @@ -1,239 +0,0 @@ -# Contributing to Vault Helm - -**Please note:** We take Vault's security and our users' trust very seriously. -If you believe you have found a security issue in Vault, please responsibly -disclose by contacting us at security@hashicorp.com. - -**First:** if you're unsure or afraid of _anything_, just ask or submit the -issue or pull request anyways. You won't be yelled at for giving it your best -effort. The worst that can happen is that you'll be politely asked to change -something. We appreciate any sort of contributions, and don't want a wall of -rules to get in the way of that. - -That said, if you want to ensure that a pull request is likely to be merged, -talk to us! You can find out our thoughts and ensure that your contribution -won't clash or be obviated by Vault's normal direction. A great way to do this -is via the [Vault Google Group][2]. Sometimes Vault devs are in `#vault-tool` -on Freenode, too. - -This document will cover what we're looking for in terms of reporting issues. -By addressing all the points we're looking for, it raises the chances we can -quickly merge or address your contributions. - -## Issues - -### Reporting an Issue - -* Make sure you test against the latest released version. It is possible - we already fixed the bug you're experiencing. Even better is if you can test - against `main`, as bugs are fixed regularly but new versions are only - released every few months. - -* Provide steps to reproduce the issue, and if possible include the expected - results as well as the actual results. Please provide text, not screen shots! - -* Respond as promptly as possible to any questions made by the Vault - team to your issue. Stale issues will be closed periodically. - -### Issue Lifecycle - -1. The issue is reported. - -2. The issue is verified and categorized by a Vault Helm collaborator. - Categorization is done via tags. For example, bugs are marked as "bugs". - -3. Unless it is critical, the issue may be left for a period of time (sometimes - many weeks), giving outside contributors -- maybe you!? -- a chance to - address the issue. - -4. The issue is addressed in a pull request or commit. The issue will be - referenced in the commit message so that the code that fixes it is clearly - linked. - -5. The issue is closed. Sometimes, valid issues will be closed to keep - the issue tracker clean. The issue is still indexed and available for - future viewers, or can be re-opened if necessary. - -## Testing - -The Helm chart ships with both unit and acceptance tests. - -The unit tests don't require any active Kubernetes cluster and complete -very quickly. These should be used for fast feedback during development. -The acceptance tests require a Kubernetes cluster with a configured `kubectl`. - -### Test Using Docker Container - -The following are the instructions for running bats tests using a Docker container. - -#### Prerequisites - -* Docker installed -* `vault-helm` checked out locally - -#### Test - -**Note:** the following commands should be run from the `vault-helm` directory. - -First, build the Docker image for running the tests: - -```shell -docker build -f ${PWD}/test/docker/Test.dockerfile ${PWD}/test/docker/ -t vault-helm-test -``` -Next, execute the tests with the following commands: -```shell -docker run -it --rm -v "${PWD}:/test" vault-helm-test bats /test/test/unit -``` -It's possible to only run specific bats tests using regular expressions. -For example, the following will run only tests with "injector" in the name: -```shell -docker run -it --rm -v "${PWD}:/test" vault-helm-test bats /test/test/unit -f "injector" -``` - -### Test Manually -The following are the instructions for running bats tests on your workstation. -#### Prerequisites -* [Bats](https://github.com/bats-core/bats-core) - ```bash - brew install bats-core - ``` -* [yq](https://pypi.org/project/yq/) - ```bash - brew install python-yq - ``` -* [helm](https://helm.sh) - ```bash - brew install kubernetes-helm - ``` - -#### Test - -To run the unit tests: - - bats ./test/unit - -To run the acceptance tests: - - bats ./test/acceptance - -If the acceptance tests fail, deployed resources in the Kubernetes cluster -may not be properly cleaned up. We recommend recycling the Kubernetes cluster to -start from a clean slate. - -**Note:** There is a Terraform configuration in the -[`test/terraform/`](https://github.com/hashicorp/vault-helm/tree/main/test/terraform) directory -that can be used to quickly bring up a GKE cluster and configure -`kubectl` and `helm` locally. This can be used to quickly spin up a test -cluster for acceptance tests. Unit tests _do not_ require a running Kubernetes -cluster. - -### Writing Unit Tests - -Changes to the Helm chart should be accompanied by appropriate unit tests. - -#### Formatting - -- Put tests in the test file in the same order as the variables appear in the `values.yaml`. -- Start tests for a chart value with a header that says what is being tested, like this: - ``` - #-------------------------------------------------------------------- - # annotations - ``` - -- Name the test based on what it's testing in the following format (this will be its first line): - ``` - @test "
: " { - ``` - - When adding tests to an existing file, the first section will be the same as the other tests in the file. - -#### Test Details - -[Bats](https://github.com/bats-core/bats-core) provides a way to run commands in a shell and inspect the output in an automated way. -In all of the tests in this repo, the base command being run is [helm template](https://docs.helm.sh/helm/#helm-template) which turns the templated files into straight yaml output. -In this way, we're able to test that the various conditionals in the templates render as we would expect. - -Each test defines the files that should be rendered using the `--show-only` flag, then it might adjust chart values by adding `--set` flags as well. -The output from this `helm template` command is then piped to [yq](https://pypi.org/project/yq/). -`yq` allows us to pull out just the information we're interested in, either by referencing its position in the yaml file directly or giving information about it (like its length). -The `-r` flag can be used with `yq` to return a raw string instead of a quoted one which is especially useful when looking for an exact match. - -The test passes or fails based on the conditional at the end that is in square brackets, which is a comparison of our expected value and the output of `helm template` piped to `yq`. - -The `| tee /dev/stderr ` pieces direct any terminal output of the `helm template` and `yq` commands to stderr so that it doesn't interfere with `bats`. - -#### Test Examples - -Here are some examples of common test patterns: - -- Check that a value is disabled by default - - ``` - @test "ui/Service: no type by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "null" ] - } - ``` - - In this example, nothing is changed from the default templates (no `--set` flags), then we use `yq` to retrieve the value we're checking, `.spec.type`. - This output is then compared against our expected value (`null` in this case) in the assertion `[ "${actual}" = "null" ]`. - - -- Check that a template value is rendered to a specific value - ``` - @test "ui/Service: specified type" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'ui.serviceType=LoadBalancer' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "LoadBalancer" ] - } - ``` - - This is very similar to the last example, except we've changed a default value with the `--set` flag and correspondingly changed the expected value. - -- Check that a template value contains several values - ``` - @test "server/standalone-StatefulSet: custom resources" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.resources.requests.memory=256Mi' \ - --set 'server.resources.requests.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) - [ "${actual}" = "256Mi" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.resources.limits.memory=256Mi' \ - --set 'server.resources.limits.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) - [ "${actual}" = "256Mi" ] - ``` - - *Note:* If testing more than two conditions, it would be good to separate the `helm template` part of the command from the `yq` sections to reduce redundant work. - -- Check that an entire template file is not rendered - ``` - @test "syncCatalog/Deployment: disabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - } - ``` - Here we are check the length of the command output to see if the anything is rendered. - This style can easily be switched to check that a file is rendered instead. diff --git a/vault/Chart.yaml b/vault/Chart.yaml deleted file mode 100644 index 91565e3b..00000000 --- a/vault/Chart.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v2 -name: vault -version: 0.18.0 -appVersion: 1.9.0 -kubeVersion: ">= 1.14.0-0" -description: Official HashiCorp Vault Chart -home: https://www.vaultproject.io -icon: https://github.com/hashicorp/vault/raw/f22d202cde2018f9455dec755118a9b84586e082/Vault_PrimaryLogo_Black.png -keywords: ["vault", "security", "encryption", "secrets", "management", "automation", "infrastructure"] -sources: - - https://github.com/hashicorp/vault - - https://github.com/hashicorp/vault-helm - - https://github.com/hashicorp/vault-k8s - - https://github.com/hashicorp/vault-csi-provider diff --git a/vault/LICENSE.md b/vault/LICENSE.md deleted file mode 100644 index 82b4de97..00000000 --- a/vault/LICENSE.md +++ /dev/null @@ -1,353 +0,0 @@ -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. “Contributor” - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. “Contributor Version” - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor’s Contribution. - -1.3. “Contribution” - - means Covered Software of a particular Contributor. - -1.4. “Covered Software” - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. “Incompatible With Secondary Licenses” - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of version - 1.1 or earlier of the License, but not also under the terms of a - Secondary License. - -1.6. “Executable Form” - - means any form of the work other than Source Code Form. - -1.7. “Larger Work” - - means a work that combines Covered Software with other material, in a separate - file or files, that is not Covered Software. - -1.8. “License” - - means this document. - -1.9. “Licensable” - - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed by - this License. - -1.10. “Modifications” - - means any of the following: - - a. any file in Source Code Form that results from an addition to, deletion - from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. “Patent Claims” of a Contributor - - means any patent claim(s), including without limitation, method, process, - and apparatus claims, in any patent Licensable by such Contributor that - would be infringed, but for the grant of the License, by the making, - using, selling, offering for sale, having made, import, or transfer of - either its Contributions or its Contributor Version. - -1.12. “Secondary License” - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. “Source Code Form” - - means the form of the work preferred for making modifications. - -1.14. “You” (or “Your”) - - means an individual or a legal entity exercising rights under this - License. For legal entities, “You” includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, “control” means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or as - part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its Contributions - or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution become - effective for each Contribution on the date the Contributor first distributes - such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under this - License. No additional rights or licenses will be implied from the distribution - or licensing of Covered Software under this License. Notwithstanding Section - 2.1(b) above, no patent license is granted by a Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party’s - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of its - Contributions. - - This License does not grant any rights in the trademarks, service marks, or - logos of any Contributor (except as may be necessary to comply with the - notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this License - (see Section 10.2) or under the terms of a Secondary License (if permitted - under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its Contributions - are its original creation(s) or it has sufficient rights to grant the - rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under applicable - copyright doctrines of fair use, fair dealing, or other equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the - terms of this License. You must inform recipients that the Source Code Form - of the Covered Software is governed by the terms of this License, and how - they can obtain a copy of this License. You may not attempt to alter or - restrict the recipients’ rights in the Source Code Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this License, - or sublicense it under different terms, provided that the license for - the Executable Form does not attempt to limit or alter the recipients’ - rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for the - Covered Software. If the Larger Work is a combination of Covered Software - with a work governed by one or more Secondary Licenses, and the Covered - Software is not Incompatible With Secondary Licenses, this License permits - You to additionally distribute such Covered Software under the terms of - such Secondary License(s), so that the recipient of the Larger Work may, at - their option, further distribute the Covered Software under the terms of - either this License or such Secondary License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations - of liability) contained within the Source Code Form of the Covered - Software, except that You may alter any license notices to the extent - required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on behalf - of any Contributor. You must make it absolutely clear that any such - warranty, support, indemnity, or liability obligation is offered by You - alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, judicial - order, or regulation then You must: (a) comply with the terms of this License - to the maximum extent possible; and (b) describe the limitations and the code - they affect. Such description must be placed in a text file included with all - distributions of the Covered Software under this License. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing basis, - if such Contributor fails to notify You of the non-compliance by some - reasonable means prior to 60 days after You have come back into compliance. - Moreover, Your grants from a particular Contributor are reinstated on an - ongoing basis if such Contributor notifies You of the non-compliance by - some reasonable means, this is the first time You have received notice of - non-compliance with this License from such Contributor, and You become - compliant prior to 30 days after Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, counter-claims, - and cross-claims) alleging that a Contributor Version directly or - indirectly infringes any patent, then the rights granted to You by any and - all Contributors for the Covered Software under Section 2.1 of this License - shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an “as is” basis, without - warranty of any kind, either expressed, implied, or statutory, including, - without limitation, warranties that the Covered Software is free of defects, - merchantable, fit for a particular purpose or non-infringing. The entire - risk as to the quality and performance of the Covered Software is with You. - Should any Covered Software prove defective in any respect, You (not any - Contributor) assume the cost of any necessary servicing, repair, or - correction. This disclaimer of warranty constitutes an essential part of this - License. No use of any Covered Software is authorized under this License - except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from such - party’s negligence to the extent applicable law prohibits such limitation. - Some jurisdictions do not allow the exclusion or limitation of incidental or - consequential damages, so this exclusion and limitation may not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts of - a jurisdiction where the defendant maintains its principal place of business - and such litigation shall be governed by laws of that jurisdiction, without - reference to its conflict-of-law provisions. Nothing in this Section shall - prevent a party’s ability to bring cross-claims or counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject matter - hereof. If any provision of this License is held to be unenforceable, such - provision shall be reformed only to the extent necessary to make it - enforceable. Any law or regulation which provides that the language of a - contract shall be construed against the drafter shall not be used to construe - this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version of - the License under which You originally received the Covered Software, or - under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a modified - version of this License if you rename the license and remove any - references to the name of the license steward (except to note that such - modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses - If You choose to distribute Source Code Form that is Incompatible With - Secondary Licenses under the terms of this version of the License, the - notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, then -You may include the notice in a location (such as a LICENSE file in a relevant -directory) where a recipient would be likely to look for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - “Incompatible With Secondary Licenses” Notice - - This Source Code Form is “Incompatible - With Secondary Licenses”, as defined by - the Mozilla Public License, v. 2.0. diff --git a/vault/Makefile b/vault/Makefile deleted file mode 100644 index 0ac68501..00000000 --- a/vault/Makefile +++ /dev/null @@ -1,101 +0,0 @@ -TEST_IMAGE?=vault-helm-test -GOOGLE_CREDENTIALS?=vault-helm-test.json -CLOUDSDK_CORE_PROJECT?=vault-helm-dev-246514 -# set to run a single test - e.g acceptance/server-ha-enterprise-dr.bats -ACCEPTANCE_TESTS?=acceptance - -# filter bats unit tests to run. -UNIT_TESTS_FILTER?='.*' - -# set to 'true' to run acceptance tests locally in a kind cluster -LOCAL_ACCEPTANCE_TESTS?=false - -# kind cluster name -KIND_CLUSTER_NAME?=vault-helm - -# kind k8s version -KIND_K8S_VERSION?=v1.20.2 - -# Generate json schema for chart values. See test/README.md for more details. -values-schema: - helm schema-gen values.yaml > values.schema.json - -test-image: - @docker build --rm -t $(TEST_IMAGE) -f $(CURDIR)/test/docker/Test.dockerfile $(CURDIR) - -test-unit: - @docker run --rm -it -v ${PWD}:/helm-test $(TEST_IMAGE) bats -f $(UNIT_TESTS_FILTER) /helm-test/test/unit - -test-bats: test-unit test-acceptance - -test: test-image test-bats - -# run acceptance tests on GKE -# set google project/credential vars above -test-acceptance: -ifeq ($(LOCAL_ACCEPTANCE_TESTS),true) - make setup-kind acceptance -else - @docker run -it -v ${PWD}:/helm-test \ - -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ - -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ - -e KUBECONFIG=/helm-test/.kube/config \ - -e VAULT_LICENSE_CI=${VAULT_LICENSE_CI} \ - -w /helm-test \ - $(TEST_IMAGE) \ - make acceptance -endif - -# destroy GKE cluster using terraform -test-destroy: - @docker run -it -v ${PWD}:/helm-test \ - -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ - -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ - -w /helm-test \ - $(TEST_IMAGE) \ - make destroy-cluster - -# provision GKE cluster using terraform -test-provision: - @docker run -it -v ${PWD}:/helm-test \ - -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ - -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ - -e KUBECONFIG=/helm-test/.kube/config \ - -w /helm-test \ - $(TEST_IMAGE) \ - make provision-cluster - -# this target is for running the acceptance tests -# it is run in the docker container above when the test-acceptance target is invoked -acceptance: -ifneq ($(LOCAL_ACCEPTANCE_TESTS),true) - gcloud auth activate-service-account --key-file=${GOOGLE_CREDENTIALS} -endif - bats test/${ACCEPTANCE_TESTS} - -# this target is for provisioning the GKE cluster -# it is run in the docker container above when the test-provision target is invoked -provision-cluster: - gcloud auth activate-service-account --key-file=${GOOGLE_CREDENTIALS} - terraform init test/terraform - terraform apply -var project=${CLOUDSDK_CORE_PROJECT} -var init_cli=true -auto-approve test/terraform - -# this target is for removing the GKE cluster -# it is run in the docker container above when the test-destroy target is invoked -destroy-cluster: - terraform destroy -auto-approve - -# create a kind cluster for running the acceptance tests locally -setup-kind: - kind get clusters | grep -q "^${KIND_CLUSTER_NAME}$$" || \ - kind create cluster \ - --image kindest/node:${KIND_K8S_VERSION} \ - --name ${KIND_CLUSTER_NAME} \ - --config $(CURDIR)/test/kind/config.yaml - kubectl config use-context kind-${KIND_CLUSTER_NAME} - -# delete the kind cluster -delete-kind: - kind delete cluster --name ${KIND_CLUSTER_NAME} || : - -.PHONY: values-schema test-image test-unit test-bats test test-acceptance test-destroy test-provision acceptance provision-cluster destroy-cluster diff --git a/vault/README.md b/vault/README.md deleted file mode 100644 index f95b26fc..00000000 --- a/vault/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Vault Helm Chart - -> :warning: **Please note**: We take Vault's security and our users' trust very seriously. If -you believe you have found a security issue in Vault Helm, _please responsibly disclose_ -by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). - -This repository contains the official HashiCorp Helm chart for installing -and configuring Vault on Kubernetes. This chart supports multiple use -cases of Vault on Kubernetes depending on the values provided. - -For full documentation on this Helm chart along with all the ways you can -use Vault with Kubernetes, please see the -[Vault and Kubernetes documentation](https://www.vaultproject.io/docs/platform/k8s/). - -## Prerequisites - -To use the charts here, [Helm](https://helm.sh/) must be configured for your -Kubernetes cluster. Setting up Kubernetes and Helm is outside the scope of -this README. Please refer to the Kubernetes and Helm documentation. - -The versions required are: - - * **Helm 3.0+** - This is the earliest version of Helm tested. It is possible - it works with earlier versions but this chart is untested for those versions. - * **Kubernetes 1.14+** - This is the earliest version of Kubernetes tested. - It is possible that this chart works with earlier versions but it is - untested. - -## Usage - -To install the latest version of this chart, add the Hashicorp helm repository -and run `helm install`: - -```console -$ helm repo add hashicorp https://helm.releases.hashicorp.com -"hashicorp" has been added to your repositories - -$ helm install vault hashicorp/vault -``` - -Please see the many options supported in the `values.yaml` file. These are also -fully documented directly on the [Vault -website](https://www.vaultproject.io/docs/platform/k8s/helm) along with more -detailed installation instructions. diff --git a/vault/templates/NOTES.txt b/vault/templates/NOTES.txt deleted file mode 100644 index 8e267121..00000000 --- a/vault/templates/NOTES.txt +++ /dev/null @@ -1,14 +0,0 @@ - -Thank you for installing HashiCorp Vault! - -Now that you have deployed Vault, you should look over the docs on using -Vault with Kubernetes available here: - -https://www.vaultproject.io/docs/ - - -Your release is named {{ .Release.Name }}. To learn more about the release, try: - - $ helm status {{ .Release.Name }} - $ helm get manifest {{ .Release.Name }} - diff --git a/vault/templates/_helpers.tpl b/vault/templates/_helpers.tpl deleted file mode 100644 index 731119a9..00000000 --- a/vault/templates/_helpers.tpl +++ /dev/null @@ -1,692 +0,0 @@ -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to -this (by the DNS naming spec). If release name contains chart name it will -be used as a full name. -*/}} -{{- define "vault.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "vault.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Expand the name of the chart. -*/}} -{{- define "vault.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Compute the maximum number of unavailable replicas for the PodDisruptionBudget. -This defaults to (n/2)-1 where n is the number of members of the server cluster. -Add a special case for replicas=1, where it should default to 0 as well. -*/}} -{{- define "vault.pdb.maxUnavailable" -}} -{{- if eq (int .Values.server.ha.replicas) 1 -}} -{{ 0 }} -{{- else if .Values.server.ha.disruptionBudget.maxUnavailable -}} -{{ .Values.server.ha.disruptionBudget.maxUnavailable -}} -{{- else -}} -{{- div (sub (div (mul (int .Values.server.ha.replicas) 10) 2) 1) 10 -}} -{{- end -}} -{{- end -}} - -{{/* -Set the variable 'mode' to the server mode requested by the user to simplify -template logic. -*/}} -{{- define "vault.mode" -}} - {{- if .Values.injector.externalVaultAddr -}} - {{- $_ := set . "mode" "external" -}} - {{- else if ne (.Values.server.enabled | toString) "true" -}} - {{- $_ := set . "mode" "external" -}} - {{- else if eq (.Values.server.dev.enabled | toString) "true" -}} - {{- $_ := set . "mode" "dev" -}} - {{- else if eq (.Values.server.ha.enabled | toString) "true" -}} - {{- $_ := set . "mode" "ha" -}} - {{- else if or (eq (.Values.server.standalone.enabled | toString) "true") (eq (.Values.server.standalone.enabled | toString) "-") -}} - {{- $_ := set . "mode" "standalone" -}} - {{- else -}} - {{- $_ := set . "mode" "" -}} - {{- end -}} -{{- end -}} - -{{/* -Set's the replica count based on the different modes configured by user -*/}} -{{- define "vault.replicas" -}} - {{ if eq .mode "standalone" }} - {{- default 1 -}} - {{ else if eq .mode "ha" }} - {{- .Values.server.ha.replicas | default 3 -}} - {{ else }} - {{- default 1 -}} - {{ end }} -{{- end -}} - -{{/* -Set's up configmap mounts if this isn't a dev deployment and the user -defined a custom configuration. Additionally iterates over any -extra volumes the user may have specified (such as a secret with TLS). -*/}} -{{- define "vault.volumes" -}} - {{- if and (ne .mode "dev") (or (.Values.server.standalone.config) (.Values.server.ha.config)) }} - - name: config - configMap: - name: {{ template "vault.fullname" . }}-config - {{ end }} - {{- range .Values.server.extraVolumes }} - - name: userconfig-{{ .name }} - {{ .type }}: - {{- if (eq .type "configMap") }} - name: {{ .name }} - {{- else if (eq .type "secret") }} - secretName: {{ .name }} - {{- end }} - defaultMode: {{ .defaultMode | default 420 }} - {{- end }} - {{- if .Values.server.volumes }} - {{- toYaml .Values.server.volumes | nindent 8}} - {{- end }} - {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} - - name: vault-license - secret: - secretName: {{ .Values.server.enterpriseLicense.secretName }} - defaultMode: 0440 - {{- end }} -{{- end -}} - -{{/* -Set's the args for custom command to render the Vault configuration -file with IP addresses to make the out of box experience easier -for users looking to use this chart with Consul Helm. -*/}} -{{- define "vault.args" -}} - {{ if or (eq .mode "standalone") (eq .mode "ha") }} - - | - cp /vault/config/extraconfig-from-values.hcl /tmp/storageconfig.hcl; - [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /tmp/storageconfig.hcl; - [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /tmp/storageconfig.hcl; - [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /tmp/storageconfig.hcl; - [ -n "${API_ADDR}" ] && sed -Ei "s|API_ADDR|${API_ADDR?}|g" /tmp/storageconfig.hcl; - [ -n "${TRANSIT_ADDR}" ] && sed -Ei "s|TRANSIT_ADDR|${TRANSIT_ADDR?}|g" /tmp/storageconfig.hcl; - [ -n "${RAFT_ADDR}" ] && sed -Ei "s|RAFT_ADDR|${RAFT_ADDR?}|g" /tmp/storageconfig.hcl; - /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl {{ .Values.server.extraArgs }} - {{ else if eq .mode "dev" }} - - | - /usr/local/bin/docker-entrypoint.sh vault server -dev {{ .Values.server.extraArgs }} - {{ end }} -{{- end -}} - -{{/* -Set's additional environment variables based on the mode. -*/}} -{{- define "vault.envs" -}} - {{ if eq .mode "dev" }} - - name: VAULT_DEV_ROOT_TOKEN_ID - value: {{ .Values.server.dev.devRootToken }} - - name: VAULT_DEV_LISTEN_ADDRESS - value: "[::]:8200" - {{ end }} -{{- end -}} - -{{/* -Set's which additional volumes should be mounted to the container -based on the mode configured. -*/}} -{{- define "vault.mounts" -}} - {{ if eq (.Values.server.auditStorage.enabled | toString) "true" }} - - name: audit - mountPath: {{ .Values.server.auditStorage.mountPath }} - {{ end }} - {{ if or (eq .mode "standalone") (and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true")) }} - {{ if eq (.Values.server.dataStorage.enabled | toString) "true" }} - - name: data - mountPath: {{ .Values.server.dataStorage.mountPath }} - {{ end }} - {{ end }} - {{ if and (ne .mode "dev") (or (.Values.server.standalone.config) (.Values.server.ha.config)) }} - - name: config - mountPath: /vault/config - {{ end }} - {{- range .Values.server.extraVolumes }} - - name: userconfig-{{ .name }} - readOnly: true - mountPath: {{ .path | default "/vault/userconfig" }}/{{ .name }} - {{- end }} - {{- if .Values.server.volumeMounts }} - {{- toYaml .Values.server.volumeMounts | nindent 12}} - {{- end }} - {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} - - name: vault-license - mountPath: /vault/license - readOnly: true - {{- end }} -{{- end -}} - -{{/* -Set's up the volumeClaimTemplates when data or audit storage is required. HA -might not use data storage since Consul is likely it's backend, however, audit -storage might be desired by the user. -*/}} -{{- define "vault.volumeclaims" -}} - {{- if and (ne .mode "dev") (or .Values.server.dataStorage.enabled .Values.server.auditStorage.enabled) }} - volumeClaimTemplates: - {{- if and (eq (.Values.server.dataStorage.enabled | toString) "true") (or (eq .mode "standalone") (eq (.Values.server.ha.raft.enabled | toString ) "true" )) }} - - metadata: - name: data - {{- include "vault.dataVolumeClaim.annotations" . | nindent 6 }} - spec: - accessModes: - - {{ .Values.server.dataStorage.accessMode | default "ReadWriteOnce" }} - resources: - requests: - storage: {{ .Values.server.dataStorage.size }} - {{- if .Values.server.dataStorage.storageClass }} - storageClassName: {{ .Values.server.dataStorage.storageClass }} - {{- end }} - {{ end }} - {{- if eq (.Values.server.auditStorage.enabled | toString) "true" }} - - metadata: - name: audit - {{- include "vault.auditVolumeClaim.annotations" . | nindent 6 }} - spec: - accessModes: - - {{ .Values.server.auditStorage.accessMode | default "ReadWriteOnce" }} - resources: - requests: - storage: {{ .Values.server.auditStorage.size }} - {{- if .Values.server.auditStorage.storageClass }} - storageClassName: {{ .Values.server.auditStorage.storageClass }} - {{- end }} - {{ end }} - {{ end }} -{{- end -}} - -{{/* -Set's the affinity for pod placement when running in standalone and HA modes. -*/}} -{{- define "vault.affinity" -}} - {{- if and (ne .mode "dev") .Values.server.affinity }} - affinity: - {{ $tp := typeOf .Values.server.affinity }} - {{- if eq $tp "string" }} - {{- tpl .Values.server.affinity . | nindent 8 | trim }} - {{- else }} - {{- toYaml .Values.server.affinity | nindent 8 }} - {{- end }} - {{ end }} -{{- end -}} - -{{/* -Sets the injector affinity for pod placement -*/}} -{{- define "injector.affinity" -}} - {{- if .Values.injector.affinity }} - affinity: - {{ $tp := typeOf .Values.injector.affinity }} - {{- if eq $tp "string" }} - {{- tpl .Values.injector.affinity . | nindent 8 | trim }} - {{- else }} - {{- toYaml .Values.injector.affinity | nindent 8 }} - {{- end }} - {{ end }} -{{- end -}} - -{{/* -Sets the toleration for pod placement when running in standalone and HA modes. -*/}} -{{- define "vault.tolerations" -}} - {{- if and (ne .mode "dev") .Values.server.tolerations }} - tolerations: - {{- $tp := typeOf .Values.server.tolerations }} - {{- if eq $tp "string" }} - {{ tpl .Values.server.tolerations . | nindent 8 | trim }} - {{- else }} - {{- toYaml .Values.server.tolerations | nindent 8 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets the injector toleration for pod placement -*/}} -{{- define "injector.tolerations" -}} - {{- if .Values.injector.tolerations }} - tolerations: - {{- $tp := typeOf .Values.injector.tolerations }} - {{- if eq $tp "string" }} - {{ tpl .Values.injector.tolerations . | nindent 8 | trim }} - {{- else }} - {{- toYaml .Values.injector.tolerations | nindent 8 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Set's the node selector for pod placement when running in standalone and HA modes. -*/}} -{{- define "vault.nodeselector" -}} - {{- if and (ne .mode "dev") .Values.server.nodeSelector }} - nodeSelector: - {{- $tp := typeOf .Values.server.nodeSelector }} - {{- if eq $tp "string" }} - {{ tpl .Values.server.nodeSelector . | nindent 8 | trim }} - {{- else }} - {{- toYaml .Values.server.nodeSelector | nindent 8 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets the injector node selector for pod placement -*/}} -{{- define "injector.nodeselector" -}} - {{- if .Values.injector.nodeSelector }} - nodeSelector: - {{- $tp := typeOf .Values.injector.nodeSelector }} - {{- if eq $tp "string" }} - {{ tpl .Values.injector.nodeSelector . | nindent 8 | trim }} - {{- else }} - {{- toYaml .Values.injector.nodeSelector | nindent 8 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets extra pod annotations -*/}} -{{- define "vault.annotations" -}} - {{- if .Values.server.annotations }} - annotations: - {{- $tp := typeOf .Values.server.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.server.annotations . | nindent 8 }} - {{- else }} - {{- toYaml .Values.server.annotations | nindent 8 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets extra injector pod annotations -*/}} -{{- define "injector.annotations" -}} - {{- if .Values.injector.annotations }} - annotations: - {{- $tp := typeOf .Values.injector.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.injector.annotations . | nindent 8 }} - {{- else }} - {{- toYaml .Values.injector.annotations | nindent 8 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets extra injector service annotations -*/}} -{{- define "injector.service.annotations" -}} - {{- if .Values.injector.service.annotations }} - annotations: - {{- $tp := typeOf .Values.injector.service.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.injector.service.annotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.injector.service.annotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets extra injector webhook annotations -*/}} -{{- define "injector.webhookAnnotations" -}} - {{- if .Values.injector.webhookAnnotations }} - annotations: - {{- $tp := typeOf .Values.injector.webhookAnnotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.injector.webhookAnnotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.injector.webhookAnnotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets extra ui service annotations -*/}} -{{- define "vault.ui.annotations" -}} - {{- if .Values.ui.annotations }} - annotations: - {{- $tp := typeOf .Values.ui.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.ui.annotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.ui.annotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Create the name of the service account to use -*/}} -{{- define "vault.serviceAccount.name" -}} -{{- if .Values.server.serviceAccount.create -}} - {{ default (include "vault.fullname" .) .Values.server.serviceAccount.name }} -{{- else -}} - {{ default "default" .Values.server.serviceAccount.name }} -{{- end -}} -{{- end -}} - -{{/* -Sets extra service account annotations -*/}} -{{- define "vault.serviceAccount.annotations" -}} - {{- if and (ne .mode "dev") .Values.server.serviceAccount.annotations }} - annotations: - {{- $tp := typeOf .Values.server.serviceAccount.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.server.serviceAccount.annotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.server.serviceAccount.annotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets extra ingress annotations -*/}} -{{- define "vault.ingress.annotations" -}} - {{- if .Values.server.ingress.annotations }} - annotations: - {{- $tp := typeOf .Values.server.ingress.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.server.ingress.annotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.server.ingress.annotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets extra route annotations -*/}} -{{- define "vault.route.annotations" -}} - {{- if .Values.server.route.annotations }} - annotations: - {{- $tp := typeOf .Values.server.route.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.server.route.annotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.server.route.annotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets extra vault server Service annotations -*/}} -{{- define "vault.service.annotations" -}} - {{- if .Values.server.service.annotations }} - {{- $tp := typeOf .Values.server.service.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.server.service.annotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.server.service.annotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets PodSecurityPolicy annotations -*/}} -{{- define "vault.psp.annotations" -}} - {{- if .Values.global.psp.annotations }} - annotations: - {{- $tp := typeOf .Values.global.psp.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.global.psp.annotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.global.psp.annotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets extra statefulset annotations -*/}} -{{- define "vault.statefulSet.annotations" -}} - {{- if .Values.server.statefulSet.annotations }} - annotations: - {{- $tp := typeOf .Values.server.statefulSet.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.server.statefulSet.annotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.server.statefulSet.annotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets VolumeClaim annotations for data volume -*/}} -{{- define "vault.dataVolumeClaim.annotations" -}} - {{- if and (ne .mode "dev") (.Values.server.dataStorage.enabled) (.Values.server.dataStorage.annotations) }} - annotations: - {{- $tp := typeOf .Values.server.dataStorage.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.server.dataStorage.annotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.server.dataStorage.annotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets VolumeClaim annotations for audit volume -*/}} -{{- define "vault.auditVolumeClaim.annotations" -}} - {{- if and (ne .mode "dev") (.Values.server.auditStorage.enabled) (.Values.server.auditStorage.annotations) }} - annotations: - {{- $tp := typeOf .Values.server.auditStorage.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.server.auditStorage.annotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.server.auditStorage.annotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Set's the container resources if the user has set any. -*/}} -{{- define "vault.resources" -}} - {{- if .Values.server.resources -}} - resources: -{{ toYaml .Values.server.resources | indent 12}} - {{ end }} -{{- end -}} - -{{/* -Sets the container resources if the user has set any. -*/}} -{{- define "injector.resources" -}} - {{- if .Values.injector.resources -}} - resources: -{{ toYaml .Values.injector.resources | indent 12}} - {{ end }} -{{- end -}} - -{{/* -Sets the container resources if the user has set any. -*/}} -{{- define "csi.resources" -}} - {{- if .Values.csi.resources -}} - resources: -{{ toYaml .Values.csi.resources | indent 12}} - {{ end }} -{{- end -}} - -{{/* -Sets extra CSI daemonset annotations -*/}} -{{- define "csi.daemonSet.annotations" -}} - {{- if .Values.csi.daemonSet.annotations }} - annotations: - {{- $tp := typeOf .Values.csi.daemonSet.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.csi.daemonSet.annotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.csi.daemonSet.annotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets the injector toleration for pod placement -*/}} -{{- define "csi.pod.tolerations" -}} - {{- if .Values.csi.pod.tolerations }} - tolerations: - {{- $tp := typeOf .Values.csi.pod.tolerations }} - {{- if eq $tp "string" }} - {{ tpl .Values.csi.pod.tolerations . | nindent 8 | trim }} - {{- else }} - {{- toYaml .Values.csi.pod.tolerations | nindent 8 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets extra CSI provider pod annotations -*/}} -{{- define "csi.pod.annotations" -}} - {{- if .Values.csi.pod.annotations }} - annotations: - {{- $tp := typeOf .Values.csi.pod.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.csi.pod.annotations . | nindent 8 }} - {{- else }} - {{- toYaml .Values.csi.pod.annotations | nindent 8 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Sets extra CSI service account annotations -*/}} -{{- define "csi.serviceAccount.annotations" -}} - {{- if .Values.csi.serviceAccount.annotations }} - annotations: - {{- $tp := typeOf .Values.csi.serviceAccount.annotations }} - {{- if eq $tp "string" }} - {{- tpl .Values.csi.serviceAccount.annotations . | nindent 4 }} - {{- else }} - {{- toYaml .Values.csi.serviceAccount.annotations | nindent 4 }} - {{- end }} - {{- end }} -{{- end -}} - -{{/* -Inject extra environment vars in the format key:value, if populated -*/}} -{{- define "vault.extraEnvironmentVars" -}} -{{- if .extraEnvironmentVars -}} -{{- range $key, $value := .extraEnvironmentVars }} -- name: {{ printf "%s" $key | replace "." "_" | upper | quote }} - value: {{ $value | quote }} -{{- end }} -{{- end -}} -{{- end -}} - -{{/* -Inject extra environment populated by secrets, if populated -*/}} -{{- define "vault.extraSecretEnvironmentVars" -}} -{{- if .extraSecretEnvironmentVars -}} -{{- range .extraSecretEnvironmentVars }} -- name: {{ .envName }} - valueFrom: - secretKeyRef: - name: {{ .secretName }} - key: {{ .secretKey }} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* Scheme for health check and local endpoint */}} -{{- define "vault.scheme" -}} -{{- if .Values.global.tlsDisable -}} -{{ "http" }} -{{- else -}} -{{ "https" }} -{{- end -}} -{{- end -}} - -{{/* -imagePullSecrets generates pull secrets from either string or map values. -A map value must be indexable by the key 'name'. -*/}} -{{- define "imagePullSecrets" -}} -{{- with .Values.global.imagePullSecrets -}} -imagePullSecrets: -{{- range . -}} -{{- if typeIs "string" . }} - - name: {{ . }} -{{- else if index . "name" }} - - name: {{ .name }} -{{- end }} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -externalTrafficPolicy sets a Service's externalTrafficPolicy if applicable. -Supported inputs are Values.server.service and Values.ui -*/}} -{{- define "service.externalTrafficPolicy" -}} -{{- $type := "" -}} -{{- if .serviceType -}} -{{- $type = .serviceType -}} -{{- else if .type -}} -{{- $type = .type -}} -{{- end -}} -{{- if and .externalTrafficPolicy (or (eq $type "LoadBalancer") (eq $type "NodePort")) }} - externalTrafficPolicy: {{ .externalTrafficPolicy }} -{{- else }} -{{- end }} -{{- end -}} - -{{/* -loadBalancer configuration for the the UI service. -Supported inputs are Values.ui -*/}} -{{- define "service.loadBalancer" -}} -{{- if eq (.serviceType | toString) "LoadBalancer" }} -{{- if .loadBalancerIP }} - loadBalancerIP: {{ .loadBalancerIP }} -{{- end }} -{{- with .loadBalancerSourceRanges }} - loadBalancerSourceRanges: -{{- range . }} - - {{ . }} -{{- end }} -{{- end -}} -{{- end }} -{{- end -}} diff --git a/vault/templates/csi-clusterrole.yaml b/vault/templates/csi-clusterrole.yaml deleted file mode 100644 index a19e520f..00000000 --- a/vault/templates/csi-clusterrole.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if and (eq (.Values.csi.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ template "vault.fullname" . }}-csi-provider-clusterrole - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -rules: -- apiGroups: - - "" - resources: - - serviceaccounts/token - verbs: - - create -{{- end }} diff --git a/vault/templates/csi-clusterrolebinding.yaml b/vault/templates/csi-clusterrolebinding.yaml deleted file mode 100644 index 63d69c7b..00000000 --- a/vault/templates/csi-clusterrolebinding.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if and (eq (.Values.csi.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ template "vault.fullname" . }}-csi-provider-clusterrolebinding - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ template "vault.fullname" . }}-csi-provider-clusterrole -subjects: -- kind: ServiceAccount - name: {{ template "vault.fullname" . }}-csi-provider - namespace: {{ .Release.Namespace }} -{{- end }} diff --git a/vault/templates/csi-daemonset.yaml b/vault/templates/csi-daemonset.yaml deleted file mode 100644 index a6461fbd..00000000 --- a/vault/templates/csi-daemonset.yaml +++ /dev/null @@ -1,81 +0,0 @@ -{{- if and (eq (.Values.csi.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: {{ template "vault.fullname" . }}-csi-provider - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - {{ template "csi.daemonSet.annotations" . }} -spec: - updateStrategy: - type: {{ .Values.csi.daemonSet.updateStrategy.type }} - {{- if .Values.csi.daemonSet.updateStrategy.maxUnavailable }} - rollingUpdate: - maxUnavailable: {{ .Values.csi.daemonSet.updateStrategy.maxUnavailable }} - {{- end }} - selector: - matchLabels: - app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider - app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ template "vault.name" . }}-csi-provider - app.kubernetes.io/instance: {{ .Release.Name }} - {{ template "csi.pod.annotations" . }} - spec: - serviceAccountName: {{ template "vault.fullname" . }}-csi-provider - {{- template "csi.pod.tolerations" . }} - containers: - - name: {{ include "vault.name" . }}-csi-provider - {{ template "csi.resources" . }} - image: "{{ .Values.csi.image.repository }}:{{ .Values.csi.image.tag }}" - imagePullPolicy: {{ .Values.csi.image.pullPolicy }} - args: - - --endpoint=/provider/vault.sock - - --debug={{ .Values.csi.debug }} - {{- if .Values.csi.extraArgs }} - {{- toYaml .Values.csi.extraArgs | nindent 12 }} - {{- end }} - volumeMounts: - - name: providervol - mountPath: "/provider" - - name: mountpoint-dir - mountPath: {{ .Values.csi.daemonSet.kubeletRootDir }}/pods - mountPropagation: HostToContainer - {{- if .Values.csi.volumeMounts }} - {{- toYaml .Values.csi.volumeMounts | nindent 12}} - {{- end }} - livenessProbe: - httpGet: - path: /health/ready - port: 8080 - failureThreshold: {{ .Values.csi.livenessProbe.failureThreshold }} - initialDelaySeconds: {{ .Values.csi.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.csi.livenessProbe.periodSeconds }} - successThreshold: {{ .Values.csi.livenessProbe.successThreshold }} - timeoutSeconds: {{ .Values.csi.livenessProbe.timeoutSeconds }} - readinessProbe: - httpGet: - path: /health/ready - port: 8080 - failureThreshold: {{ .Values.csi.readinessProbe.failureThreshold }} - initialDelaySeconds: {{ .Values.csi.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.csi.readinessProbe.periodSeconds }} - successThreshold: {{ .Values.csi.readinessProbe.successThreshold }} - timeoutSeconds: {{ .Values.csi.readinessProbe.timeoutSeconds }} - volumes: - - name: providervol - hostPath: - path: {{ .Values.csi.daemonSet.providersDir }} - - name: mountpoint-dir - hostPath: - path: {{ .Values.csi.daemonSet.kubeletRootDir }}/pods - {{- if .Values.csi.volumes }} - {{- toYaml .Values.csi.volumes | nindent 8}} - {{- end }} - {{- include "imagePullSecrets" . | nindent 6 }} -{{- end }} diff --git a/vault/templates/csi-serviceaccount.yaml b/vault/templates/csi-serviceaccount.yaml deleted file mode 100644 index ee127481..00000000 --- a/vault/templates/csi-serviceaccount.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- if and (eq (.Values.csi.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ template "vault.fullname" . }}-csi-provider - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - {{ template "csi.serviceAccount.annotations" . }} -{{- end }} diff --git a/vault/templates/injector-certs-secret.yaml b/vault/templates/injector-certs-secret.yaml deleted file mode 100644 index 78363be5..00000000 --- a/vault/templates/injector-certs-secret.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} -apiVersion: v1 -kind: Secret -metadata: - name: vault-injector-certs - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} diff --git a/vault/templates/injector-clusterrole.yaml b/vault/templates/injector-clusterrole.yaml deleted file mode 100644 index 4ff25abe..00000000 --- a/vault/templates/injector-clusterrole.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ template "vault.fullname" . }}-agent-injector-clusterrole - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -rules: -- apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: - - "get" - - "list" - - "watch" - - "patch" -{{ end }} diff --git a/vault/templates/injector-clusterrolebinding.yaml b/vault/templates/injector-clusterrolebinding.yaml deleted file mode 100644 index 35d30b39..00000000 --- a/vault/templates/injector-clusterrolebinding.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ template "vault.fullname" . }}-agent-injector-binding - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ template "vault.fullname" . }}-agent-injector-clusterrole -subjects: -- kind: ServiceAccount - name: {{ template "vault.fullname" . }}-agent-injector - namespace: {{ .Release.Namespace }} -{{ end }} diff --git a/vault/templates/injector-deployment.yaml b/vault/templates/injector-deployment.yaml deleted file mode 100644 index aefbf088..00000000 --- a/vault/templates/injector-deployment.yaml +++ /dev/null @@ -1,157 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -# Deployment for the injector -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "vault.fullname" . }}-agent-injector - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - component: webhook -spec: - replicas: {{ .Values.injector.replicas }} - selector: - matchLabels: - app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - component: webhook - template: - metadata: - labels: - app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - component: webhook - {{- if .Values.injector.extraLabels -}} - {{- toYaml .Values.injector.extraLabels | nindent 8 -}} - {{- end -}} - {{ template "injector.annotations" . }} - spec: - {{ template "injector.affinity" . }} - {{ template "injector.tolerations" . }} - {{ template "injector.nodeselector" . }} - {{- if .Values.injector.priorityClassName }} - priorityClassName: {{ .Values.injector.priorityClassName }} - {{- end }} - serviceAccountName: "{{ template "vault.fullname" . }}-agent-injector" - {{- if not .Values.global.openshift }} - hostNetwork: {{ .Values.injector.hostNetwork }} - securityContext: - runAsNonRoot: true - runAsGroup: {{ .Values.injector.gid | default 1000 }} - runAsUser: {{ .Values.injector.uid | default 100 }} - {{- end }} - containers: - - name: sidecar-injector - {{ template "injector.resources" . }} - image: "{{ .Values.injector.image.repository }}:{{ .Values.injector.image.tag }}" - imagePullPolicy: "{{ .Values.injector.image.pullPolicy }}" - {{- if not .Values.global.openshift }} - securityContext: - allowPrivilegeEscalation: false - {{- end }} - env: - - name: AGENT_INJECT_LISTEN - value: {{ printf ":%v" .Values.injector.port }} - - name: AGENT_INJECT_LOG_LEVEL - value: {{ .Values.injector.logLevel | default "info" }} - - name: AGENT_INJECT_VAULT_ADDR - {{- if .Values.injector.externalVaultAddr }} - value: "{{ .Values.injector.externalVaultAddr }}" - {{- else }} - value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} - {{- end }} - - name: AGENT_INJECT_VAULT_AUTH_PATH - value: {{ .Values.injector.authPath }} - - name: AGENT_INJECT_VAULT_IMAGE - value: "{{ .Values.injector.agentImage.repository }}:{{ .Values.injector.agentImage.tag }}" - {{- if .Values.injector.certs.secretName }} - - name: AGENT_INJECT_TLS_CERT_FILE - value: "/etc/webhook/certs/{{ .Values.injector.certs.certName }}" - - name: AGENT_INJECT_TLS_KEY_FILE - value: "/etc/webhook/certs/{{ .Values.injector.certs.keyName }}" - {{- else }} - - name: AGENT_INJECT_TLS_AUTO - value: {{ template "vault.fullname" . }}-agent-injector-cfg - - name: AGENT_INJECT_TLS_AUTO_HOSTS - value: {{ template "vault.fullname" . }}-agent-injector-svc,{{ template "vault.fullname" . }}-agent-injector-svc.{{ .Release.Namespace }},{{ template "vault.fullname" . }}-agent-injector-svc.{{ .Release.Namespace }}.svc - {{- end }} - - name: AGENT_INJECT_LOG_FORMAT - value: {{ .Values.injector.logFormat | default "standard" }} - - name: AGENT_INJECT_REVOKE_ON_SHUTDOWN - value: "{{ .Values.injector.revokeOnShutdown | default false }}" - {{- if .Values.global.openshift }} - - name: AGENT_INJECT_SET_SECURITY_CONTEXT - value: "false" - {{- end }} - {{- if .Values.injector.metrics.enabled }} - - name: AGENT_INJECT_TELEMETRY_PATH - value: "/metrics" - {{- end }} - {{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} - - name: AGENT_INJECT_USE_LEADER_ELECTOR - value: "true" - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - {{- end }} - - name: AGENT_INJECT_CPU_REQUEST - value: "{{ .Values.injector.agentDefaults.cpuRequest }}" - - name: AGENT_INJECT_CPU_LIMIT - value: "{{ .Values.injector.agentDefaults.cpuLimit }}" - - name: AGENT_INJECT_MEM_REQUEST - value: "{{ .Values.injector.agentDefaults.memRequest }}" - - name: AGENT_INJECT_MEM_LIMIT - value: "{{ .Values.injector.agentDefaults.memLimit }}" - - name: AGENT_INJECT_DEFAULT_TEMPLATE - value: "{{ .Values.injector.agentDefaults.template }}" - - name: AGENT_INJECT_TEMPLATE_CONFIG_EXIT_ON_RETRY_FAILURE - value: "{{ .Values.injector.agentDefaults.templateConfig.exitOnRetryFailure }}" - {{- if .Values.injector.agentDefaults.templateConfig.staticSecretRenderInterval }} - - name: AGENT_INJECT_TEMPLATE_STATIC_SECRET_RENDER_INTERVAL - value: "{{ .Values.injector.agentDefaults.templateConfig.staticSecretRenderInterval }}" - {{- end }} - {{- include "vault.extraEnvironmentVars" .Values.injector | nindent 12 }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - args: - - agent-inject - - 2>&1 - livenessProbe: - httpGet: - path: /health/ready - port: {{ .Values.injector.port }} - scheme: HTTPS - failureThreshold: 2 - initialDelaySeconds: 5 - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 5 - readinessProbe: - httpGet: - path: /health/ready - port: {{ .Values.injector.port }} - scheme: HTTPS - failureThreshold: 2 - initialDelaySeconds: 5 - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 5 -{{- if .Values.injector.certs.secretName }} - volumeMounts: - - name: webhook-certs - mountPath: /etc/webhook/certs - readOnly: true -{{- end }} -{{- if .Values.injector.certs.secretName }} - volumes: - - name: webhook-certs - secret: - secretName: "{{ .Values.injector.certs.secretName }}" -{{- end }} - {{- include "imagePullSecrets" . | nindent 6 }} -{{ end }} diff --git a/vault/templates/injector-mutating-webhook.yaml b/vault/templates/injector-mutating-webhook.yaml deleted file mode 100644 index de7dd562..00000000 --- a/vault/templates/injector-mutating-webhook.yaml +++ /dev/null @@ -1,43 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -{{- if .Capabilities.APIVersions.Has "admissionregistration.k8s.io/v1" }} -apiVersion: admissionregistration.k8s.io/v1 -{{- else }} -apiVersion: admissionregistration.k8s.io/v1beta1 -{{- end }} -kind: MutatingWebhookConfiguration -metadata: - name: {{ template "vault.fullname" . }}-agent-injector-cfg - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - {{- template "injector.webhookAnnotations" . }} -webhooks: - - name: vault.hashicorp.com - sideEffects: None - admissionReviewVersions: - - "v1beta1" - - "v1" - clientConfig: - service: - name: {{ template "vault.fullname" . }}-agent-injector-svc - namespace: {{ .Release.Namespace }} - path: "/mutate" - caBundle: {{ .Values.injector.certs.caBundle | quote }} - rules: - - operations: ["CREATE", "UPDATE"] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] -{{- if .Values.injector.namespaceSelector }} - namespaceSelector: -{{ toYaml .Values.injector.namespaceSelector | indent 6}} -{{ end }} -{{- if .Values.injector.objectSelector }} - objectSelector: -{{ toYaml .Values.injector.objectSelector | indent 6}} -{{ end }} -{{- with .Values.injector.failurePolicy }} - failurePolicy: {{.}} -{{ end }} -{{ end }} diff --git a/vault/templates/injector-network-policy.yaml b/vault/templates/injector-network-policy.yaml deleted file mode 100644 index 7a399a53..00000000 --- a/vault/templates/injector-network-policy.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.openshift | toString) "true") }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ template "vault.fullname" . }}-agent-injector - labels: - app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} -spec: - podSelector: - matchLabels: - app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - component: webhook - ingress: - - from: - - namespaceSelector: {} - ports: - - port: 8080 - protocol: TCP -{{ end }} diff --git a/vault/templates/injector-psp-role.yaml b/vault/templates/injector-psp-role.yaml deleted file mode 100644 index 20c87bb2..00000000 --- a/vault/templates/injector-psp-role.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.psp.enable | toString) "true") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ template "vault.fullname" . }}-agent-injector-psp - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -rules: -- apiGroups: ['policy'] - resources: ['podsecuritypolicies'] - verbs: ['use'] - resourceNames: - - {{ template "vault.fullname" . }}-agent-injector -{{- end }} diff --git a/vault/templates/injector-psp-rolebinding.yaml b/vault/templates/injector-psp-rolebinding.yaml deleted file mode 100644 index d6d0d5e2..00000000 --- a/vault/templates/injector-psp-rolebinding.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.psp.enable | toString) "true") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ template "vault.fullname" . }}-agent-injector-psp - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -roleRef: - kind: Role - name: {{ template "vault.fullname" . }}-agent-injector-psp - apiGroup: rbac.authorization.k8s.io -subjects: - - kind: ServiceAccount - name: {{ template "vault.fullname" . }}-agent-injector -{{- end }} diff --git a/vault/templates/injector-psp.yaml b/vault/templates/injector-psp.yaml deleted file mode 100644 index c024ac10..00000000 --- a/vault/templates/injector-psp.yaml +++ /dev/null @@ -1,43 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.psp.enable | toString) "true") }} -apiVersion: policy/v1beta1 -kind: PodSecurityPolicy -metadata: - name: {{ template "vault.fullname" . }}-agent-injector - labels: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- template "vault.psp.annotations" . }} -spec: - privileged: false - # Required to prevent escalations to root. - allowPrivilegeEscalation: false - volumes: - - configMap - - emptyDir - - projected - - secret - - downwardAPI - hostNetwork: false - hostIPC: false - hostPID: false - runAsUser: - # Require the container to run without root privileges. - rule: MustRunAsNonRoot - seLinux: - # This policy assumes the nodes are using AppArmor rather than SELinux. - rule: RunAsAny - supplementalGroups: - rule: MustRunAs - ranges: - # Forbid adding the root group. - - min: 1 - max: 65535 - fsGroup: - rule: MustRunAs - ranges: - # Forbid adding the root group. - - min: 1 - max: 65535 - readOnlyRootFilesystem: false -{{- end }} diff --git a/vault/templates/injector-role.yaml b/vault/templates/injector-role.yaml deleted file mode 100644 index e7e383d1..00000000 --- a/vault/templates/injector-role.yaml +++ /dev/null @@ -1,25 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-role - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -rules: - - apiGroups: [""] - resources: ["secrets", "configmaps"] - verbs: - - "create" - - "get" - - "watch" - - "list" - - "update" - - apiGroups: [""] - resources: ["pods"] - verbs: - - "get" - - "patch" - - "delete" -{{- end }} diff --git a/vault/templates/injector-rolebinding.yaml b/vault/templates/injector-rolebinding.yaml deleted file mode 100644 index aa817942..00000000 --- a/vault/templates/injector-rolebinding.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-binding - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-role -subjects: - - kind: ServiceAccount - name: {{ template "vault.fullname" . }}-agent-injector - namespace: {{ .Release.Namespace }} -{{- end }} diff --git a/vault/templates/injector-service.yaml b/vault/templates/injector-service.yaml deleted file mode 100644 index 3138b7a5..00000000 --- a/vault/templates/injector-service.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -apiVersion: v1 -kind: Service -metadata: - name: {{ template "vault.fullname" . }}-agent-injector-svc - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - {{ template "injector.service.annotations" . }} -spec: - ports: - - name: https - port: 443 - targetPort: {{ .Values.injector.port }} - selector: - app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - component: webhook -{{- end }} diff --git a/vault/templates/injector-serviceaccount.yaml b/vault/templates/injector-serviceaccount.yaml deleted file mode 100644 index a28d38fa..00000000 --- a/vault/templates/injector-serviceaccount.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ template "vault.fullname" . }}-agent-injector - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -{{ end }} diff --git a/vault/templates/server-clusterrolebinding.yaml b/vault/templates/server-clusterrolebinding.yaml deleted file mode 100644 index e5e0f5fe..00000000 --- a/vault/templates/server-clusterrolebinding.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{ template "vault.mode" . }} -{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") (eq (.Values.server.authDelegator.enabled | toString) "true") }} -{{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1" -}} -apiVersion: rbac.authorization.k8s.io/v1 -{{- else }} -apiVersion: rbac.authorization.k8s.io/v1beta1 -{{- end }} -kind: ClusterRoleBinding -metadata: - name: {{ template "vault.fullname" . }}-server-binding - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:auth-delegator -subjects: -- kind: ServiceAccount - name: {{ template "vault.serviceAccount.name" . }} - namespace: {{ .Release.Namespace }} -{{ end }} diff --git a/vault/templates/server-config-configmap.yaml b/vault/templates/server-config-configmap.yaml deleted file mode 100644 index b8093ad0..00000000 --- a/vault/templates/server-config-configmap.yaml +++ /dev/null @@ -1,38 +0,0 @@ -{{ template "vault.mode" . }} -{{- if ne .mode "external" }} -{{- if and (eq (.Values.global.enabled | toString) "true") (ne .mode "dev") -}} -{{ if or (.Values.server.standalone.config) (.Values.server.ha.config) -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "vault.fullname" . }}-config - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -data: - extraconfig-from-values.hcl: |- - {{- if or (eq .mode "ha") (eq .mode "standalone") }} - {{- $type := typeOf (index .Values.server .mode).config }} - {{- if eq $type "string" }} - disable_mlock = true - {{- if eq .mode "standalone" }} - {{ tpl .Values.server.standalone.config . | nindent 4 | trim }} - {{- else if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "false") }} - {{ tpl .Values.server.ha.config . | nindent 4 | trim }} - {{- else if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true") }} - {{ tpl .Values.server.ha.raft.config . | nindent 4 | trim }} - {{ end }} - {{- else }} - {{- if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true") }} -{{ merge (dict "disable_mlock" true) (index .Values.server .mode).raft.config | toPrettyJson | indent 4 }} - {{- else }} -{{ merge (dict "disable_mlock" true) (index .Values.server .mode).config | toPrettyJson | indent 4 }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} -{{- end }} -{{- end }} diff --git a/vault/templates/server-discovery-role.yaml b/vault/templates/server-discovery-role.yaml deleted file mode 100644 index 4a39cec2..00000000 --- a/vault/templates/server-discovery-role.yaml +++ /dev/null @@ -1,19 +0,0 @@ -{{ template "vault.mode" . }} -{{- if ne .mode "external" }} -{{- if and (eq .mode "ha" ) (eq (.Values.global.enabled | toString) "true") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - namespace: {{ .Release.Namespace }} - name: {{ template "vault.fullname" . }}-discovery-role - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -rules: -- apiGroups: [""] - resources: ["pods"] - verbs: ["get", "watch", "list", "update", "patch"] -{{ end }} -{{ end }} diff --git a/vault/templates/server-discovery-rolebinding.yaml b/vault/templates/server-discovery-rolebinding.yaml deleted file mode 100644 index 47526650..00000000 --- a/vault/templates/server-discovery-rolebinding.yaml +++ /dev/null @@ -1,27 +0,0 @@ -{{ template "vault.mode" . }} -{{- if ne .mode "external" }} -{{- if and (eq .mode "ha" ) (eq (.Values.global.enabled | toString) "true") }} -{{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1" -}} -apiVersion: rbac.authorization.k8s.io/v1 -{{- else }} -apiVersion: rbac.authorization.k8s.io/v1beta1 -{{- end }} -kind: RoleBinding -metadata: - name: {{ template "vault.fullname" . }}-discovery-rolebinding - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ template "vault.fullname" . }}-discovery-role -subjects: -- kind: ServiceAccount - name: {{ template "vault.serviceAccount.name" . }} - namespace: {{ .Release.Namespace }} -{{ end }} -{{ end }} diff --git a/vault/templates/server-disruptionbudget.yaml b/vault/templates/server-disruptionbudget.yaml deleted file mode 100644 index 3c45cc04..00000000 --- a/vault/templates/server-disruptionbudget.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{ template "vault.mode" . }} -{{- if ne .mode "external" -}} -{{- if and (eq (.Values.global.enabled | toString) "true") (eq .mode "ha") (eq (.Values.server.ha.disruptionBudget.enabled | toString) "true") -}} -# PodDisruptionBudget to prevent degrading the server cluster through -# voluntary cluster changes. -apiVersion: policy/v1beta1 -kind: PodDisruptionBudget -metadata: - name: {{ template "vault.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - maxUnavailable: {{ template "vault.pdb.maxUnavailable" . }} - selector: - matchLabels: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - component: server -{{- end -}} -{{- end -}} diff --git a/vault/templates/server-ha-active-service.yaml b/vault/templates/server-ha-active-service.yaml deleted file mode 100644 index c2a4f022..00000000 --- a/vault/templates/server-ha-active-service.yaml +++ /dev/null @@ -1,42 +0,0 @@ -{{ template "vault.mode" . }} -{{- if ne .mode "external" }} -{{- if and (eq .mode "ha" ) (eq (.Values.server.service.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -# Service for active Vault pod -apiVersion: v1 -kind: Service -metadata: - name: {{ template "vault.fullname" . }}-active - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - annotations: -{{ template "vault.service.annotations" .}} -spec: - {{- if .Values.server.service.type}} - type: {{ .Values.server.service.type }} - {{- end}} - {{- if .Values.server.service.clusterIP }} - clusterIP: {{ .Values.server.service.clusterIP }} - {{- end }} - {{- include "service.externalTrafficPolicy" .Values.server.service }} - publishNotReadyAddresses: true - ports: - - name: {{ include "vault.scheme" . }} - port: {{ .Values.server.service.port }} - targetPort: {{ .Values.server.service.targetPort }} - {{- if and (.Values.server.service.nodePort) (eq (.Values.server.service.type | toString) "NodePort") }} - nodePort: {{ .Values.server.service.nodePort }} - {{- end }} - - name: https-internal - port: 8201 - targetPort: 8201 - selector: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - component: server - vault-active: "true" -{{- end }} -{{- end }} diff --git a/vault/templates/server-ha-standby-service.yaml b/vault/templates/server-ha-standby-service.yaml deleted file mode 100644 index fef92a1b..00000000 --- a/vault/templates/server-ha-standby-service.yaml +++ /dev/null @@ -1,42 +0,0 @@ -{{ template "vault.mode" . }} -{{- if ne .mode "external" }} -{{- if and (eq .mode "ha" ) (eq (.Values.server.service.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -# Service for standby Vault pod -apiVersion: v1 -kind: Service -metadata: - name: {{ template "vault.fullname" . }}-standby - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - annotations: -{{ template "vault.service.annotations" .}} -spec: - {{- if .Values.server.service.type}} - type: {{ .Values.server.service.type }} - {{- end}} - {{- if .Values.server.service.clusterIP }} - clusterIP: {{ .Values.server.service.clusterIP }} - {{- end }} - {{- include "service.externalTrafficPolicy" .Values.server.service }} - publishNotReadyAddresses: true - ports: - - name: {{ include "vault.scheme" . }} - port: {{ .Values.server.service.port }} - targetPort: {{ .Values.server.service.targetPort }} - {{- if and (.Values.server.service.nodePort) (eq (.Values.server.service.type | toString) "NodePort") }} - nodePort: {{ .Values.server.service.nodePort }} - {{- end }} - - name: https-internal - port: 8201 - targetPort: 8201 - selector: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - component: server - vault-active: "false" -{{- end }} -{{- end }} diff --git a/vault/templates/server-headless-service.yaml b/vault/templates/server-headless-service.yaml deleted file mode 100644 index a37c6395..00000000 --- a/vault/templates/server-headless-service.yaml +++ /dev/null @@ -1,32 +0,0 @@ -{{ template "vault.mode" . }} -{{- if ne .mode "external" }} -{{- if and (eq (.Values.server.service.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -# Service for Vault cluster -apiVersion: v1 -kind: Service -metadata: - name: {{ template "vault.fullname" . }}-internal - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - annotations: -{{ template "vault.service.annotations" .}} -spec: - clusterIP: None - publishNotReadyAddresses: true - ports: - - name: "{{ include "vault.scheme" . }}" - port: {{ .Values.server.service.port }} - targetPort: {{ .Values.server.service.targetPort }} - - name: https-internal - port: 8201 - targetPort: 8201 - selector: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - component: server -{{- end }} -{{- end }} diff --git a/vault/templates/server-ingress.yaml b/vault/templates/server-ingress.yaml deleted file mode 100644 index 48c76a82..00000000 --- a/vault/templates/server-ingress.yaml +++ /dev/null @@ -1,74 +0,0 @@ -{{- if not .Values.global.openshift }} -{{ template "vault.mode" . }} -{{- if ne .mode "external" }} -{{- if .Values.server.ingress.enabled -}} -{{- $extraPaths := .Values.server.ingress.extraPaths -}} -{{- $serviceName := include "vault.fullname" . -}} -{{- if and (eq .mode "ha" ) (eq (.Values.server.service.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") (eq (.Values.server.ingress.activeService | toString) "true") }} -{{- $serviceName = printf "%s-%s" $serviceName "active" -}} -{{- end }} -{{- $servicePort := .Values.server.service.port -}} -{{- $pathType := .Values.server.ingress.pathType -}} -{{- $kubeVersion := .Capabilities.KubeVersion.Version }} -{{ if semverCompare ">= 1.19.0-0" $kubeVersion }} -apiVersion: networking.k8s.io/v1 -{{ else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" }} -apiVersion: networking.k8s.io/v1beta1 -{{ else }} -apiVersion: extensions/v1beta1 -{{ end }} -kind: Ingress -metadata: - name: {{ template "vault.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - {{- with .Values.server.ingress.labels }} - {{- toYaml . | nindent 4 }} - {{- end }} - {{- template "vault.ingress.annotations" . }} -spec: -{{- if .Values.server.ingress.tls }} - tls: - {{- range .Values.server.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} -{{- if .Values.server.ingress.ingressClassName }} - ingressClassName: {{ .Values.server.ingress.ingressClassName }} -{{- end }} - rules: - {{- range .Values.server.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: -{{ if $extraPaths }} -{{ toYaml $extraPaths | indent 10 }} -{{- end }} - {{- range (.paths | default (list "/")) }} - - path: {{ . }} - {{ if semverCompare ">= 1.19.0-0" $kubeVersion }} - pathType: {{ $pathType }} - {{ end }} - backend: - {{ if semverCompare ">= 1.19.0-0" $kubeVersion }} - service: - name: {{ $serviceName }} - port: - number: {{ $servicePort }} - {{ else }} - serviceName: {{ $serviceName }} - servicePort: {{ $servicePort }} - {{ end }} - {{- end }} - {{- end }} -{{- end }} -{{- end }} -{{- end }} diff --git a/vault/templates/server-network-policy.yaml b/vault/templates/server-network-policy.yaml deleted file mode 100644 index 5f4c21a4..00000000 --- a/vault/templates/server-network-policy.yaml +++ /dev/null @@ -1,26 +0,0 @@ -{{- if eq (.Values.server.networkPolicy.enabled | toString) "true" }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ template "vault.fullname" . }} - labels: - app.kubernetes.io/name: {{ template "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} -spec: - podSelector: - matchLabels: - app.kubernetes.io/name: {{ template "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - ingress: - - from: - - namespaceSelector: {} - ports: - - port: 8200 - protocol: TCP - - port: 8201 - protocol: TCP - {{- if .Values.server.networkPolicy.egress }} - egress: - {{- toYaml .Values.server.networkPolicy.egress | nindent 4 }} - {{ end }} -{{ end }} diff --git a/vault/templates/server-psp-role.yaml b/vault/templates/server-psp-role.yaml deleted file mode 100644 index fd12e1eb..00000000 --- a/vault/templates/server-psp-role.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{ template "vault.mode" . }} -{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.psp.enable | toString) "true") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ template "vault.fullname" . }}-psp - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -rules: -- apiGroups: ['policy'] - resources: ['podsecuritypolicies'] - verbs: ['use'] - resourceNames: - - {{ template "vault.fullname" . }} -{{- end }} diff --git a/vault/templates/server-psp-rolebinding.yaml b/vault/templates/server-psp-rolebinding.yaml deleted file mode 100644 index b2a43c83..00000000 --- a/vault/templates/server-psp-rolebinding.yaml +++ /dev/null @@ -1,19 +0,0 @@ -{{ template "vault.mode" . }} -{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.psp.enable | toString) "true") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ template "vault.fullname" . }}-psp - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -roleRef: - kind: Role - name: {{ template "vault.fullname" . }}-psp - apiGroup: rbac.authorization.k8s.io -subjects: - - kind: ServiceAccount - name: {{ template "vault.fullname" . }} -{{- end }} diff --git a/vault/templates/server-psp.yaml b/vault/templates/server-psp.yaml deleted file mode 100644 index 2d942681..00000000 --- a/vault/templates/server-psp.yaml +++ /dev/null @@ -1,47 +0,0 @@ -{{ template "vault.mode" . }} -{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") (eq (.Values.global.psp.enable | toString) "true") }} -apiVersion: policy/v1beta1 -kind: PodSecurityPolicy -metadata: - name: {{ template "vault.fullname" . }} - labels: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- template "vault.psp.annotations" . }} -spec: - privileged: false - # Required to prevent escalations to root. - allowPrivilegeEscalation: false - volumes: - - configMap - - emptyDir - - projected - - secret - - downwardAPI - {{- if eq (.Values.server.dataStorage.enabled | toString) "true" }} - - persistentVolumeClaim - {{- end }} - hostNetwork: false - hostIPC: false - hostPID: false - runAsUser: - # Require the container to run without root privileges. - rule: MustRunAsNonRoot - seLinux: - # This policy assumes the nodes are using AppArmor rather than SELinux. - rule: RunAsAny - supplementalGroups: - rule: MustRunAs - ranges: - # Forbid adding the root group. - - min: 1 - max: 65535 - fsGroup: - rule: MustRunAs - ranges: - # Forbid adding the root group. - - min: 1 - max: 65535 - readOnlyRootFilesystem: false -{{- end }} diff --git a/vault/templates/server-route.yaml b/vault/templates/server-route.yaml deleted file mode 100644 index 63055db3..00000000 --- a/vault/templates/server-route.yaml +++ /dev/null @@ -1,33 +0,0 @@ -{{- if .Values.global.openshift }} -{{- if ne .mode "external" }} -{{- if .Values.server.route.enabled -}} -{{- $serviceName := include "vault.fullname" . -}} -{{- if and (eq .mode "ha" ) (eq (.Values.server.route.activeService | toString) "true") }} -{{- $serviceName = printf "%s-%s" $serviceName "active" -}} -{{- end }} -kind: Route -apiVersion: route.openshift.io/v1 -metadata: - name: {{ template "vault.fullname" . }} - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - {{- with .Values.server.route.labels }} - {{- toYaml . | nindent 4 }} - {{- end }} - {{- template "vault.route.annotations" . }} -spec: - host: {{ .Values.server.route.host }} - to: - kind: Service - name: {{ $serviceName }} - weight: 100 - port: - targetPort: 8200 - tls: - termination: passthrough -{{- end }} -{{- end }} -{{- end }} diff --git a/vault/templates/server-service.yaml b/vault/templates/server-service.yaml deleted file mode 100644 index 00996aa2..00000000 --- a/vault/templates/server-service.yaml +++ /dev/null @@ -1,43 +0,0 @@ -{{ template "vault.mode" . }} -{{- if ne .mode "external" }} -{{- if and (eq (.Values.server.service.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }} -# Service for Vault cluster -apiVersion: v1 -kind: Service -metadata: - name: {{ template "vault.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - annotations: -{{ template "vault.service.annotations" .}} -spec: - {{- if .Values.server.service.type}} - type: {{ .Values.server.service.type }} - {{- end}} - {{- if .Values.server.service.clusterIP }} - clusterIP: {{ .Values.server.service.clusterIP }} - {{- end }} - {{- include "service.externalTrafficPolicy" .Values.server.service }} - # We want the servers to become available even if they're not ready - # since this DNS is also used for join operations. - publishNotReadyAddresses: true - ports: - - name: {{ include "vault.scheme" . }} - port: {{ .Values.server.service.port }} - targetPort: {{ .Values.server.service.targetPort }} - {{- if and (.Values.server.service.nodePort) (eq (.Values.server.service.type | toString) "NodePort") }} - nodePort: {{ .Values.server.service.nodePort }} - {{- end }} - - name: https-internal - port: 8201 - targetPort: 8201 - selector: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - component: server -{{- end }} -{{- end }} diff --git a/vault/templates/server-serviceaccount.yaml b/vault/templates/server-serviceaccount.yaml deleted file mode 100644 index 925b166b..00000000 --- a/vault/templates/server-serviceaccount.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{ template "vault.mode" . }} -{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") }} -{{- if (eq (.Values.server.serviceAccount.create | toString) "true" ) }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ template "vault.serviceAccount.name" . }} - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - {{ template "vault.serviceAccount.annotations" . }} -{{ end }} -{{ end }} diff --git a/vault/templates/server-statefulset.yaml b/vault/templates/server-statefulset.yaml deleted file mode 100644 index 031b1790..00000000 --- a/vault/templates/server-statefulset.yaml +++ /dev/null @@ -1,208 +0,0 @@ -{{ template "vault.mode" . }} -{{- if ne .mode "external" }} -{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") }} -# StatefulSet to run the actual vault server cluster. -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: {{ template "vault.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - {{- template "vault.statefulSet.annotations" . }} -spec: - serviceName: {{ template "vault.fullname" . }}-internal - podManagementPolicy: Parallel - replicas: {{ template "vault.replicas" . }} - updateStrategy: - type: {{ .Values.server.updateStrategyType }} - selector: - matchLabels: - app.kubernetes.io/name: {{ template "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - component: server - template: - metadata: - labels: - helm.sh/chart: {{ template "vault.chart" . }} - app.kubernetes.io/name: {{ template "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - component: server - {{- if .Values.server.extraLabels -}} - {{- toYaml .Values.server.extraLabels | nindent 8 -}} - {{- end -}} - {{ template "vault.annotations" . }} - spec: - {{ template "vault.affinity" . }} - {{ template "vault.tolerations" . }} - {{ template "vault.nodeselector" . }} - {{- if .Values.server.priorityClassName }} - priorityClassName: {{ .Values.server.priorityClassName }} - {{- end }} - terminationGracePeriodSeconds: 10 - serviceAccountName: {{ template "vault.serviceAccount.name" . }} - {{ if .Values.server.shareProcessNamespace }} - shareProcessNamespace: true - {{ end }} - {{- if not .Values.global.openshift }} - securityContext: - runAsNonRoot: true - runAsGroup: {{ .Values.server.gid | default 1000 }} - runAsUser: {{ .Values.server.uid | default 100 }} - fsGroup: {{ .Values.server.gid | default 1000 }} - {{- end }} - volumes: - {{ template "vault.volumes" . }} - - name: home - emptyDir: {} - {{- if .Values.server.extraInitContainers }} - initContainers: - {{ toYaml .Values.server.extraInitContainers | nindent 8}} - {{- end }} - containers: - - name: vault - {{ template "vault.resources" . }} - image: {{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default "latest" }} - imagePullPolicy: {{ .Values.server.image.pullPolicy }} - command: - - "/bin/sh" - - "-ec" - args: {{ template "vault.args" . }} - {{- if not .Values.global.openshift }} - securityContext: - allowPrivilegeEscalation: false - {{- end }} - env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: POD_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: VAULT_K8S_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: VAULT_K8S_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: VAULT_ADDR - value: "{{ include "vault.scheme" . }}://127.0.0.1:8200" - - name: VAULT_API_ADDR - {{- if .Values.server.ha.apiAddr }} - value: {{ .Values.server.ha.apiAddr }} - {{- else }} - value: "{{ include "vault.scheme" . }}://$(POD_IP):8200" - {{- end }} - - name: SKIP_CHOWN - value: "true" - - name: SKIP_SETCAP - value: "true" - - name: HOSTNAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: VAULT_CLUSTER_ADDR - value: "https://$(HOSTNAME).{{ template "vault.fullname" . }}-internal:8201" - {{- if and (eq (.Values.server.ha.raft.enabled | toString) "true") (eq (.Values.server.ha.raft.setNodeId | toString) "true") }} - - name: VAULT_RAFT_NODE_ID - valueFrom: - fieldRef: - fieldPath: metadata.name - {{- end }} - - name: HOME - value: "/home/vault" - {{- if .Values.server.logLevel }} - - name: VAULT_LOG_LEVEL - value: "{{ .Values.server.logLevel }}" - {{- end }} - {{- if .Values.server.logFormat }} - - name: VAULT_LOG_FORMAT - value: "{{ .Values.server.logFormat }}" - {{- end }} - {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} - - name: VAULT_LICENSE_PATH - value: /vault/license/{{ .Values.server.enterpriseLicense.secretKey }} - {{- end }} - {{ template "vault.envs" . }} - {{- include "vault.extraEnvironmentVars" .Values.server | nindent 12 }} - {{- include "vault.extraSecretEnvironmentVars" .Values.server | nindent 12 }} - volumeMounts: - {{ template "vault.mounts" . }} - - name: home - mountPath: /home/vault - ports: - - containerPort: 8200 - name: {{ include "vault.scheme" . }} - - containerPort: 8201 - name: https-internal - - containerPort: 8202 - name: {{ include "vault.scheme" . }}-rep - {{- if .Values.server.readinessProbe.enabled }} - readinessProbe: - {{- if .Values.server.readinessProbe.path }} - httpGet: - path: {{ .Values.server.readinessProbe.path | quote }} - port: 8200 - scheme: {{ include "vault.scheme" . | upper }} - {{- else }} - # Check status; unsealed vault servers return 0 - # The exit code reflects the seal status: - # 0 - unsealed - # 1 - error - # 2 - sealed - exec: - command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] - {{- end }} - failureThreshold: {{ .Values.server.readinessProbe.failureThreshold }} - initialDelaySeconds: {{ .Values.server.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.server.readinessProbe.periodSeconds }} - successThreshold: {{ .Values.server.readinessProbe.successThreshold }} - timeoutSeconds: {{ .Values.server.readinessProbe.timeoutSeconds }} - {{- end }} - {{- if .Values.server.livenessProbe.enabled }} - livenessProbe: - httpGet: - path: {{ .Values.server.livenessProbe.path | quote }} - port: 8200 - scheme: {{ include "vault.scheme" . | upper }} - failureThreshold: {{ .Values.server.livenessProbe.failureThreshold }} - initialDelaySeconds: {{ .Values.server.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.server.livenessProbe.periodSeconds }} - successThreshold: {{ .Values.server.livenessProbe.successThreshold }} - timeoutSeconds: {{ .Values.server.livenessProbe.timeoutSeconds }} - {{- end }} - lifecycle: - # Vault container doesn't receive SIGTERM from Kubernetes - # and after the grace period ends, Kube sends SIGKILL. This - # causes issues with graceful shutdowns such as deregistering itself - # from Consul (zombie services). - preStop: - exec: - command: [ - "/bin/sh", "-c", - # Adding a sleep here to give the pod eviction a - # chance to propagate, so requests will not be made - # to this pod while it's terminating - "sleep {{ .Values.server.preStopSleepSeconds }} && kill -SIGTERM $(pidof vault)", - ] - {{- if .Values.server.postStart }} - postStart: - exec: - command: - {{- range (.Values.server.postStart) }} - - {{ . | quote }} - {{- end }} - {{- end }} - {{- if .Values.server.extraContainers }} - {{ toYaml .Values.server.extraContainers | nindent 8}} - {{- end }} - {{- include "imagePullSecrets" . | nindent 6 }} - {{ template "vault.volumeclaims" . }} -{{ end }} -{{ end }} diff --git a/vault/templates/tests/server-test.yaml b/vault/templates/tests/server-test.yaml deleted file mode 100644 index 66aa178f..00000000 --- a/vault/templates/tests/server-test.yaml +++ /dev/null @@ -1,40 +0,0 @@ -{{- if .Values.server.enabled }} -apiVersion: v1 -kind: Pod -metadata: - name: "{{ .Release.Name }}-server-test" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook": test -spec: - {{- include "imagePullSecrets" . | nindent 2 }} - containers: - - name: {{ .Release.Name }}-server-test - image: {{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default "latest" }} - imagePullPolicy: {{ .Values.server.image.pullPolicy }} - env: - - name: VAULT_ADDR - value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} - command: - - /bin/sh - - -c - - | - echo "Checking for sealed info in 'vault status' output" - ATTEMPTS=10 - n=0 - until [ "$n" -ge $ATTEMPTS ] - do - echo "Attempt" $n... - vault status -format yaml | grep -E '^sealed: (true|false)' && break - n=$((n+1)) - sleep 5 - done - if [ $n -ge $ATTEMPTS ]; then - echo "timed out looking for sealed info in 'vault status' output" - exit 1 - fi - - exit 0 - - restartPolicy: Never -{{- end }} diff --git a/vault/templates/ui-service.yaml b/vault/templates/ui-service.yaml deleted file mode 100644 index ea27de28..00000000 --- a/vault/templates/ui-service.yaml +++ /dev/null @@ -1,37 +0,0 @@ -{{ template "vault.mode" . }} -{{- if ne .mode "external" }} -{{- if and (ne .mode "") (eq (.Values.global.enabled | toString) "true") }} -{{- if eq (.Values.ui.enabled | toString) "true" }} -apiVersion: v1 -kind: Service -metadata: - name: {{ template "vault.fullname" . }}-ui - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ include "vault.chart" . }} - app.kubernetes.io/name: {{ include "vault.name" . }}-ui - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - {{- template "vault.ui.annotations" . }} -spec: - selector: - app.kubernetes.io/name: {{ include "vault.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - component: server - {{- if and (.Values.ui.activeVaultPodOnly) (eq .mode "ha") }} - vault-active: "true" - {{- end }} - publishNotReadyAddresses: {{ .Values.ui.publishNotReadyAddresses }} - ports: - - name: {{ include "vault.scheme" . }} - port: {{ .Values.ui.externalPort }} - targetPort: {{ .Values.ui.targetPort }} - {{- if .Values.ui.serviceNodePort }} - nodePort: {{ .Values.ui.serviceNodePort }} - {{- end }} - type: {{ .Values.ui.serviceType }} - {{- include "service.externalTrafficPolicy" .Values.ui }} - {{- include "service.loadBalancer" .Values.ui }} -{{- end -}} -{{- end }} -{{- end }} diff --git a/vault/test/README.md b/vault/test/README.md deleted file mode 100644 index 951a0616..00000000 --- a/vault/test/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Vault Helm Tests - -## Running Vault Helm Acceptance tests - -The Makefile at the top level of this repo contains a few target that should help with running acceptance tests in your own GKE instance or in a kind cluster. - -Note that for the Vault Enterprise tests to pass, a `VAULT_LICENSE_CI` environment variable needs to be set to the contents of a valid Vault Enterprise license. - -### Running in a GKE cluster - -* Set the `GOOGLE_CREDENTIALS` and `CLOUDSDK_CORE_PROJECT` variables at the top of the file. `GOOGLE_CREDENTIALS` should contain the local path to your Google Cloud Platform account credentials in JSON format. `CLOUDSDK_CORE_PROJECT` should be set to the ID of your GCP project. -* Run `make test-image` to create the docker image (with dependencies installed) that will be re-used in the below steps. -* Run `make test-provision` to provision the GKE cluster using terraform. -* Run `make test-acceptance` to run the acceptance tests in this already provisioned cluster. -* You can choose to only run certain tests by setting the ACCEPTANCE_TESTS variable and re-running the above target. -* Run `make test-destroy` when you have finished testing and want to tear-down and remove the cluster. - -### Running in a kind cluster - -* Run `make test-acceptance LOCAL_ACCEPTANCE_TESTS=true` -* You can choose to only run certain tests by setting the `ACCEPTANCE_TESTS` variable and re-running the above target. -* Run `make delete-kind` when you have finished testing and want to tear-down and remove the cluster. -* You can set an alternate kind cluster name by specifying the `KIND_CLUSTER_NAME` variable for any of the above targets. -* You can set an alternate K8S version by specifying the `KIND_K8S_VERSION` variable for any of the above targets. - -See [kind-quick-start](https://kind.sigs.k8s.io/docs/user/quick-start/) if you don't have kind installed on your system. - -## Running chart verification tests - -If [chart-verifier](https://github.com/redhat-certification/chart-verifier) is built and available in your PATH, run: - - bats test/chart/verifier.bats - -Or if you'd rather use the latest chart-verifier docker container, set -USE_DOCKER: - - USE_DOCKER=true bats test/chart/verifier.bats - -## Generating the values json schema - -There is a make target for generating values.schema.json: - - make values-schema - -It relies on the helm [schema-gen plugin][schema-gen]. Note that some manual -editing will be required, since several properties accept multiple data types. - -[schema-gen]: https://github.com/karuppiah7890/helm-schema-gen - -## Helm test - -Vault Helm also contains a simple helm test under -[templates/tests/](../templates/tests/) that may be run against a helm release: - - helm test diff --git a/vault/test/acceptance/_helpers.bash b/vault/test/acceptance/_helpers.bash deleted file mode 100644 index 466a5173..00000000 --- a/vault/test/acceptance/_helpers.bash +++ /dev/null @@ -1,159 +0,0 @@ -# name_prefix returns the prefix of the resources within Kubernetes. -name_prefix() { - printf "vault" -} - -# chart_dir returns the directory for the chart -chart_dir() { - echo ${BATS_TEST_DIRNAME}/../.. -} - -# helm_install installs the vault chart. This will source overridable -# values from the "values.yaml" file in this directory. This can be set -# by CI or other environments to do test-specific overrides. Note that its -# easily possible to break tests this way so be careful. -helm_install() { - local values="${BATS_TEST_DIRNAME}/values.yaml" - if [ ! -f "${values}" ]; then - touch $values - fi - - helm install -f ${values} \ - --name vault \ - ${BATS_TEST_DIRNAME}/../.. -} - -# helm_install_ha installs the vault chart using HA mode. This will source -# overridable values from the "values.yaml" file in this directory. This can be -# set by CI or other environments to do test-specific overrides. Note that its -# easily possible to break tests this way so be careful. -helm_install_ha() { - local values="${BATS_TEST_DIRNAME}/values.yaml" - if [ ! -f "${values}" ]; then - touch $values - fi - - helm install -f ${values} \ - --name vault \ - --set 'server.enabled=false' \ - --set 'serverHA.enabled=true' \ - ${BATS_TEST_DIRNAME}/../.. -} - -# wait for consul to be running -wait_for_running_consul() { - check() { - # This requests the pod and checks whether the status is running - # and the ready state is true. If so, it outputs the name. Otherwise - # it outputs empty. Therefore, to check for success, check for nonzero - # string length. - kubectl get pods -l component=client -o json | \ - jq -r '.items[0] | select( - .status.phase == "Running" and - ([ .status.conditions[] | select(.type == "Ready" and .status == "True") ] | length) == 1 - ) | .metadata.name' - } - - for i in $(seq 60); do - if [ -n "$(check ${POD_NAME})" ]; then - echo "consul clients are ready." - return - fi - - echo "Waiting for ${POD_NAME} to be ready..." - sleep 2 - done - - echo "consul clients never became ready." - return 1 -} - -# wait for a pod to be ready -wait_for_running() { - POD_NAME=$1 - - check() { - # This requests the pod and checks whether the status is running - # and the ready state is true. If so, it outputs the name. Otherwise - # it outputs empty. Therefore, to check for success, check for nonzero - # string length. - kubectl get pods $1 -o json | \ - jq -r 'select( - .status.phase == "Running" and - ([ .status.conditions[] | select(.type == "Ready" and .status == "False") ] | length) == 1 - ) | .metadata.namespace + "/" + .metadata.name' - } - - for i in $(seq 60); do - if [ -n "$(check ${POD_NAME})" ]; then - echo "${POD_NAME} is ready." - sleep 5 - return - fi - - echo "Waiting for ${POD_NAME} to be ready..." - sleep 2 - done - - echo "${POD_NAME} never became ready." - return 1 -} - -wait_for_ready() { - POD_NAME=$1 - - check() { - # This requests the pod and checks whether the status is running - # and the ready state is true. If so, it outputs the name. Otherwise - # it outputs empty. Therefore, to check for success, check for nonzero - # string length. - kubectl get pods $1 -o json | \ - jq -r 'select( - .status.phase == "Running" and - ([ .status.conditions[] | select(.type == "Ready" and .status == "True") ] | length) == 1 - ) | .metadata.namespace + "/" + .metadata.name' - } - - for i in $(seq 60); do - if [ -n "$(check ${POD_NAME})" ]; then - echo "${POD_NAME} is ready." - sleep 5 - return - fi - - echo "Waiting for ${POD_NAME} to be ready..." - sleep 2 - done - - echo "${POD_NAME} never became ready." - return 1 -} - -wait_for_complete_job() { - POD_NAME=$1 - - check() { - # This requests the pod and checks whether the status is running - # and the ready state is true. If so, it outputs the name. Otherwise - # it outputs empty. Therefore, to check for success, check for nonzero - # string length. - kubectl get job $1 -o json | \ - jq -r 'select( - .status.succeeded == 1 - ) | .metadata.namespace + "/" + .metadata.name' - } - - for i in $(seq 60); do - if [ -n "$(check ${POD_NAME})" ]; then - echo "${POD_NAME} is complete." - sleep 5 - return - fi - - echo "Waiting for ${POD_NAME} to be complete..." - sleep 2 - done - - echo "${POD_NAME} never completed." - return 1 -} diff --git a/vault/test/acceptance/csi-test/nginx.yaml b/vault/test/acceptance/csi-test/nginx.yaml deleted file mode 100644 index fed1137f..00000000 --- a/vault/test/acceptance/csi-test/nginx.yaml +++ /dev/null @@ -1,27 +0,0 @@ ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: nginx ---- -kind: Pod -apiVersion: v1 -metadata: - name: nginx -spec: - terminationGracePeriodSeconds: 0 - serviceAccountName: nginx - containers: - - image: docker.mirror.hashicorp.services/nginx - name: nginx - volumeMounts: - - name: secrets-store-inline - mountPath: "/mnt/secrets-store" - readOnly: true - volumes: - - name: secrets-store-inline - csi: - driver: secrets-store.csi.k8s.io - readOnly: true - volumeAttributes: - secretProviderClass: "vault-kv" diff --git a/vault/test/acceptance/csi-test/vault-kv-secretproviderclass.yaml b/vault/test/acceptance/csi-test/vault-kv-secretproviderclass.yaml deleted file mode 100644 index e793bde6..00000000 --- a/vault/test/acceptance/csi-test/vault-kv-secretproviderclass.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# The "Hello World" Vault SecretProviderClass -apiVersion: secrets-store.csi.x-k8s.io/v1alpha1 -kind: SecretProviderClass -metadata: - name: vault-kv -spec: - provider: vault - parameters: - roleName: "kv-role" - vaultAddress: http://vault:8200 - objects: | - - objectName: "bar" - secretPath: "secret/data/kv1" - secretKey: "bar1" diff --git a/vault/test/acceptance/csi-test/vault-policy.hcl b/vault/test/acceptance/csi-test/vault-policy.hcl deleted file mode 100644 index 48b670ea..00000000 --- a/vault/test/acceptance/csi-test/vault-policy.hcl +++ /dev/null @@ -1,3 +0,0 @@ -path "secret/data/kv1" { - capabilities = ["read"] -} \ No newline at end of file diff --git a/vault/test/acceptance/csi.bats b/vault/test/acceptance/csi.bats deleted file mode 100644 index d222ca27..00000000 --- a/vault/test/acceptance/csi.bats +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "csi: testing deployment" { - cd `chart_dir` - - kubectl delete namespace acceptance --ignore-not-found=true - kubectl create namespace acceptance - - # Install Secrets Store CSI driver - CSI_DRIVER_VERSION=0.2.0 - helm install secrets-store-csi-driver https://github.com/kubernetes-sigs/secrets-store-csi-driver/blob/v${CSI_DRIVER_VERSION}/charts/secrets-store-csi-driver-${CSI_DRIVER_VERSION}.tgz?raw=true \ - --wait --timeout=5m \ - --namespace=acceptance \ - --set linux.image.pullPolicy="IfNotPresent" - # Install Vault and Vault provider - helm install vault \ - --wait --timeout=5m \ - --namespace=acceptance \ - --set="server.dev.enabled=true" \ - --set="csi.enabled=true" \ - --set="injector.enabled=false" . - kubectl --namespace=acceptance wait --for=condition=Ready --timeout=5m pod -l app.kubernetes.io/name=vault - kubectl --namespace=acceptance wait --for=condition=Ready --timeout=5m pod -l app.kubernetes.io/name=vault-csi-provider - - # Set up k8s auth and a kv secret. - cat ./test/acceptance/csi-test/vault-policy.hcl | kubectl --namespace=acceptance exec -i vault-0 -- vault policy write kv-policy - - kubectl --namespace=acceptance exec vault-0 -- vault auth enable kubernetes - kubectl --namespace=acceptance exec vault-0 -- sh -c 'vault write auth/kubernetes/config \ - token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ - kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \ - kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ - disable_iss_validation=true' - kubectl --namespace=acceptance exec vault-0 -- vault write auth/kubernetes/role/kv-role \ - bound_service_account_names=nginx \ - bound_service_account_namespaces=acceptance \ - policies=kv-policy \ - ttl=20m - kubectl --namespace=acceptance exec vault-0 -- vault kv put secret/kv1 bar1=hello1 - - kubectl --namespace=acceptance apply -f ./test/acceptance/csi-test/vault-kv-secretproviderclass.yaml - kubectl --namespace=acceptance apply -f ./test/acceptance/csi-test/nginx.yaml - kubectl --namespace=acceptance wait --for=condition=Ready --timeout=5m pod nginx - - result=$(kubectl --namespace=acceptance exec nginx -- cat /mnt/secrets-store/bar) - [[ "$result" == "hello1" ]] -} - -# Clean up -teardown() { - if [[ ${CLEANUP:-true} == "true" ]] - then - echo "helm/pvc teardown" - helm --namespace=acceptance delete vault - helm --namespace=acceptance delete secrets-store-csi-driver - kubectl delete --all pvc - kubectl delete namespace acceptance - fi -} diff --git a/vault/test/acceptance/helm-test.bats b/vault/test/acceptance/helm-test.bats deleted file mode 100644 index c5f9553d..00000000 --- a/vault/test/acceptance/helm-test.bats +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "helm/test: running helm test" { - cd `chart_dir` - - kubectl delete namespace acceptance --ignore-not-found=true - kubectl create namespace acceptance - kubectl config set-context --current --namespace=acceptance - - helm install "$(name_prefix)" . - wait_for_running $(name_prefix)-0 - - helm test "$(name_prefix)" -} - -# Clean up -teardown() { - if [[ ${CLEANUP:-true} == "true" ]] - then - echo "helm/pvc teardown" - helm delete vault - kubectl delete --all pvc - kubectl delete namespace acceptance --ignore-not-found=true - fi -} diff --git a/vault/test/acceptance/injector-leader-elector.bats b/vault/test/acceptance/injector-leader-elector.bats deleted file mode 100644 index 0f91e02a..00000000 --- a/vault/test/acceptance/injector-leader-elector.bats +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "injector: testing leader elector" { - cd `chart_dir` - - kubectl delete namespace acceptance --ignore-not-found=true - kubectl create namespace acceptance - kubectl config set-context --current --namespace=acceptance - - helm install "$(name_prefix)" \ - --wait \ - --timeout=5m \ - --set="injector.replicas=3" . - kubectl wait --for condition=Ready pod -l app.kubernetes.io/name=vault-agent-injector --timeout=5m - - pods=($(kubectl get pods -l app.kubernetes.io/name=vault-agent-injector -o json | jq -r '.items[] | .metadata.name')) - [ "${#pods[@]}" == 3 ] - - leader='' - tries=0 - until [ $tries -ge 60 ] - do - owner=$(kubectl get configmaps vault-k8s-leader -o json | jq -r .metadata.ownerReferences\[0\].name) - leader=$(kubectl get pods $owner -o json | jq -r .metadata.name) - [ -n "${leader}" ] && [ "${leader}" != "null" ] && break - ((++tries)) - sleep .5 - done - - # Check the leader name is valid - i.e. one of the 3 pods - [[ " ${pods[@]} " =~ " ${leader} " ]] - -} - -setup() { - kubectl delete namespace acceptance --ignore-not-found=true - kubectl create namespace acceptance - kubectl config set-context --current --namespace=acceptance -} - -# Clean up -teardown() { - if [[ ${CLEANUP:-true} == "true" ]] - then - echo "helm/pvc teardown" - helm delete vault - kubectl delete --all pvc - kubectl delete namespace acceptance - fi -} \ No newline at end of file diff --git a/vault/test/acceptance/injector-test/bootstrap.sh b/vault/test/acceptance/injector-test/bootstrap.sh deleted file mode 100755 index d738fd28..00000000 --- a/vault/test/acceptance/injector-test/bootstrap.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh - -OUTPUT=/tmp/output.txt - -vault operator init -n 1 -t 1 >> ${OUTPUT?} - -unseal=$(cat ${OUTPUT?} | grep "Unseal Key 1:" | sed -e "s/Unseal Key 1: //g") -root=$(cat ${OUTPUT?} | grep "Initial Root Token:" | sed -e "s/Initial Root Token: //g") - -vault operator unseal ${unseal?} - -vault login -no-print ${root?} - -vault policy write db-backup /vault/userconfig/test/pgdump-policy.hcl - -vault auth enable kubernetes - -vault write auth/kubernetes/config \ - token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ - kubernetes_host=https://${KUBERNETES_PORT_443_TCP_ADDR}:443 \ - kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt - -vault write auth/kubernetes/role/db-backup \ - bound_service_account_names=pgdump \ - bound_service_account_namespaces=acceptance \ - policies=db-backup \ - ttl=1h - -vault secrets enable database - -vault write database/config/postgresql \ - plugin_name=postgresql-database-plugin \ - allowed_roles="db-backup" \ - connection_url="postgresql://{{username}}:{{password}}@postgres:5432/mydb?sslmode=disable" \ - username="vault" \ - password="vault" - -vault write database/roles/db-backup \ - db_name=postgresql \ - creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \ - GRANT CONNECT ON DATABASE mydb TO \"{{name}}\"; \ - GRANT USAGE ON SCHEMA app TO \"{{name}}\"; \ - GRANT SELECT ON ALL TABLES IN SCHEMA app TO \"{{name}}\";" \ - revocation_statements="ALTER ROLE \"{{name}}\" NOLOGIN;"\ - default_ttl="1h" \ - max_ttl="24h" diff --git a/vault/test/acceptance/injector-test/job.yaml b/vault/test/acceptance/injector-test/job.yaml deleted file mode 100644 index d665383c..00000000 --- a/vault/test/acceptance/injector-test/job.yaml +++ /dev/null @@ -1,39 +0,0 @@ ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: pgdump - labels: - app: pgdump ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: pgdump -spec: - backoffLimit: 0 - template: - metadata: - name: pgdump - labels: - app: pgdump - annotations: - vault.hashicorp.com/agent-inject: "true" - vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/db-backup" - vault.hashicorp.com/agent-inject-template-db-creds: | - {{- with secret "database/creds/db-backup" -}} - postgresql://{{ .Data.username }}:{{ .Data.password }}@postgres.acceptance.svc.cluster.local:5432/mydb - {{- end }} - vault.hashicorp.com/role: "db-backup" - vault.hashicorp.com/agent-pre-populate-only: "true" - spec: - serviceAccountName: pgdump - containers: - - name: pgdump - image: postgres:11.5 - command: - - "/bin/sh" - - "-ec" - args: - - "/usr/bin/pg_dump $(cat /vault/secrets/db-creds) --no-owner > /dev/stdout" - restartPolicy: Never diff --git a/vault/test/acceptance/injector-test/pg-deployment.yaml b/vault/test/acceptance/injector-test/pg-deployment.yaml deleted file mode 100644 index caf8605d..00000000 --- a/vault/test/acceptance/injector-test/pg-deployment.yaml +++ /dev/null @@ -1,69 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: postgres - labels: - app: postgres -spec: - type: ClusterIP - ports: - - port: 5432 - targetPort: 5432 - selector: - app: postgres ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: postgres -spec: - replicas: 1 - selector: - matchLabels: - app: postgres - template: - metadata: - labels: - service: postgres - app: postgres - spec: - containers: - - name: postgres - image: postgres:11.5 - ports: - - containerPort: 5432 - env: - - name: POSTGRES_DB - value: mydb - - name: POSTGRES_USER - value: postgres - - name: POSTGRES_PASSWORD - value: password - volumeMounts: - - mountPath: "/var/lib/postgresql" - name: "pgdata" - - mountPath: "/docker-entrypoint-initdb.d" - name: "pgconf" - volumes: - - name: pgdata - emptyDir: {} - - name: pgconf - configMap: - name: "pg-init" ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: pg-init - labels: - app: postgres -data: - setup.sql: | - CREATE ROLE vault; - ALTER ROLE vault WITH SUPERUSER LOGIN PASSWORD 'vault'; - - \c mydb - CREATE SCHEMA app; - CREATE TABLE app.inventory(id int); - INSERT INTO app.inventory(id) VALUES (0); diff --git a/vault/test/acceptance/injector-test/pgdump-policy.hcl b/vault/test/acceptance/injector-test/pgdump-policy.hcl deleted file mode 100644 index 88a6cd66..00000000 --- a/vault/test/acceptance/injector-test/pgdump-policy.hcl +++ /dev/null @@ -1,3 +0,0 @@ -path "database/creds/db-backup" { - capabilities = ["read"] -} diff --git a/vault/test/acceptance/injector.bats b/vault/test/acceptance/injector.bats deleted file mode 100644 index e7fb393a..00000000 --- a/vault/test/acceptance/injector.bats +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "injector: testing deployment" { - cd `chart_dir` - - kubectl delete namespace acceptance --ignore-not-found=true - kubectl create namespace acceptance - kubectl config set-context --current --namespace=acceptance - - kubectl create -f ./test/acceptance/injector-test/pg-deployment.yaml - sleep 5 - wait_for_ready $(kubectl get pod -l app=postgres -o jsonpath="{.items[0].metadata.name}") - - kubectl create secret generic test \ - --from-file ./test/acceptance/injector-test/pgdump-policy.hcl \ - --from-file ./test/acceptance/injector-test/bootstrap.sh - - kubectl label secret test app=vault-agent-demo - - helm install "$(name_prefix)" \ - --set="server.extraVolumes[0].type=secret" \ - --set="server.extraVolumes[0].name=test" . - wait_for_running $(name_prefix)-0 - - wait_for_ready $(kubectl get pod -l component=webhook -o jsonpath="{.items[0].metadata.name}") - - kubectl exec -ti "$(name_prefix)-0" -- /bin/sh -c "cp /vault/userconfig/test/bootstrap.sh /tmp/bootstrap.sh && chmod +x /tmp/bootstrap.sh && /tmp/bootstrap.sh" - sleep 5 - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "false" ] - - local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "true" ] - - - kubectl create -f ./test/acceptance/injector-test/job.yaml - wait_for_complete_job "pgdump" -} - -# Clean up -teardown() { - if [[ ${CLEANUP:-true} == "true" ]] - then - echo "helm/pvc teardown" - helm delete vault - kubectl delete --all pvc - kubectl delete secret test - kubectl delete job pgdump - kubectl delete deployment postgres - kubectl delete namespace acceptance - fi -} diff --git a/vault/test/acceptance/server-annotations.bats b/vault/test/acceptance/server-annotations.bats deleted file mode 100644 index d382788a..00000000 --- a/vault/test/acceptance/server-annotations.bats +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/annotations: testing yaml and yaml-formatted string formats" { - cd `chart_dir` - kubectl delete namespace acceptance --ignore-not-found=true - kubectl create namespace acceptance - kubectl config set-context --current --namespace=acceptance - - helm install "$(name_prefix)" -f ./test/acceptance/server-test/annotations-overrides.yaml . - wait_for_running $(name_prefix)-0 - - # service annotations - local awesome=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.metadata.annotations.active') - [ "${awesome}" == "sometimes" ] - - local pickMe=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.metadata.annotations.pickMe') - [ "${pickMe}" == "please" ] - - local environment=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.metadata.annotations.environment') - [ "${environment}" == "production" ] - - local milk=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.metadata.annotations.milk') - [ "${milk}" == "oat" ] - - local myName=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.metadata.annotations.myName') - [ "${myName}" == "$(name_prefix)" ] - -} - -# Clean up -teardown() { - if [[ ${CLEANUP:-true} == "true" ]] - then - echo "helm/pvc teardown" - helm delete $(name_prefix) - kubectl delete --all pvc - kubectl delete namespace acceptance --ignore-not-found=true - fi -} diff --git a/vault/test/acceptance/server-dev.bats b/vault/test/acceptance/server-dev.bats deleted file mode 100644 index 0619c289..00000000 --- a/vault/test/acceptance/server-dev.bats +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/dev: testing deployment" { - cd `chart_dir` - kubectl delete namespace acceptance --ignore-not-found=true - kubectl create namespace acceptance - kubectl config set-context --current --namespace=acceptance - - helm install "$(name_prefix)" --set='server.dev.enabled=true' . - wait_for_running $(name_prefix)-0 - - # Replicas - local replicas=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.replicas') - [ "${replicas}" == "1" ] - - # Volume Mounts - local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.containers[0].volumeMounts | length') - [ "${volumeCount}" == "1" ] - - # Service - local service=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.clusterIP') - [ "${service}" != "None" ] - - local service=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.type') - [ "${service}" == "ClusterIP" ] - - local ports=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.ports | length') - [ "${ports}" == "2" ] - - local ports=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.ports[0].port') - [ "${ports}" == "8200" ] - - local ports=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.ports[1].port') - [ "${ports}" == "8201" ] - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "false" ] - - local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "true" ] -} - -# Clean up -teardown() { - if [[ ${CLEANUP:-true} == "true" ]] - then - echo "helm/pvc teardown" - helm delete vault - kubectl delete --all pvc - kubectl delete namespace acceptance --ignore-not-found=true - fi -} diff --git a/vault/test/acceptance/server-ha-enterprise-dr.bats b/vault/test/acceptance/server-ha-enterprise-dr.bats deleted file mode 100644 index ee27518f..00000000 --- a/vault/test/acceptance/server-ha-enterprise-dr.bats +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/ha-enterprise-raft: testing DR deployment" { - cd `chart_dir` - - helm install "$(name_prefix)-east" \ - --set='server.image.repository=hashicorp/vault-enterprise' \ - --set='server.image.tag=1.9.0_ent' \ - --set='injector.enabled=false' \ - --set='server.ha.enabled=true' \ - --set='server.ha.raft.enabled=true' \ - --set='server.enterpriseLicense.secretName=vault-license' . - wait_for_running "$(name_prefix)-east-0" - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "true" ] - - local init_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "false" ] - - # Vault Init - local init=$(kubectl exec -ti "$(name_prefix)-east-0" -- \ - vault operator init -format=json -n 1 -t 1) - - local primary_token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') - [ "${primary_token}" != "" ] - - local primary_root=$(echo ${init} | jq -r '.root_token') - [ "${primary_root}" != "" ] - - kubectl exec -ti "$(name_prefix)-east-0" -- vault operator unseal ${primary_token} - wait_for_ready "$(name_prefix)-east-0" - - sleep 10 - - # Vault Unseal - local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) - for pod in "${pods[@]}" - do - if [[ ${pod?} != "$(name_prefix)-east-0" ]] - then - kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-east-0.$(name_prefix)-east-internal:8200 - kubectl exec -ti ${pod} -- vault operator unseal ${primary_token} - wait_for_ready "${pod}" - fi - done - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "false" ] - - local init_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "true" ] - - kubectl exec "$(name_prefix)-east-0" -- vault login ${primary_root} - - local raft_status=$(kubectl exec "$(name_prefix)-east-0" -- vault operator raft list-peers -format=json | - jq -r '.data.config.servers | length') - [ "${raft_status}" == "3" ] - - kubectl exec -ti $(name_prefix)-east-0 -- vault write -f sys/replication/dr/primary/enable primary_cluster_addr=https://$(name_prefix)-east-active:8201 - - local secondary=$(kubectl exec -ti "$(name_prefix)-east-0" -- vault write sys/replication/dr/primary/secondary-token id=secondary -format=json) - [ "${secondary}" != "" ] - - local secondary_replica_token=$(echo ${secondary} | jq -r '.wrap_info.token') - [ "${secondary_replica_token}" != "" ] - - # Install vault-west - helm install "$(name_prefix)-west" \ - --set='injector.enabled=false' \ - --set='server.image.repository=hashicorp/vault-enterprise' \ - --set='server.image.tag=1.9.0_ent' \ - --set='server.ha.enabled=true' \ - --set='server.ha.raft.enabled=true' \ - --set='server.enterpriseLicense.secretName=vault-license' . - wait_for_running "$(name_prefix)-west-0" - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "true" ] - - local init_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "false" ] - - # Vault Init - local init=$(kubectl exec -ti "$(name_prefix)-west-0" -- \ - vault operator init -format=json -n 1 -t 1) - - local secondary_token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') - [ "${secondary_token}" != "" ] - - local secondary_root=$(echo ${init} | jq -r '.root_token') - [ "${secondary_root}" != "" ] - - kubectl exec -ti "$(name_prefix)-west-0" -- vault operator unseal ${secondary_token} - wait_for_ready "$(name_prefix)-west-0" - - sleep 10 - - # Vault Unseal - local pods=($(kubectl get pods --selector='app.kubernetes.io/instance=vault-west' -o json | jq -r '.items[].metadata.name')) - for pod in "${pods[@]}" - do - if [[ ${pod?} != "$(name_prefix)-west-0" ]] - then - kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-west-0.$(name_prefix)-west-internal:8200 - kubectl exec -ti ${pod} -- vault operator unseal ${secondary_token} - wait_for_ready "${pod}" - fi - done - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "false" ] - - local init_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "true" ] - - kubectl exec "$(name_prefix)-west-0" -- vault login ${secondary_root} - - local raft_status=$(kubectl exec "$(name_prefix)-west-0" -- vault operator raft list-peers -format=json | - jq -r '.data.config.servers | length') - [ "${raft_status}" == "3" ] - - kubectl exec -ti "$(name_prefix)-west-0" -- vault write sys/replication/dr/secondary/enable token=${secondary_replica_token} - - sleep 10 - - local pods=($(kubectl get pods --selector='app.kubernetes.io/instance=vault-west' -o json | jq -r '.items[].metadata.name')) - for pod in "${pods[@]}" - do - if [[ ${pod?} != "$(name_prefix)-west-0" ]] - then - kubectl delete pod "${pod?}" - wait_for_running "${pod?}" - kubectl exec -ti ${pod} -- vault operator unseal ${primary_token} - wait_for_ready "${pod}" - fi - done -} - -setup() { - kubectl delete namespace acceptance --ignore-not-found=true - kubectl create namespace acceptance - kubectl config set-context --current --namespace=acceptance - kubectl create secret generic vault-license --from-literal license=$VAULT_LICENSE_CI -} - -#cleanup -teardown() { - if [[ ${CLEANUP:-true} == "true" ]] - then - helm delete vault-east - helm delete vault-west - kubectl delete --all pvc - kubectl delete namespace acceptance --ignore-not-found=true - fi -} diff --git a/vault/test/acceptance/server-ha-enterprise-perf.bats b/vault/test/acceptance/server-ha-enterprise-perf.bats deleted file mode 100644 index c359c1c2..00000000 --- a/vault/test/acceptance/server-ha-enterprise-perf.bats +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/ha-enterprise-raft: testing performance replica deployment" { - cd `chart_dir` - - helm install "$(name_prefix)-east" \ - --set='injector.enabled=false' \ - --set='server.image.repository=hashicorp/vault-enterprise' \ - --set='server.image.tag=1.9.0_ent' \ - --set='server.ha.enabled=true' \ - --set='server.ha.raft.enabled=true' \ - --set='server.enterpriseLicense.secretName=vault-license' . - wait_for_running "$(name_prefix)-east-0" - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "true" ] - - local init_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "false" ] - - # Vault Init - local init=$(kubectl exec -ti "$(name_prefix)-east-0" -- \ - vault operator init -format=json -n 1 -t 1) - - local primary_token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') - [ "${primary_token}" != "" ] - - local primary_root=$(echo ${init} | jq -r '.root_token') - [ "${primary_root}" != "" ] - - kubectl exec -ti "$(name_prefix)-east-0" -- vault operator unseal ${primary_token} - wait_for_ready "$(name_prefix)-east-0" - - sleep 30 - - # Vault Unseal - local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) - for pod in "${pods[@]}" - do - if [[ ${pod?} != "$(name_prefix)-east-0" ]] - then - kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-east-0.$(name_prefix)-east-internal:8200 - kubectl exec -ti ${pod} -- vault operator unseal ${primary_token} - wait_for_ready "${pod}" - fi - done - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "false" ] - - local init_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "true" ] - - kubectl exec "$(name_prefix)-east-0" -- vault login ${primary_root} - - local raft_status=$(kubectl exec "$(name_prefix)-east-0" -- vault operator raft list-peers -format=json | - jq -r '.data.config.servers | length') - [ "${raft_status}" == "3" ] - - kubectl exec -ti $(name_prefix)-east-0 -- vault write -f sys/replication/performance/primary/enable primary_cluster_addr=https://$(name_prefix)-east-active:8201 - - local secondary=$(kubectl exec -ti "$(name_prefix)-east-0" -- vault write sys/replication/performance/primary/secondary-token id=secondary -format=json) - [ "${secondary}" != "" ] - - local secondary_replica_token=$(echo ${secondary} | jq -r '.wrap_info.token') - [ "${secondary_replica_token}" != "" ] - - # Install vault-west - helm install "$(name_prefix)-west" \ - --set='injector.enabled=false' \ - --set='server.image.repository=hashicorp/vault-enterprise' \ - --set='server.image.tag=1.9.0_ent' \ - --set='server.ha.enabled=true' \ - --set='server.ha.raft.enabled=true' \ - --set='server.enterpriseLicense.secretName=vault-license' . - wait_for_running "$(name_prefix)-west-0" - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "true" ] - - local init_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "false" ] - - # Vault Init - local init=$(kubectl exec -ti "$(name_prefix)-west-0" -- \ - vault operator init -format=json -n 1 -t 1) - - local secondary_token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') - [ "${secondary_token}" != "" ] - - local secondary_root=$(echo ${init} | jq -r '.root_token') - [ "${secondary_root}" != "" ] - - kubectl exec -ti "$(name_prefix)-west-0" -- vault operator unseal ${secondary_token} - wait_for_ready "$(name_prefix)-west-0" - - sleep 30 - - # Vault Unseal - local pods=($(kubectl get pods --selector='app.kubernetes.io/instance=vault-west' -o json | jq -r '.items[].metadata.name')) - for pod in "${pods[@]}" - do - if [[ ${pod?} != "$(name_prefix)-west-0" ]] - then - kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-west-0.$(name_prefix)-west-internal:8200 - kubectl exec -ti ${pod} -- vault operator unseal ${secondary_token} - wait_for_ready "${pod}" - fi - done - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "false" ] - - local init_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "true" ] - - kubectl exec "$(name_prefix)-west-0" -- vault login ${secondary_root} - - local raft_status=$(kubectl exec "$(name_prefix)-west-0" -- vault operator raft list-peers -format=json | - jq -r '.data.config.servers | length') - [ "${raft_status}" == "3" ] - - kubectl exec -ti "$(name_prefix)-west-0" -- vault write sys/replication/performance/secondary/enable token=${secondary_replica_token} - - sleep 30 - - local pods=($(kubectl get pods --selector='app.kubernetes.io/instance=vault-west' -o json | jq -r '.items[].metadata.name')) - for pod in "${pods[@]}" - do - if [[ ${pod?} != "$(name_prefix)-west-0" ]] - then - kubectl exec -ti ${pod} -- vault operator unseal ${primary_token} - wait_for_ready "${pod}" - fi - done -} - -setup() { - kubectl delete namespace acceptance --ignore-not-found=true - kubectl create namespace acceptance - kubectl config set-context --current --namespace=acceptance - kubectl create secret generic vault-license --from-literal license=$VAULT_LICENSE_CI -} - -#cleanup -teardown() { - if [[ ${CLEANUP:-true} == "true" ]] - then - helm delete vault-east - helm delete vault-west - kubectl delete --all pvc - kubectl delete namespace acceptance --ignore-not-found=true - fi -} diff --git a/vault/test/acceptance/server-ha-raft.bats b/vault/test/acceptance/server-ha-raft.bats deleted file mode 100644 index 9f9f3dec..00000000 --- a/vault/test/acceptance/server-ha-raft.bats +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/ha-raft: testing deployment" { - cd `chart_dir` - - helm install "$(name_prefix)" \ - --set='server.ha.enabled=true' \ - --set='server.ha.raft.enabled=true' . - wait_for_running $(name_prefix)-0 - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "true" ] - - local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "false" ] - - # Replicas - local replicas=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.replicas') - [ "${replicas}" == "3" ] - - # Volume Mounts - local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.containers[0].volumeMounts | length') - [ "${volumeCount}" == "3" ] - - # Volumes - local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.volumes | length') - [ "${volumeCount}" == "2" ] - - local volume=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.volumes[0].configMap.name') - [ "${volume}" == "$(name_prefix)-config" ] - - # Service - local service=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.clusterIP') - [ "${service}" != "None" ] - - local service=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.type') - [ "${service}" == "ClusterIP" ] - - local ports=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.ports | length') - [ "${ports}" == "2" ] - - local ports=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.ports[0].port') - [ "${ports}" == "8200" ] - - local ports=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.ports[1].port') - [ "${ports}" == "8201" ] - - # Vault Init - local init=$(kubectl exec -ti "$(name_prefix)-0" -- \ - vault operator init -format=json -n 1 -t 1) - - local token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') - [ "${token}" != "" ] - - local root=$(echo ${init} | jq -r '.root_token') - [ "${root}" != "" ] - - kubectl exec -ti vault-0 -- vault operator unseal ${token} - wait_for_ready "$(name_prefix)-0" - - sleep 5 - - # Vault Unseal - local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) - for pod in "${pods[@]}" - do - if [[ ${pod?} != "$(name_prefix)-0" ]] - then - kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-0.$(name_prefix)-internal:8200 - kubectl exec -ti ${pod} -- vault operator unseal ${token} - wait_for_ready "${pod}" - fi - done - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "false" ] - - local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "true" ] - - kubectl exec "$(name_prefix)-0" -- vault login ${root} - - local raft_status=$(kubectl exec "$(name_prefix)-0" -- vault operator raft list-peers -format=json | - jq -r '.data.config.servers | length') - [ "${raft_status}" == "3" ] -} - -setup() { - kubectl delete namespace acceptance --ignore-not-found=true - kubectl create namespace acceptance - kubectl config set-context --current --namespace=acceptance -} - -#cleanup -teardown() { - if [[ ${CLEANUP:-true} == "true" ]] - then - helm delete vault - kubectl delete --all pvc - kubectl delete namespace acceptance --ignore-not-found=true - fi -} diff --git a/vault/test/acceptance/server-ha.bats b/vault/test/acceptance/server-ha.bats deleted file mode 100644 index 3d629598..00000000 --- a/vault/test/acceptance/server-ha.bats +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/ha: testing deployment" { - cd `chart_dir` - - helm install "$(name_prefix)" \ - --set='server.ha.enabled=true' . - wait_for_running $(name_prefix)-0 - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "true" ] - - local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "false" ] - - # Replicas - local replicas=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.replicas') - [ "${replicas}" == "3" ] - - # Volume Mounts - local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.containers[0].volumeMounts | length') - [ "${volumeCount}" == "2" ] - - # Volumes - local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.volumes | length') - [ "${volumeCount}" == "2" ] - - local volume=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.volumes[0].configMap.name') - [ "${volume}" == "$(name_prefix)-config" ] - - # Service - local service=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.clusterIP') - [ "${service}" != "None" ] - - local service=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.type') - [ "${service}" == "ClusterIP" ] - - local ports=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.ports | length') - [ "${ports}" == "2" ] - - local ports=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.ports[0].port') - [ "${ports}" == "8200" ] - - local ports=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.ports[1].port') - [ "${ports}" == "8201" ] - - # Vault Init - local token=$(kubectl exec -ti "$(name_prefix)-0" -- \ - vault operator init -format=json -n 1 -t 1 | \ - jq -r '.unseal_keys_b64[0]') - [ "${token}" != "" ] - - # Vault Unseal - local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) - for pod in "${pods[@]}" - do - kubectl exec -ti ${pod} -- vault operator unseal ${token} - done - - wait_for_ready "$(name_prefix)-0" - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "false" ] - - local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "true" ] -} - -# setup a consul env -setup() { - kubectl delete namespace acceptance --ignore-not-found=true - kubectl create namespace acceptance - kubectl config set-context --current --namespace=acceptance - - helm install consul \ - https://github.com/hashicorp/consul-helm/archive/v0.28.0.tar.gz \ - --set 'ui.enabled=false' \ - - wait_for_running_consul -} - -#cleanup -teardown() { - if [[ ${CLEANUP:-true} == "true" ]] - then - helm delete vault - helm delete consul - kubectl delete --all pvc - kubectl delete namespace acceptance --ignore-not-found=true - fi -} diff --git a/vault/test/acceptance/server-test/annotations-overrides.yaml b/vault/test/acceptance/server-test/annotations-overrides.yaml deleted file mode 100644 index 459576a9..00000000 --- a/vault/test/acceptance/server-test/annotations-overrides.yaml +++ /dev/null @@ -1,9 +0,0 @@ -server: - annotations: | - environment: production - milk: oat - myName: "{{ .Release.Name }}" - service: - annotations: - active: sometimes - pickMe: please diff --git a/vault/test/acceptance/server.bats b/vault/test/acceptance/server.bats deleted file mode 100644 index 84a4e7d9..00000000 --- a/vault/test/acceptance/server.bats +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/standalone: testing deployment" { - cd `chart_dir` - - kubectl delete namespace acceptance --ignore-not-found=true - kubectl create namespace acceptance - kubectl config set-context --current --namespace=acceptance - - helm install "$(name_prefix)" . - wait_for_running $(name_prefix)-0 - - # Sealed, not initialized - local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "true" ] - - local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "false" ] - - # Replicas - local replicas=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.replicas') - [ "${replicas}" == "1" ] - - # Affinity - local affinity=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.affinity') - [ "${affinity}" != "null" ] - - # Volume Mounts - local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.containers[0].volumeMounts | length') - [ "${volumeCount}" == "3" ] - - local mountName=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.containers[0].volumeMounts[0].name') - [ "${mountName}" == "data" ] - - local mountPath=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.containers[0].volumeMounts[0].mountPath') - [ "${mountPath}" == "/vault/data" ] - - # Volumes - local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.volumes | length') - [ "${volumeCount}" == "2" ] - - local volume=$(kubectl get statefulset "$(name_prefix)" --output json | - jq -r '.spec.template.spec.volumes[0].configMap.name') - [ "${volume}" == "$(name_prefix)-config" ] - - # Service - local service=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.clusterIP') - [ "${service}" != "None" ] - - local service=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.type') - [ "${service}" == "ClusterIP" ] - - local ports=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.ports | length') - [ "${ports}" == "2" ] - - local ports=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.ports[0].port') - [ "${ports}" == "8200" ] - - local ports=$(kubectl get service "$(name_prefix)" --output json | - jq -r '.spec.ports[1].port') - [ "${ports}" == "8201" ] - - # Vault Init - local token=$(kubectl exec -ti "$(name_prefix)-0" -- \ - vault operator init -format=json -n 1 -t 1 | \ - jq -r '.unseal_keys_b64[0]') - [ "${token}" != "" ] - - # Vault Unseal - local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) - for pod in "${pods[@]}" - do - kubectl exec -ti ${pod} -- vault operator unseal ${token} - done - - wait_for_ready "$(name_prefix)-0" - - # Unsealed, initialized - local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.sealed' ) - [ "${sealed_status}" == "false" ] - - local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | - jq -r '.initialized') - [ "${init_status}" == "true" ] -} - -# Clean up -teardown() { - if [[ ${CLEANUP:-true} == "true" ]] - then - echo "helm/pvc teardown" - helm delete vault - kubectl delete --all pvc - kubectl delete namespace acceptance --ignore-not-found=true - fi -} diff --git a/vault/test/chart/_helpers.bash b/vault/test/chart/_helpers.bash deleted file mode 100644 index fb9db31d..00000000 --- a/vault/test/chart/_helpers.bash +++ /dev/null @@ -1,18 +0,0 @@ -# chart_dir returns the directory for the chart -chart_dir() { - echo ${BATS_TEST_DIRNAME}/../.. -} - -# check_result checks if the specified test passed -# results schema example: -# { -# "check": "has-minkubeversion", -# "type": "Mandatory", -# "outcome": "PASS", -# "reason": "Minimum Kubernetes version specified" -# } -check_result() { - local -r var="$1" - local check=$(cat $VERIFY_OUTPUT | jq -r ".results[] | select(.check==\"${var}\").outcome") - [ "$check" = "PASS" ] -} diff --git a/vault/test/chart/verifier.bats b/vault/test/chart/verifier.bats deleted file mode 100644 index 63c79395..00000000 --- a/vault/test/chart/verifier.bats +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -setup_file() { - cd `chart_dir` - export VERIFY_OUTPUT="/$BATS_RUN_TMPDIR/verify.json" - export CHART_VOLUME=vault-helm-chart-src - local IMAGE="quay.io/redhat-certification/chart-verifier:1.2.1" - # chart-verifier requires an openshift version if a cluster isn't available - local OPENSHIFT_VERSION="4.8" - local DISABLED_TESTS="chart-testing" - - local run_cmd="chart-verifier" - local chart_src="." - - if [ ! -e $USE_DOCKER ]; then - chart_src="/chart" - # Create a dummy container which will hold a volume with chart source - docker create -v $chart_src --name $CHART_VOLUME alpine:3 /bin/true - # Copy the chart source into this volume - docker cp . $CHART_VOLUME:$chart_src - # Make sure we have the latest version of chart-verifier - docker pull $IMAGE - # Start chart-verifier using this volume - run_cmd="docker run --rm --volumes-from $CHART_VOLUME -w $chart_src $IMAGE" - fi - - $run_cmd verify $chart_src \ - --output json \ - --openshift-version $OPENSHIFT_VERSION \ - --disable $DISABLED_TESTS \ - --chart-values values.openshift.yaml 2>&1 | tee $VERIFY_OUTPUT -} - -teardown_file() { - if [ ! -e $USE_DOCKER ]; then - docker rm $CHART_VOLUME - fi -} - -@test "has-kubeversion" { - check_result v1.0/has-kubeversion -} - -@test "is-helm-v3" { - check_result v1.0/is-helm-v3 -} - -@test "not-contains-crds" { - check_result v1.0/not-contains-crds -} - -@test "helm-lint" { - check_result v1.0/helm-lint -} - -@test "not-contain-csi-objects" { - check_result v1.0/not-contain-csi-objects -} - -@test "has-readme" { - check_result v1.0/has-readme -} - -@test "contains-values" { - check_result v1.0/contains-values -} - -@test "contains-values-schema" { - check_result v1.0/contains-values-schema -} - -@test "contains-test" { - check_result v1.0/contains-test -} - -@test "images-are-certified" { - check_result v1.0/images-are-certified -} - -@test "chart-testing" { - skip "Skipping since this test requires a kubernetes/openshift cluster" - check_result v1.0/chart-testing -} diff --git a/vault/test/docker/Test.dockerfile b/vault/test/docker/Test.dockerfile deleted file mode 100644 index 98afeace..00000000 --- a/vault/test/docker/Test.dockerfile +++ /dev/null @@ -1,51 +0,0 @@ -# This Dockerfile installs all the dependencies necessary to run the unit and -# acceptance tests. This image also contains gcloud so you can run tests -# against a GKE cluster easily. -# -# This image has no automatic entrypoint. It is expected that you'll run -# a script to configure kubectl, potentially install Helm, and run the tests -# manually. This image only has the dependencies pre-installed. - -FROM docker.mirror.hashicorp.services/alpine:latest -WORKDIR /root - -ENV BATS_VERSION "1.3.0" -ENV TERRAFORM_VERSION "0.12.10" - -# base packages -RUN apk update && apk add --no-cache --virtual .build-deps \ - ca-certificates \ - curl \ - tar \ - bash \ - openssl \ - py-pip \ - git \ - make \ - jq - -# yq -RUN pip install yq - -# gcloud -RUN curl -OL https://dl.google.com/dl/cloudsdk/channels/rapid/install_google_cloud_sdk.bash && \ - bash install_google_cloud_sdk.bash --disable-prompts --install-dir='/root/' && \ - ln -s /root/google-cloud-sdk/bin/gcloud /usr/local/bin/gcloud - -# terraform -RUN curl -sSL https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip -o /tmp/tf.zip \ - && unzip /tmp/tf.zip \ - && ln -s /root/terraform /usr/local/bin/terraform - -# kubectl -RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && \ - chmod +x ./kubectl && \ - mv ./kubectl /usr/local/bin/kubectl - -# helm -RUN curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash - -# bats -RUN curl -sSL https://github.com/bats-core/bats-core/archive/v${BATS_VERSION}.tar.gz -o /tmp/bats.tgz \ - && tar -zxf /tmp/bats.tgz -C /tmp \ - && /bin/bash /tmp/bats-core-$BATS_VERSION/install.sh /usr/local diff --git a/vault/test/kind/config.yaml b/vault/test/kind/config.yaml deleted file mode 100644 index 25096640..00000000 --- a/vault/test/kind/config.yaml +++ /dev/null @@ -1,7 +0,0 @@ -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -nodes: -- role: control-plane -- role: worker -- role: worker -- role: worker diff --git a/vault/test/terraform/.gitignore b/vault/test/terraform/.gitignore deleted file mode 100644 index d6800621..00000000 --- a/vault/test/terraform/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vault-helm-dev-creds.json diff --git a/vault/test/terraform/main.tf b/vault/test/terraform/main.tf deleted file mode 100644 index 5c3570f2..00000000 --- a/vault/test/terraform/main.tf +++ /dev/null @@ -1,72 +0,0 @@ -provider "google" { - project = "${var.project}" -} - -resource "random_id" "suffix" { - byte_length = 4 -} - -data "google_container_engine_versions" "main" { - location = "${var.zone}" - version_prefix = "1.19." -} - -data "google_service_account" "gcpapi" { - account_id = "${var.gcp_service_account}" -} - -resource "google_container_cluster" "cluster" { - name = "vault-helm-dev-${random_id.suffix.dec}" - project = "${var.project}" - enable_legacy_abac = true - initial_node_count = 3 - location = "${var.zone}" - min_master_version = "${data.google_container_engine_versions.main.latest_master_version}" - node_version = "${data.google_container_engine_versions.main.latest_node_version}" - - node_config { - #service account for nodes to use - oauth_scopes = [ - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/compute", - "https://www.googleapis.com/auth/devstorage.read_write", - "https://www.googleapis.com/auth/logging.write", - "https://www.googleapis.com/auth/monitoring", - "https://www.googleapis.com/auth/service.management.readonly", - "https://www.googleapis.com/auth/servicecontrol", - "https://www.googleapis.com/auth/trace.append", - ] - - service_account = "${data.google_service_account.gcpapi.email}" - } -} - -resource "null_resource" "kubectl" { - count = "${var.init_cli ? 1 : 0 }" - - triggers = { - cluster = "${google_container_cluster.cluster.id}" - } - - # On creation, we want to setup the kubectl credentials. The easiest way - # to do this is to shell out to gcloud. - provisioner "local-exec" { - command = "gcloud container clusters get-credentials --zone=${var.zone} ${google_container_cluster.cluster.name}" - } - - # On destroy we want to try to clean up the kubectl credentials. This - # might fail if the credentials are already cleaned up or something so we - # want this to continue on failure. Generally, this works just fine since - # it only operates on local data. - provisioner "local-exec" { - when = "destroy" - on_failure = "continue" - command = "kubectl config get-clusters | grep ${google_container_cluster.cluster.name} | xargs -n1 kubectl config delete-cluster" - } - - provisioner "local-exec" { - when = "destroy" - on_failure = "continue" - command = "kubectl config get-contexts | grep ${google_container_cluster.cluster.name} | xargs -n1 kubectl config delete-context" - } -} diff --git a/vault/test/terraform/outputs.tf b/vault/test/terraform/outputs.tf deleted file mode 100644 index 6435d2b7..00000000 --- a/vault/test/terraform/outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -output "cluster_id" { - value = "${google_container_cluster.cluster.id}" -} - -output "cluster_name" { - value = "${google_container_cluster.cluster.name}" -} diff --git a/vault/test/terraform/variables.tf b/vault/test/terraform/variables.tf deleted file mode 100644 index 971af4e5..00000000 --- a/vault/test/terraform/variables.tf +++ /dev/null @@ -1,28 +0,0 @@ -variable "project" { - default = "vault-helm-dev-246514" - - description = < 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "csi/ClusterRole: enabled with csi.enabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-clusterrole.yaml \ - --set 'csi.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -# ClusterRole name -@test "csi/ClusterRole: name" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-clusterrole.yaml \ - --set "csi.enabled=true" \ - . | tee /dev/stderr | - yq -r '.metadata.name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault-csi-provider-clusterrole" ] -} \ No newline at end of file diff --git a/vault/test/unit/csi-clusterrolebinding.bats b/vault/test/unit/csi-clusterrolebinding.bats deleted file mode 100644 index cff3a369..00000000 --- a/vault/test/unit/csi-clusterrolebinding.bats +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "csi/ClusterRoleBinding: disabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/csi-clusterrolebinding.yaml \ - . || echo "---")| tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "csi/ClusterRoleBinding: enabled with csi.enabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-clusterrolebinding.yaml \ - --set 'csi.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -# ClusterRoleBinding cluster role ref name -@test "csi/ClusterRoleBinding: cluster role ref name" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-clusterrolebinding.yaml \ - --set "csi.enabled=true" \ - . | tee /dev/stderr | - yq -r '.roleRef.name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault-csi-provider-clusterrole" ] -} - -# ClusterRoleBinding service account name -@test "csi/ClusterRoleBinding: service account name" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-clusterrolebinding.yaml \ - --set "csi.enabled=true" \ - . | tee /dev/stderr | - yq -r '.subjects[0].name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault-csi-provider" ] -} \ No newline at end of file diff --git a/vault/test/unit/csi-daemonset.bats b/vault/test/unit/csi-daemonset.bats deleted file mode 100644 index 5cfd8a7e..00000000 --- a/vault/test/unit/csi-daemonset.bats +++ /dev/null @@ -1,516 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -#-------------------------------------------------------------------- -# Daemonset - -# Enabled -@test "csi/daemonset: created only when enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/csi-daemonset.yaml \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$( (helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - --set "global.enabled=false" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -# serviceAccountName reference name -@test "csi/daemonset: serviceAccountName reference name" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.serviceAccountName' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault-csi-provider" ] -} - -# Image -@test "csi/daemonset: image is configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - --set "csi.image.repository=SomeOtherImage" \ - --set "csi.image.tag=0.0.1" \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) - [ "${actual}" = "SomeOtherImage:0.0.1" ] - - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - --set "csi.image.pullPolicy=SomePullPolicy" \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].imagePullPolicy' | tee /dev/stderr) - [ "${actual}" = "SomePullPolicy" ] -} - -@test "csi/daemonset: Custom imagePullSecrets" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - --set 'global.imagePullSecrets[0].name=foo' \ - --set 'global.imagePullSecrets[1].name=bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.imagePullSecrets' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '. | length' | tee /dev/stderr) - [ "${actual}" = "2" ] - - local actual=$(echo $object | - yq -r '.[0].name' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - local actual=$(echo $object | - yq -r '.[1].name' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -@test "csi/daemonset: Custom imagePullSecrets - string array" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - --set 'global.imagePullSecrets[0]=foo' \ - --set 'global.imagePullSecrets[1]=bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.imagePullSecrets' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '. | length' | tee /dev/stderr) - [ "${actual}" = "2" ] - - local actual=$(echo $object | - yq -r '.[0].name' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - local actual=$(echo $object | - yq -r '.[1].name' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -@test "csi/daemonset: default imagePullSecrets" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.imagePullSecrets' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -# Debug arg -@test "csi/daemonset: debug arg is configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].args[1]' | tee /dev/stderr) - [ "${actual}" = "--debug=false" ] - - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - --set "csi.debug=true" \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].args[1]' | tee /dev/stderr) - [ "${actual}" = "--debug=true" ] -} - -# Extra args -@test "csi/daemonset: extra args can be passed" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].args | length' | tee /dev/stderr) - [ "${actual}" = "2" ] - - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - --set "csi.extraArgs={--foo=bar,--bar baz,first}" \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0]') - local actual=$(echo $object | - yq -r '.args | length' | tee /dev/stderr) - [ "${actual}" = "5" ] - local actual=$(echo $object | - yq -r '.args[2]' | tee /dev/stderr) - [ "${actual}" = "--foo=bar" ] - local actual=$(echo $object | - yq -r '.args[3]' | tee /dev/stderr) - [ "${actual}" = "--bar baz" ] - local actual=$(echo $object | - yq -r '.args[4]' | tee /dev/stderr) - [ "${actual}" = "first" ] -} - -# updateStrategy -@test "csi/daemonset: updateStrategy is configurable" { - cd `chart_dir` - # Default - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - . | tee /dev/stderr | - yq -r '.spec.updateStrategy.type' | tee /dev/stderr) - [ "${actual}" = "RollingUpdate" ] - - # OnDelete - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - --set "csi.daemonSet.updateStrategy.type=OnDelete" \ - . | tee /dev/stderr | - yq -r '.spec.updateStrategy.type' | tee /dev/stderr) - [ "${actual}" = "OnDelete" ] - - # Max unavailable - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set "csi.enabled=true" \ - --set "csi.daemonSet.updateStrategy.maxUnavailable=25%" \ - . | tee /dev/stderr | - yq -r '.spec.updateStrategy.rollingUpdate.maxUnavailable' | tee /dev/stderr) - [ "${actual}" = "25%" ] -} - -#-------------------------------------------------------------------- -# Extra annotations -@test "csi/daemonset: default csi.daemonSet.annotations" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "csi/daemonset: specify csi.daemonSet.annotations yaml" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - --set 'csi.daemonSet.annotations.foo=bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -@test "csi/daemonset: specify csi.daemonSet.annotations yaml string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - --set 'csi.daemonSet.annotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -@test "csi/daemonset: default csi.pod.annotations" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "csi/daemonset: specify csi.pod.annotations yaml" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - --set 'csi.pod.annotations.foo=bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -@test "csi/daemonset: specify csi.pod.annotations yaml string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - --set 'csi.pod.annotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -@test "csi/daemonset: tolerations not set by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec | .tolerations? == null' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "csi/daemonset: tolerations can be set as string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - --set 'csi.pod.tolerations=foobar' \ - . | tee /dev/stderr | - yq '.spec.template.spec.tolerations == "foobar"' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "csi/daemonset: tolerations can be set as YAML" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - --set "csi.pod.tolerations[0].foo=bar,csi.pod.tolerations[1].baz=qux" \ - . | tee /dev/stderr | - yq '.spec.template.spec.tolerations == [{"foo": "bar"}, {"baz": "qux"}]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -#-------------------------------------------------------------------- -# volumes - -@test "csi/daemonset: csi.volumes adds volume" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - --set 'csi.volumes[0].name=plugins' \ - --set 'csi.volumes[0].emptyDir=\{\}' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "plugins")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.emptyDir' | tee /dev/stderr) - [ "${actual}" = "{}" ] -} - -@test "csi/daemonset: csi providersDir default" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "providervol")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.hostPath.path' | tee /dev/stderr) - [ "${actual}" = "/etc/kubernetes/secrets-store-csi-providers" ] -} - -@test "csi/daemonset: csi kubeletRootDir default" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "mountpoint-dir")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.hostPath.path' | tee /dev/stderr) - [ "${actual}" = "/var/lib/kubelet/pods" ] -} - -@test "csi/daemonset: csi providersDir override " { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - --set 'csi.daemonSet.providersDir=/alt/csi-prov-dir' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "providervol")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.hostPath.path' | tee /dev/stderr) - [ "${actual}" = "/alt/csi-prov-dir" ] -} - -@test "csi/daemonset: csi kubeletRootDir override" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - --set 'csi.daemonSet.kubeletRootDir=/alt/kubelet-root' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "mountpoint-dir")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.hostPath.path' | tee /dev/stderr) - [ "${actual}" = "/alt/kubelet-root/pods" ] -} - -#-------------------------------------------------------------------- -# volumeMounts - -@test "csi/daemonset: csi.volumeMounts adds volume mounts" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - --set 'csi.volumeMounts[0].name=plugins' \ - --set 'csi.volumeMounts[0].mountPath=/usr/local/libexec/vault' \ - --set 'csi.volumeMounts[0].readOnly=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "plugins")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/usr/local/libexec/vault" ] - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -#-------------------------------------------------------------------- -# Readiness/liveness probes - -@test "csi/daemonset: csi.livenessProbe is configurable" { - cd `chart_dir` - - # Test the defaults - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.failureThreshold' | tee /dev/stderr) - [ "${actual}" = "2" ] - local actual=$(echo $object | - yq -r '.initialDelaySeconds' | tee /dev/stderr) - [ "${actual}" = "5" ] - local actual=$(echo $object | - yq -r '.periodSeconds' | tee /dev/stderr) - [ "${actual}" = "5" ] - local actual=$(echo $object | - yq -r '.successThreshold' | tee /dev/stderr) - [ "${actual}" = "1" ] - local actual=$(echo $object | - yq -r '.timeoutSeconds' | tee /dev/stderr) - [ "${actual}" = "3" ] - - # Test it is configurable - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - --set 'csi.livenessProbe.failureThreshold=10' \ - --set 'csi.livenessProbe.initialDelaySeconds=11' \ - --set 'csi.livenessProbe.periodSeconds=12' \ - --set 'csi.livenessProbe.successThreshold=13' \ - --set 'csi.livenessProbe.timeoutSeconds=14' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.failureThreshold' | tee /dev/stderr) - [ "${actual}" = "10" ] - local actual=$(echo $object | - yq -r '.initialDelaySeconds' | tee /dev/stderr) - [ "${actual}" = "11" ] - local actual=$(echo $object | - yq -r '.periodSeconds' | tee /dev/stderr) - [ "${actual}" = "12" ] - local actual=$(echo $object | - yq -r '.successThreshold' | tee /dev/stderr) - [ "${actual}" = "13" ] - local actual=$(echo $object | - yq -r '.timeoutSeconds' | tee /dev/stderr) - [ "${actual}" = "14" ] -} - -@test "csi/daemonset: csi.readinessProbe is configurable" { - cd `chart_dir` - - # Test the defaults - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.failureThreshold' | tee /dev/stderr) - [ "${actual}" = "2" ] - local actual=$(echo $object | - yq -r '.initialDelaySeconds' | tee /dev/stderr) - [ "${actual}" = "5" ] - local actual=$(echo $object | - yq -r '.periodSeconds' | tee /dev/stderr) - [ "${actual}" = "5" ] - local actual=$(echo $object | - yq -r '.successThreshold' | tee /dev/stderr) - [ "${actual}" = "1" ] - local actual=$(echo $object | - yq -r '.timeoutSeconds' | tee /dev/stderr) - [ "${actual}" = "3" ] - - # Test it is configurable - local object=$(helm template \ - --show-only templates/csi-daemonset.yaml \ - --set 'csi.enabled=true' \ - --set 'csi.readinessProbe.failureThreshold=10' \ - --set 'csi.readinessProbe.initialDelaySeconds=11' \ - --set 'csi.readinessProbe.periodSeconds=12' \ - --set 'csi.readinessProbe.successThreshold=13' \ - --set 'csi.readinessProbe.timeoutSeconds=14' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.failureThreshold' | tee /dev/stderr) - [ "${actual}" = "10" ] - local actual=$(echo $object | - yq -r '.initialDelaySeconds' | tee /dev/stderr) - [ "${actual}" = "11" ] - local actual=$(echo $object | - yq -r '.periodSeconds' | tee /dev/stderr) - [ "${actual}" = "12" ] - local actual=$(echo $object | - yq -r '.successThreshold' | tee /dev/stderr) - [ "${actual}" = "13" ] - local actual=$(echo $object | - yq -r '.timeoutSeconds' | tee /dev/stderr) - [ "${actual}" = "14" ] -} diff --git a/vault/test/unit/csi-serviceaccount.bats b/vault/test/unit/csi-serviceaccount.bats deleted file mode 100644 index 22ba06df..00000000 --- a/vault/test/unit/csi-serviceaccount.bats +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "csi/ServiceAccount: disabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/csi-serviceaccount.yaml \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "csi/ServiceAccount: enable with csi.enabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-serviceaccount.yaml \ - --set 'csi.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -# serviceAccountName reference name -@test "csi/daemonset: serviceAccountName name" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/csi-serviceaccount.yaml \ - --set "csi.enabled=true" \ - . | tee /dev/stderr | - yq -r '.metadata.name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault-csi-provider" ] -} - -@test "csi/serviceAccount: specify annotations" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-serviceaccount.yaml \ - --set 'csi.enabled=true' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(helm template \ - --show-only templates/server-serviceaccount.yaml \ - --set 'csi.enabled=true' \ - --set 'csi.serviceAccount.annotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(helm template \ - --show-only templates/server-serviceaccount.yaml \ - --set 'csi.enabled=true' \ - --set 'server.serviceAccount.annotations.foo=bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} \ No newline at end of file diff --git a/vault/test/unit/injector-clusterrole.bats b/vault/test/unit/injector-clusterrole.bats deleted file mode 100755 index 7c25f39d..00000000 --- a/vault/test/unit/injector-clusterrole.bats +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "injector/ClusterRole: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-clusterrole.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/ClusterRole: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-clusterrole.yaml \ - --set 'global.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} diff --git a/vault/test/unit/injector-clusterrolebinding.bats b/vault/test/unit/injector-clusterrolebinding.bats deleted file mode 100755 index 6e217878..00000000 --- a/vault/test/unit/injector-clusterrolebinding.bats +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "injector/ClusterRoleBinding: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-clusterrolebinding.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/ClusterRoleBinding: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-clusterrolebinding.yaml \ - --set 'global.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} diff --git a/vault/test/unit/injector-deployment.bats b/vault/test/unit/injector-deployment.bats deleted file mode 100755 index 3bae2af7..00000000 --- a/vault/test/unit/injector-deployment.bats +++ /dev/null @@ -1,723 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "injector/deployment: default injector.enabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/deployment: enable with injector.enabled true" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/deployment: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'global.enabled=false' \ - --set 'injector.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "injector/deployment: image defaults to injector.image" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.image.repository=foo' \ - --set 'injector.image.tag=1.2.3' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) - [ "${actual}" = "foo:1.2.3" ] - - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.image.repository=foo' \ - --set 'injector.image.tag=1.2.3' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) - [ "${actual}" = "foo:1.2.3" ] -} - -@test "injector/deployment: default imagePullPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].imagePullPolicy' | tee /dev/stderr) - [ "${actual}" = "IfNotPresent" ] -} - -@test "injector/deployment: default resources" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "injector/deployment: custom resources" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.enabled=true' \ - --set 'injector.resources.requests.memory=256Mi' \ - --set 'injector.resources.requests.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) - [ "${actual}" = "256Mi" ] - - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.enabled=true' \ - --set 'injector.resources.limits.memory=256Mi' \ - --set 'injector.resources.limits.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) - [ "${actual}" = "256Mi" ] - - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.enabled=true' \ - --set 'injector.resources.requests.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.requests.cpu' | tee /dev/stderr) - [ "${actual}" = "250m" ] - - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.enabled=true' \ - --set 'injector.resources.limits.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.limits.cpu' | tee /dev/stderr) - [ "${actual}" = "250m" ] -} - -@test "injector/deployment: enable metrics" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.metrics.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.[9].name' | tee /dev/stderr) - [ "${actual}" = "AGENT_INJECT_TELEMETRY_PATH" ] - - local actual=$(echo $object | - yq -r '.[9].value' | tee /dev/stderr) - [ "${actual}" = "/metrics" ] -} - -@test "injector/deployment: manual TLS environment vars" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.certs.secretName=foobar' \ - --set 'injector.certs.certName=test.crt' \ - --set 'injector.certs.keyName=test.key' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_TLS_CERT_FILE")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "/etc/webhook/certs/test.crt" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_TLS_KEY_FILE")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "/etc/webhook/certs/test.key" ] -} - -@test "injector/deployment: auto TLS by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts | length' | tee /dev/stderr) - [ "${actual}" = "0" ] - - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_TLS_AUTO")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "RELEASE-NAME-vault-agent-injector-cfg" ] - - # helm template does uses current context namespace and ignores namespace flags, so - # discover the targeted namespace so we can check the rendered value correctly. - local namespace=$(kubectl config view --minify --output 'jsonpath={..namespace}') - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_TLS_AUTO_HOSTS")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "RELEASE-NAME-vault-agent-injector-svc,RELEASE-NAME-vault-agent-injector-svc.${namespace:-default},RELEASE-NAME-vault-agent-injector-svc.${namespace:-default}.svc" ] -} - -@test "injector/deployment: manual TLS adds volume mount" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.enabled=true' \ - --set 'injector.certs.secretName=vault-tls' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "webhook-certs")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/etc/webhook/certs" ] - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/deployment: with externalVaultAddr" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_VAULT_ADDR")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "http://vault-outside" ] -} - -@test "injector/deployment: without externalVaultAddr" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --release-name not-external-test \ - --namespace default \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_VAULT_ADDR")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "http://not-external-test-vault.default.svc:8200" ] -} - -@test "injector/deployment: default authPath" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_VAULT_AUTH_PATH")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "auth/kubernetes" ] -} - -@test "injector/deployment: custom authPath" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.authPath=auth/k8s' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_VAULT_AUTH_PATH")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "auth/k8s" ] -} - -@test "injector/deployment: default logLevel" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_LOG_LEVEL")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "info" ] -} - -@test "injector/deployment: custom logLevel" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.logLevel=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_LOG_LEVEL")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "foo" ] -} - -@test "injector/deployment: default logFormat" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_LOG_FORMAT")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "standard" ] -} - -@test "injector/deployment: custom logFormat" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.logFormat=json' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_LOG_FORMAT")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "json" ] -} - -@test "injector/deployment: default revoke on shutdown" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_REVOKE_ON_SHUTDOWN")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "false" ] -} - -@test "injector/deployment: custom revoke on shutdown" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.revokeOnShutdown=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_REVOKE_ON_SHUTDOWN")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "true" ] -} - -@test "injector/deployment: disable security context when openshift enabled" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'global.openshift=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_SET_SECURITY_CONTEXT")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "false" ] -} - -#-------------------------------------------------------------------- -# extraEnvironmentVars - -@test "injector/deployment: set extraEnvironmentVars" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.extraEnvironmentVars.FOO=bar' \ - --set 'injector.extraEnvironmentVars.FOOBAR=foobar' \ - --set 'injector.extraEnvironmentVars.lower\.case=sanitized' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="FOO")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "bar" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="FOOBAR")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "foobar" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="LOWER_CASE")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "sanitized" ] -} - -#-------------------------------------------------------------------- -# extra annotations - -@test "injector/deployment: default annotations" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "injector/deployment: specify annotations yaml" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.annotations.foo=bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -@test "injector/deployment: specify annotations yaml string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.annotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -#-------------------------------------------------------------------- -# agent port - -@test "injector/deployment: default agentPort" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.[0].name' | tee /dev/stderr) - [ "${actual}" = "AGENT_INJECT_LISTEN" ] - - local actual=$(echo $object | - yq -r '.[0].value' | tee /dev/stderr) - [ "${actual}" = ":8080" ] -} - -@test "injector/deployment: custom agentPort" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.port=8443' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.[0].name' | tee /dev/stderr) - [ "${actual}" = "AGENT_INJECT_LISTEN" ] - - local actual=$(echo $object | - yq -r '.[0].value' | tee /dev/stderr) - [ "${actual}" = ":8443" ] -} - -#-------------------------------------------------------------------- -# affinity - -@test "injector/deployment: affinity set by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq '.spec.template.spec | .affinity? == null' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "injector/deployment: affinity can be set as string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.affinity=foobar' \ - . | tee /dev/stderr | - yq '.spec.template.spec.affinity == "foobar"' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/deployment: affinity can be set as YAML" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.affinity.podAntiAffinity=foobar' \ - . | tee /dev/stderr | - yq '.spec.template.spec.affinity.podAntiAffinity == "foobar"' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -#-------------------------------------------------------------------- -# tolerations - -@test "injector/deployment: tolerations not set by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq '.spec.template.spec | .tolerations? == null' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/deployment: tolerations can be set as string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.tolerations=foobar' \ - . | tee /dev/stderr | - yq '.spec.template.spec.tolerations == "foobar"' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/deployment: tolerations can be set as YAML" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set "injector.tolerations[0].foo=bar,injector.tolerations[1].baz=qux" \ - . | tee /dev/stderr | - yq '.spec.template.spec.tolerations == [{"foo": "bar"}, {"baz": "qux"}]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -#-------------------------------------------------------------------- -# nodeSelector - -@test "injector/deployment: nodeSelector is not set by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq '.spec.template.spec.nodeSelector' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "injector/deployment: nodeSelector can be set as string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.nodeSelector=testing' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.nodeSelector' | tee /dev/stderr) - [ "${actual}" = "testing" ] -} - -@test "injector/deployment: nodeSelector can be set as YAML" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set "injector.nodeSelector.beta\.kubernetes\.io/arch=amd64" \ - . | tee /dev/stderr | - yq '.spec.template.spec.nodeSelector == {"beta.kubernetes.io/arch": "amd64"}' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - - -#-------------------------------------------------------------------- -# priorityClassName - -@test "injector/deployment: priorityClassName not set by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq '.spec.template.spec | .priorityClassName? == null' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/deployment: priorityClassName can be set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.priorityClassName=armaggeddon' \ - . | tee /dev/stderr | - yq '.spec.template.spec | .priorityClassName == "armaggeddon"' | tee /dev/stderr) - [ "${actual}" = "true" ] -} -#-------------------------------------------------------------------- -# OpenShift - -@test "injector/deployment: OpenShift - runAsUser disabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'global.openshift=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.securityContext.runAsUser | length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "injector/deployment: OpenShift - runAsGroup disabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'global.openshift=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.securityContext.runAsGroup | length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} -#-------------------------------------------------------------------- -# extra labels - -@test "injector/deployment: specify extraLabels" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.extraLabels.foo=bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -#-------------------------------------------------------------------- -# hostNetwork - -@test "injector/deployment: injector.hostNetwork not set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.hostNetwork' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "injector/deployment: injector.hostNetwork is set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.hostNetwork=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.hostNetwork' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/deployment: agent default resources" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_CPU_LIMIT")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "500m" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_CPU_REQUEST")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "250m" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_MEM_LIMIT")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "128Mi" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_MEM_REQUEST")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "64Mi" ] -} - -@test "injector/deployment: can set agent default resources" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set 'injector.agentDefaults.cpuLimit=cpuLimit' \ - --set 'injector.agentDefaults.cpuRequest=cpuRequest' \ - --set 'injector.agentDefaults.memLimit=memLimit' \ - --set 'injector.agentDefaults.memRequest=memRequest' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_CPU_LIMIT")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "cpuLimit" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_CPU_REQUEST")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "cpuRequest" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_MEM_LIMIT")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "memLimit" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_MEM_REQUEST")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "memRequest" ] -} - -@test "injector/deployment: agent default template" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_DEFAULT_TEMPLATE")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "map" ] -} - -@test "injector/deployment: can set agent default template" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set='injector.agentDefaults.template=json' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_DEFAULT_TEMPLATE")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "json" ] -} - -@test "injector/deployment: agent default template_config.exit_on_retry_failure" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_TEMPLATE_CONFIG_EXIT_ON_RETRY_FAILURE")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "true" ] -} - -@test "injector/deployment: can set agent template_config.exit_on_retry_failure" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set='injector.agentDefaults.templateConfig.exitOnRetryFailure=false' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_TEMPLATE_CONFIG_EXIT_ON_RETRY_FAILURE")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "false" ] -} - -@test "injector/deployment: agent default template_config.static_secret_render_interval" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_TEMPLATE_STATIC_SECRET_RENDER_INTERVAL")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "" ] -} - -@test "injector/deployment: can set agent template_config.static_secret_render_interval" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set='injector.agentDefaults.templateConfig.staticSecretRenderInterval=1m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="AGENT_INJECT_TEMPLATE_STATIC_SECRET_RENDER_INTERVAL")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "1m" ] -} diff --git a/vault/test/unit/injector-leader-elector.bats b/vault/test/unit/injector-leader-elector.bats deleted file mode 100644 index b6fa4ae6..00000000 --- a/vault/test/unit/injector-leader-elector.bats +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -#-------------------------------------------------------------------- -# Deployment - -@test "injector/deployment: replica count" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set "injector.replicas=2" \ - . | tee /dev/stderr | - yq '.spec.replicas' | tee /dev/stderr) - [ "${actual}" = "2" ] -} - -@test "injector/deployment: leader elector configuration for sidecar-injector" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env[] | select(.name == "AGENT_INJECT_USE_LEADER_ELECTOR") | .value' | tee /dev/stderr) - [ "${actual}" = "" ] - - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set "injector.replicas=2" \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env[] | select(.name == "AGENT_INJECT_USE_LEADER_ELECTOR") | .value' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env[] | select(.name == "NAMESPACE") | .valueFrom.fieldRef.fieldPath' | tee /dev/stderr) - [ "${actual}" = "" ] - - local actual=$(helm template \ - --show-only templates/injector-deployment.yaml \ - --set "injector.replicas=2" \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env[] | select(.name == "NAMESPACE") | .valueFrom.fieldRef.fieldPath' | tee /dev/stderr) - [ "${actual}" = "metadata.namespace" ] -} - -#-------------------------------------------------------------------- -# Resource creation - -@test "injector/certs-secret: created/skipped as appropriate" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-certs-secret.yaml \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-certs-secret.yaml \ - --set "injector.replicas=2" \ - --set "global.enabled=false" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-certs-secret.yaml \ - --set "injector.replicas=2" \ - --set "injector.enabled=false" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-certs-secret.yaml \ - --set "injector.replicas=2" \ - --set "injector.leaderElector.enabled=false" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-certs-secret.yaml \ - --set "injector.replicas=2" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/role: created/skipped as appropriate" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-role.yaml \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-role.yaml \ - --set "injector.replicas=2" \ - --set "global.enabled=false" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-role.yaml \ - --set "injector.replicas=2" \ - --set "injector.enabled=false" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-role.yaml \ - --set "injector.replicas=2" \ - --set "injector.leaderElector.enabled=false" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-role.yaml \ - --set "injector.replicas=2" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/rolebinding: created/skipped as appropriate" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-rolebinding.yaml \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-rolebinding.yaml \ - --set "injector.replicas=2" \ - --set "global.enabled=false" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-rolebinding.yaml \ - --set "injector.replicas=2" \ - --set "injector.enabled=false" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-rolebinding.yaml \ - --set "injector.replicas=2" \ - --set "injector.leaderElector.enabled=false" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-rolebinding.yaml \ - --set "injector.replicas=2" \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} diff --git a/vault/test/unit/injector-mutating-webhook.bats b/vault/test/unit/injector-mutating-webhook.bats deleted file mode 100755 index 1e6e150d..00000000 --- a/vault/test/unit/injector-mutating-webhook.bats +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "injector/MutatingWebhookConfiguration: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/MutatingWebhookConfiguration: disable with global.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - --set 'global.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "injector/MutatingWebhookConfiguration: disable with injector.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - --set 'injector.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "injector/MutatingWebhookConfiguration: namespace is set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - --set 'injector.enabled=true' \ - --namespace foo \ - . | tee /dev/stderr | - yq '.webhooks[0].clientConfig.service.namespace' | tee /dev/stderr) - [ "${actual}" = "\"foo\"" ] -} - -@test "injector/MutatingWebhookConfiguration: caBundle is empty string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - --set 'injector.enabled=true' \ - --namespace foo \ - . | tee /dev/stderr | - yq '.webhooks[0].clientConfig.caBundle' | tee /dev/stderr) - [ "${actual}" = "\"\"" ] -} - -@test "injector/MutatingWebhookConfiguration: namespaceSelector empty by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - --set 'injector.enabled=true' \ - --namespace foo \ - . | tee /dev/stderr | - yq '.webhooks[0].namespaceSelector' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "injector/MutatingWebhookConfiguration: can set namespaceSelector" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - --set 'injector.enabled=true' \ - --set 'injector.namespaceSelector.matchLabels.injector=true' \ - . | tee /dev/stderr | - yq '.webhooks[0].namespaceSelector.matchLabels.injector' | tee /dev/stderr) - - [ "${actual}" = "true" ] -} - -@test "injector/MutatingWebhookConfiguration: objectSelector empty by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - --set 'injector.enabled=true' \ - --namespace foo \ - . | tee /dev/stderr | - yq '.webhooks[0].objectSelector' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "injector/MutatingWebhookConfiguration: can set objectSelector" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - --set 'injector.enabled=true' \ - --set 'injector.objectSelector.matchLabels.injector=true' \ - . | tee /dev/stderr | - yq '.webhooks[0].objectSelector.matchLabels.injector' | tee /dev/stderr) - - [ "${actual}" = "true" ] -} - -@test "injector/MutatingWebhookConfiguration: failurePolicy 'Ignore' by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - --set 'injector.enabled=true' \ - --namespace foo \ - . | tee /dev/stderr | - yq '.webhooks[0].failurePolicy' | tee /dev/stderr) - [ "${actual}" = "\"Ignore\"" ] -} - -@test "injector/MutatingWebhookConfiguration: can set failurePolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - --set 'injector.enabled=true' \ - --set 'injector.failurePolicy=Fail' \ - . | tee /dev/stderr | - yq '.webhooks[0].failurePolicy' | tee /dev/stderr) - - [ "${actual}" = "\"Fail\"" ] -} - -#-------------------------------------------------------------------- -# annotations - -@test "injector/MutatingWebhookConfiguration: default annotations" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - . | tee /dev/stderr | - yq -r '.metadata.annotations' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "injector/MutatingWebhookConfiguration: specify annotations yaml" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - --set 'injector.webhookAnnotations.foo=bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -@test "injector/MutatingWebhookConfiguration: specify annotations yaml string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-mutating-webhook.yaml \ - --set 'injector.webhookAnnotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} diff --git a/vault/test/unit/injector-psp-role.bats b/vault/test/unit/injector-psp-role.bats deleted file mode 100644 index c6dc522a..00000000 --- a/vault/test/unit/injector-psp-role.bats +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "injector/PodSecurityPolicy-Role: PodSecurityPolicy-Role not enabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-psp-role.yaml \ - . || echo "---" ) | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "injector/PodSecurityPolicy-Role: enable with injector.enabled and global.psp.enable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-psp-role.yaml \ - --set 'injector.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/PodSecurityPolicy-Role: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-psp-role.yaml \ - --set 'global.enabled=false' \ - --set 'injector.enabled=true' \ - --set 'global.psp.enable=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} diff --git a/vault/test/unit/injector-psp-rolebinding.bats b/vault/test/unit/injector-psp-rolebinding.bats deleted file mode 100644 index f8a8255f..00000000 --- a/vault/test/unit/injector-psp-rolebinding.bats +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "injector/PodSecurityPolicy-RoleBinding: PodSecurityPolicy-RoleBinding not enabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-psp-rolebinding.yaml \ - . || echo "---" ) | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "injector/PodSecurityPolicy-RoleBinding: enable with injector.enabled and global.psp.enable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-psp-rolebinding.yaml \ - --set 'injector.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/PodSecurityPolicy-RoleBinding: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-psp-rolebinding.yaml \ - --set 'global.enabled=false' \ - --set 'injector.enabled=true' \ - --set 'global.psp.enable=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} diff --git a/vault/test/unit/injector-psp.bats b/vault/test/unit/injector-psp.bats deleted file mode 100644 index fa14b0f1..00000000 --- a/vault/test/unit/injector-psp.bats +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "injector/PodSecurityPolicy: PodSecurityPolicy not enabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-psp.yaml \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "injector/PodSecurityPolicy: enable with injector.enabled and global.psp.enable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-psp.yaml \ - --set 'injector.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/PodSecurityPolicy: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-psp.yaml \ - --set 'global.enabled=false' \ - --set 'injector.enabled=true' \ - --set 'global.psp.enable=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "injector/PodSecurityPolicy: annotations are templated correctly by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-psp.yaml \ - --set 'injector.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq '.metadata.annotations | length == 4' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/PodSecurityPolicy: annotations are added - string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-psp.yaml \ - --set 'injector.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'global.psp.annotations=vault-is: amazing' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) - [ "${actual}" = "amazing" ] -} - -@test "injector/PodSecurityPolicy: annotations are added - object" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-psp.yaml \ - --set 'injector.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'global.psp.annotations.vault-is=amazing' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) - [ "${actual}" = "amazing" ] -} diff --git a/vault/test/unit/injector-service.bats b/vault/test/unit/injector-service.bats deleted file mode 100755 index ad480099..00000000 --- a/vault/test/unit/injector-service.bats +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "injector/Service: service enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-service.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/injector-service.yaml \ - --set 'injector.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/Service: service with default port" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-service.yaml \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) - [ "${actual}" = "8080" ] -} - -@test "injector/Service: service with custom port" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-service.yaml \ - --set 'injector.port=8443' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) - [ "${actual}" = "8443" ] -} - -@test "injector/Service: disable with global.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-service.yaml \ - --set 'global.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/injector-service.yaml \ - --set 'global.enabled=false' \ - --set 'injector.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "injector/Service: generic annotations" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-service.yaml \ - --set 'injector.service.annotations=vaultIsAwesome: true' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} diff --git a/vault/test/unit/injector-serviceaccount.bats b/vault/test/unit/injector-serviceaccount.bats deleted file mode 100755 index 1055d906..00000000 --- a/vault/test/unit/injector-serviceaccount.bats +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "injector/ServiceAccount: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/injector-serviceaccount.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "injector/ServiceAccount: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/injector-serviceaccount.yaml \ - --set 'global.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} diff --git a/vault/test/unit/schema.bats b/vault/test/unit/schema.bats deleted file mode 100644 index a42614bf..00000000 --- a/vault/test/unit/schema.bats +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -# These tests are just to verify there is a schema file used in the chart. Since -# .enabled is defined as a boolean type for each of the top-level blocks in the -# schema, setting it as a string fails 'helm template'. -@test "schema: csi enabled datatype" { - cd `chart_dir` - run helm template . --set csi.enabled="nope" - [ "$status" -eq 1 ] - [ "${lines[2]}" = "- csi.enabled: Invalid type. Expected: boolean, given: string" ] - - run helm template . --set csi.enabled=true - [ "$status" -eq 0 ] -} - -@test "schema: injector enabled datatype" { - cd `chart_dir` - run helm template . --set injector.enabled="nope" - [ "$status" -eq 1 ] - [ "${lines[2]}" = "- injector.enabled: Invalid type. Expected: boolean, given: string" ] - - run helm template . --set injector.enabled=true - [ "$status" -eq 0 ] -} - -@test "schema: server enabled datatype" { - cd `chart_dir` - run helm template . --set server.enabled="nope" - [ "$status" -eq 1 ] - [ "${lines[2]}" = "- server.enabled: Invalid type. Expected: boolean, given: string" ] - - run helm template . --set server.enabled=true - [ "$status" -eq 0 ] -} - -@test "schema: ui enabled datatype" { - cd `chart_dir` - run helm template . --set ui.enabled="nope" - [ "$status" -eq 1 ] - [ "${lines[2]}" = "- ui.enabled: Invalid type. Expected: boolean, given: string" ] - - run helm template . --set ui.enabled=true - [ "$status" -eq 0 ] -} diff --git a/vault/test/unit/server-clusterrolebinding.bats b/vault/test/unit/server-clusterrolebinding.bats deleted file mode 100755 index bf0a4d85..00000000 --- a/vault/test/unit/server-clusterrolebinding.bats +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/ClusterRoleBinding: enabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-clusterrolebinding.yaml \ - --set 'server.dev.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$( (helm template \ - --show-only templates/server-clusterrolebinding.yaml \ - --set 'server.ha.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$( (helm template \ - --show-only templates/server-clusterrolebinding.yaml \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/ClusterRoleBinding: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-clusterrolebinding.yaml \ - --set 'global.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ClusterRoleBinding: can disable with server.authDelegator" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-clusterrolebinding.yaml \ - --set 'server.authDelegator.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-clusterrolebinding.yaml \ - --set 'server.authDelegator.enabled=false' \ - --set 'server.ha.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-clusterrolebinding.yaml \ - --set 'server.authDelegator.enabled=false' \ - --set 'server.dev.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ClusterRoleBinding: also deploy with injector.externalVaultAddr" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-clusterrolebinding.yaml \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} diff --git a/vault/test/unit/server-configmap.bats b/vault/test/unit/server-configmap.bats deleted file mode 100755 index fe2ac125..00000000 --- a/vault/test/unit/server-configmap.bats +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/ConfigMap: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-config-configmap.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-config-configmap.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-config-configmap.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.ha.raft.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-config-configmap.yaml \ - --set 'server.standalone.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/ConfigMap: raft config disabled by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-config-configmap.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - grep "raft" | yq 'length > 0' | tee /dev/stderr) - [ "${actual}" != "true" ] -} - -@test "server/ConfigMap: raft config can be enabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-config-configmap.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.ha.raft.enabled=true' \ - . | tee /dev/stderr | - grep "raft" | yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - - -@test "server/ConfigMap: disabled by server.dev.enabled true" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-config-configmap.yaml \ - --set 'server.dev.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ConfigMap: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-config-configmap.yaml \ - --set 'global.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ConfigMap: standalone extraConfig is set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-config-configmap.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.standalone.config="{\"hello\": \"world\"}"' \ - . | tee /dev/stderr | - yq '.data["extraconfig-from-values.hcl"] | match("world") | length' | tee /dev/stderr) - [ ! -z "${actual}" ] - - local actual=$(helm template \ - --show-only templates/server-config-configmap.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.standalone.config="{\"foo\": \"bar\"}"' \ - . | tee /dev/stderr | - yq '.data["extraconfig-from-values.hcl"] | match("bar") | length' | tee /dev/stderr) - [ ! -z "${actual}" ] -} - -@test "server/ConfigMap: ha extraConfig is set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-config-configmap.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.ha.config="{\"hello\": \"world\"}"' \ - . | tee /dev/stderr | - yq '.data["extraconfig-from-values.hcl"] | match("world") | length' | tee /dev/stderr) - [ ! -z "${actual}" ] - - local actual=$(helm template \ - --show-only templates/server-config-configmap.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.ha.config="{\"foo\": \"bar\"}"' \ - . | tee /dev/stderr | - yq '.data["extraconfig-from-values.hcl"] | match("bar") | length' | tee /dev/stderr) - [ ! -z "${actual}" ] -} - -@test "server/ConfigMap: disabled by injector.externalVaultAddr" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-config-configmap.yaml \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} diff --git a/vault/test/unit/server-dev-statefulset.bats b/vault/test/unit/server-dev-statefulset.bats deleted file mode 100755 index 3c5f9d8f..00000000 --- a/vault/test/unit/server-dev-statefulset.bats +++ /dev/null @@ -1,461 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/dev-StatefulSet: enable with server.dev.enabled true" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/dev-StatefulSet: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.enabled=false' \ - --set 'server.dev.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/dev-StatefulSet: disable with injector.externalVaultAddr" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - --set 'server.dev.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/dev-StatefulSet: image defaults to server.image.repository:tag" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.image.repository=foo' \ - --set 'server.image.tag=1.2.3' \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) - [ "${actual}" = "foo:1.2.3" ] -} - -@test "server/ha-StatefulSet: image tag defaults to latest" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.image.repository=foo' \ - --set 'server.image.tag=' \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) - [ "${actual}" = "foo:latest" ] -} - -#-------------------------------------------------------------------- -# replicas - -@test "server/dev-StatefulSet: default replicas" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.replicas' | tee /dev/stderr) - [ "${actual}" = "1" ] -} - -@test "server/dev-StatefulSet: cant set replicas" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.dev.replicas=100' \ - . | tee /dev/stderr | - yq -r '.spec.replicas' | tee /dev/stderr) - [ "${actual}" = "1" ] -} - -#-------------------------------------------------------------------- -# updateStrategy - -@test "server/dev-StatefulSet: updateStrategy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.updateStrategy.type' | tee /dev/stderr) - [ "${actual}" = "OnDelete" ] -} - -#-------------------------------------------------------------------- -# resources - -@test "server/dev-StatefulSet: default resources" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/dev-StatefulSet: custom resources" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.resources.requests.memory=256Mi' \ - --set 'server.resources.requests.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) - [ "${actual}" = "256Mi" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.resources.limits.memory=256Mi' \ - --set 'server.resources.limits.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) - [ "${actual}" = "256Mi" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.resources.requests.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.requests.cpu' | tee /dev/stderr) - [ "${actual}" = "250m" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.resources.limits.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.limits.cpu' | tee /dev/stderr) - [ "${actual}" = "250m" ] -} - -#-------------------------------------------------------------------- -# extraVolumes - -@test "server/dev-StatefulSet: adds extra volume" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.configMap.name' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - local actual=$(echo $object | - yq -r '.configMap.secretName' | tee /dev/stderr) - [ "${actual}" = "null" ] - - # Test that it mounts it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/vault/userconfig/foo" ] -} - -@test "server/dev-StatefulSet: adds extra secret volume" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.extraVolumes[0].type=secret' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.secret.name' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(echo $object | - yq -r '.secret.secretName' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - # Test that it mounts it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/vault/userconfig/foo" ] -} - -@test "server/dev-StatefulSet: no storageClass on claim by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates[0].spec.storageClassName' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -#-------------------------------------------------------------------- -# devRootToken - -@test "server/dev-StatefulSet: set default devRootToken" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local name=$(echo $object | - yq -r 'map(select(.name=="VAULT_DEV_ROOT_TOKEN_ID")) | .[] .value' | tee /dev/stderr) - [ "${name}" = "root" ] -} - -@test "server/dev-StatefulSet: set custom devRootToken" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.dev.devRootToken=customtoken' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local name=$(echo $object | - yq -r 'map(select(.name=="VAULT_DEV_ROOT_TOKEN_ID")) | .[] .value' | tee /dev/stderr) - [ "${name}" = "customtoken" ] -} - -#-------------------------------------------------------------------- -# dev listen address - -@test "server/dev-StatefulSet: set dev listen address in dev mode" { - cd `chart_dir` - local objects=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $objects | - yq -r 'map(select(.name=="VAULT_DEV_LISTEN_ADDRESS")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "[::]:8200" ] -} - -@test "server/dev-StatefulSet: dev listen address isn't set in non-dev mode" { - cd `chart_dir` - local objects=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=false' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local name=$(echo $objects | - yq -r 'map(select(.name=="VAULT_DEV_LISTEN_ADDRESS")) | .[] .name' | tee /dev/stderr) - [ "${name}" = "" ] -} - -#-------------------------------------------------------------------- -# extraEnvironmentVars - -@test "server/dev-StatefulSet: set extraEnvironmentVars" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.extraEnvironmentVars.FOO=bar' \ - --set 'server.extraEnvironmentVars.FOOBAR=foobar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="FOO")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "bar" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="FOOBAR")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "foobar" ] -} - -#-------------------------------------------------------------------- -# extraSecretEnvironmentVars - -@test "server/dev-StatefulSet: set extraSecretEnvironmentVars" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.extraSecretEnvironmentVars[0].envName=ENV_FOO_0' \ - --set 'server.extraSecretEnvironmentVars[0].secretName=secret_name_0' \ - --set 'server.extraSecretEnvironmentVars[0].secretKey=secret_key_0' \ - --set 'server.extraSecretEnvironmentVars[1].envName=ENV_FOO_1' \ - --set 'server.extraSecretEnvironmentVars[1].secretName=secret_name_1' \ - --set 'server.extraSecretEnvironmentVars[1].secretKey=secret_key_1' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="ENV_FOO_0")) | .[] .valueFrom.secretKeyRef.name' | tee /dev/stderr) - [ "${value}" = "secret_name_0" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="ENV_FOO_0")) | .[] .valueFrom.secretKeyRef.key' | tee /dev/stderr) - [ "${value}" = "secret_key_0" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="ENV_FOO_1")) | .[] .valueFrom.secretKeyRef.name' | tee /dev/stderr) - [ "${value}" = "secret_name_1" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="ENV_FOO_1")) | .[] .valueFrom.secretKeyRef.key' | tee /dev/stderr) - [ "${value}" = "secret_key_1" ] -} - -#-------------------------------------------------------------------- -# storage class - -@test "server/dev-StatefulSet: can't set storageClass" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.dataStorage.enabled=true' \ - --set 'server.dataStorage.storageClass=foo' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.auditStorage.storageClass=foo' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.auditStorage.storageClass=foo' \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.auditStorage.storageClass=foo' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -#-------------------------------------------------------------------- -# Security Contexts -@test "server/dev-StatefulSet: uid default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -@test "server/dev-StatefulSet: uid configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.uid=2000' \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) - [ "${actual}" = "2000" ] -} - -@test "server/dev-StatefulSet: gid default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.runAsGroup' | tee /dev/stderr) - [ "${actual}" = "1000" ] -} - -@test "server/dev-StatefulSet: gid configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.gid=2000' \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.runAsGroup' | tee /dev/stderr) - [ "${actual}" = "2000" ] -} - -@test "server/dev-StatefulSet: fsgroup default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.fsGroup' | tee /dev/stderr) - [ "${actual}" = "1000" ] -} - -@test "server/dev-StatefulSet: fsgroup configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.gid=2000' \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.fsGroup' | tee /dev/stderr) - [ "${actual}" = "2000" ] -} - -@test "server/dev-StatefulSet: add extraArgs" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.extraArgs=foobar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].args[0]' | tee /dev/stderr) - [[ "${actual}" = *"foobar"* ]] -} diff --git a/vault/test/unit/server-ha-active-service.bats b/vault/test/unit/server-ha-active-service.bats deleted file mode 100755 index a835c9d9..00000000 --- a/vault/test/unit/server-ha-active-service.bats +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/ha-active-Service: generic annotations" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.annotations=vaultIsAwesome: true' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/ha-active-Service: disable with ha.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=false' \ - --set 'server.service.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ha-active-Service: disable with server.service.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ha-active-Service: type empty by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/ha-active-Service: type can set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=NodePort' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "NodePort" ] -} - -@test "server/ha-active-Service: clusterIP empty by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.clusterIP' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/ha-active-Service: clusterIP can set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.clusterIP=None' \ - . | tee /dev/stderr | - yq -r '.spec.clusterIP' | tee /dev/stderr) - [ "${actual}" = "None" ] -} - -@test "server/ha-active-Service: port and targetPort will be 8200 by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].port' | tee /dev/stderr) - [ "${actual}" = "8200" ] - - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) - [ "${actual}" = "8200" ] -} - -@test "server/ha-active-Service: port and targetPort can be set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.port=8000' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].port' | tee /dev/stderr) - [ "${actual}" = "8000" ] - - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.targetPort=80' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) - [ "${actual}" = "80" ] -} - -@test "server/ha-active-Service: nodeport can set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=NodePort' \ - --set 'server.service.nodePort=30009' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) - [ "${actual}" = "30009" ] -} - -@test "server/ha-active-Service: nodeport can't set when type isn't NodePort" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.nodePort=30009' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/ha-active-Service: vault port name is http, when tlsDisable is true" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.tlsDisable=true' \ - . | tee /dev/stderr | - yq -r '.spec.ports | map(select(.port==8200)) | .[] .name' | tee /dev/stderr) - [ "${actual}" = "http" ] -} - -@test "server/ha-active-Service: vault port name is https, when tlsDisable is false" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.tlsDisable=false' \ - . | tee /dev/stderr | - yq -r '.spec.ports | map(select(.port==8200)) | .[] .name' | tee /dev/stderr) - [ "${actual}" = "https" ] -} - -# duplicated in server-service.bats -@test "server/ha-active-Service: NodePort assert externalTrafficPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=NodePort' \ - --set 'server.service.externalTrafficPolicy=Foo' \ - . | tee /dev/stderr | - yq -r '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "Foo" ] -} - -# duplicated in server-service.bats -@test "server/ha-active-Service: NodePort assert no externalTrafficPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=NodePort' \ - --set 'server.service.externalTrafficPolicy=' \ - . | tee /dev/stderr | - yq '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -# duplicated in server-service.bats -@test "server/ha-active-Service: ClusterIP assert no externalTrafficPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-active-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=ClusterIP' \ - --set 'server.service.externalTrafficPolicy=Foo' \ - . | tee /dev/stderr | - yq '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - diff --git a/vault/test/unit/server-ha-disruptionbudget.bats b/vault/test/unit/server-ha-disruptionbudget.bats deleted file mode 100755 index 6cf21f20..00000000 --- a/vault/test/unit/server-ha-disruptionbudget.bats +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/DisruptionBudget: enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-disruptionbudget.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/DisruptionBudget: disable with server.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-disruptionbudget.yaml \ - --set 'globa.enabled=false' \ - --set 'server.ha.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/DisruptionBudget: disable with server.disruptionBudget.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-disruptionbudget.yaml \ - --set 'server.ha.disruptionBudget.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/DisruptionBudget: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-disruptionbudget.yaml \ - --set 'global.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/DisruptionBudget: disable with injector.exernalVaultAddr" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-disruptionbudget.yaml \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/DisruptionBudget: correct maxUnavailable with n=1" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-disruptionbudget.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.ha.replicas=1' \ - . | tee /dev/stderr | - yq '.spec.maxUnavailable' | tee /dev/stderr) - [ "${actual}" = "0" ] -} - -@test "server/DisruptionBudget: correct maxUnavailable with n=3" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-disruptionbudget.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.ha.replicas=3' \ - . | tee /dev/stderr | - yq '.spec.maxUnavailable' | tee /dev/stderr) - [ "${actual}" = "1" ] -} - -@test "server/DisruptionBudget: correct maxUnavailable with n=5" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-disruptionbudget.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.ha.replicas=5' \ - . | tee /dev/stderr | - yq '.spec.maxUnavailable' | tee /dev/stderr) - [ "${actual}" = "2" ] -} - -@test "server/DisruptionBudget: correct maxUnavailable with custom value" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-disruptionbudget.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.ha.replicas=3' \ - --set 'server.ha.disruptionBudget.maxUnavailable=2' \ - . | tee /dev/stderr | - yq '.spec.maxUnavailable' | tee /dev/stderr) - [ "${actual}" = "2" ] -} \ No newline at end of file diff --git a/vault/test/unit/server-ha-standby-service.bats b/vault/test/unit/server-ha-standby-service.bats deleted file mode 100755 index 7dfd5d7f..00000000 --- a/vault/test/unit/server-ha-standby-service.bats +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/ha-standby-Service: generic annotations string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.annotations=vaultIsAwesome: true' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/ha-standby-Service: generic annotations yaml" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.annotations.vaultIsAwesome=true' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/ha-standby-Service: disable with ha.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=false' \ - --set 'server.service.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ha-standby-Service: disable with server.service.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ha-standby-Service: type empty by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/ha-standby-Service: type can set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=NodePort' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "NodePort" ] -} - -@test "server/ha-standby-Service: clusterIP empty by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.clusterIP' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/ha-standby-Service: clusterIP can set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.clusterIP=None' \ - . | tee /dev/stderr | - yq -r '.spec.clusterIP' | tee /dev/stderr) - [ "${actual}" = "None" ] -} - -@test "server/ha-standby-Service: port and targetPort will be 8200 by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].port' | tee /dev/stderr) - [ "${actual}" = "8200" ] - - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) - [ "${actual}" = "8200" ] -} - -@test "server/ha-standby-Service: port and targetPort can be set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.port=8000' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].port' | tee /dev/stderr) - [ "${actual}" = "8000" ] - - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.targetPort=80' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) - [ "${actual}" = "80" ] -} - -@test "server/ha-standby-Service: nodeport can set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=NodePort' \ - --set 'server.service.nodePort=30009' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) - [ "${actual}" = "30009" ] -} - -@test "server/ha-standby-Service: nodeport can't set when type isn't NodePort" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.nodePort=30009' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/ha-standby-Service: vault port name is http, when tlsDisable is true" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.tlsDisable=true' \ - . | tee /dev/stderr | - yq -r '.spec.ports | map(select(.port==8200)) | .[] .name' | tee /dev/stderr) - [ "${actual}" = "http" ] -} - -@test "server/ha-standby-Service: vault port name is https, when tlsDisable is false" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.tlsDisable=false' \ - . | tee /dev/stderr | - yq -r '.spec.ports | map(select(.port==8200)) | .[] .name' | tee /dev/stderr) - [ "${actual}" = "https" ] -} - -# duplicated in server-service.bats -@test "server/ha-standby-Service: NodePort assert externalTrafficPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=NodePort' \ - --set 'server.service.externalTrafficPolicy=Foo' \ - . | tee /dev/stderr | - yq -r '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "Foo" ] -} - -# duplicated in server-service.bats -@test "server/ha-standby-Service: NodePort assert no externalTrafficPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=NodePort' \ - --set 'server.service.externalTrafficPolicy=' \ - . | tee /dev/stderr | - yq '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -# duplicated in server-service.bats -@test "server/ha-standby-Service: ClusterIP assert no externalTrafficPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ha-standby-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=ClusterIP' \ - --set 'server.service.externalTrafficPolicy=Foo' \ - . | tee /dev/stderr | - yq '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - diff --git a/vault/test/unit/server-ha-statefulset.bats b/vault/test/unit/server-ha-statefulset.bats deleted file mode 100755 index cc77e7e3..00000000 --- a/vault/test/unit/server-ha-statefulset.bats +++ /dev/null @@ -1,682 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/ha-StatefulSet: enable with server.ha.enabled true" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/ha-StatefulSet: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.enabled=false' \ - --set 'server.ha.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ha-StatefulSet: disable with injector.externalVaultAddr" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - --set 'server.ha.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ha-StatefulSet: image defaults to server.image.repository:tag" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.image.repository=foo' \ - --set 'server.image.tag=1.2.3' \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) - [ "${actual}" = "foo:1.2.3" ] -} - -@test "server/ha-StatefulSet: image tag defaults to latest" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.image.repository=foo' \ - --set 'server.image.tag=' \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) - [ "${actual}" = "foo:latest" ] -} - -#-------------------------------------------------------------------- -# TLS - -@test "server/ha-StatefulSet: tls disabled" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.tlsDisable=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="VAULT_ADDR")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "http://127.0.0.1:8200" ] -} - -@test "server/ha-StatefulSet: tls enabled" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.tlsDisable=false' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="VAULT_ADDR")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "https://127.0.0.1:8200" ] -} - -#-------------------------------------------------------------------- -# updateStrategy - -@test "server/ha-StatefulSet: OnDelete updateStrategy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.updateStrategy.type' | tee /dev/stderr) - [ "${actual}" = "OnDelete" ] -} - -@test "server/ha-StatefulSet: RollingUpdate updateStrategy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.updateStrategyType="RollingUpdate"' \ - . | tee /dev/stderr | - yq -r '.spec.updateStrategy.type' | tee /dev/stderr) - [ "${actual}" = "RollingUpdate" ] -} - -#-------------------------------------------------------------------- -# affinity - -@test "server/ha-StatefulSet: default affinity" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.affinity' | tee /dev/stderr) - [ "${actual}" != "null" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.affinity=' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.affinity' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -#-------------------------------------------------------------------- -# replicas - -@test "server/ha-StatefulSet: default replicas" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.replicas' | tee /dev/stderr) - [ "${actual}" = "3" ] -} - -@test "server/ha-StatefulSet: custom replicas" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.ha.replicas=10' \ - . | tee /dev/stderr | - yq -r '.spec.replicas' | tee /dev/stderr) - [ "${actual}" = "10" ] -} - -#-------------------------------------------------------------------- -# resources - -@test "server/ha-StatefulSet: default resources" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/ha-StatefulSet: custom resources" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.resources.requests.memory=256Mi' \ - --set 'server.resources.requests.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) - [ "${actual}" = "256Mi" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.resources.limits.memory=256Mi' \ - --set 'server.resources.limits.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) - [ "${actual}" = "256Mi" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.resources.requests.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.requests.cpu' | tee /dev/stderr) - [ "${actual}" = "250m" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.resources.limits.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.limits.cpu' | tee /dev/stderr) - [ "${actual}" = "250m" ] -} - -#-------------------------------------------------------------------- -# extraVolumes - -@test "server/ha-StatefulSet: adds extra volume" { - cd `chart_dir` - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.configMap.name' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - local actual=$(echo $object | - yq -r '.configMap.secretName' | tee /dev/stderr) - [ "${actual}" = "null" ] - - # Test that it mounts it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/vault/userconfig/foo" ] -} - -@test "server/ha-StatefulSet: adds extra volume custom mount path" { - cd `chart_dir` - # Test that it mounts it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - --set 'server.extraVolumes[0].path=/custom/path' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/custom/path/foo" ] -} - -@test "server/ha-StatefulSet: adds extra secret volume custom mount path" { - cd `chart_dir` - - # Test that it mounts it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - --set 'server.extraVolumes[0].path=/custom/path' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/custom/path/foo" ] -} - -@test "server/ha-StatefulSet: adds extra secret volume" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.extraVolumes[0].type=secret' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.secret.name' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(echo $object | - yq -r '.secret.secretName' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - # Test that it mounts it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/vault/userconfig/foo" ] -} - -#-------------------------------------------------------------------- -# extraEnvironmentVars - -@test "server/ha-StatefulSet: set extraEnvironmentVars" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.extraEnvironmentVars.FOO=bar' \ - --set 'server.extraEnvironmentVars.FOOBAR=foobar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="FOO")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "bar" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="FOOBAR")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "foobar" ] -} - -#-------------------------------------------------------------------- -# extraSecretEnvironmentVars - -@test "server/ha-StatefulSet: set extraSecretEnvironmentVars" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.extraSecretEnvironmentVars[0].envName=ENV_FOO_0' \ - --set 'server.extraSecretEnvironmentVars[0].secretName=secret_name_0' \ - --set 'server.extraSecretEnvironmentVars[0].secretKey=secret_key_0' \ - --set 'server.extraSecretEnvironmentVars[1].envName=ENV_FOO_1' \ - --set 'server.extraSecretEnvironmentVars[1].secretName=secret_name_1' \ - --set 'server.extraSecretEnvironmentVars[1].secretKey=secret_key_1' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="ENV_FOO_0")) | .[] .valueFrom.secretKeyRef.name' | tee /dev/stderr) - [ "${value}" = "secret_name_0" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="ENV_FOO_0")) | .[] .valueFrom.secretKeyRef.key' | tee /dev/stderr) - [ "${value}" = "secret_key_0" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="ENV_FOO_1")) | .[] .valueFrom.secretKeyRef.name' | tee /dev/stderr) - [ "${value}" = "secret_name_1" ] - - local value=$(echo $object | - yq -r 'map(select(.name=="ENV_FOO_1")) | .[] .valueFrom.secretKeyRef.key' | tee /dev/stderr) - [ "${value}" = "secret_key_1" ] -} - -#-------------------------------------------------------------------- -# VAULT_API_ADDR renders - -@test "server/ha-StatefulSet: api addr renders to Pod IP by default" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="VAULT_API_ADDR")) | .[] .value' | tee /dev/stderr) - [ "${value}" = 'http://$(POD_IP):8200' ] -} - -@test "server/ha-StatefulSet: api addr is configurable" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.ha.apiAddr="https://example.com:8200"' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="VAULT_API_ADDR")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "https://example.com:8200" ] -} - -#-------------------------------------------------------------------- -# VAULT_CLUSTER_ADDR renders - -@test "server/ha-StatefulSet: cluster addr renders" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.ha.raft.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="VAULT_CLUSTER_ADDR")) | .[] .value' | tee /dev/stderr) - [ "${value}" = 'https://$(HOSTNAME).RELEASE-NAME-vault-internal:8201' ] -} - -#-------------------------------------------------------------------- -# VAULT_RAFT_NODE_ID renders - -@test "server/ha-StatefulSet: raft node ID renders" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.ha.raft.enabled=true' \ - --set 'server.ha.raft.setNodeId=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $object | - yq -r 'map(select(.name=="VAULT_RAFT_NODE_ID")) | .[] .valueFrom.fieldRef.fieldPath' | tee /dev/stderr) - [ "${value}" = "metadata.name" ] -} - -#-------------------------------------------------------------------- -# storage class - -@test "server/ha-StatefulSet: no storage by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "0" ] -} - - -@test "server/ha-StatefulSet: cant set data storage" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.dataStorage.enabled=true' \ - --set 'server.dataStorage.storageClass=foo' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/ha-StatefulSet: can set storageClass" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.dataStorage.enabled=false' \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.auditStorage.storageClass=foo' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates[0].spec.storageClassName' | tee /dev/stderr) - [ "${actual}" = "foo" ] -} - -@test "server/ha-StatefulSet: can disable storage" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.auditStorage.enabled=false' \ - --set 'server.dataStorage.enabled=false' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "0" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.dataStorage.enabled=false' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "1" ] -} - -@test "server/ha-StatefulSet: can mount audit" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.auditStorage.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "audit")' | tee /dev/stderr) -} - -@test "server/ha-StatefulSet: no data storage" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.auditStorage.enabled=false' \ - --set 'server.dataStorage.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "0" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.dataStorage.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "1" ] -} - -@test "server/ha-StatefulSet: tolerations not set by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec | .tolerations? == null' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/ha-StatefulSet: tolerations can be set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.tolerations=foobar' \ - . | tee /dev/stderr | - yq '.spec.template.spec.tolerations == "foobar"' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/ha-StatefulSet: nodeSelector is not set by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.nodeSelector' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/ha-StatefulSet: specified nodeSelector as string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.nodeSelector=testing' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.nodeSelector' | tee /dev/stderr) - [ "${actual}" = "testing" ] -} - -@test "server/ha-StatefulSet: nodeSelector can be set as YAML" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - --set "server.nodeSelector.beta\.kubernetes\.io/arch=amd64" \ - . | tee /dev/stderr | - yq '.spec.template.spec.nodeSelector == {"beta.kubernetes.io/arch": "amd64"}' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -#-------------------------------------------------------------------- -# Security Contexts -@test "server/ha-StatefulSet: uid default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -@test "server/ha-StatefulSet: uid configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.uid=2000' \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) - [ "${actual}" = "2000" ] -} - -@test "server/ha-StatefulSet: gid default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.runAsGroup' | tee /dev/stderr) - [ "${actual}" = "1000" ] -} - -@test "server/ha-StatefulSet: gid configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.gid=2000' \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.runAsGroup' | tee /dev/stderr) - [ "${actual}" = "2000" ] -} - -@test "server/ha-StatefulSet: fsgroup default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.fsGroup' | tee /dev/stderr) - [ "${actual}" = "1000" ] -} - -@test "server/ha-StatefulSet: fsgroup configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.gid=2000' \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.fsGroup' | tee /dev/stderr) - [ "${actual}" = "2000" ] -} - -#-------------------------------------------------------------------- -# OpenShift - -@test "server/ha-statefulset: OpenShift - runAsUser disabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.openshift=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.securityContext.runAsUser | length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ha-statefulset: OpenShift - runAsGroup disabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.openshift=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.securityContext.runAsGroup | length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} diff --git a/vault/test/unit/server-ingress.bats b/vault/test/unit/server-ingress.bats deleted file mode 100755 index 4132c16a..00000000 --- a/vault/test/unit/server-ingress.bats +++ /dev/null @@ -1,267 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/ingress: disabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-ingress.yaml \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ingress: disable by injector.externalVaultAddr" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ingress: checking host entry gets added and path is /" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.ingress.hosts[0].host=test.com' \ - --set 'server.ingress.hosts[0].paths[0]=/' \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].host' | tee /dev/stderr) - [ "${actual}" = 'test.com' ] - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.ingress.hosts[0].host=test.com' \ - --set 'server.ingress.hosts[0].paths[0]=/' \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[0].path' | tee /dev/stderr) - [ "${actual}" = '/' ] -} - -@test "server/ingress: vault backend should be added when I specify a path" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.ingress.hosts[0].host=test.com' \ - --set 'server.ingress.hosts[0].paths[0]=/' \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[0].backend.service.name | length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - -} - -@test "server/ingress: extra paths prepend host configuration" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.ingress.hosts[0].host=test.com' \ - --set 'server.ingress.hosts[0].paths[0]=/' \ - --set 'server.ingress.extraPaths[0].path=/annotation-service' \ - --set 'server.ingress.extraPaths[0].backend.service.name=ssl-redirect' \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[0].backend.service.name' | tee /dev/stderr) - [ "${actual}" = 'ssl-redirect' ] - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.ingress.hosts[0].host=test.com' \ - --set 'server.ingress.hosts[0].paths[0]=/' \ - --set 'server.ingress.extraPaths[0].path=/annotation-service' \ - --set 'server.ingress.extraPaths[0].backend.service.name=ssl-redirect' \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[0].path' | tee /dev/stderr) - [ "${actual}" = '/annotation-service' ] - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.ingress.hosts[0].host=test.com' \ - --set 'server.ingress.hosts[0].paths[0]=/' \ - --set 'server.ingress.extraPaths[0].path=/annotation-service' \ - --set 'server.ingress.extraPaths[0].backend.service.name=ssl-redirect' \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[1].path' | tee /dev/stderr) - [ "${actual}" = '/' ] -} - -@test "server/ingress: labels gets added to object" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.ingress.labels.traffic=external' \ - --set 'server.ingress.labels.team=dev' \ - . | tee /dev/stderr | - yq -r '.metadata.labels.traffic' | tee /dev/stderr) - [ "${actual}" = "external" ] -} - -@test "server/ingress: annotations added to object - string" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.ingress.annotations=kubernetes.io/ingress.class: nginx' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["kubernetes.io/ingress.class"]' | tee /dev/stderr) - [ "${actual}" = "nginx" ] -} - -@test "server/ingress: annotations added to object - yaml" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set server.ingress.annotations."kubernetes\.io/ingress\.class"=nginx \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["kubernetes.io/ingress.class"]' | tee /dev/stderr) - [ "${actual}" = "nginx" ] -} - -@test "server/ingress: ingressClassName added to object spec - string" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set server.ingress.ingressClassName=nginx \ - . | tee /dev/stderr | - yq -r '.spec.ingressClassName' | tee /dev/stderr) - [ "${actual}" = "nginx" ] -} - -@test "server/ingress: ingressClassName is not added by default" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.ingressClassName' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/ingress: uses active service when ha by default - yaml" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.dev.enabled=false' \ - --set 'server.ha.enabled=true' \ - --set 'server.service.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[0].backend.service.name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault-active" ] -} - -@test "server/ingress: uses regular service when configured with ha - yaml" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.ingress.activeService=false' \ - --set 'server.dev.enabled=false' \ - --set 'server.ha.enabled=true' \ - --set 'server.service.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[0].backend.service.name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault" ] -} - -@test "server/ingress: uses regular service when not ha - yaml" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.dev.enabled=false' \ - --set 'server.ha.enabled=false' \ - --set 'server.service.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[0].backend.service.name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault" ] -} - -@test "server/ingress: k8s 1.18.3 uses regular service when not ha - yaml" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.dev.enabled=false' \ - --set 'server.ha.enabled=false' \ - --set 'server.service.enabled=true' \ - --kube-version 1.18.3 \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[0].backend.serviceName' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault" ] -} - -@test "server/ingress: uses regular service when not ha and activeService is true - yaml" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set 'server.ingress.activeService=true' \ - --set 'server.dev.enabled=false' \ - --set 'server.ha.enabled=false' \ - --set 'server.service.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[0].backend.service.name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault" ] -} - -@test "server/ingress: pathType is added to Kubernetes version == 1.19.0" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set server.ingress.pathType=ImplementationSpecific \ - --kube-version 1.19.0 \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[0].pathType' | tee /dev/stderr) - [ "${actual}" = "ImplementationSpecific" ] -} - -@test "server/ingress: pathType is not added to Kubernetes versions < 1.19" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set server.ingress.pathType=ImplementationSpecific \ - --kube-version 1.18.3 \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[0].pathType' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/ingress: pathType is added to Kubernetes versions > 1.19" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-ingress.yaml \ - --set 'server.ingress.enabled=true' \ - --set server.ingress.pathType=Prefix \ - --kube-version 1.20.0 \ - . | tee /dev/stderr | - yq -r '.spec.rules[0].http.paths[0].pathType' | tee /dev/stderr) - [ "${actual}" = "Prefix" ] -} diff --git a/vault/test/unit/server-network-policy.bats b/vault/test/unit/server-network-policy.bats deleted file mode 100755 index 1364321d..00000000 --- a/vault/test/unit/server-network-policy.bats +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/network-policy: disabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-network-policy.yaml \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/network-policy: enabled by server.networkPolicy.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --set 'server.networkPolicy.enabled=true' \ - --show-only templates/server-network-policy.yaml \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/network-policy: egress enabled by server.networkPolicy.egress" { - cd `chart_dir` - local actual=$(helm template \ - --set 'server.networkPolicy.enabled=true' \ - --set 'server.networkPolicy.egress[0].to[0].ipBlock.cidr=10.0.0.0/24' \ - --set 'server.networkPolicy.egress[0].ports[0].protocol=TCP' \ - --set 'server.networkPolicy.egress[0].ports[0].port=443' \ - --show-only templates/server-network-policy.yaml \ - . | tee /dev/stderr | - yq -r '.spec.egress[0].to[0].ipBlock.cidr' | tee /dev/stderr) - [ "${actual}" = "10.0.0.0/24" ] -} diff --git a/vault/test/unit/server-psp-role.bats b/vault/test/unit/server-psp-role.bats deleted file mode 100644 index 1d3e62c4..00000000 --- a/vault/test/unit/server-psp-role.bats +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/PSP-Role: PSP-Role not enabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-psp-role.yaml \ - --set 'server.dev.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp-role.yaml \ - --set 'server.ha.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp-role.yaml \ - --set 'server.standalone.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/PSP-Role: PSP-Role can be enabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-psp-role.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-psp-role.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-psp-role.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/PSP-Role: disable with global.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-psp-role.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.enabled=false' \ - --set 'global.psp.enable=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp-role.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.enabled=false' \ - --set 'global.psp.enable=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp-role.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.enabled=false' \ - --set 'global.psp.enable=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/PSP-Role: disable with global.psp.enable false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-psp-role.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.psp.enable=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp-role.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.psp.enable=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp-role.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.psp.enable=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} diff --git a/vault/test/unit/server-psp-rolebinding.bats b/vault/test/unit/server-psp-rolebinding.bats deleted file mode 100644 index 4171219f..00000000 --- a/vault/test/unit/server-psp-rolebinding.bats +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/PSP-RoleBinding: PSP-RoleBinding not enabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-psp-rolebinding.yaml \ - --set 'server.dev.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp-rolebinding.yaml \ - --set 'server.ha.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp-rolebinding.yaml \ - --set 'server.standalone.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/PSP-RoleBinding: PSP-RoleBinding can be enabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-psp-rolebinding.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-psp-rolebinding.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-psp-rolebinding.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/PSP-RoleBinding: disable with global.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-psp-rolebinding.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.enabled=false' \ - --set 'global.psp.enable=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp-rolebinding.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.enabled=false' \ - --set 'global.psp.enable=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp-rolebinding.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.enabled=false' \ - --set 'global.psp.enable=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/PSP-RoleBinding: disable with global.psp.enable false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-psp-rolebinding.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.psp.enable=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp-rolebinding.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.psp.enable=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp-rolebinding.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.psp.enable=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} diff --git a/vault/test/unit/server-psp.bats b/vault/test/unit/server-psp.bats deleted file mode 100644 index 400e76d1..00000000 --- a/vault/test/unit/server-psp.bats +++ /dev/null @@ -1,285 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/PodSecurityPolicy: PodSecurityPolicy not enabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.dev.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.ha.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.standalone.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/PodSecurityPolicy: PodSecurityPolicy can be enabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/PodSecurityPolicy: PodSecurityPolicy annotations are templated correctly" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq '.metadata.annotations | length == 4' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq '.metadata.annotations | length == 4' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq '.metadata.annotations | length == 4' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/PodSecurityPolicy: annotations are added - string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'global.psp.annotations=vault-is: amazing' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) - [ "${actual}" = "amazing" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'global.psp.annotations=vault-is: amazing' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) - [ "${actual}" = "amazing" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'global.psp.annotations=vault-is: amazing' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) - [ "${actual}" = "amazing" ] -} - -@test "server/PodSecurityPolicy: annotations are added - object" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'global.psp.annotations.vault-is=amazing' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) - [ "${actual}" = "amazing" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'global.psp.annotations.vault-is=amazing' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) - [ "${actual}" = "amazing" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'global.psp.annotations.vault-is=amazing' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) - [ "${actual}" = "amazing" ] -} - -@test "server/PodSecurityPolicy: disable with global.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.enabled=false' \ - --set 'global.psp.enable=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.enabled=false' \ - --set 'global.psp.enable=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.enabled=false' \ - --set 'global.psp.enable=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/PodSecurityPolicy: disable with global.psp.enable false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.psp.enable=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.psp.enable=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.psp.enable=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/PodSecurityPolicy: PodSecurityPolicy allows PVC by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.psp.enable=true' \ - . | tee /dev/stderr | - yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/PodSecurityPolicy: PodSecurityPolicy allows PVC with dataStorage" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'server.dataStorage.enabled=true' \ - . | tee /dev/stderr | - yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'server.dataStorage.enabled=true' \ - . | tee /dev/stderr | - yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'server.dataStorage.enabled=true' \ - . | tee /dev/stderr | - yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/PodSecurityPolicy: PodSecurityPolicy does not allow PVC without dataStorage" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'server.dataStorage.enabled=false' \ - . | tee /dev/stderr | - yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'server.dataStorage.enabled=false' \ - . | tee /dev/stderr | - yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$(helm template \ - --show-only templates/server-psp.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.psp.enable=true' \ - --set 'server.dataStorage.enabled=false' \ - . | tee /dev/stderr | - yq '.spec.volumes | contains(["persistentVolumeClaim"])' | tee /dev/stderr) - [ "${actual}" = "false" ] -} diff --git a/vault/test/unit/server-route.bats b/vault/test/unit/server-route.bats deleted file mode 100755 index d141fb63..00000000 --- a/vault/test/unit/server-route.bats +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/route: OpenShift - disabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --set 'global.openshift=true' \ - --show-only templates/server-route.yaml \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/route: OpenShift -disable by injector.externalVaultAddr" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-route.yaml \ - --set 'global.openshift=true' \ - --set 'server.route.enabled=true' \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/route: OpenShift - checking host entry gets added and path is /" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-route.yaml \ - --set 'global.openshift=true' \ - --set 'server.route.enabled=true' \ - --set 'server.route.host=test.com' \ - . | tee /dev/stderr | - yq -r '.spec.host' | tee /dev/stderr) - [ "${actual}" = 'test.com' ] -} - -@test "server/route: OpenShift - vault backend should be added when I specify a path" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-route.yaml \ - --set 'global.openshift=true' \ - --set 'server.route.enabled=true' \ - --set 'server.route.host=test.com' \ - . | tee /dev/stderr | - yq -r '.spec.to.name | length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - -} - -@test "server/route: OpenShift - labels gets added to object" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-route.yaml \ - --set 'global.openshift=true' \ - --set 'server.route.enabled=true' \ - --set 'server.route.labels.traffic=external' \ - --set 'server.route.labels.team=dev' \ - . | tee /dev/stderr | - yq -r '.metadata.labels.traffic' | tee /dev/stderr) - [ "${actual}" = "external" ] -} - -@test "server/route: OpenShift - annotations added to object - string" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-route.yaml \ - --set 'global.openshift=true' \ - --set 'server.route.enabled=true' \ - --set 'server.route.annotations=kubernetes.io/route.class: haproxy' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["kubernetes.io/route.class"]' | tee /dev/stderr) - [ "${actual}" = "haproxy" ] -} - -@test "server/route: OpenShift - annotations added to object - yaml" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-route.yaml \ - --set 'global.openshift=true' \ - --set 'server.route.enabled=true' \ - --set server.route.annotations."kubernetes\.io/route\.class"=haproxy \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["kubernetes.io/route.class"]' | tee /dev/stderr) - [ "${actual}" = "haproxy" ] -} - -@test "server/route: OpenShift - route points to main service by default" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-route.yaml \ - --set 'global.openshift=true' \ - --set 'server.route.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.to.name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault" ] -} - -@test "server/route: OpenShift - route points to main service when not ha and activeService is true" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-route.yaml \ - --set 'global.openshift=true' \ - --set 'server.route.enabled=true' \ - --set 'server.route.activeService=true' \ - . | tee /dev/stderr | - yq -r '.spec.to.name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault" ] -} - -@test "server/route: OpenShift - route points to active service by when HA by default" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-route.yaml \ - --set 'global.openshift=true' \ - --set 'server.route.enabled=true' \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.to.name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault-active" ] -} - -@test "server/route: OpenShift - route points to general service by when HA when configured" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-route.yaml \ - --set 'global.openshift=true' \ - --set 'server.route.enabled=true' \ - --set 'server.route.activeService=false' \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.to.name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault" ] -} diff --git a/vault/test/unit/server-service.bats b/vault/test/unit/server-service.bats deleted file mode 100755 index 4695f2ff..00000000 --- a/vault/test/unit/server-service.bats +++ /dev/null @@ -1,426 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/Service: service enabled by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.standalone.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/Service: disable with global.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.enabled=false' \ - --set 'server.service.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.enabled=false' \ - --set 'server.service.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.enabled=false' \ - --set 'server.service.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/Service: disable with server.service.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.service.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.service.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/Service: disable with global.enabled false server.service.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.enabled=false' \ - --set 'server.service.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.enabled=false' \ - --set 'server.service.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.enabled=false' \ - --set 'server.service.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/Service: disable with injector.externalVaultAddr" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - --set 'server.service.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - --set 'server.service.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - --set 'server.service.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/Service: generic annotations" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.service.annotations=vaultIsAwesome: true' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/Service: publish not ready" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.publishNotReadyAddresses' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.publishNotReadyAddresses' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.standalone.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.publishNotReadyAddresses' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/Service: type empty by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/Service: type can set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.service.type=NodePort' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "NodePort" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=NodePort' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "NodePort" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.service.type=NodePort' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "NodePort" ] -} - -@test "server/Service: clusterIP empty by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.clusterIP' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.clusterIP' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - . | tee /dev/stderr | - yq -r '.spec.clusterIP' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/Service: clusterIP can set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.service.clusterIP=None' \ - . | tee /dev/stderr | - yq -r '.spec.clusterIP' | tee /dev/stderr) - [ "${actual}" = "None" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.clusterIP=None' \ - . | tee /dev/stderr | - yq -r '.spec.clusterIP' | tee /dev/stderr) - [ "${actual}" = "None" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.service.clusterIP=None' \ - . | tee /dev/stderr | - yq -r '.spec.clusterIP' | tee /dev/stderr) - [ "${actual}" = "None" ] -} - -@test "server/Service: port and targetPort will be 8200 by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].port' | tee /dev/stderr) - [ "${actual}" = "8200" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) - [ "${actual}" = "8200" ] -} - -@test "server/Service: port and targetPort can be set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.service.port=8000' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].port' | tee /dev/stderr) - [ "${actual}" = "8000" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.service.targetPort=80' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) - [ "${actual}" = "80" ] -} - -@test "server/Service: nodeport can set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.service.type=NodePort' \ - --set 'server.service.nodePort=30008' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) - [ "${actual}" = "30008" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=NodePort' \ - --set 'server.service.nodePort=30009' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) - [ "${actual}" = "30009" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.service.type=NodePort' \ - --set 'server.service.nodePort=30010' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) - [ "${actual}" = "30010" ] -} - -@test "server/Service: nodeport can't set when type isn't NodePort" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.service.nodePort=30008' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.nodePort=30009' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.service.nodePort=30010' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/Service: vault port name is http, when tlsDisable is true" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'global.tlsDisable=true' \ - . | tee /dev/stderr | - yq -r '.spec.ports | map(select(.port==8200)) | .[] .name' | tee /dev/stderr) - [ "${actual}" = "http" ] -} - -@test "server/Service: vault port name is https, when tlsDisable is false" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'global.tlsDisable=false' \ - . | tee /dev/stderr | - yq -r '.spec.ports | map(select(.port==8200)) | .[] .name' | tee /dev/stderr) - [ "${actual}" = "https" ] -} - -# duplicated in server-ha-active-service.bats -@test "server/Service: NodePort assert externalTrafficPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=NodePort' \ - --set 'server.service.externalTrafficPolicy=Foo' \ - . | tee /dev/stderr | - yq -r '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "Foo" ] -} - -# duplicated in server-ha-active-service.bats -@test "server/ha-active-Service: NodePort assert no externalTrafficPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=NodePort' \ - --set 'server.service.externalTrafficPolicy=' \ - . | tee /dev/stderr | - yq '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -# duplicated in server-ha-active-service.bats -@test "server/Service: ClusterIP assert no externalTrafficPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.service.type=ClusterIP' \ - --set 'server.service.externalTrafficPolicy=Foo' \ - . | tee /dev/stderr | - yq '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - diff --git a/vault/test/unit/server-serviceaccount.bats b/vault/test/unit/server-serviceaccount.bats deleted file mode 100755 index 29e18b56..00000000 --- a/vault/test/unit/server-serviceaccount.bats +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "server/ServiceAccount: specify service account name" { - cd `chart_dir` - - local actual=$( (helm template \ - --show-only templates/server-serviceaccount.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.serviceAccount.create=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$(helm template \ - --show-only templates/server-serviceaccount.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.serviceAccount.name=user-defined-ksa' \ - . | tee /dev/stderr | - yq -r '.metadata.name' | tee /dev/stderr) - [ "${actual}" = "user-defined-ksa" ] - - local actual=$(helm template \ - --show-only templates/server-serviceaccount.yaml \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.metadata.name' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault" ] - -} - -@test "server/ServiceAccount: specify annotations" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-serviceaccount.yaml \ - --set 'server.dev.enabled=true' \ - --set 'server.serviceAccount.annotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(helm template \ - --show-only templates/server-serviceaccount.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.serviceAccount.annotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) - [ "${actual}" = "bar" ] - - local actual=$(helm template \ - --show-only templates/server-serviceaccount.yaml \ - --set 'server.ha.enabled=true' \ - --set 'server.serviceAccount.annotations.foo=bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) - [ "${actual}" = "bar" ] - - local actual=$(helm template \ - --show-only templates/server-serviceaccount.yaml \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/ServiceAccount: disable with global.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'global.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'global.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'global.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/ServiceAccount: disable by injector.externalVaultAddr" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/server-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} diff --git a/vault/test/unit/server-statefulset.bats b/vault/test/unit/server-statefulset.bats deleted file mode 100755 index b9390518..00000000 --- a/vault/test/unit/server-statefulset.bats +++ /dev/null @@ -1,1681 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -#-------------------------------------------------------------------- -# disable / enable server deployment - -@test "server/StatefulSet: disabled server.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/StatefulSet: disabled server.enabled random string" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.enabled=blabla' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/StatefulSet: enabled server.enabled explicit true" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -#-------------------------------------------------------------------- - -@test "server/standalone-StatefulSet: default server.standalone.enabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/standalone-StatefulSet: enable with server.standalone.enabled true" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - . | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/standalone-StatefulSet: disable with global.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.enabled=false' \ - --set 'server.standalone.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/standalone-StatefulSet: disable with injector.externalVaultAddr" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - --set 'server.standalone.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/standalone-StatefulSet: image defaults to server.image.repository:tag" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.image.repository=foo' \ - --set 'server.image.tag=1.2.3' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) - [ "${actual}" = "foo:1.2.3" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.image.repository=foo' \ - --set 'server.image.tag=1.2.3' \ - --set 'server.standalone.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) - [ "${actual}" = "foo:1.2.3" ] -} - -@test "server/standalone-StatefulSet: image tag defaults to latest" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.image.repository=foo' \ - --set 'server.image.tag=' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) - [ "${actual}" = "foo:latest" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.image.repository=foo' \ - --set 'server.image.tag=' \ - --set 'server.standalone.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].image' | tee /dev/stderr) - [ "${actual}" = "foo:latest" ] -} - -@test "server/standalone-StatefulSet: default imagePullPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].imagePullPolicy' | tee /dev/stderr) - [ "${actual}" = "IfNotPresent" ] -} - -@test "server/standalone-StatefulSet: Custom imagePullPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.image.pullPolicy=Always' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].imagePullPolicy' | tee /dev/stderr) - [ "${actual}" = "Always" ] -} - -@test "server/standalone-StatefulSet: Custom imagePullSecrets" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.imagePullSecrets[0].name=foo' \ - --set 'global.imagePullSecrets[1].name=bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.imagePullSecrets' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '. | length' | tee /dev/stderr) - [ "${actual}" = "2" ] - - local actual=$(echo $object | - yq -r '.[0].name' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - local actual=$(echo $object | - yq -r '.[1].name' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -@test "server/standalone-StatefulSet: Custom imagePullSecrets - string array" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.imagePullSecrets[0]=foo' \ - --set 'global.imagePullSecrets[1]=bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.imagePullSecrets' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '. | length' | tee /dev/stderr) - [ "${actual}" = "2" ] - - local actual=$(echo $object | - yq -r '.[0].name' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - local actual=$(echo $object | - yq -r '.[1].name' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -@test "server/standalone-StatefulSet: default imagePullSecrets" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.imagePullSecrets' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -#-------------------------------------------------------------------- -# updateStrategy - -@test "server/standalone-StatefulSet: OnDelete updateStrategy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.updateStrategy.type' | tee /dev/stderr) - [ "${actual}" = "OnDelete" ] -} - -#-------------------------------------------------------------------- -# replicas - -@test "server/standalone-StatefulSet: default replicas" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.replicas' | tee /dev/stderr) - [ "${actual}" = "1" ] -} - -@test "server/standalone-StatefulSet: custom replicas" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.replicas=100' \ - . | tee /dev/stderr | - yq -r '.spec.replicas' | tee /dev/stderr) - [ "${actual}" = "1" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.standalone.replicas=100' \ - . | tee /dev/stderr | - yq -r '.spec.replicas' | tee /dev/stderr) - [ "${actual}" = "1" ] -} - -#-------------------------------------------------------------------- -# resources - -@test "server/standalone-StatefulSet: default resources" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/standalone-StatefulSet: custom resources" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.resources.requests.memory=256Mi' \ - --set 'server.resources.requests.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) - [ "${actual}" = "256Mi" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.resources.limits.memory=256Mi' \ - --set 'server.resources.limits.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) - [ "${actual}" = "256Mi" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.resources.requests.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.requests.cpu' | tee /dev/stderr) - [ "${actual}" = "250m" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.resources.limits.cpu=250m' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].resources.limits.cpu' | tee /dev/stderr) - [ "${actual}" = "250m" ] -} - -#-------------------------------------------------------------------- -# extraVolumes - -@test "server/standalone-StatefulSet: server.extraVolumes adds extra volume" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.configMap.name' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - local actual=$(echo $object | - yq -r '.configMap.secretName' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.configMap.name' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - local actual=$(echo $object | - yq -r '.configMap.secretName' | tee /dev/stderr) - [ "${actual}" = "null" ] - - # Test that it mounts it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/vault/userconfig/foo" ] - - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/vault/userconfig/foo" ] -} - -@test "server/standalone-StatefulSet: server.extraVolumes adds extra secret volume" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.extraVolumes[0].type=secret' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.secret.name' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(echo $object | - yq -r '.secret.secretName' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.extraVolumes[0].type=secret' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.secret.name' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(echo $object | - yq -r '.secret.secretName' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - # Test that it mounts it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/vault/userconfig/foo" ] - - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.extraVolumes[0].type=configMap' \ - --set 'server.extraVolumes[0].name=foo' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "userconfig-foo")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/vault/userconfig/foo" ] -} - -@test "server/standalone-StatefulSet: can mount audit" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.auditStorage.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "audit")' | tee /dev/stderr) -} - -#-------------------------------------------------------------------- -# volumes - -@test "server/standalone-StatefulSet: server.volumes adds volume" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.volumes[0].name=plugins' \ - --set 'server.volumes[0].emptyDir=\{\}' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.volumes[] | select(.name == "plugins")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.emptyDir' | tee /dev/stderr) - [ "${actual}" = "{}" ] -} - -#-------------------------------------------------------------------- -# volumeMounts - -@test "server/standalone-StatefulSet: server.volumeMounts adds volumeMount" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.volumeMounts[0].name=plugins' \ - --set 'server.volumeMounts[0].mountPath=/usr/local/libexec/vault' \ - --set 'server.volumeMounts[0].readOnly=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "plugins")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/usr/local/libexec/vault" ] - - local actual=$(echo $object | - yq -r '.readOnly' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -#-------------------------------------------------------------------- -# log level - -@test "server/standalone-StatefulSet: default log level to empty" { - cd `chart_dir` - local objects=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $objects | - yq -r 'map(select(.name=="VAULT_LOG_LEVEL")) | .[] .name' | tee /dev/stderr) - [ "${value}" = "" ] -} - -@test "server/standalone-StatefulSet: log level can be changed" { - cd `chart_dir` - local objects=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set='server.logLevel=debug' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $objects | - yq -r 'map(select(.name=="VAULT_LOG_LEVEL")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "debug" ] -} - -#-------------------------------------------------------------------- -# log format - -@test "server/standalone-StatefulSet: default log format to empty" { - cd `chart_dir` - local objects=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $objects | - yq -r 'map(select(.name=="VAULT_LOG_FORMAT")) | .[] .name' | tee /dev/stderr) - [ "${value}" = "" ] -} - -@test "server/standalone-StatefulSet: can set log format" { - cd `chart_dir` - local objects=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set='server.logFormat=json' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local value=$(echo $objects | - yq -r 'map(select(.name=="VAULT_LOG_FORMAT")) | .[] .value' | tee /dev/stderr) - [ "${value}" = "json" ] -} - -#-------------------------------------------------------------------- -# extraEnvironmentVars - -@test "server/standalone-StatefulSet: set extraEnvironmentVars" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.stanadlone.enabled=true' \ - --set 'server.extraEnvironmentVars.FOO=bar' \ - --set 'server.extraEnvironmentVars.FOOBAR=foobar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local name=$(echo $object | - yq -r 'map(select(.name=="FOO")) | .[] .value' | tee /dev/stderr) - [ "${name}" = "bar" ] - - local name=$(echo $object | - yq -r 'map(select(.name=="FOOBAR")) | .[] .value' | tee /dev/stderr) - [ "${name}" = "foobar" ] - - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.extraEnvironmentVars.FOO=bar' \ - --set 'server.extraEnvironmentVars.FOOBAR=foobar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) - - local name=$(echo $object | - yq -r 'map(select(.name=="FOO")) | .[] .value' | tee /dev/stderr) - [ "${name}" = "bar" ] - - local name=$(echo $object | - yq -r 'map(select(.name=="FOOBAR")) | .[] .value' | tee /dev/stderr) - [ "${name}" = "foobar" ] -} - -#-------------------------------------------------------------------- -# storage class - -@test "server/standalone-StatefulSet: storageClass on claim by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates[0].spec.storageClassName' | tee /dev/stderr) - [ "${actual}" = "null" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates[0].spec.storageClassName' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - - -@test "server/standalone-StatefulSet: can set storageClass" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dataStorage.enabled=true' \ - --set 'server.dataStorage.storageClass=foo' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates[0].spec.storageClassName' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.dataStorage.enabled=false' \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.auditStorage.storageClass=foo' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates[0].spec.storageClassName' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.auditStorage.storageClass=foo' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates[1].spec.storageClassName' | tee /dev/stderr) - [ "${actual}" = "foo" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.dataStorage.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "2" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.dataStorage.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "2" ] -} - -@test "server/standalone-StatefulSet: can disable storage" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.auditStorage.enabled=false' \ - --set 'server.dataStorage.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "1" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.dataStorage.enabled=false' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "1" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.auditStorage.enabled=false' \ - --set 'server.dataStorage.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "1" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.dataStorage.enabled=false' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "1" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.dataStorage.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "2" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.dataStorage.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "2" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.auditStorage.enabled=fa;se' \ - --set 'server.dataStorage.enabled=false' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "0" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'server.auditStorage.enabled=false' \ - --set 'server.dataStorage.enabled=false' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates | length' | tee /dev/stderr) - [ "${actual}" = "0" ] -} - -@test "server/standalone-StatefulSet: default audit storage mount path" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.auditStorage.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "audit")' | tee /dev/stderr) - - local actual=$(echo $object | yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/vault/audit" ] -} - -@test "server/standalone-StatefulSet: can configure audit storage mount path" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.auditStorage.mountPath=/var/log/vault' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "audit")' | tee /dev/stderr) - - local actual=$(echo $object | yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/var/log/vault" ] -} - -@test "server/standalone-StatefulSet: default data storage mount path" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dataStorage.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "data")' | tee /dev/stderr) - - local actual=$(echo $object | yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/vault/data" ] -} - -@test "server/standalone-StatefulSet: can configure data storage mount path" { - cd `chart_dir` - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dataStorage.enabled=true' \ - --set 'server.dataStorage.mountPath=/opt/vault' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "data")' | tee /dev/stderr) - - local actual=$(echo $object | yq -r '.mountPath' | tee /dev/stderr) - [ "${actual}" = "/opt/vault" ] -} - -@test "server/standalone-StatefulSet: affinity is set by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq '.spec.template.spec.affinity["podAntiAffinity"]? != null' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/standalone-StatefulSet: affinity can be set as string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.affinity=foobar' \ - . | tee /dev/stderr | - yq '.spec.template.spec.affinity == "foobar"' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/standalone-StatefulSet: affinity can be set as YAML" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.affinity.podAntiAffinity=foobar' \ - . | tee /dev/stderr | - yq '.spec.template.spec.affinity.podAntiAffinity == "foobar"' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - - -@test "server/standalone-StatefulSet: tolerations not set by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq '.spec.template.spec | .tolerations? == null' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/standalone-StatefulSet: tolerations can be set as string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.tolerations=foobar' \ - . | tee /dev/stderr | - yq '.spec.template.spec.tolerations == "foobar"' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/standalone-StatefulSet: tolerations can be set as YAML" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set "server.tolerations[0].foo=bar,server.tolerations[1].baz=qux" \ - . | tee /dev/stderr | - yq '.spec.template.spec.tolerations == [{"foo": "bar"}, {"baz": "qux"}]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/standalone-StatefulSet: nodeSelector is not set by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq '.spec.template.spec.nodeSelector' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/standalone-StatefulSet: specified nodeSelector as string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.nodeSelector=testing' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.nodeSelector' | tee /dev/stderr) - [ "${actual}" = "testing" ] -} - -@test "server/standalone-StatefulSet: nodeSelector can be set as YAML" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set "server.nodeSelector.beta\.kubernetes\.io/arch=amd64" \ - . | tee /dev/stderr | - yq '.spec.template.spec.nodeSelector == {"beta.kubernetes.io/arch": "amd64"}' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -#-------------------------------------------------------------------- -# extraInitContainers - -@test "server/standalone-StatefulSet: adds extra init containers" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.extraInitContainers[0].image=test-image' \ - --set 'server.extraInitContainers[0].name=test-container' \ - --set 'server.extraInitContainers[0].ports[0].name=test-port' \ - --set 'server.extraInitContainers[0].ports[0].containerPort=9410' \ - --set 'server.extraInitContainers[0].ports[0].protocol=TCP' \ - --set 'server.extraInitContainers[0].env[0].name=TEST_ENV' \ - --set 'server.extraInitContainers[0].env[0].value=test_env_value' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.initContainers[] | select(.name == "test-container")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.name' | tee /dev/stderr) - [ "${actual}" = "test-container" ] - - local actual=$(echo $object | - yq -r '.image' | tee /dev/stderr) - [ "${actual}" = "test-image" ] - - local actual=$(echo $object | - yq -r '.ports[0].name' | tee /dev/stderr) - [ "${actual}" = "test-port" ] - - local actual=$(echo $object | - yq -r '.ports[0].containerPort' | tee /dev/stderr) - [ "${actual}" = "9410" ] - - local actual=$(echo $object | - yq -r '.ports[0].protocol' | tee /dev/stderr) - [ "${actual}" = "TCP" ] - - local actual=$(echo $object | - yq -r '.env[0].name' | tee /dev/stderr) - [ "${actual}" = "TEST_ENV" ] - - local actual=$(echo $object | - yq -r '.env[0].value' | tee /dev/stderr) - [ "${actual}" = "test_env_value" ] - -} - -@test "server/standalone-StatefulSet: add two extra init containers" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.extraInitContainers[0].image=test-image' \ - --set 'server.extraInitContainers[0].name=test-container' \ - --set 'server.extraInitContainers[1].image=test-image' \ - --set 'server.extraInitContainers[1].name=test-container-2' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.initContainers' | tee /dev/stderr) - - local containers_count=$(echo $object | - yq -r 'length' | tee /dev/stderr) - [ "${containers_count}" = 2 ] - -} - -#-------------------------------------------------------------------- -# extraContainers - -@test "server/standalone-StatefulSet: adds extra containers" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.extraContainers[0].image=test-image' \ - --set 'server.extraContainers[0].name=test-container' \ - --set 'server.extraContainers[0].ports[0].name=test-port' \ - --set 'server.extraContainers[0].ports[0].containerPort=9410' \ - --set 'server.extraContainers[0].ports[0].protocol=TCP' \ - --set 'server.extraContainers[0].env[0].name=TEST_ENV' \ - --set 'server.extraContainers[0].env[0].value=test_env_value' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[] | select(.name == "test-container")' | tee /dev/stderr) - - local actual=$(echo $object | - yq -r '.name' | tee /dev/stderr) - [ "${actual}" = "test-container" ] - - local actual=$(echo $object | - yq -r '.image' | tee /dev/stderr) - [ "${actual}" = "test-image" ] - - local actual=$(echo $object | - yq -r '.ports[0].name' | tee /dev/stderr) - [ "${actual}" = "test-port" ] - - local actual=$(echo $object | - yq -r '.ports[0].containerPort' | tee /dev/stderr) - [ "${actual}" = "9410" ] - - local actual=$(echo $object | - yq -r '.ports[0].protocol' | tee /dev/stderr) - [ "${actual}" = "TCP" ] - - local actual=$(echo $object | - yq -r '.env[0].name' | tee /dev/stderr) - [ "${actual}" = "TEST_ENV" ] - - local actual=$(echo $object | - yq -r '.env[0].value' | tee /dev/stderr) - [ "${actual}" = "test_env_value" ] - -} - -@test "server/standalone-StatefulSet: add two extra containers" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.extraContainers[0].image=test-image' \ - --set 'server.extraContainers[0].name=test-container' \ - --set 'server.extraContainers[1].image=test-image' \ - --set 'server.extraContainers[1].name=test-container-2' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers' | tee /dev/stderr) - - local containers_count=$(echo $object | - yq -r 'length' | tee /dev/stderr) - [ "${containers_count}" = 3 ] - -} - -@test "server/standalone-StatefulSet: no extra containers added" { - cd `chart_dir` - - # Test that it defines it - local object=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers' | tee /dev/stderr) - - local containers_count=$(echo $object | - yq -r 'length' | tee /dev/stderr) - [ "${containers_count}" = 1 ] -} - -# sharedProcessNamespace - -@test "server/standalone-StatefulSet: shareProcessNamespace disabled by default" { - cd `chart_dir` - - # Test that it defines it - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.shareProcessNamespace' | tee /dev/stderr) - - [ "${actual}" = "null" ] -} - -@test "server/standalone-StatefulSet: shareProcessNamespace enabled" { - cd `chart_dir` - - # Test that it defines it - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.shareProcessNamespace=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.shareProcessNamespace' | tee /dev/stderr) - - [ "${actual}" = "true" ] -} - -# extra labels - -@test "server/standalone-StatefulSet: specify extraLabels" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.extraLabels.foo=bar' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -# extra annotations - -@test "server/standalone-StatefulSet: default statefulSet.annotations" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.metadata.annotations' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/standalone-StatefulSet: specify statefulSet.annotations yaml" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.statefulSet.annotations.foo=bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -@test "server/standalone-StatefulSet: specify statefulSet.annotations yaml string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.statefulSet.annotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations.foo' | tee /dev/stderr) - [ "${actual}" = "bar" ] -} - -#-------------------------------------------------------------------- -# Security Contexts -@test "server/standalone-StatefulSet: uid default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -@test "server/standalone-StatefulSet: uid configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.uid=2000' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) - [ "${actual}" = "2000" ] -} - -@test "server/standalone-StatefulSet: gid default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.runAsGroup' | tee /dev/stderr) - [ "${actual}" = "1000" ] -} - -@test "server/standalone-StatefulSet: gid configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.gid=2000' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.runAsGroup' | tee /dev/stderr) - [ "${actual}" = "2000" ] -} - -@test "server/standalone-StatefulSet: fsgroup default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.fsGroup' | tee /dev/stderr) - [ "${actual}" = "1000" ] -} - -@test "server/standalone-StatefulSet: fsgroup configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.gid=2000' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.securityContext.fsGroup' | tee /dev/stderr) - [ "${actual}" = "2000" ] -} - -#-------------------------------------------------------------------- -# health checks - -@test "server/standalone-StatefulSet: readinessProbe default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe.exec.command[2]' | tee /dev/stderr) - [ "${actual}" = "vault status -tls-skip-verify" ] -} - -@test "server/standalone-StatefulSet: readinessProbe configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.readinessProbe.enabled=false' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/standalone-StatefulSet: readiness failureThreshold default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.readinessProbe.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe.failureThreshold' | tee /dev/stderr) - [ "${actual}" = "2" ] -} - -@test "server/standalone-StatefulSet: readiness failureThreshold configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.readinessProbe.enabled=true' \ - --set 'server.readinessProbe.failureThreshold=100' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe.failureThreshold' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -@test "server/standalone-StatefulSet: readiness initialDelaySeconds default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.readinessProbe.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe.initialDelaySeconds' | tee /dev/stderr) - [ "${actual}" = "5" ] -} - -@test "server/standalone-StatefulSet: readiness initialDelaySeconds configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.readinessProbe.enabled=true' \ - --set 'server.readinessProbe.initialDelaySeconds=100' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe.initialDelaySeconds' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -@test "server/standalone-StatefulSet: readiness periodSeconds default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.readinessProbe.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe.periodSeconds' | tee /dev/stderr) - [ "${actual}" = "5" ] -} - -@test "server/standalone-StatefulSet: readiness periodSeconds configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.readinessProbe.enabled=true' \ - --set 'server.readinessProbe.periodSeconds=100' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe.periodSeconds' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -@test "server/standalone-StatefulSet: readiness successThreshold default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.readinessProbe.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe.successThreshold' | tee /dev/stderr) - [ "${actual}" = "1" ] -} - -@test "server/standalone-StatefulSet: readiness successThreshold configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.readinessProbe.enabled=true' \ - --set 'server.readinessProbe.successThreshold=100' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe.successThreshold' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -@test "server/standalone-StatefulSet: readiness timeoutSeconds default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.readinessProbe.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe.timeoutSeconds' | tee /dev/stderr) - [ "${actual}" = "3" ] -} - -@test "server/standalone-StatefulSet: readiness timeoutSeconds configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.readinessProbe.enabled=true' \ - --set 'server.readinessProbe.timeoutSeconds=100' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].readinessProbe.timeoutSeconds' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -@test "server/standalone-StatefulSet: livenessProbe default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/standalone-StatefulSet: livenessProbe configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.livenessProbe.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe.httpGet.path' | tee /dev/stderr) - [ "${actual}" = "/v1/sys/health?standbyok=true" ] -} - -@test "server/standalone-StatefulSet: liveness failureThreshold default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.livenessProbe.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe.failureThreshold' | tee /dev/stderr) - [ "${actual}" = "2" ] -} - -@test "server/standalone-StatefulSet: liveness failureThreshold configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.livenessProbe.enabled=true' \ - --set 'server.livenessProbe.failureThreshold=100' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe.failureThreshold' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -@test "server/standalone-StatefulSet: liveness initialDelaySeconds default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.livenessProbe.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe.initialDelaySeconds' | tee /dev/stderr) - [ "${actual}" = "60" ] -} - -@test "server/standalone-StatefulSet: liveness initialDelaySeconds configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.livenessProbe.enabled=true' \ - --set 'server.livenessProbe.initialDelaySeconds=100' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe.initialDelaySeconds' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -@test "server/standalone-StatefulSet: liveness periodSeconds default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.livenessProbe.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe.periodSeconds' | tee /dev/stderr) - [ "${actual}" = "5" ] -} - -@test "server/standalone-StatefulSet: liveness periodSeconds configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.livenessProbe.enabled=true' \ - --set 'server.livenessProbe.periodSeconds=100' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe.periodSeconds' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -@test "server/standalone-StatefulSet: liveness successThreshold default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.livenessProbe.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe.successThreshold' | tee /dev/stderr) - [ "${actual}" = "1" ] -} - -@test "server/standalone-StatefulSet: liveness successThreshold configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.livenessProbe.enabled=true' \ - --set 'server.livenessProbe.successThreshold=100' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe.successThreshold' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -@test "server/standalone-StatefulSet: liveness timeoutSeconds default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.livenessProbe.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe.timeoutSeconds' | tee /dev/stderr) - [ "${actual}" = "3" ] -} - -@test "server/standalone-StatefulSet: liveness timeoutSeconds configurable" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.livenessProbe.enabled=true' \ - --set 'server.livenessProbe.timeoutSeconds=100' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].livenessProbe.timeoutSeconds' | tee /dev/stderr) - [ "${actual}" = "100" ] -} - -#-------------------------------------------------------------------- -# args -@test "server/standalone-StatefulSet: add extraArgs" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.extraArgs=foobar' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].args[0]' | tee /dev/stderr) - [[ "${actual}" = *"foobar"* ]] -} - -#-------------------------------------------------------------------- -# preStop -@test "server/standalone-StatefulSet: preStop sleep duration default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].lifecycle.preStop.exec.command[2]' | tee /dev/stderr) - [[ "${actual}" = "sleep 5 &&"* ]] -} - -@test "server/standalone-StatefulSet: preStop sleep duration 10" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.preStopSleepSeconds=10' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].lifecycle.preStop.exec.command[2]' | tee /dev/stderr) - [[ "${actual}" = "sleep 10 &&"* ]] -} - -@test "server/standalone-StatefulSet: vault port name is http, when tlsDisable is true" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.tlsDisable=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].ports | map(select(.containerPort==8200)) | .[] .name' | tee /dev/stderr) - [ "${actual}" = "http" ] -} - -@test "server/standalone-StatefulSet: vault replication port name is http-rep, when tlsDisable is true" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.tlsDisable=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].ports | map(select(.containerPort==8202)) | .[] .name' | tee /dev/stderr) - [ "${actual}" = "http-rep" ] -} - -@test "server/standalone-StatefulSet: vault port name is https, when tlsDisable is false" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.tlsDisable=false' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].ports | map(select(.containerPort==8200)) | .[] .name' | tee /dev/stderr) - [ "${actual}" = "https" ] -} - -@test "server/standalone-StatefulSet: vault replication port name is https-rep, when tlsDisable is false" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.tlsDisable=false' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].ports | map(select(.containerPort==8202)) | .[] .name' | tee /dev/stderr) - [ "${actual}" = "https-rep" ] -} - -#-------------------------------------------------------------------- -# annotations -@test "server/standalone-StatefulSet: generic annotations string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.annotations=vaultIsAwesome: true' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/standalone-StatefulSet: auditStorage volumeClaim annotations string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.auditStorage.annotations=vaultIsAwesome: true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates[1].metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/standalone-StatefulSet: dataStorage volumeClaim annotations string" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dataStorage.enabled=true' \ - --set 'server.dataStorage.annotations=vaultIsAwesome: true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates[0].metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/standalone-StatefulSet: auditStorage volumeClaim annotations yaml" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.auditStorage.enabled=true' \ - --set 'server.auditStorage.annotations.vaultIsAwesome=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates[1].metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/standalone-StatefulSet: dataStorage volumeClaim annotations yaml" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.dataStorage.enabled=true' \ - --set 'server.dataStorage.annotations.vaultIsAwesome=true' \ - . | tee /dev/stderr | - yq -r '.spec.volumeClaimTemplates[0].metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/ha-standby-Service: generic annotations yaml" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.annotations.vaultIsAwesome=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -#-------------------------------------------------------------------- -# priorityClassName - -@test "server/standalone-StatefulSet: priorityClassName not set by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq '.spec.template.spec | .priorityClassName? == null' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "server/standalone-StatefulSet: priorityClassName can be set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.priorityClassName=armaggeddon' \ - . | tee /dev/stderr | - yq '.spec.template.spec | .priorityClassName == "armaggeddon"' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -# postStart -@test "server/standalone-StatefulSet: postStart disabled by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].lifecycle.postStart' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "server/standalone-StatefulSet: postStart can be set" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set='server.postStart={/bin/sh,-c,sleep}' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.containers[0].lifecycle.postStart.exec.command[0]' | tee /dev/stderr) - [ "${actual}" = "/bin/sh" ] -} - -#-------------------------------------------------------------------- -# OpenShift - -@test "server/standalone-StatefulSet: OpenShift - runAsUser disabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.openshift=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.securityContext.runAsUser | length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "server/standalone-StatefulSet: OpenShift - runAsGroup disabled" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'global.openshift=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.securityContext.runAsGroup | length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -#-------------------------------------------------------------------- -# serviceAccount - -@test "server/standalone-StatefulSet: serviceAccount.name is set" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.serviceAccount.create=false' \ - --set 'server.serviceAccount.name=user-defined-ksa' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.serviceAccountName' | tee /dev/stderr) - [ "${actual}" = "user-defined-ksa" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.serviceAccount.create=true' \ - --set 'server.serviceAccount.name=user-defined-ksa' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.serviceAccountName' | tee /dev/stderr) - [ "${actual}" = "user-defined-ksa" ] -} - -@test "server/standalone-StatefulSet: serviceAccount.name is not set" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.serviceAccount.create=false' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.serviceAccountName' | tee /dev/stderr) - [ "${actual}" = "default" ] - - local actual=$(helm template \ - --show-only templates/server-statefulset.yaml \ - --set 'server.serviceAccount.create=true' \ - . | tee /dev/stderr | - yq -r '.spec.template.spec.serviceAccountName' | tee /dev/stderr) - [ "${actual}" = "RELEASE-NAME-vault" ] - - -} - -#-------------------------------------------------------------------- -# enterprise license autoload support -@test "server/StatefulSet: adds volume for license secret when enterprise license secret name and key are provided" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/server-statefulset.yaml \ - --set 'server.enterpriseLicense.secretName=foo' \ - --set 'server.enterpriseLicense.secretKey=bar' \ - . | tee /dev/stderr | - yq -r -c '.spec.template.spec.volumes[] | select(.name == "vault-license")' | tee /dev/stderr) - [ "${actual}" = '{"name":"vault-license","secret":{"secretName":"foo","defaultMode":288}}' ] -} - -@test "server/StatefulSet: adds volume mount for license secret when enterprise license secret name and key are provided" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/server-statefulset.yaml \ - --set 'server.enterpriseLicense.secretName=foo' \ - --set 'server.enterpriseLicense.secretKey=bar' \ - . | tee /dev/stderr | - yq -r -c '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "vault-license")' | tee /dev/stderr) - [ "${actual}" = '{"name":"vault-license","mountPath":"/vault/license","readOnly":true}' ] -} - -@test "server/StatefulSet: adds env var for license path when enterprise license secret name and key are provided" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/server-statefulset.yaml \ - --set 'server.enterpriseLicense.secretName=foo' \ - --set 'server.enterpriseLicense.secretKey=bar' \ - . | tee /dev/stderr | - yq -r -c '.spec.template.spec.containers[0].env[] | select(.name == "VAULT_LICENSE_PATH")' | tee /dev/stderr) - [ "${actual}" = '{"name":"VAULT_LICENSE_PATH","value":"/vault/license/bar"}' ] -} - -@test "server/StatefulSet: blank secretName does not set env var" { - cd `chart_dir` - - # setting secretName=null - local actual=$(helm template \ - -s templates/server-statefulset.yaml \ - --set 'server.enterpriseLicense.secretName=null' \ - --set 'server.enterpriseLicense.secretKey=bar' \ - . | tee /dev/stderr | - yq -r -c '.spec.template.spec.containers[0].env[] | select(.name == "VAULT_LICENSE_PATH")' | tee /dev/stderr) - [ "${actual}" = '' ] - - # omitting secretName - local actual=$(helm template \ - -s templates/server-statefulset.yaml \ - --set 'server.enterpriseLicense.secretKey=bar' \ - . | tee /dev/stderr | - yq -r -c '.spec.template.spec.containers[0].env[] | select(.name == "VAULT_LICENSE_PATH")' | tee /dev/stderr) - [ "${actual}" = '' ] -} diff --git a/vault/test/unit/ui-service.bats b/vault/test/unit/ui-service.bats deleted file mode 100755 index 0603303c..00000000 --- a/vault/test/unit/ui-service.bats +++ /dev/null @@ -1,375 +0,0 @@ -#!/usr/bin/env bats - -load _helpers - -@test "ui/Service: disabled by default" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.dev.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.ha.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.standalone.enabled=true' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "ui/Service: disable with ui.enabled" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'ui.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'ui.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'ui.enabled=false' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "ui/Service: disable with injector.externalVaultAddr" { - cd `chart_dir` - local actual=$( (helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] - - local actual=$( (helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'injector.externalVaultAddr=http://vault-outside' \ - . || echo "---") | tee /dev/stderr | - yq 'length > 0' | tee /dev/stderr) - [ "${actual}" = "false" ] -} - -@test "ui/Service: ClusterIP type by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "ClusterIP" ] - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "ClusterIP" ] - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "ClusterIP" ] -} - -@test "ui/Service: specified type" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'ui.serviceType=LoadBalancer' \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "LoadBalancer" ] - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'ui.serviceType=LoadBalancer' \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "LoadBalancer" ] - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'ui.serviceType=LoadBalancer' \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.type' | tee /dev/stderr) - [ "${actual}" = "LoadBalancer" ] - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'ui.serviceType=LoadBalancer' \ - --set 'ui.externalTrafficPolicy=Local' \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "Local" ] -} - -@test "ui/Service: LoadBalancerIP set if specified and serviceType == LoadBalancer" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'ui.serviceType=LoadBalancer' \ - --set 'ui.enabled=true' \ - --set 'ui.loadBalancerIP=123.123.123.123' \ - . | tee /dev/stderr | - yq -r '.spec.loadBalancerIP' | tee /dev/stderr) - [ "${actual}" = "123.123.123.123" ] - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'ui.serviceType=ClusterIP' \ - --set 'ui.enabled=true' \ - --set 'ui.loadBalancerIP=123.123.123.123' \ - . | tee /dev/stderr | - yq -r '.spec.loadBalancerIP' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "ui/Service: set loadBalancerSourceRanges when LoadBalancer is configured as serviceType" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'ui.serviceType=LoadBalancer' \ - --set 'ui.enabled=true' \ - --set 'ui.loadBalancerSourceRanges={"123.123.123.123"}' \ - . | tee /dev/stderr | - yq -r '.spec.loadBalancerSourceRanges[0]' | tee /dev/stderr) - [ "${actual}" = "123.123.123.123" ] - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'ui.serviceType=ClusterIP' \ - --set 'ui.enabled=true' \ - --set 'ui.loadBalancerSourceRanges={"123.123.123.123"}' \ - . | tee /dev/stderr | - yq -r '.spec.loadBalancerSourceRanges[0]' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "ui/Service: ClusterIP assert no externalTrafficPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.standalone.enabled=true' \ - --set 'ui.serviceType=ClusterIP' \ - --set 'ui.externalTrafficPolicy=Foo' \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "ui/Service: specify annotations" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.dev.enabled=true' \ - --set 'ui.serviceType=LoadBalancer' \ - --set 'ui.enabled=true' \ - --set 'ui.annotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) - [ "${actual}" = "bar" ] - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'ui.serviceType=LoadBalancer' \ - --set 'ui.enabled=true' \ - --set 'ui.annotations=foo: bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) - [ "${actual}" = "bar" ] - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'ui.serviceType=LoadBalancer' \ - --set 'ui.enabled=true' \ - --set 'ui.annotations.foo=bar' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) - [ "${actual}" = "bar" ] - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'server.ha.enabled=true' \ - --set 'ui.serviceType=LoadBalancer' \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "ui/Service: port name is http, when tlsDisable is true" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'global.tlsDisable=true' \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].name' | tee /dev/stderr) - [ "${actual}" = "http" ] -} - -@test "ui/Service: port name is https, when tlsDisable is false" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'global.tlsDisable=false' \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].name' | tee /dev/stderr) - [ "${actual}" = "https" ] -} - -@test "ui/Service: publishNotReadyAddresses set true by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.publishNotReadyAddresses' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "ui/Service: publishNotReadyAddresses can be set to false" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'ui.enabled=true' \ - --set 'ui.publishNotReadyAddresses=false' \ - . | tee /dev/stderr | - yq -r '.spec.publishNotReadyAddresses' | tee /dev/stderr) - [ "${actual}" = 'false' ] -} - -@test "ui/Service: active pod only selector not set by default" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.selector["vault-active"]' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "ui/Service: active pod only selector can be set on HA" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'ui.enabled=true' \ - --set 'ui.activeVaultPodOnly=true' \ - --set 'server.dev.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.selector["vault-active"]' | tee /dev/stderr) - [ "${actual}" = 'null' ] - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'ui.enabled=true' \ - --set 'ui.activeVaultPodOnly=true' \ - --set 'server.ha.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.selector["vault-active"]' | tee /dev/stderr) - [ "${actual}" = 'true' ] -} - -@test "ui/Service: default is no nodePort" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'ui.enabled=true' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) - [ "${actual}" = "null" ] -} - -@test "ui/Service: can set nodePort" { - cd `chart_dir` - - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'ui.enabled=true' \ - --set 'ui.serviceNodePort=123' \ - . | tee /dev/stderr | - yq -r '.spec.ports[0].nodePort' | tee /dev/stderr) - [ "${actual}" = "123" ] -} - -@test "ui/Service: LoadBalancer assert externalTrafficPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'ui.enabled=true' \ - --set 'server.standalone.enabled=true' \ - --set 'ui.serviceType=LoadBalancer' \ - --set 'ui.externalTrafficPolicy=Foo' \ - . | tee /dev/stderr | - yq -r '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "Foo" ] -} - -@test "ui/Service: LoadBalancer assert no externalTrafficPolicy" { - cd `chart_dir` - local actual=$(helm template \ - --show-only templates/ui-service.yaml \ - --set 'ui.enabled=true' \ - --set 'server.standalone.enabled=true' \ - --set 'ui.serviceType=LoadBalancer' \ - --set 'ui.externalTrafficPolicy=' \ - . | tee /dev/stderr | - yq '.spec.externalTrafficPolicy' | tee /dev/stderr) - [ "${actual}" = "null" ] - -} diff --git a/vault/values.openshift.yaml b/vault/values.openshift.yaml deleted file mode 100644 index afbe1f98..00000000 --- a/vault/values.openshift.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# These overrides are appropriate defaults for deploying this chart on OpenShift - -global: - openshift: true - -injector: - image: - repository: "registry.connect.redhat.com/hashicorp/vault-k8s" - tag: "0.14.1-ubi" - - agentImage: - repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.9.0-ubi" - -server: - image: - repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.9.0-ubi" diff --git a/vault/values.schema.json b/vault/values.schema.json deleted file mode 100644 index 26f13674..00000000 --- a/vault/values.schema.json +++ /dev/null @@ -1,841 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema#", - "type": "object", - "properties": { - "csi": { - "type": "object", - "properties": { - "daemonSet": { - "type": "object", - "properties": { - "annotations": { - "type": [ - "object", - "string" - ] - }, - "updateStrategy": { - "type": "object", - "properties": { - "maxUnavailable": { - "type": "string" - }, - "type": { - "type": "string" - } - } - }, - "providersDir": { - "type": "string" - }, - "kubeletRootDir": { - "type": "string" - } - } - }, - "debug": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - }, - "extraArgs": { - "type": "array" - }, - "image": { - "type": "object", - "properties": { - "pullPolicy": { - "type": "string" - }, - "repository": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - }, - "livenessProbe": { - "type": "object", - "properties": { - "failureThreshold": { - "type": "integer" - }, - "initialDelaySeconds": { - "type": "integer" - }, - "periodSeconds": { - "type": "integer" - }, - "successThreshold": { - "type": "integer" - }, - "timeoutSeconds": { - "type": "integer" - } - } - }, - "pod": { - "type": "object", - "properties": { - "annotations": { - "type": [ - "object", - "string" - ] - }, - "tolerations": { - "type": [ - "null", - "array", - "string" - ] - } - } - }, - "readinessProbe": { - "type": "object", - "properties": { - "failureThreshold": { - "type": "integer" - }, - "initialDelaySeconds": { - "type": "integer" - }, - "periodSeconds": { - "type": "integer" - }, - "successThreshold": { - "type": "integer" - }, - "timeoutSeconds": { - "type": "integer" - } - } - }, - "resources": { - "type": "object" - }, - "serviceAccount": { - "type": "object", - "properties": { - "annotations": { - "type": [ - "object", - "string" - ] - } - } - }, - "volumeMounts": { - "type": [ - "null", - "array" - ] - }, - "volumes": { - "type": [ - "null", - "array" - ] - } - } - }, - "global": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "imagePullSecrets": { - "type": "array" - }, - "openshift": { - "type": "boolean" - }, - "psp": { - "type": "object", - "properties": { - "annotations": { - "type": [ - "object", - "string" - ] - }, - "enable": { - "type": "boolean" - } - } - }, - "tlsDisable": { - "type": "boolean" - } - } - }, - "injector": { - "type": "object", - "properties": { - "affinity": { - "type": [ - "object", - "string" - ] - }, - "agentDefaults": { - "type": "object", - "properties": { - "cpuLimit": { - "type": "string" - }, - "cpuRequest": { - "type": "string" - }, - "memLimit": { - "type": "string" - }, - "memRequest": { - "type": "string" - }, - "template": { - "type": "string" - }, - "templateConfig": { - "type": "object", - "properties": { - "exitOnRetryFailure": { - "type": "boolean" - }, - "staticSecretRenderInterval": { - "type": "string" - } - } - } - } - }, - "agentImage": { - "type": "object", - "properties": { - "repository": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - }, - "annotations": { - "type": [ - "object", - "string" - ] - }, - "authPath": { - "type": "string" - }, - "certs": { - "type": "object", - "properties": { - "caBundle": { - "type": "string" - }, - "certName": { - "type": "string" - }, - "keyName": { - "type": "string" - }, - "secretName": { - "type": [ - "null", - "string" - ] - } - } - }, - "enabled": { - "type": "boolean" - }, - "externalVaultAddr": { - "type": "string" - }, - "extraEnvironmentVars": { - "type": "object" - }, - "extraLabels": { - "type": "object" - }, - "failurePolicy": { - "type": "string" - }, - "hostNetwork": { - "type": "boolean" - }, - "image": { - "type": "object", - "properties": { - "pullPolicy": { - "type": "string" - }, - "repository": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - }, - "leaderElector": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - } - } - }, - "logFormat": { - "type": "string" - }, - "logLevel": { - "type": "string" - }, - "metrics": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - } - } - }, - "namespaceSelector": { - "type": "object" - }, - "nodeSelector": { - "type": [ - "null", - "object", - "string" - ] - }, - "objectSelector": { - "type": "object" - }, - "port": { - "type": "integer" - }, - "priorityClassName": { - "type": "string" - }, - "replicas": { - "type": "integer" - }, - "resources": { - "type": "object" - }, - "revokeOnShutdown": { - "type": "boolean" - }, - "service": { - "type": "object", - "properties": { - "annotations": { - "type": [ - "object", - "string" - ] - } - } - }, - "tolerations": { - "type": [ - "null", - "array", - "string" - ] - }, - "webhookAnnotations": { - "type": [ - "object", - "string" - ] - } - } - }, - "server": { - "type": "object", - "properties": { - "affinity": { - "type": [ - "object", - "string" - ] - }, - "annotations": { - "type": [ - "object", - "string" - ] - }, - "auditStorage": { - "type": "object", - "properties": { - "accessMode": { - "type": "string" - }, - "annotations": { - "type": [ - "object", - "string" - ] - }, - "enabled": { - "type": [ - "boolean", - "string" - ] - }, - "mountPath": { - "type": "string" - }, - "size": { - "type": "string" - }, - "storageClass": { - "type": [ - "null", - "string" - ] - } - } - }, - "authDelegator": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - } - } - }, - "dataStorage": { - "type": "object", - "properties": { - "accessMode": { - "type": "string" - }, - "annotations": { - "type": [ - "object", - "string" - ] - }, - "enabled": { - "type": [ - "boolean", - "string" - ] - }, - "mountPath": { - "type": "string" - }, - "size": { - "type": "string" - }, - "storageClass": { - "type": [ - "null", - "string" - ] - } - } - }, - "dev": { - "type": "object", - "properties": { - "devRootToken": { - "type": "string" - }, - "enabled": { - "type": "boolean" - } - } - }, - "enabled": { - "type": "boolean" - }, - "enterpriseLicense": { - "type": "object", - "properties": { - "secretKey": { - "type": "string" - }, - "secretName": { - "type": "string" - } - } - }, - "extraArgs": { - "type": "string" - }, - "extraContainers": { - "type": [ - "null", - "array" - ] - }, - "extraEnvironmentVars": { - "type": "object" - }, - "extraInitContainers": { - "type": [ - "null", - "array" - ] - }, - "extraLabels": { - "type": "object" - }, - "extraSecretEnvironmentVars": { - "type": "array" - }, - "extraVolumes": { - "type": "array" - }, - "ha": { - "type": "object", - "properties": { - "apiAddr": { - "type": [ - "null", - "string" - ] - }, - "config": { - "type": "string" - }, - "disruptionBudget": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "maxUnavailable": { - "type": [ - "null", - "integer" - ] - } - } - }, - "enabled": { - "type": "boolean" - }, - "raft": { - "type": "object", - "properties": { - "config": { - "type": "string" - }, - "enabled": { - "type": "boolean" - }, - "setNodeId": { - "type": "boolean" - } - } - }, - "replicas": { - "type": "integer" - } - } - }, - "image": { - "type": "object", - "properties": { - "pullPolicy": { - "type": "string" - }, - "repository": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - }, - "ingress": { - "type": "object", - "properties": { - "activeService": { - "type": "boolean" - }, - "annotations": { - "type": [ - "object", - "string" - ] - }, - "enabled": { - "type": "boolean" - }, - "extraPaths": { - "type": "array" - }, - "hosts": { - "type": "array", - "items": { - "type": "object", - "properties": { - "host": { - "type": "string" - }, - "paths": { - "type": "array" - } - } - } - }, - "ingressClassName": { - "type": "string" - }, - "labels": { - "type": "object" - }, - "tls": { - "type": "array" - } - } - }, - "livenessProbe": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "failureThreshold": { - "type": "integer" - }, - "initialDelaySeconds": { - "type": "integer" - }, - "path": { - "type": "string" - }, - "periodSeconds": { - "type": "integer" - }, - "successThreshold": { - "type": "integer" - }, - "timeoutSeconds": { - "type": "integer" - } - } - }, - "logFormat": { - "type": "string" - }, - "logLevel": { - "type": "string" - }, - "networkPolicy": { - "type": "object", - "properties": { - "egress": { - "type": "array" - }, - "enabled": { - "type": "boolean" - } - } - }, - "nodeSelector": { - "type": [ - "null", - "object", - "string" - ] - }, - "postStart": { - "type": "array" - }, - "preStopSleepSeconds": { - "type": "integer" - }, - "priorityClassName": { - "type": "string" - }, - "readinessProbe": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "failureThreshold": { - "type": "integer" - }, - "initialDelaySeconds": { - "type": "integer" - }, - "periodSeconds": { - "type": "integer" - }, - "successThreshold": { - "type": "integer" - }, - "timeoutSeconds": { - "type": "integer" - } - } - }, - "resources": { - "type": "object" - }, - "route": { - "type": "object", - "properties": { - "activeService": { - "type": "boolean" - }, - "annotations": { - "type": [ - "object", - "string" - ] - }, - "enabled": { - "type": "boolean" - }, - "host": { - "type": "string" - }, - "labels": { - "type": "object" - } - } - }, - "service": { - "type": "object", - "properties": { - "annotations": { - "type": [ - "object", - "string" - ] - }, - "enabled": { - "type": "boolean" - }, - "port": { - "type": "integer" - }, - "targetPort": { - "type": "integer" - } - } - }, - "serviceAccount": { - "type": "object", - "properties": { - "annotations": { - "type": [ - "object", - "string" - ] - }, - "create": { - "type": "boolean" - }, - "name": { - "type": "string" - } - } - }, - "shareProcessNamespace": { - "type": "boolean" - }, - "standalone": { - "type": "object", - "properties": { - "config": { - "type": "string" - }, - "enabled": { - "type": [ - "string", - "boolean" - ] - } - } - }, - "statefulSet": { - "type": "object", - "properties": { - "annotations": { - "type": [ - "object", - "string" - ] - } - } - }, - "tolerations": { - "type": [ - "null", - "array", - "string" - ] - }, - "updateStrategyType": { - "type": "string" - }, - "volumeMounts": { - "type": [ - "null", - "array" - ] - }, - "volumes": { - "type": [ - "null", - "array" - ] - } - } - }, - "ui": { - "type": "object", - "properties": { - "activeVaultPodOnly": { - "type": "boolean" - }, - "annotations": { - "type": [ - "object", - "string" - ] - }, - "enabled": { - "type": "boolean" - }, - "externalPort": { - "type": "integer" - }, - "publishNotReadyAddresses": { - "type": "boolean" - }, - "serviceNodePort": { - "type": [ - "null", - "integer" - ] - }, - "serviceType": { - "type": "string" - }, - "targetPort": { - "type": "integer" - } - } - } - } -} diff --git a/vault/values.yaml b/vault/values.yaml deleted file mode 100644 index 5ba57d41..00000000 --- a/vault/values.yaml +++ /dev/null @@ -1,831 +0,0 @@ -# Available parameters and their default values for the Vault chart. - -global: - # enabled is the master enabled switch. Setting this to true or false - # will enable or disable all the components within this chart by default. - enabled: true - # Image pull secret to use for registry authentication. - # Alternatively, the value may be specified as an array of strings. - imagePullSecrets: [] - # imagePullSecrets: - # - name: image-pull-secret - # TLS for end-to-end encrypted transport - tlsDisable: true - # If deploying to OpenShift - openshift: false - # Create PodSecurityPolicy for pods - psp: - enable: false - # Annotation for PodSecurityPolicy. - # This is a multi-line templated string map, and can also be set as YAML. - annotations: | - seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default,runtime/default - apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default - seccomp.security.alpha.kubernetes.io/defaultProfileName: runtime/default - apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default - -injector: - # True if you want to enable vault agent injection. - enabled: true - - replicas: 1 - - # Configures the port the injector should listen on - port: 8080 - - # If multiple replicas are specified, by default a leader will be determined - # so that only one injector attempts to create TLS certificates. - leaderElector: - enabled: true - - # If true, will enable a node exporter metrics endpoint at /metrics. - metrics: - enabled: false - - # External vault server address for the injector to use. Setting this will - # disable deployment of a vault server along with the injector. - externalVaultAddr: "" - - # image sets the repo and tag of the vault-k8s image to use for the injector. - image: - repository: "hashicorp/vault-k8s" - tag: "0.14.1" - pullPolicy: IfNotPresent - - # agentImage sets the repo and tag of the Vault image to use for the Vault Agent - # containers. This should be set to the official Vault image. Vault 1.3.1+ is - # required. - agentImage: - repository: "hashicorp/vault" - tag: "1.9.0" - - # The default values for the injected Vault Agent containers. - agentDefaults: - # For more information on configuring resources, see the K8s documentation: - # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - cpuLimit: "500m" - cpuRequest: "250m" - memLimit: "128Mi" - memRequest: "64Mi" - - # Default template type for secrets when no custom template is specified. - # Possible values include: "json" and "map". - template: "map" - - # Default values within Agent's template_config stanza. - templateConfig: - exitOnRetryFailure: true - staticSecretRenderInterval: "" - - # Mount Path of the Vault Kubernetes Auth Method. - authPath: "auth/kubernetes" - - # Configures the log verbosity of the injector. - # Supported log levels include: trace, debug, info, warn, error - logLevel: "info" - - # Configures the log format of the injector. Supported log formats: "standard", "json". - logFormat: "standard" - - # Configures all Vault Agent sidecars to revoke their token when shutting down - revokeOnShutdown: false - - # namespaceSelector is the selector for restricting the webhook to only - # specific namespaces. - # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector - # for more details. - # Example: - # namespaceSelector: - # matchLabels: - # sidecar-injector: enabled - namespaceSelector: {} - # objectSelector is the selector for restricting the webhook to only - # specific labels. - # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector - # for more details. - # Example: - # objectSelector: - # matchLabels: - # vault-sidecar-injector: enabled - objectSelector: {} - - # Configures failurePolicy of the webhook. The "unspecified" default behaviour deoends on the - # API Version of the WebHook. - # To block pod creation while webhook is unavailable, set the policy to `Fail` below. - # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy - # - failurePolicy: Ignore - - # Extra annotations to attach to the webhook - webhookAnnotations: {} - - certs: - # secretName is the name of the secret that has the TLS certificate and - # private key to serve the injector webhook. If this is null, then the - # injector will default to its automatic management mode that will assign - # a service account to the injector to generate its own certificates. - secretName: null - - # caBundle is a base64-encoded PEM-encoded certificate bundle for the CA - # that signed the TLS certificate that the webhook serves. This must be set - # if secretName is non-null, unless an external service like cert-manager is - # keeping the caBundle updated. - caBundle: "" - - # certName and keyName are the names of the files within the secret for - # the TLS cert and private key, respectively. These have reasonable - # defaults but can be customized if necessary. - certName: tls.crt - keyName: tls.key - - resources: {} - # resources: - # requests: - # memory: 256Mi - # cpu: 250m - # limits: - # memory: 256Mi - # cpu: 250m - - # extraEnvironmentVars is a list of extra environment variables to set in the - # injector deployment. - extraEnvironmentVars: {} - # KUBERNETES_SERVICE_HOST: kubernetes.default.svc - - # Affinity Settings for injector pods - # This can either be multi-line string or YAML matching the PodSpec's affinity field. - # Commenting out or setting as empty the affinity variable, will allow - # deployment of multiple replicas to single node services such as Minikube. - affinity: | - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchLabels: - app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector - app.kubernetes.io/instance: "{{ .Release.Name }}" - component: webhook - topologyKey: kubernetes.io/hostname - - # Toleration Settings for injector pods - # This should be either a multi-line string or YAML matching the Toleration array - # in a PodSpec. - tolerations: [] - - # nodeSelector labels for server pod assignment, formatted as a multi-line string or YAML map. - # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector - # Example: - # nodeSelector: - # beta.kubernetes.io/arch: amd64 - nodeSelector: {} - - # Priority class for injector pods - priorityClassName: "" - - # Extra annotations to attach to the injector pods - # This can either be YAML or a YAML-formatted multi-line templated string map - # of the annotations to apply to the injector pods - annotations: {} - - # Extra labels to attach to the agent-injector - # This should be a YAML map of the labels to apply to the injector - extraLabels: {} - - # Should the injector pods run on the host network (useful when using - # an alternate CNI in EKS) - hostNetwork: false - - # Injector service specific config - service: - # Extra annotations to attach to the injector service - annotations: {} - -server: - # If not set to true, Vault server will not be installed. See vault.mode in _helpers.tpl for implementation details - enabled: true - - # [Enterprise Only] This value refers to a Kubernetes secret that you have - # created that contains your enterprise license. If you are not using an - # enterprise image or if you plan to introduce the license key via another - # route, then leave secretName blank ("") or set it to null. - # Requires Vault Enterprise 1.8 or later. - enterpriseLicense: - # The name of the Kubernetes secret that holds the enterprise license. The - # secret must be in the same namespace that Vault is installed into. - secretName: "" - # The key within the Kubernetes secret that holds the enterprise license. - secretKey: "license" - - # Resource requests, limits, etc. for the server cluster placement. This - # should map directly to the value of the resources field for a PodSpec. - # By default no direct resource request is made. - - image: - repository: "hashicorp/vault" - tag: "1.9.0" - # Overrides the default Image Pull Policy - pullPolicy: IfNotPresent - - # Configure the Update Strategy Type for the StatefulSet - # See https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies - updateStrategyType: "OnDelete" - - # Configure the logging verbosity for the Vault server. - # Supported log levels include: trace, debug, info, warn, error - logLevel: "" - - # Configure the logging format for the Vault server. - # Supported log formats include: standard, json - logFormat: "" - - resources: {} - # resources: - # requests: - # memory: 256Mi - # cpu: 250m - # limits: - # memory: 256Mi - # cpu: 250m - - # Ingress allows ingress services to be created to allow external access - # from Kubernetes to access Vault pods. - # If deployment is on OpenShift, the following block is ignored. - # In order to expose the service, use the route section below - ingress: - enabled: false - labels: {} - # traffic: external - annotations: {} - # | - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - # or - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - - # Optionally use ingressClassName instead of deprecated annotation. - # See: https://kubernetes.io/docs/concepts/services-networking/ingress/#deprecated-annotation - ingressClassName: "" - - # As of Kubernetes 1.19, all Ingress Paths must have a pathType configured. The default value below should be sufficient in most cases. - # See: https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types for other possible values. - pathType: Prefix - - # When HA mode is enabled and K8s service registration is being used, - # configure the ingress to point to the Vault active service. - activeService: true - hosts: - - host: chart-example.local - paths: [] - ## Extra paths to prepend to the host configuration. This is useful when working with annotation based services. - extraPaths: [] - # - path: /* - # backend: - # service: - # name: ssl-redirect - # port: - # number: use-annotation - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - - # OpenShift only - create a route to expose the service - # The created route will be of type passthrough - route: - enabled: false - - # When HA mode is enabled and K8s service registration is being used, - # configure the route to point to the Vault active service. - activeService: true - - labels: {} - annotations: {} - host: chart-example.local - - # authDelegator enables a cluster role binding to be attached to the service - # account. This cluster role binding can be used to setup Kubernetes auth - # method. https://www.vaultproject.io/docs/auth/kubernetes.html - authDelegator: - enabled: true - - # extraInitContainers is a list of init containers. Specified as a YAML list. - # This is useful if you need to run a script to provision TLS certificates or - # write out configuration files in a dynamic way. - extraInitContainers: null - # # This example installs a plugin pulled from github into the /usr/local/libexec/vault/oauthapp folder, - # # which is defined in the volumes value. - # - name: oauthapp - # image: "alpine" - # command: [sh, -c] - # args: - # - cd /tmp && - # wget https://github.com/puppetlabs/vault-plugin-secrets-oauthapp/releases/download/v1.2.0/vault-plugin-secrets-oauthapp-v1.2.0-linux-amd64.tar.xz -O oauthapp.xz && - # tar -xf oauthapp.xz && - # mv vault-plugin-secrets-oauthapp-v1.2.0-linux-amd64 /usr/local/libexec/vault/oauthapp && - # chmod +x /usr/local/libexec/vault/oauthapp - # volumeMounts: - # - name: plugins - # mountPath: /usr/local/libexec/vault - - # extraContainers is a list of sidecar containers. Specified as a YAML list. - extraContainers: null - - # shareProcessNamespace enables process namespace sharing between Vault and the extraContainers - # This is useful if Vault must be signaled, e.g. to send a SIGHUP for log rotation - shareProcessNamespace: false - - # extraArgs is a string containing additional Vault server arguments. - extraArgs: "" - - # Used to define custom readinessProbe settings - readinessProbe: - enabled: true - # If you need to use a http path instead of the default exec - # path: /v1/sys/health?standbyok=true - - # When a probe fails, Kubernetes will try failureThreshold times before giving up - failureThreshold: 2 - # Number of seconds after the container has started before probe initiates - initialDelaySeconds: 5 - # How often (in seconds) to perform the probe - periodSeconds: 5 - # Minimum consecutive successes for the probe to be considered successful after having failed - successThreshold: 1 - # Number of seconds after which the probe times out. - timeoutSeconds: 3 - # Used to enable a livenessProbe for the pods - livenessProbe: - enabled: false - path: "/v1/sys/health?standbyok=true" - # When a probe fails, Kubernetes will try failureThreshold times before giving up - failureThreshold: 2 - # Number of seconds after the container has started before probe initiates - initialDelaySeconds: 60 - # How often (in seconds) to perform the probe - periodSeconds: 5 - # Minimum consecutive successes for the probe to be considered successful after having failed - successThreshold: 1 - # Number of seconds after which the probe times out. - timeoutSeconds: 3 - - # Used to set the sleep time during the preStop step - preStopSleepSeconds: 5 - - # Used to define commands to run after the pod is ready. - # This can be used to automate processes such as initialization - # or boostrapping auth methods. - postStart: [] - # - /bin/sh - # - -c - # - /vault/userconfig/myscript/run.sh - - # extraEnvironmentVars is a list of extra environment variables to set with the stateful set. These could be - # used to include variables required for auto-unseal. - extraEnvironmentVars: {} - # GOOGLE_REGION: global - # GOOGLE_PROJECT: myproject - # GOOGLE_APPLICATION_CREDENTIALS: /vault/userconfig/myproject/myproject-creds.json - - # extraSecretEnvironmentVars is a list of extra environment variables to set with the stateful set. - # These variables take value from existing Secret objects. - extraSecretEnvironmentVars: [] - # - envName: AWS_SECRET_ACCESS_KEY - # secretName: vault - # secretKey: AWS_SECRET_ACCESS_KEY - - # Deprecated: please use 'volumes' instead. - # extraVolumes is a list of extra volumes to mount. These will be exposed - # to Vault in the path `/vault/userconfig//`. The value below is - # an array of objects, examples are shown below. - extraVolumes: [] - # - type: secret (or "configMap") - # name: my-secret - # path: null # default is `/vault/userconfig` - - # volumes is a list of volumes made available to all containers. These are rendered - # via toYaml rather than pre-processed like the extraVolumes value. - # The purpose is to make it easy to share volumes between containers. - volumes: null - # - name: plugins - # emptyDir: {} - - # volumeMounts is a list of volumeMounts for the main server container. These are rendered - # via toYaml rather than pre-processed like the extraVolumes value. - # The purpose is to make it easy to share volumes between containers. - volumeMounts: null - # - mountPath: /usr/local/libexec/vault - # name: plugins - # readOnly: true - - # Affinity Settings - # Commenting out or setting as empty the affinity variable, will allow - # deployment to single node services such as Minikube - # This should be either a multi-line string or YAML matching the PodSpec's affinity field. - affinity: | - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchLabels: - app.kubernetes.io/name: {{ template "vault.name" . }} - app.kubernetes.io/instance: "{{ .Release.Name }}" - component: server - topologyKey: kubernetes.io/hostname - - # Toleration Settings for server pods - # This should be either a multi-line string or YAML matching the Toleration array - # in a PodSpec. - tolerations: [] - - # nodeSelector labels for server pod assignment, formatted as a multi-line string or YAML map. - # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector - # Example: - # nodeSelector: - # beta.kubernetes.io/arch: amd64 - nodeSelector: {} - - # Enables network policy for server pods - networkPolicy: - enabled: false - egress: [] - # egress: - # - to: - # - ipBlock: - # cidr: 10.0.0.0/24 - # ports: - # - protocol: TCP - # port: 443 - - # Priority class for server pods - priorityClassName: "" - - # Extra labels to attach to the server pods - # This should be a YAML map of the labels to apply to the server pods - extraLabels: {} - - # Extra annotations to attach to the server pods - # This can either be YAML or a YAML-formatted multi-line templated string map - # of the annotations to apply to the server pods - annotations: {} - - # Enables a headless service to be used by the Vault Statefulset - service: - enabled: true - # clusterIP controls whether a Cluster IP address is attached to the - # Vault service within Kubernetes. By default the Vault service will - # be given a Cluster IP address, set to None to disable. When disabled - # Kubernetes will create a "headless" service. Headless services can be - # used to communicate with pods directly through DNS instead of a round robin - # load balancer. - # clusterIP: None - - # Configures the service type for the main Vault service. Can be ClusterIP - # or NodePort. - #type: ClusterIP - - # The externalTrafficPolicy can be set to either Cluster or Local - # and is only valid for LoadBalancer and NodePort service types. - # The default value is Cluster. - # ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-traffic-policy - externalTrafficPolicy: Cluster - - # If type is set to "NodePort", a specific nodePort value can be configured, - # will be random if left blank. - #nodePort: 30000 - - # Port on which Vault server is listening - port: 8200 - # Target port to which the service should be mapped to - targetPort: 8200 - # Extra annotations for the service definition. This can either be YAML or a - # YAML-formatted multi-line templated string map of the annotations to apply - # to the service. - annotations: {} - - # This configures the Vault Statefulset to create a PVC for data - # storage when using the file or raft backend storage engines. - # See https://www.vaultproject.io/docs/configuration/storage/index.html to know more - dataStorage: - enabled: true - # Size of the PVC created - size: 10Gi - # Location where the PVC will be mounted. - mountPath: "/vault/data" - # Name of the storage class to use. If null it will use the - # configured default Storage Class. - storageClass: null - # Access Mode of the storage device being used for the PVC - accessMode: ReadWriteOnce - # Annotations to apply to the PVC - annotations: {} - - # This configures the Vault Statefulset to create a PVC for audit - # logs. Once Vault is deployed, initialized and unseal, Vault must - # be configured to use this for audit logs. This will be mounted to - # /vault/audit - # See https://www.vaultproject.io/docs/audit/index.html to know more - auditStorage: - enabled: false - # Size of the PVC created - size: 10Gi - # Location where the PVC will be mounted. - mountPath: "/vault/audit" - # Name of the storage class to use. If null it will use the - # configured default Storage Class. - storageClass: null - # Access Mode of the storage device being used for the PVC - accessMode: ReadWriteOnce - # Annotations to apply to the PVC - annotations: {} - - # Run Vault in "dev" mode. This requires no further setup, no state management, - # and no initialization. This is useful for experimenting with Vault without - # needing to unseal, store keys, et. al. All data is lost on restart - do not - # use dev mode for anything other than experimenting. - # See https://www.vaultproject.io/docs/concepts/dev-server.html to know more - dev: - enabled: false - - # Set VAULT_DEV_ROOT_TOKEN_ID value - devRootToken: "root" - - # Run Vault in "standalone" mode. This is the default mode that will deploy if - # no arguments are given to helm. This requires a PVC for data storage to use - # the "file" backend. This mode is not highly available and should not be scaled - # past a single replica. - standalone: - enabled: "-" - - # config is a raw string of default configuration when using a Stateful - # deployment. Default is to use a PersistentVolumeClaim mounted at /vault/data - # and store data there. This is only used when using a Replica count of 1, and - # using a stateful set. This should be HCL. - - # Note: Configuration files are stored in ConfigMaps so sensitive data - # such as passwords should be either mounted through extraSecretEnvironmentVars - # or through a Kube secret. For more information see: - # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations - config: | - ui = true - - listener "tcp" { - tls_disable = 1 - address = "[::]:8200" - cluster_address = "[::]:8201" - } - storage "file" { - path = "/vault/data" - } - - # Example configuration for using auto-unseal, using Google Cloud KMS. The - # GKMS keys must already exist, and the cluster must have a service account - # that is authorized to access GCP KMS. - #seal "gcpckms" { - # project = "vault-helm-dev" - # region = "global" - # key_ring = "vault-helm-unseal-kr" - # crypto_key = "vault-helm-unseal-key" - #} - - # Run Vault in "HA" mode. There are no storage requirements unless audit log - # persistence is required. In HA mode Vault will configure itself to use Consul - # for its storage backend. The default configuration provided will work the Consul - # Helm project by default. It is possible to manually configure Vault to use a - # different HA backend. - ha: - enabled: false - replicas: 3 - - # Set the api_addr configuration for Vault HA - # See https://www.vaultproject.io/docs/configuration#api_addr - # If set to null, this will be set to the Pod IP Address - apiAddr: null - - # Enables Vault's integrated Raft storage. Unlike the typical HA modes where - # Vault's persistence is external (such as Consul), enabling Raft mode will create - # persistent volumes for Vault to store data according to the configuration under server.dataStorage. - # The Vault cluster will coordinate leader elections and failovers internally. - raft: - - # Enables Raft integrated storage - enabled: false - # Set the Node Raft ID to the name of the pod - setNodeId: false - - # Note: Configuration files are stored in ConfigMaps so sensitive data - # such as passwords should be either mounted through extraSecretEnvironmentVars - # or through a Kube secret. For more information see: - # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations - config: | - ui = true - - listener "tcp" { - tls_disable = 1 - address = "[::]:8200" - cluster_address = "[::]:8201" - } - - storage "raft" { - path = "/vault/data" - } - - service_registration "kubernetes" {} - - # config is a raw string of default configuration when using a Stateful - # deployment. Default is to use a Consul for its HA storage backend. - # This should be HCL. - - # Note: Configuration files are stored in ConfigMaps so sensitive data - # such as passwords should be either mounted through extraSecretEnvironmentVars - # or through a Kube secret. For more information see: - # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations - config: | - ui = true - - listener "tcp" { - tls_disable = 1 - address = "[::]:8200" - cluster_address = "[::]:8201" - } - storage "consul" { - path = "vault" - address = "HOST_IP:8500" - } - - service_registration "kubernetes" {} - - # Example configuration for using auto-unseal, using Google Cloud KMS. The - # GKMS keys must already exist, and the cluster must have a service account - # that is authorized to access GCP KMS. - #seal "gcpckms" { - # project = "vault-helm-dev-246514" - # region = "global" - # key_ring = "vault-helm-unseal-kr" - # crypto_key = "vault-helm-unseal-key" - #} - - # A disruption budget limits the number of pods of a replicated application - # that are down simultaneously from voluntary disruptions - disruptionBudget: - enabled: true - - # maxUnavailable will default to (n/2)-1 where n is the number of - # replicas. If you'd like a custom value, you can specify an override here. - maxUnavailable: null - - # Definition of the serviceAccount used to run Vault. - # These options are also used when using an external Vault server to validate - # Kubernetes tokens. - serviceAccount: - # Specifies whether a service account should be created - create: true - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - # Extra annotations for the serviceAccount definition. This can either be - # YAML or a YAML-formatted multi-line templated string map of the - # annotations to apply to the serviceAccount. - annotations: {} - - # Settings for the statefulSet used to run Vault. - statefulSet: - # Extra annotations for the statefulSet. This can either be YAML or a - # YAML-formatted multi-line templated string map of the annotations to apply - # to the statefulSet. - annotations: {} - -# Vault UI -ui: - # True if you want to create a Service entry for the Vault UI. - # - # serviceType can be used to control the type of service created. For - # example, setting this to "LoadBalancer" will create an external load - # balancer (for supported K8S installations) to access the UI. - enabled: false - publishNotReadyAddresses: true - # The service should only contain selectors for active Vault pod - activeVaultPodOnly: false - serviceType: "ClusterIP" - serviceNodePort: null - externalPort: 8200 - targetPort: 8200 - - # The externalTrafficPolicy can be set to either Cluster or Local - # and is only valid for LoadBalancer and NodePort service types. - # The default value is Cluster. - # ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-traffic-policy - externalTrafficPolicy: Cluster - - #loadBalancerSourceRanges: - # - 10.0.0.0/16 - # - 1.78.23.3/32 - - # loadBalancerIP: - - # Extra annotations to attach to the ui service - # This can either be YAML or a YAML-formatted multi-line templated string map - # of the annotations to apply to the ui service - annotations: {} - -# secrets-store-csi-driver-provider-vault -csi: - # True if you want to install a secrets-store-csi-driver-provider-vault daemonset. - # - # Requires installing the secrets-store-csi-driver separately, see: - # https://github.com/kubernetes-sigs/secrets-store-csi-driver#install-the-secrets-store-csi-driver - # - # With the driver and provider installed, you can mount Vault secrets into volumes - # similar to the Vault Agent injector, and you can also sync those secrets into - # Kubernetes secrets. - enabled: false - - image: - repository: "hashicorp/vault-csi-provider" - tag: "0.3.0" - pullPolicy: IfNotPresent - - # volumes is a list of volumes made available to all containers. These are rendered - # via toYaml rather than pre-processed like the extraVolumes value. - # The purpose is to make it easy to share volumes between containers. - volumes: null - # - name: tls - # secret: - # secretName: vault-tls - - # volumeMounts is a list of volumeMounts for the main server container. These are rendered - # via toYaml rather than pre-processed like the extraVolumes value. - # The purpose is to make it easy to share volumes between containers. - volumeMounts: null - # - name: tls - # mountPath: "/vault/tls" - # readOnly: true - - resources: {} - # resources: - # requests: - # cpu: 50m - # memory: 128Mi - # limits: - # cpu: 50m - # memory: 128Mi - - # Settings for the daemonSet used to run the provider. - daemonSet: - updateStrategy: - type: RollingUpdate - maxUnavailable: "" - # Extra annotations for the daemonSet. This can either be YAML or a - # YAML-formatted multi-line templated string map of the annotations to apply - # to the daemonSet. - annotations: {} - # Provider host path (must match the CSI provider's path) - providersDir: "/etc/kubernetes/secrets-store-csi-providers" - # Kubelet host path - kubeletRootDir: "/var/lib/kubelet" - - pod: - # Extra annotations for the provider pods. This can either be YAML or a - # YAML-formatted multi-line templated string map of the annotations to apply - # to the pod. - annotations: {} - - # Toleration Settings for provider pods - # This should be either a multi-line string or YAML matching the Toleration array - # in a PodSpec. - tolerations: [] - - serviceAccount: - # Extra annotations for the serviceAccount definition. This can either be - # YAML or a YAML-formatted multi-line templated string map of the - # annotations to apply to the serviceAccount. - annotations: {} - - # Used to configure readinessProbe for the pods. - readinessProbe: - # When a probe fails, Kubernetes will try failureThreshold times before giving up - failureThreshold: 2 - # Number of seconds after the container has started before probe initiates - initialDelaySeconds: 5 - # How often (in seconds) to perform the probe - periodSeconds: 5 - # Minimum consecutive successes for the probe to be considered successful after having failed - successThreshold: 1 - # Number of seconds after which the probe times out. - timeoutSeconds: 3 - # Used to configure livenessProbe for the pods. - livenessProbe: - # When a probe fails, Kubernetes will try failureThreshold times before giving up - failureThreshold: 2 - # Number of seconds after the container has started before probe initiates - initialDelaySeconds: 5 - # How often (in seconds) to perform the probe - periodSeconds: 5 - # Minimum consecutive successes for the probe to be considered successful after having failed - successThreshold: 1 - # Number of seconds after which the probe times out. - timeoutSeconds: 3 - - # Enables debug logging. - debug: false - - # Pass arbitrary additional arguments to vault-csi-provider. - extraArgs: [] From 1f61814f8eddcb30b73f3d281c7ceb6f95dd87a7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 22 Dec 2021 11:24:40 +0100 Subject: [PATCH 0181/1263] Protect ourselves from calling vault-init twice Without this we risk to easily overwrite the seal + token file and hence lose future access to the vault. --- scripts/vault-utils.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 2a203d16..b1481f80 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -61,6 +61,11 @@ vault_init() file=common/vault.init fi + if [ -f "$file" ] && grep -q -e '^Unseal' "$file"; then + echo "$file already exists and contains seal secrets. If this is what you really wanted, move that file away first or call vault_delete" + exit 1 + fi + # The vault is ready to be initialized when it is "Running" but not "ready". Unsealing it makes it ready rdy_check=`get_vault_ready` until [ "$rdy_check" = "0/1 Running" ] From 96aaf36d20645fb0b38204528408b70c2e953805 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 22 Dec 2021 13:22:39 -0600 Subject: [PATCH 0182/1263] Add script for copying vault-token --- scripts/vault-token.sh | 64 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100755 scripts/vault-token.sh diff --git a/scripts/vault-token.sh b/scripts/vault-token.sh new file mode 100755 index 00000000..8a141fd5 --- /dev/null +++ b/scripts/vault-token.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +#This script is written to be POSIX-compliant, as not all sh are created equal - many are bash but +#Debian-derivatives use dash which doesn't support some bash syntax +# +#TARGET_NAMESPACE=manuela-ci + +TARGET_NAMESPACE="external-secrets-deployable" +token_resource="vault" +src_ns="vault" + +ns=0 +ok=0 + +# Check for Namespaces and Secrets to be ready (it takes the cluster a few minutes to deploy them) +while [ 1 ] ; do + echo -n "Checking for namespace $TARGET_NAMESPACE to exist..." + if [ oc get namespace $TARGET_NAMESPACE >/dev/null 2>/dev/null ]; then + echo "not yet" + ns=0 + sleep 2 + continue + else + echo "OK" + ns=1 + fi + + echo -n "Checking for $token_resource to be populated in $src_ns..." + tok=`oc sa get-token -n $src_ns $token_resource 2>/dev/null` + if [ "$?" = 0 ] && [ -n "$tok" ]; then + echo "OK" + ok=1 + else + echo "not yet" + ok=0 + sleep 2 + continue + fi + + echo "Conditions met, managing sa token $token_resource in $TARGET_NAMESPACE" + break +done + +TOKEN=$(echo $tok | base64 -w 0) +cat << HERE | oc apply -f- +apiVersion: apps.open-cluster-management.io/v1 +kind: Deployable +metadata: + name: custom-kubernetes-token + namespace: external-secrets-deployable +spec: + channels: + - external-secrets-ns + template: + apiVersion: v1 + kind: Secret + metadata: + name: custom-kubernetes-token + data: + token: ${TOKEN} + type: Opaque +HERE + +#echo "{ \"apiVersion\": \"apps.open-cluster-management.io/v1\", \"kind\": \"Deployable\", \"metadata\": { \"name\": \"custom-kubernetes-token\", \"namespace\": \"external-secrets-deployable\" }, { spec: { \"channels\": \[\"data\": { \"ARGOCD_PASSWORD\": \"$password\", \"ARGOCD_USERNAME\": \"$user\" }, \"type\": \"Opaque\" }" | oc apply -f- From b8bbd329189c8a6cef9ccff817b299de27ecc2bc Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 5 Jan 2022 11:55:33 -0600 Subject: [PATCH 0183/1263] Add Hub cluster domain as a global variable based on ingress config --- Makefile | 3 ++- acm/templates/policies/application-policies.yaml | 2 ++ clustergroup/templates/applications.yaml | 2 ++ clustergroup/templates/argocd.yaml | 1 + install/templates/argocd/application.yaml | 2 ++ 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0c28de8d..f9d313bd 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,10 @@ NAME=$(shell basename `pwd`) TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL:[[:space:]]*//' -e 's%:[a-z].*@%@%' -e 's%:%/%' -e 's%git@%https://%' ) # git branch --show-current is also available as of git 2.22, but we will use this for compatibility TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) +HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spec.domain}) # --set values always take precedence over the contents of -f -HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) +HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set main.options.bootstrap=$(BOOTSTRAP) --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" PATTERN_OPTS=-f common/examples/values-example.yaml diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 070b0eb0..eb2fde94 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -55,6 +55,8 @@ spec: value: {{ $.Values.global.pattern }} - name: global.valuesDirectoryURL value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} + - name: global.hubClusterDomain + value: {{ .Values.global.hubClusterDomain }} {{- range .helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 7f1dd647..952a129d 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -37,6 +37,8 @@ spec: value: {{ $.Values.global.pattern }} - name: global.valuesDirectoryURL value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} + - name: global.hubClusterDomain + value: {{ $.Values.global.hubClusterDomain }} {{- range .overrides }} - name: {{ .name }} value: {{ .value }} diff --git a/clustergroup/templates/argocd.yaml b/clustergroup/templates/argocd.yaml index 8abcb3f3..523e4268 100644 --- a/clustergroup/templates/argocd.yaml +++ b/clustergroup/templates/argocd.yaml @@ -24,6 +24,7 @@ spec: --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern={{ .Values.global.pattern }} + --set global.hubClusterDomain={{ .Values.global.hubClusterDomain }} --set global.valuesDirectoryURL={{ .Values.global.valuesDirectoryURL }} --post-renderer ./kustomize\"] \ \n" diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 54c6a629..a462ddde 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -30,6 +30,8 @@ spec: value: {{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }} - name: global.pattern value: {{ .Release.Name }} + - name: global.hubClusterDomain + value: {{ .Values.global.hubClusterDomain }} {{- if eq .Values.main.options.syncPolicy "Automatic" }} syncPolicy: automated: {} From 6c744aaf60b55b501dc087532b1aed0cc741e978 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 6 Jan 2022 10:17:04 -0600 Subject: [PATCH 0184/1263] Add code to extract root token --- .gitignore | 1 + scripts/vault-utils.sh | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/.gitignore b/.gitignore index 2f656a0a..adc3fb83 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ values-secret.yaml .*-expected.yaml pattern-vault.init +vault.init diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index b1481f80..b3c4bee4 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -78,4 +78,18 @@ vault_init() vault_unseal $file } +vault_get_root_token() +{ + # Argument is expected to be the text output of the vault operator init command which includes Unseal Keys + # (5 by default) and a root token. + if [ -n "$1" ]; then + file=$1 + else + file=common/vault.init + fi + + token=`grep "Initial Root Token" $file | awk '{ print $4 }'` + echo -n $token +} + $@ From f655f9e8d96a4ff88326c981cdc655b60cb615b6 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 6 Jan 2022 10:36:37 -0600 Subject: [PATCH 0185/1263] Add function to wrap an exec including the token --- scripts/vault-utils.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index b3c4bee4..21ea5eb0 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/sh -x # Assumptions - vault in the demo will be running in non-HA mode so there will only be a vault-0 # vault will be running in the "vault" namespace @@ -92,4 +92,14 @@ vault_get_root_token() echo -n $token } +vault_token_exec() +{ + file="$1" + token=`vault_get_root_token $file` + shift + cmd="$@" + + oc -n vault exec vault-0 -- bash -c "VAULT_TOKEN=$token $cmd" +} + $@ From fb4088ecbf0b7710b96c9ee1d1c3326546093853 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 6 Jan 2022 10:59:49 -0600 Subject: [PATCH 0186/1263] Add pki init to vault init makefile target --- Makefile | 1 + scripts/vault-utils.sh | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f9d313bd..15dc12c8 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,7 @@ uninstall: vault-init: common/scripts/vault-utils.sh vault_init common/pattern-vault.init + common/scripts/vault-utils.sh vault_pki_init common/pattern-vault.init vault-unseal: common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 21ea5eb0..ce2e5cf9 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -1,4 +1,4 @@ -#!/bin/sh -x +#!/bin/sh # Assumptions - vault in the demo will be running in non-HA mode so there will only be a vault-0 # vault will be running in the "vault" namespace @@ -78,6 +78,7 @@ vault_init() vault_unseal $file } +# Retrieves the root token specified in the file $1 vault_get_root_token() { # Argument is expected to be the text output of the vault operator init command which includes Unseal Keys @@ -92,6 +93,8 @@ vault_get_root_token() echo -n $token } +# Exec a vault command wrapped with the vault root token specified in the file +# $1 vault_token_exec() { file="$1" @@ -102,4 +105,24 @@ vault_token_exec() oc -n vault exec vault-0 -- bash -c "VAULT_TOKEN=$token $cmd" } +oc_get_domain() +{ + oc get ingresses.config/cluster -o jsonpath={.spec.domain} +} + +vault_pki_init() +{ + file="$1" + token=`vault_get_root_token $file` + shift + + domain=`oc_get_domain` + + vault_token_exec $file "vault secrets enable pki" + vault_token_exec $file "vault secrets tune --max-lease=8760h pki" + vault_token_exec $file "vault write pki/root/generate/internal common_name=$domain ttl=8760h" + vault_token_exec $file 'vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl"' + vault_token_exec $file "vault write pki/roles/certificate allowed_domains=$domain allow_subdomains=true max_ttl=8760h" +} + $@ From bfbe33203417d09e37fcc4c8fb7eb5b4a03a8f42 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 6 Jan 2022 11:20:33 -0600 Subject: [PATCH 0187/1263] Expand the PKI domain (knock off the first two domains for the hub cluster, e.g. apps and the next one up to allow the PKI to be bigger --- scripts/vault-utils.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index ce2e5cf9..4a5e7d3c 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -110,19 +110,25 @@ oc_get_domain() oc get ingresses.config/cluster -o jsonpath={.spec.domain} } +oc_get_pki_domain() +{ + basedomain=`oc_get_domain | cut -d. -f3-` +} + vault_pki_init() { file="$1" token=`vault_get_root_token $file` shift - domain=`oc_get_domain` + pkidomain=`oc_get_pki_domain` + certrole=`sed -e 's|\.|_|g' $pkidomain` vault_token_exec $file "vault secrets enable pki" vault_token_exec $file "vault secrets tune --max-lease=8760h pki" - vault_token_exec $file "vault write pki/root/generate/internal common_name=$domain ttl=8760h" + vault_token_exec $file "vault write pki/root/generate/internal common_name=$pkidomain ttl=8760h" vault_token_exec $file 'vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl"' - vault_token_exec $file "vault write pki/roles/certificate allowed_domains=$domain allow_subdomains=true max_ttl=8760h" + vault_token_exec $file "vault write pki/roles/$certrole allowed_domains=$pkidomain allow_subdomains=true max_ttl=8760h" } $@ From 079f51da479c278bd2932649d49b9db5f36b1316 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 6 Jan 2022 11:33:54 -0600 Subject: [PATCH 0188/1263] Correct pki role and domain creation --- scripts/vault-utils.sh | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 4a5e7d3c..bf965995 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -112,7 +112,14 @@ oc_get_domain() oc_get_pki_domain() { - basedomain=`oc_get_domain | cut -d. -f3-` + echo -n `oc_get_domain | cut -d. -f3-` +} + +oc_get_pki_role() +{ + pkidomain=`oc_get_pki_domain` + certrole=`echo $pkidomain | sed 's|\.|_|g'` + echo -n $certrole } vault_pki_init() @@ -121,14 +128,14 @@ vault_pki_init() token=`vault_get_root_token $file` shift - pkidomain=`oc_get_pki_domain` - certrole=`sed -e 's|\.|_|g' $pkidomain` + pkidomain=`oc_get_pki_role` + pkirole=`oc_get_pki_role` vault_token_exec $file "vault secrets enable pki" vault_token_exec $file "vault secrets tune --max-lease=8760h pki" vault_token_exec $file "vault write pki/root/generate/internal common_name=$pkidomain ttl=8760h" vault_token_exec $file 'vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl"' - vault_token_exec $file "vault write pki/roles/$certrole allowed_domains=$pkidomain allow_subdomains=true max_ttl=8760h" + vault_token_exec $file "vault write pki/roles/$pkirole allowed_domains=$pkidomain allow_subdomains=true max_ttl=8760h" } $@ From f0de29509c6b112acab13efb4467229c6b3349e5 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 6 Jan 2022 17:05:47 -0600 Subject: [PATCH 0189/1263] Add more functions for secrets management --- scripts/vault-utils.sh | 75 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index bf965995..ee32f65a 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -45,10 +45,12 @@ vault_unseal() file=common/vault.init fi - for unseal in `cat $1 | grep "Unseal Key" | awk '{ print $4 }'` + for unseal in `cat $file | grep "Unseal Key" | awk '{ print $4 }'` do oc -n vault exec vault-0 -- vault operator unseal $unseal done + + vault_login $file } vault_init() @@ -75,7 +77,13 @@ vault_init() done oc -n vault exec vault-0 -- vault operator init | tee $file + vault_unseal $file + vault_login $file + + vault_pki_init $file + vault_k8s_init $file + vault_secrets_init $file } # Retrieves the root token specified in the file $1 @@ -102,7 +110,27 @@ vault_token_exec() shift cmd="$@" - oc -n vault exec vault-0 -- bash -c "VAULT_TOKEN=$token $cmd" + vault_exec $file "VAULT_TOKEN=$token $cmd" +} + +vault_exec() +{ + file="$1" + token=`vault_get_root_token $file` + shift + cmd="$@" + + oc -n vault exec vault-0 -- bash -c "$cmd" +} + +vault_login() +{ + file="$1" + token=`vault_get_root_token $file` + shift + cmd="$@" + + vault_exec $file "vault login $token" } oc_get_domain() @@ -125,17 +153,44 @@ oc_get_pki_role() vault_pki_init() { file="$1" - token=`vault_get_root_token $file` - shift - pkidomain=`oc_get_pki_role` + pkidomain=`oc_get_pki_domain` pkirole=`oc_get_pki_role` - vault_token_exec $file "vault secrets enable pki" - vault_token_exec $file "vault secrets tune --max-lease=8760h pki" - vault_token_exec $file "vault write pki/root/generate/internal common_name=$pkidomain ttl=8760h" - vault_token_exec $file 'vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl"' - vault_token_exec $file "vault write pki/roles/$pkirole allowed_domains=$pkidomain allow_subdomains=true max_ttl=8760h" + vault_exec $file "vault secrets enable pki" + vault_exec $file "vault secrets tune --max-lease=8760h pki" + vault_exec $file "vault write pki/root/generate/internal common_name=$pkidomain ttl=8760h" + vault_exec $file 'vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl"' + vault_exec $file "vault write pki/roles/$pkirole allowed_domains=$pkidomain allow_subdomains=true max_ttl=8760h" +} + +vault_kubernetes_init() +{ + file="$1" + + vault_token=$(oc sa get-token -n vault vault) + k8s_host='kubernetes_host=https://$KUBERNETES_PORT_443_TCP_ADDR:443' + + vault_exec $file "vault auth enable kubernetes" + vault_exec $file "vault write auth/kubernetes/config token_reviewer_jwt=$vault_token $k8s_host kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc" +} + +vault_secrets_init() +{ + file="$1" + + vault_exec $file "vault secrets enable -path=secret kv-v2" +} + +vault_create_secret() +{ + file="$1" + shift + secret_path="$1" + shift + secret="$@" + + vault_exec $file "vault kv put $secret_path $secret" } $@ From 9284430a2526fcfac2e8a91e6201413a72b693d9 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 6 Jan 2022 17:07:12 -0600 Subject: [PATCH 0190/1263] pki init is done in vault_init, no need to have a separate makefile task --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 15dc12c8..f9d313bd 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,6 @@ uninstall: vault-init: common/scripts/vault-utils.sh vault_init common/pattern-vault.init - common/scripts/vault-utils.sh vault_pki_init common/pattern-vault.init vault-unseal: common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init From 64a6fc63859b6638b0843298e3028a69816bb9f2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jan 2022 11:39:39 +0100 Subject: [PATCH 0191/1263] Fix the name of the function to initialize the kubernetes backend Otherwise we'll error out with: common/scripts/vault-utils.sh: line 85: vault_k8s_init: command not found --- scripts/vault-utils.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index ee32f65a..ec2e6320 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -82,7 +82,7 @@ vault_init() vault_login $file vault_pki_init $file - vault_k8s_init $file + vault_kubernetes_init $file vault_secrets_init $file } From bff0f91b9f173518dfc5a037eb5ac3b7839a2810 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jan 2022 11:40:43 +0100 Subject: [PATCH 0192/1263] Add --interactive to the oc vault exec calls This allows us to read stdin and push a file via stdin. This is particularly useful when configuring the vault policy --- scripts/vault-utils.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index ec2e6320..3f52c8fd 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -120,7 +120,7 @@ vault_exec() shift cmd="$@" - oc -n vault exec vault-0 -- bash -c "$cmd" + oc -n vault exec -i vault-0 -- bash -c "$cmd" } vault_login() From 6d7c51f98b287d4b472afa3be4705836c51d43dd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jan 2022 11:42:19 +0100 Subject: [PATCH 0193/1263] Add a policy init function to setup initial policy for the vault --- scripts/vault-utils.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 3f52c8fd..97f39901 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -84,6 +84,7 @@ vault_init() vault_pki_init $file vault_kubernetes_init $file vault_secrets_init $file + vault_policy_init $file } # Retrieves the root token specified in the file $1 @@ -175,6 +176,18 @@ vault_kubernetes_init() vault_exec $file "vault write auth/kubernetes/config token_reviewer_jwt=$vault_token $k8s_host kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc" } +vault_policy_init() +{ + file="$1" + + vault_exec $file 'vault policy write hub-policy - << EOF +path "secret/*" + { capabilities = ["read"] +} +EOF +' +} + vault_secrets_init() { file="$1" From a5fff7fc8019e441b23c7c837b99e976ceaf7ebf Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 7 Jan 2022 11:13:19 -0600 Subject: [PATCH 0194/1263] Add variable qualification to prevent helm template errors --- acm/templates/policies/application-policies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index eb2fde94..21be9744 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -56,7 +56,7 @@ spec: - name: global.valuesDirectoryURL value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} - name: global.hubClusterDomain - value: {{ .Values.global.hubClusterDomain }} + value: {{ $.Values.global.hubClusterDomain }} {{- range .helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} From 83a61eeed8e1abd3723c29ad945c31c96be0dc63 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 7 Jan 2022 15:15:27 -0600 Subject: [PATCH 0195/1263] Add vault-route to workaround hard-coding of passthrough mode in vault helm chart 0.18.0 --- vault-route/.helmignore | 1 + vault-route/Chart.yaml | 6 ++++++ vault-route/templates/vault-route.yaml | 24 ++++++++++++++++++++++++ vault-route/values.yaml | 2 ++ 4 files changed, 33 insertions(+) create mode 100644 vault-route/.helmignore create mode 100644 vault-route/Chart.yaml create mode 100644 vault-route/templates/vault-route.yaml create mode 100644 vault-route/values.yaml diff --git a/vault-route/.helmignore b/vault-route/.helmignore new file mode 100644 index 00000000..b25c15b8 --- /dev/null +++ b/vault-route/.helmignore @@ -0,0 +1 @@ +*~ diff --git a/vault-route/Chart.yaml b/vault-route/Chart.yaml new file mode 100644 index 00000000..2295c23c --- /dev/null +++ b/vault-route/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +description: A Helm chart to create a route for vault in edge modeArgoCD applications and any required namespaces or subscriptions +keywords: +- pattern +name: pattern-vault-route +version: 0.0.1 diff --git a/vault-route/templates/vault-route.yaml b/vault-route/templates/vault-route.yaml new file mode 100644 index 00000000..83a414a9 --- /dev/null +++ b/vault-route/templates/vault-route.yaml @@ -0,0 +1,24 @@ +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: vault + namespace: vault + labels: + app.kubernetes.io/instance: vault + app.kubernetes.io/name: vault + argocd.argoproj.io/instance: vault + annotations: + openshift.io/host.generated: 'true' +spec: + host: vault.{{ .Values.global.clusterHubDomain }} + to: + kind: Service + name: vault + weight: 100 + port: + targetPort: http + tls: + termination: edge + insecureEdgeTerminationPolicy: None + wildcardPolicy: None +status: diff --git a/vault-route/values.yaml b/vault-route/values.yaml new file mode 100644 index 00000000..7082a851 --- /dev/null +++ b/vault-route/values.yaml @@ -0,0 +1,2 @@ +global: + hubClusterDomain: example.com From 9d3a16f23e6cd65d809c38ccfc78b5b615987e1c Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 7 Jan 2022 15:39:13 -0600 Subject: [PATCH 0196/1263] Correct route resource, remove namespace and spell variable correctly --- vault-route/templates/vault-route.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vault-route/templates/vault-route.yaml b/vault-route/templates/vault-route.yaml index 83a414a9..49f575c8 100644 --- a/vault-route/templates/vault-route.yaml +++ b/vault-route/templates/vault-route.yaml @@ -1,8 +1,8 @@ +--- kind: Route apiVersion: route.openshift.io/v1 metadata: name: vault - namespace: vault labels: app.kubernetes.io/instance: vault app.kubernetes.io/name: vault @@ -10,7 +10,7 @@ metadata: annotations: openshift.io/host.generated: 'true' spec: - host: vault.{{ .Values.global.clusterHubDomain }} + host: vault.{{ $.Values.global.hubClusterDomain }} to: kind: Service name: vault From b5fb9f623401114f55ba32f23c20cb90e79f8419 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 12 Jan 2022 11:08:21 +0100 Subject: [PATCH 0197/1263] Fix TTL lease typo in vault-init Current wrong command: bash-4.4$ vault secrets tune -max-lease=8760h pki flag provided but not defined: -max-lease Add -ttl at the end to fix it: bash-4.4$ vault secrets tune --max-lease-ttl=8760h pki Success! Tuned the secrets engine at: pki/ --- scripts/vault-utils.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 97f39901..1d737604 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -159,7 +159,7 @@ vault_pki_init() pkirole=`oc_get_pki_role` vault_exec $file "vault secrets enable pki" - vault_exec $file "vault secrets tune --max-lease=8760h pki" + vault_exec $file "vault secrets tune --max-lease-ttl=8760h pki" vault_exec $file "vault write pki/root/generate/internal common_name=$pkidomain ttl=8760h" vault_exec $file 'vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl"' vault_exec $file "vault write pki/roles/$pkirole allowed_domains=$pkidomain allow_subdomains=true max_ttl=8760h" From 06c735fc1c8e9594a5e2941c3cb405feae1f1eb3 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 18 Jan 2022 09:33:13 -0600 Subject: [PATCH 0198/1263] Remove extra duplicate subcription YAML and force quoting in application install for consistency --- clustergroup/templates/applications.yaml | 2 +- clustergroup/templates/subsciptions.yaml | 40 ---------------------- clustergroup/templates/subscriptions.yaml | 41 ++++++++++++++++++----- 3 files changed, 33 insertions(+), 50 deletions(-) delete mode 100644 clustergroup/templates/subsciptions.yaml diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 952a129d..d16d6b59 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -41,7 +41,7 @@ spec: value: {{ $.Values.global.hubClusterDomain }} {{- range .overrides }} - name: {{ .name }} - value: {{ .value }} + value: {{ .value | quote }} {{- if .forceString }} forceString: true {{- end }} diff --git a/clustergroup/templates/subsciptions.yaml b/clustergroup/templates/subsciptions.yaml deleted file mode 100644 index cf295bb2..00000000 --- a/clustergroup/templates/subsciptions.yaml +++ /dev/null @@ -1,40 +0,0 @@ -{{- range .Values.clusterGroup.subscriptions }} -{{- $subs := . }} -{{- $installPlanValue := .installPlanApproval }} - -{{- if $subs.namespaces }} -{{- range .namespaces }} -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: {{ $subs.name }} - namespace: {{ default "openshift-operators" . }} -spec: - name: {{ $subs.name }} - source: {{ default "redhat-operators" $subs.source }} - sourceNamespace: {{ default "openshift-marketplace" $subs.sourceNamespace }} - channel: {{ default "stable" $subs.channel }} - installPlanApproval: {{ coalesce $installPlanValue $.Values.global.options.installPlanApproval }} - {{- if $.Values.global.options.useCSV }} - startingCSV: {{ $subs.csv }} - {{- end }} ---- -{{- end }} -{{- else }} -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: {{ $subs.name }} - namespace: {{ default "openshift-operators" $subs.namespace }} -spec: - name: {{ $subs.name }} - source: {{ default "redhat-operators" $subs.source }} - sourceNamespace: {{ default "openshift-marketplace" $subs.sourceNamespace }} - channel: {{ default "stable" $subs.channel }} - installPlanApproval: {{ coalesce $installPlanValue $.Values.global.options.installPlanApproval }} - {{- if $.Values.global.options.useCSV }} - startingCSV: {{ $subs.csv }} - {{- end }} ---- -{{- end }} -{{- end }} diff --git a/clustergroup/templates/subscriptions.yaml b/clustergroup/templates/subscriptions.yaml index e3b43ac3..cf295bb2 100644 --- a/clustergroup/templates/subscriptions.yaml +++ b/clustergroup/templates/subscriptions.yaml @@ -1,17 +1,40 @@ {{- range .Values.clusterGroup.subscriptions }} +{{- $subs := . }} +{{- $installPlanValue := .installPlanApproval }} + +{{- if $subs.namespaces }} +{{- range .namespaces }} apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: - name: {{ .name }} - namespace: {{ default "openshift-operators" .namespace }} + name: {{ $subs.name }} + namespace: {{ default "openshift-operators" . }} spec: - name: {{ .name }} - source: {{ default "redhat-operators" .source }} - sourceNamespace: {{ default "openshift-marketplace" .sourceNamespace }} - channel: {{ default "stable" .channel }} - installPlanApproval: {{ coalesce .installPlanApproval $.Values.global.options.installPlanApproval }} + name: {{ $subs.name }} + source: {{ default "redhat-operators" $subs.source }} + sourceNamespace: {{ default "openshift-marketplace" $subs.sourceNamespace }} + channel: {{ default "stable" $subs.channel }} + installPlanApproval: {{ coalesce $installPlanValue $.Values.global.options.installPlanApproval }} {{- if $.Values.global.options.useCSV }} - startingCSV: {{ .csv }} - {{- end }} + startingCSV: {{ $subs.csv }} + {{- end }} --- {{- end }} +{{- else }} +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: {{ $subs.name }} + namespace: {{ default "openshift-operators" $subs.namespace }} +spec: + name: {{ $subs.name }} + source: {{ default "redhat-operators" $subs.source }} + sourceNamespace: {{ default "openshift-marketplace" $subs.sourceNamespace }} + channel: {{ default "stable" $subs.channel }} + installPlanApproval: {{ coalesce $installPlanValue $.Values.global.options.installPlanApproval }} + {{- if $.Values.global.options.useCSV }} + startingCSV: {{ $subs.csv }} + {{- end }} +--- +{{- end }} +{{- end }} From 6f61782db3943bee5921eb1cbcfb99b085244222 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 19 Jan 2022 09:05:41 -0600 Subject: [PATCH 0199/1263] Add local domain to ACM policy --- acm/templates/policies/application-policies.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 21be9744..65e1232e 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -57,6 +57,8 @@ spec: value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} + - name: global.localClusterDomain + value: '{{`{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}`}}' {{- range .helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} From 81ceefce02b23093d937300b54ac51cfe95e6fd8 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 19 Jan 2022 13:27:14 -0600 Subject: [PATCH 0200/1263] Propogate localdomain setting to non-privileged argo --- clustergroup/templates/applications.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index d16d6b59..2e4379cd 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -39,6 +39,8 @@ spec: value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} + - name: global.localClusterDomain + value: {{ $.Values.global.localClusterDomain }} {{- range .overrides }} - name: {{ .name }} value: {{ .value | quote }} From 835c2d2f38a78eace26e05dcce3aa7ada8c69c4d Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 19 Jan 2022 14:53:13 -0600 Subject: [PATCH 0201/1263] Fix some tests --- .gitignore | 2 +- Makefile | 2 +- tests/clustergroup-naked.expected.yaml | 2 ++ tests/clustergroup-normal.expected.yaml | 44 +++---------------------- tests/install-naked.expected.yaml | 6 ++++ tests/install-normal.expected.yaml | 6 ++++ 6 files changed, 21 insertions(+), 41 deletions(-) diff --git a/.gitignore b/.gitignore index adc3fb83..765c0dda 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,6 @@ *.swp *.swo values-secret.yaml -.*-expected.yaml +.*.expected.yaml pattern-vault.init vault.init diff --git a/Makefile b/Makefile index f9d313bd..a46aabf9 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spe # --set values always take precedence over the contents of -f HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) -TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set main.options.bootstrap=$(BOOTSTRAP) --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" +TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set main.options.bootstrap=$(BOOTSTRAP) --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com PATTERN_OPTS=-f common/examples/values-example.yaml # Makefiles that use this target must provide: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index f48965df..e897228d 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -79,6 +79,7 @@ spec: --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern=common + --set global.hubClusterDomain= --set global.valuesDirectoryURL=https://github.com/pattern-clone/common/raw/main --post-renderer ./kustomize\"] \ \n" @@ -100,6 +101,7 @@ spec: cpu: 500m memory: 2Gi dex: + openShiftOAuth: true resources: limits: cpu: 500m diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 2580b372..b208cd3d 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -124,6 +124,10 @@ spec: value: mypattern - name: global.valuesDirectoryURL value: https://github.com/pattern-clone/mypattern/raw/main + - name: global.hubClusterDomain + value: hub.example.com + - name: global.localClusterDomain + value: region.example.com ignoreDifferences: [ { "group": "internal.open-cluster-management.io", @@ -220,6 +224,7 @@ spec: cpu: 500m memory: 2Gi dex: + openShiftOAuth: true resources: limits: cpu: 500m @@ -287,45 +292,6 @@ spec: targetNamespaces: - application-ci --- -# Source: pattern-clustergroup/templates/subsciptions.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: advanced-cluster-management - namespace: open-cluster-management -spec: - name: advanced-cluster-management - source: redhat-operators - sourceNamespace: openshift-marketplace - channel: release-2.3 - installPlanApproval: Automatic ---- -# Source: pattern-clustergroup/templates/subsciptions.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: openshift-pipelines-operator-rh - namespace: openshift-operators -spec: - name: openshift-pipelines-operator-rh - source: redhat-operators - sourceNamespace: openshift-marketplace - channel: stable - installPlanApproval: Automatic ---- -# Source: pattern-clustergroup/templates/subscriptions.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: advanced-cluster-management - namespace: open-cluster-management -spec: - name: advanced-cluster-management - source: redhat-operators - sourceNamespace: openshift-marketplace - channel: release-2.3 - installPlanApproval: Automatic ---- # Source: pattern-clustergroup/templates/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription diff --git a/tests/install-naked.expected.yaml b/tests/install-naked.expected.yaml index 8ec1e715..4ed9b445 100644 --- a/tests/install-naked.expected.yaml +++ b/tests/install-naked.expected.yaml @@ -38,6 +38,8 @@ spec: value: https://github.com/pattern-clone/mypattern/raw/main - name: global.pattern value: install + - name: global.hubClusterDomain + value: syncPolicy: automated: {} --- @@ -55,3 +57,7 @@ spec: name: openshift-gitops-operator source: redhat-operators sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: install-default,openshift-gitops diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yaml index 6f2fdc06..42c65416 100644 --- a/tests/install-normal.expected.yaml +++ b/tests/install-normal.expected.yaml @@ -38,6 +38,8 @@ spec: value: https://github.com/pattern-clone/mypattern/raw/main - name: global.pattern value: install + - name: global.hubClusterDomain + value: hub.example.com syncPolicy: automated: {} --- @@ -55,3 +57,7 @@ spec: name: openshift-gitops-operator source: redhat-operators sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: install-example,openshift-gitops From 1907fe0ac25a05cb0c5a52efce9291dfbd16ffe8 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 19 Jan 2022 17:14:51 -0600 Subject: [PATCH 0202/1263] Fix remaining tests --- tests/acm-normal.expected.yaml | 4 ++++ tests/clustergroup-normal.expected.yaml | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 8b3be6f1..dcfbef68 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -135,6 +135,10 @@ spec: value: mypattern - name: global.valuesDirectoryURL value: https://github.com/pattern-clone/mypattern/raw/main + - name: global.hubClusterDomain + value: hub.example.com + - name: global.localClusterDomain + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - name: clusterGroup.isHubCluster value: "false" destination: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index b208cd3d..c82ac3a7 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -172,6 +172,10 @@ spec: value: mypattern - name: global.valuesDirectoryURL value: https://github.com/pattern-clone/mypattern/raw/main + - name: global.hubClusterDomain + value: hub.example.com + - name: global.localClusterDomain + value: region.example.com syncPolicy: automated: {} # selfHeal: true @@ -203,6 +207,7 @@ spec: --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern=mypattern + --set global.hubClusterDomain=hub.example.com --set global.valuesDirectoryURL=https://github.com/pattern-clone/mypattern/raw/main --post-renderer ./kustomize\"] \ \n" @@ -295,6 +300,19 @@ spec: # Source: pattern-clustergroup/templates/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription +metadata: + name: advanced-cluster-management + namespace: open-cluster-management +spec: + name: advanced-cluster-management + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: release-2.3 + installPlanApproval: Automatic +--- +# Source: pattern-clustergroup/templates/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription metadata: name: openshift-pipelines-operator-rh namespace: openshift-operators From 7948cef2ef9d8b58621906c1b1db474e3a89ea52 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 20 Jan 2022 08:24:59 -0600 Subject: [PATCH 0203/1263] Remove manuela tag from clustergroup chart --- clustergroup/Chart.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 771b7960..249163ae 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -1,7 +1,6 @@ apiVersion: v2 description: A Helm chart to create per-clustergroup ArgoCD applications and any required namespaces or subscriptions keywords: -- manuela - pattern name: pattern-clustergroup version: 0.0.1 From a29215eed662380b1b835f23938ca43fbf4e74dc Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 20 Jan 2022 14:57:08 -0600 Subject: [PATCH 0204/1263] Add extra framework options to level with clustergroup implementation --- acm/templates/policies/application-policies.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 65e1232e..839e5a4d 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -39,7 +39,14 @@ spec: source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} - path: {{ default "common/clustergroup" .path }} + {{- if .chart }} + chart: {{ .chart }} + {{- else }} + path: {{ .path }} + {{- end }} + {{- if .plugin }} + plugin: {{ .plugin | toPrettyJson }} + {{- else if not .kustomize }} helm: valueFiles: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" From d37a6730a106b1e47923d7a2a54f154650b03c57 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 21 Jan 2022 10:42:01 +0100 Subject: [PATCH 0205/1263] Remove vault-route application Now that vault-helm v0.19.0 has been released we can use it directly to create the vault route with tls.termination set to 'edge', hence this is not needed anymore. --- vault-route/.helmignore | 1 - vault-route/Chart.yaml | 6 ------ vault-route/templates/vault-route.yaml | 24 ------------------------ vault-route/values.yaml | 2 -- 4 files changed, 33 deletions(-) delete mode 100644 vault-route/.helmignore delete mode 100644 vault-route/Chart.yaml delete mode 100644 vault-route/templates/vault-route.yaml delete mode 100644 vault-route/values.yaml diff --git a/vault-route/.helmignore b/vault-route/.helmignore deleted file mode 100644 index b25c15b8..00000000 --- a/vault-route/.helmignore +++ /dev/null @@ -1 +0,0 @@ -*~ diff --git a/vault-route/Chart.yaml b/vault-route/Chart.yaml deleted file mode 100644 index 2295c23c..00000000 --- a/vault-route/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -description: A Helm chart to create a route for vault in edge modeArgoCD applications and any required namespaces or subscriptions -keywords: -- pattern -name: pattern-vault-route -version: 0.0.1 diff --git a/vault-route/templates/vault-route.yaml b/vault-route/templates/vault-route.yaml deleted file mode 100644 index 49f575c8..00000000 --- a/vault-route/templates/vault-route.yaml +++ /dev/null @@ -1,24 +0,0 @@ ---- -kind: Route -apiVersion: route.openshift.io/v1 -metadata: - name: vault - labels: - app.kubernetes.io/instance: vault - app.kubernetes.io/name: vault - argocd.argoproj.io/instance: vault - annotations: - openshift.io/host.generated: 'true' -spec: - host: vault.{{ $.Values.global.hubClusterDomain }} - to: - kind: Service - name: vault - weight: 100 - port: - targetPort: http - tls: - termination: edge - insecureEdgeTerminationPolicy: None - wildcardPolicy: None -status: diff --git a/vault-route/values.yaml b/vault-route/values.yaml deleted file mode 100644 index 7082a851..00000000 --- a/vault-route/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -global: - hubClusterDomain: example.com From 5158b29cbc4f5610c88a1c9efd76331ff51d478b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 21 Jan 2022 10:42:01 +0100 Subject: [PATCH 0206/1263] Remove vault-route application Now that vault-helm v0.19.0 has been released we can use it directly to create the vault route with tls.termination set to 'edge', hence this is not needed anymore. --- vault-route/.helmignore | 1 - vault-route/Chart.yaml | 6 ------ vault-route/templates/vault-route.yaml | 24 ------------------------ vault-route/values.yaml | 2 -- 4 files changed, 33 deletions(-) delete mode 100644 vault-route/.helmignore delete mode 100644 vault-route/Chart.yaml delete mode 100644 vault-route/templates/vault-route.yaml delete mode 100644 vault-route/values.yaml diff --git a/vault-route/.helmignore b/vault-route/.helmignore deleted file mode 100644 index b25c15b8..00000000 --- a/vault-route/.helmignore +++ /dev/null @@ -1 +0,0 @@ -*~ diff --git a/vault-route/Chart.yaml b/vault-route/Chart.yaml deleted file mode 100644 index 2295c23c..00000000 --- a/vault-route/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -description: A Helm chart to create a route for vault in edge modeArgoCD applications and any required namespaces or subscriptions -keywords: -- pattern -name: pattern-vault-route -version: 0.0.1 diff --git a/vault-route/templates/vault-route.yaml b/vault-route/templates/vault-route.yaml deleted file mode 100644 index 49f575c8..00000000 --- a/vault-route/templates/vault-route.yaml +++ /dev/null @@ -1,24 +0,0 @@ ---- -kind: Route -apiVersion: route.openshift.io/v1 -metadata: - name: vault - labels: - app.kubernetes.io/instance: vault - app.kubernetes.io/name: vault - argocd.argoproj.io/instance: vault - annotations: - openshift.io/host.generated: 'true' -spec: - host: vault.{{ $.Values.global.hubClusterDomain }} - to: - kind: Service - name: vault - weight: 100 - port: - targetPort: http - tls: - termination: edge - insecureEdgeTerminationPolicy: None - wildcardPolicy: None -status: diff --git a/vault-route/values.yaml b/vault-route/values.yaml deleted file mode 100644 index 7082a851..00000000 --- a/vault-route/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -global: - hubClusterDomain: example.com From 2b2f4410019f0e9a276c4d7e77bd33fc91a88651 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 21 Jan 2022 10:05:53 -0600 Subject: [PATCH 0207/1263] Supply hubClusterDomain for localHubCluster where we don't have ACM to populate the lookup --- acm/templates/policies/application-policies.yaml | 15 ++++++--------- clustergroup/templates/applications.yaml | 4 ++++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 839e5a4d..2dd2240a 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -39,14 +39,7 @@ spec: source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} - {{- if .chart }} - chart: {{ .chart }} - {{- else }} - path: {{ .path }} - {{- end }} - {{- if .plugin }} - plugin: {{ .plugin | toPrettyJson }} - {{- else if not .kustomize }} + path: {{ default "common/clustergroup" .path }} helm: valueFiles: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" @@ -65,7 +58,11 @@ spec: - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain - value: '{{`{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}`}}' + {{- if $.Values.clusterGroup.isHubCluster }} + value: {{ $.Values.global.hubClusterDomain }} + {{- else }} + value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' + {{- end }} {{- range .helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 2e4379cd..0fede23e 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -40,7 +40,11 @@ spec: - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain + {{- if $.Values.clusterGroup.isHubCluster }} + value: {{ $.Values.global.hubClusterDomain }} + {{- else }} value: {{ $.Values.global.localClusterDomain }} + {{- end }} {{- range .overrides }} - name: {{ .name }} value: {{ .value | quote }} From 21cd9e2705935e706fe633aa25f9b7b8c9187488 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 24 Jan 2022 13:55:31 -0600 Subject: [PATCH 0208/1263] Don't conditionalize lookups when we know we need them --- acm/templates/policies/application-policies.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 2dd2240a..5ab3d8b8 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -58,11 +58,7 @@ spec: - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain - {{- if $.Values.clusterGroup.isHubCluster }} - value: {{ $.Values.global.hubClusterDomain }} - {{- else }} value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' - {{- end }} {{- range .helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} From 6575f608a95b70ad15214b2240f222583fd05b49 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 29 Jan 2022 08:45:26 +0100 Subject: [PATCH 0209/1263] Remove bashism in vault_exec We currently uase "bash -c" inside vault_exec. This only works when using UBI-based images. Let's move to 'sh -c' to be a bit more robust in case we're in the presence of the upstream vault images which do not have bash installed. Tested a 'vault-init' on UBI images and it correctly worked with no errors whatsoever in the log. --- scripts/vault-utils.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 1d737604..1219884f 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -121,7 +121,7 @@ vault_exec() shift cmd="$@" - oc -n vault exec -i vault-0 -- bash -c "$cmd" + oc -n vault exec -i vault-0 -- sh -c "$cmd" } vault_login() From a3e131acc16e32fadf721b2a4f314f6abd4d748c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 29 Jan 2022 08:50:19 +0100 Subject: [PATCH 0210/1263] Add namespace support to the regional gitops installations This allows argo on regional clusters to have more rights. Specifically this is needed to create the clusterbindingrole needed for the k8s-external-secret operator. As a first pass we'll use the '*' namespace. In a second iteration we'll need to look at restricting that to openshift-gitops and the namespace of the regional gitops instance. At this point of time such a change is too invasive and is at risk of breaking existing patterns. Tested this on multicloud gitops and I correctly get argo to create the clusterrolebinding in the k8s-external-secret: $ oc get clusterrolebinding | grep k8s-extern k8s-external-secrets-kubernetes-external-secrets ClusterRole/k8s-external-secrets-kubernetes-external-secrets k8s-external-secrets-kubernetes-external-secrets-auth ClusterRole/system:auth-delegator Previously this would fail with: Cluster level CustomResourceDefinition "externalsecrets.kubernetes-client.io" can not be managed when in namespaced mode --- acm/templates/policies/ocp-gitops-policy.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index 477108d9..e6f7013b 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -42,6 +42,10 @@ spec: name: openshift-gitops-operator source: redhat-operators sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: "*" --- apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding From 0d9b44578bf17a5ab1856e1fb4738114e70388a8 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 31 Jan 2022 16:21:01 -0600 Subject: [PATCH 0211/1263] Add code to validate origin for install/upgrade --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a46aabf9..4d017e17 100644 --- a/Makefile +++ b/Makefile @@ -33,13 +33,16 @@ test: # Test the charts as the pattern would drive them @for t in $(CHARTS); do common/scripts/test.sh $$t normal "$(TEST_OPTS) $(PATTERN_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done +validate-origin: + git ls-remote $(TARGET_REPO) + init: git submodule update --init --recursive -deploy: +deploy: validate-origin helm install $(NAME) common/install/ $(HELM_OPTS) -upgrade: +upgrade: validate-origin helm upgrade $(NAME) common/install/ $(HELM_OPTS) uninstall: From e7e6bc5d74a2a2f7beaabf8e15f853f30ce5a69b Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 31 Jan 2022 16:22:20 -0600 Subject: [PATCH 0212/1263] Add better domain alternation logic and Makefile validation --- clustergroup/templates/applications.yaml | 6 +----- clustergroup/templates/argocd.yaml | 1 + 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 0fede23e..aefe68d8 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -40,11 +40,7 @@ spec: - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain - {{- if $.Values.clusterGroup.isHubCluster }} - value: {{ $.Values.global.hubClusterDomain }} - {{- else }} - value: {{ $.Values.global.localClusterDomain }} - {{- end }} + value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} {{- range .overrides }} - name: {{ .name }} value: {{ .value | quote }} diff --git a/clustergroup/templates/argocd.yaml b/clustergroup/templates/argocd.yaml index 523e4268..9ed78dfe 100644 --- a/clustergroup/templates/argocd.yaml +++ b/clustergroup/templates/argocd.yaml @@ -25,6 +25,7 @@ spec: --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern={{ .Values.global.pattern }} --set global.hubClusterDomain={{ .Values.global.hubClusterDomain }} + --set global.localClusterDomain={{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }} --set global.valuesDirectoryURL={{ .Values.global.valuesDirectoryURL }} --post-renderer ./kustomize\"] \ \n" From 00e601ad32a5c8ae95567e07609cbf618ed17702 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 1 Feb 2022 18:14:36 +0100 Subject: [PATCH 0213/1263] Stop using echo when returning a string in a function This is not portable [1] and in some shells and also on bash depending on the version [2] it may or may not automatically interpret switches (like -n). Let's switch to printf which is the POSIX-blessed way of doing things [3]. Tested this on my environment and was able to still do a vault-init without any errors. [1] https://wiki.bash-hackers.org/scripting/nonportable#echo_command [2] https://stackoverflow.com/questions/11193466/echo-n-prints-n [3] https://wiki.bash-hackers.org/commands/builtin/printf --- scripts/vault-utils.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 1219884f..edd5b735 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -32,7 +32,7 @@ get_vault_ready() done fi - echo $rdy_output + printf "%s" "$rdy_output" } vault_unseal() @@ -99,7 +99,7 @@ vault_get_root_token() fi token=`grep "Initial Root Token" $file | awk '{ print $4 }'` - echo -n $token + printf "%s" "$token" } # Exec a vault command wrapped with the vault root token specified in the file @@ -141,14 +141,14 @@ oc_get_domain() oc_get_pki_domain() { - echo -n `oc_get_domain | cut -d. -f3-` + printf "%s" `oc_get_domain | cut -d. -f3-` } oc_get_pki_role() { pkidomain=`oc_get_pki_domain` - certrole=`echo $pkidomain | sed 's|\.|_|g'` - echo -n $certrole + certrole=`printf "%s" "$pkidomain" | sed 's|\.|_|g'` + printf "%s" "$certrole" } vault_pki_init() From c5b437301981d364d9a4a45861f0ddcb0da466d4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 3 Feb 2022 09:39:16 +0100 Subject: [PATCH 0214/1263] Add support for pushing the kube-root-ca.crt from the HUB to the managed clusters By default this ACM templates is inactive and will only be activated if asked explicitely via the .pushHubCA parameter. It will pull the ca.crt field from the the kube-root-ca.crt ConfigMap on the hub into a secret on the managed cluster. This will then be used by the external-secrets pod so it can trust the https://vault-vault.apps.hub-domain... API endpoint of the vault. Tested with this change and once enabled via .pushHubCA the kubernetes-external-secrets pod could correctly connect to the vault running on the HUB (without this we'd get self-signed certs errors) --- .../policies/hub-certificate-authority.yaml | 78 +++++++++++++++++++ acm/values.yaml | 1 + 2 files changed, 79 insertions(+) create mode 100644 acm/templates/policies/hub-certificate-authority.yaml diff --git a/acm/templates/policies/hub-certificate-authority.yaml b/acm/templates/policies/hub-certificate-authority.yaml new file mode 100644 index 00000000..1b24f329 --- /dev/null +++ b/acm/templates/policies/hub-certificate-authority.yaml @@ -0,0 +1,78 @@ +# We only push the hub CA to the regional clusters when the user explicitely tells us so +# This template fetches "ca.crt" from the "kube-root-ca.crt" configMap from the hub +# (this configmap is present in all namespaces) and puts it in the vault-ca secret inside +# the k8s-external-secrets namespace so the external-secrets pod knows how to trust +# the https://vault-vault.apps.hub-domain... endpoint +{{- if .Values.pushHubCA }} +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: hub-certificate-authority-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: hub-certificate-authority + spec: + remediationAction: enforce + severity: med + namespaceSelector: + exclude: + - kube-* + include: + - default + object-templates: + - complianceType: musthave + objectDefinition: + kind: Namespace + apiVersion: v1 + metadata: + name: k8s-external-secrets + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Secret + metadata: + name: vault-ca + namespace: k8s-external-secrets + data: + ca.crt: '{{ `{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}` }}' +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: hub-certificate-authority-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: hub-certificate-authority-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: hub-certificate-authority-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# We need to run this on any managed cluster but not on the HUB +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: hub-certificate-authority-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +{{- end }} diff --git a/acm/values.yaml b/acm/values.yaml index ae8c0a86..dc508f66 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -8,3 +8,4 @@ global: clusterGroup: managedClusterGroups: +pushHubCA: false From 04bd2d5bbc7103be1235fd7df252ddc14f5dca56 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 7 Feb 2022 12:59:46 -0600 Subject: [PATCH 0215/1263] Fix the TARGET_REPO calculation --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4d017e17..4ea46be9 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,9 @@ BOOTSTRAP=1 SECRETS=~/values-secret.yaml NAME=$(shell basename `pwd`) -TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL:[[:space:]]*//' -e 's%:[a-z].*@%@%' -e 's%:%/%' -e 's%git@%https://%' ) +# This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL +# This is because we expect to use tokens for repo authentication as opposed to SSH keys +TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL:[[:space:]]*//' -e 's%^git@%%' -e 's%^https://%%' -e 's%:%/%' -e 's%^%https://%') # git branch --show-current is also available as of git 2.22, but we will use this for compatibility TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spec.domain}) From f8ef0013c09fa7deeba054af7f11670722dcdb28 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 8 Feb 2022 10:34:09 +0100 Subject: [PATCH 0216/1263] Fix common/ make test Currently we fail in a bunch of tests. Let's fix these up so we'll be able to introduce some unit testing CI soon. Tested with: $ make test |grep FAIL Testing install chart (naked) Testing clustergroup chart (naked) Testing acm chart (naked) Testing install chart (normal) Testing clustergroup chart (normal) Testing acm chart (normal) --- tests/acm-naked.expected.yaml | 11 +++++++++++ tests/acm-normal.expected.yaml | 13 ++++++++++++- tests/clustergroup-naked.expected.yaml | 1 + tests/clustergroup-normal.expected.yaml | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index 313235c5..a1514518 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -2,6 +2,13 @@ # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io --- +# Source: acm/templates/policies/hub-certificate-authority.yaml +# We only push the hub CA to the regional clusters when the user explicitely tells us so +# This template fetches "ca.crt" from the "kube-root-ca.crt" configMap from the hub +# (this configmap is present in all namespaces) and puts it in the vault-ca secret inside +# the k8s-external-secrets namespace so the external-secrets pod knows how to trust +# the https://vault-vault.apps.hub-domain... endpoint +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub @@ -89,3 +96,7 @@ spec: name: openshift-gitops-operator source: redhat-operators sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: "*" diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index dcfbef68..3fe4b287 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -1,4 +1,11 @@ --- +# Source: acm/templates/policies/hub-certificate-authority.yaml +# We only push the hub CA to the regional clusters when the user explicitely tells us so +# This template fetches "ca.crt" from the "kube-root-ca.crt" configMap from the hub +# (this configmap is present in all namespaces) and puts it in the vault-ca secret inside +# the k8s-external-secrets namespace so the external-secrets pod knows how to trust +# the https://vault-vault.apps.hub-domain... endpoint +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub @@ -138,7 +145,7 @@ spec: - name: global.hubClusterDomain value: hub.example.com - name: global.localClusterDomain - value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - name: clusterGroup.isHubCluster value: "false" destination: @@ -203,3 +210,7 @@ spec: name: openshift-gitops-operator source: redhat-operators sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: "*" diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index e897228d..1db4e47d 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -80,6 +80,7 @@ spec: --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern=common --set global.hubClusterDomain= + --set global.localClusterDomain= --set global.valuesDirectoryURL=https://github.com/pattern-clone/common/raw/main --post-renderer ./kustomize\"] \ \n" diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index c82ac3a7..4ed6b6f1 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -208,6 +208,7 @@ spec: --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern=mypattern --set global.hubClusterDomain=hub.example.com + --set global.localClusterDomain=region.example.com --set global.valuesDirectoryURL=https://github.com/pattern-clone/mypattern/raw/main --post-renderer ./kustomize\"] \ \n" From 0e765d0e62a8c08d5a739d970f43e7e4a1eb8359 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 9 Feb 2022 10:07:45 -0600 Subject: [PATCH 0217/1263] Remove clusterselector for cases where we want the vault-ca installed on the hub cluster as well --- acm/templates/policies/hub-certificate-authority.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/acm/templates/policies/hub-certificate-authority.yaml b/acm/templates/policies/hub-certificate-authority.yaml index 1b24f329..a92bf04f 100644 --- a/acm/templates/policies/hub-certificate-authority.yaml +++ b/acm/templates/policies/hub-certificate-authority.yaml @@ -69,10 +69,4 @@ spec: clusterConditions: - status: 'True' type: ManagedClusterConditionAvailable - clusterSelector: - matchExpressions: - - key: local-cluster - operator: NotIn - values: - - 'true' {{- end }} From 999d586ecd55494c7370007cc504be022423ce2b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 8 Feb 2022 10:34:09 +0100 Subject: [PATCH 0218/1263] Fix common/ make test Currently we fail in a bunch of tests. Let's fix these up so we'll be able to introduce some unit testing CI soon. Tested with: $ make test |grep FAIL Testing install chart (naked) Testing clustergroup chart (naked) Testing acm chart (naked) Testing install chart (normal) Testing clustergroup chart (normal) Testing acm chart (normal) --- tests/acm-naked.expected.yaml | 11 +++++++++++ tests/acm-normal.expected.yaml | 13 ++++++++++++- tests/clustergroup-naked.expected.yaml | 1 + tests/clustergroup-normal.expected.yaml | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index 313235c5..a1514518 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -2,6 +2,13 @@ # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io --- +# Source: acm/templates/policies/hub-certificate-authority.yaml +# We only push the hub CA to the regional clusters when the user explicitely tells us so +# This template fetches "ca.crt" from the "kube-root-ca.crt" configMap from the hub +# (this configmap is present in all namespaces) and puts it in the vault-ca secret inside +# the k8s-external-secrets namespace so the external-secrets pod knows how to trust +# the https://vault-vault.apps.hub-domain... endpoint +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub @@ -89,3 +96,7 @@ spec: name: openshift-gitops-operator source: redhat-operators sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: "*" diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index dcfbef68..3fe4b287 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -1,4 +1,11 @@ --- +# Source: acm/templates/policies/hub-certificate-authority.yaml +# We only push the hub CA to the regional clusters when the user explicitely tells us so +# This template fetches "ca.crt" from the "kube-root-ca.crt" configMap from the hub +# (this configmap is present in all namespaces) and puts it in the vault-ca secret inside +# the k8s-external-secrets namespace so the external-secrets pod knows how to trust +# the https://vault-vault.apps.hub-domain... endpoint +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub @@ -138,7 +145,7 @@ spec: - name: global.hubClusterDomain value: hub.example.com - name: global.localClusterDomain - value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - name: clusterGroup.isHubCluster value: "false" destination: @@ -203,3 +210,7 @@ spec: name: openshift-gitops-operator source: redhat-operators sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: "*" diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index e897228d..1db4e47d 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -80,6 +80,7 @@ spec: --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern=common --set global.hubClusterDomain= + --set global.localClusterDomain= --set global.valuesDirectoryURL=https://github.com/pattern-clone/common/raw/main --post-renderer ./kustomize\"] \ \n" diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index c82ac3a7..4ed6b6f1 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -208,6 +208,7 @@ spec: --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern=mypattern --set global.hubClusterDomain=hub.example.com + --set global.localClusterDomain=region.example.com --set global.valuesDirectoryURL=https://github.com/pattern-clone/mypattern/raw/main --post-renderer ./kustomize\"] \ \n" From 700fd473859a1bcf16eb04f92d10cd4cf60c31d1 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 14 Feb 2022 08:06:37 -0600 Subject: [PATCH 0219/1263] Replicate https://github.com/hybrid-cloud-patterns/multicloud-gitops/pull/36 --- clustergroup/templates/argocd.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clustergroup/templates/argocd.yaml b/clustergroup/templates/argocd.yaml index 9ed78dfe..3423ecc0 100644 --- a/clustergroup/templates/argocd.yaml +++ b/clustergroup/templates/argocd.yaml @@ -1,3 +1,4 @@ +{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} apiVersion: argoproj.io/v1alpha1 kind: ArgoCD metadata: @@ -6,6 +7,7 @@ metadata: # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: {{ .Values.clusterGroup.name }}-gitops + namespace: {{ $namespace }} annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: From 33f8d772bee40abe7392172035ed969c1043e90c Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 15 Feb 2022 11:59:41 -0600 Subject: [PATCH 0220/1263] Remove policy to deploy vault CA as unnecessary --- .../policies/hub-certificate-authority.yaml | 72 ------------------- acm/values.yaml | 2 - 2 files changed, 74 deletions(-) delete mode 100644 acm/templates/policies/hub-certificate-authority.yaml diff --git a/acm/templates/policies/hub-certificate-authority.yaml b/acm/templates/policies/hub-certificate-authority.yaml deleted file mode 100644 index a92bf04f..00000000 --- a/acm/templates/policies/hub-certificate-authority.yaml +++ /dev/null @@ -1,72 +0,0 @@ -# We only push the hub CA to the regional clusters when the user explicitely tells us so -# This template fetches "ca.crt" from the "kube-root-ca.crt" configMap from the hub -# (this configmap is present in all namespaces) and puts it in the vault-ca secret inside -# the k8s-external-secrets namespace so the external-secrets pod knows how to trust -# the https://vault-vault.apps.hub-domain... endpoint -{{- if .Values.pushHubCA }} -apiVersion: policy.open-cluster-management.io/v1 -kind: Policy -metadata: - name: hub-certificate-authority-policy - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - argocd.argoproj.io/compare-options: IgnoreExtraneous -spec: - remediationAction: enforce - disabled: false - policy-templates: - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: hub-certificate-authority - spec: - remediationAction: enforce - severity: med - namespaceSelector: - exclude: - - kube-* - include: - - default - object-templates: - - complianceType: musthave - objectDefinition: - kind: Namespace - apiVersion: v1 - metadata: - name: k8s-external-secrets - - complianceType: musthave - objectDefinition: - apiVersion: v1 - kind: Secret - metadata: - name: vault-ca - namespace: k8s-external-secrets - data: - ca.crt: '{{ `{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}` }}' ---- -apiVersion: policy.open-cluster-management.io/v1 -kind: PlacementBinding -metadata: - name: hub-certificate-authority-placement-binding - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -placementRef: - name: hub-certificate-authority-placement - kind: PlacementRule - apiGroup: apps.open-cluster-management.io -subjects: - - name: hub-certificate-authority-policy - kind: Policy - apiGroup: policy.open-cluster-management.io ---- -# We need to run this on any managed cluster but not on the HUB -apiVersion: apps.open-cluster-management.io/v1 -kind: PlacementRule -metadata: - name: hub-certificate-authority-placement -spec: - clusterConditions: - - status: 'True' - type: ManagedClusterConditionAvailable -{{- end }} diff --git a/acm/values.yaml b/acm/values.yaml index dc508f66..d9c94583 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -7,5 +7,3 @@ global: clusterGroup: managedClusterGroups: - -pushHubCA: false From 69a3768c4426a4561d4136cf90f71ae5ae09abc9 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 16 Feb 2022 09:23:50 -0600 Subject: [PATCH 0221/1263] Changes to vault-utils to support vault/external-secrets combo --- scripts/vault-utils.sh | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index edd5b735..b6c27af8 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -13,7 +13,6 @@ vault_ready_check() { #NAME READY STATUS RESTARTS AGE #vault-0 1/1 Running 0 44m -#vault-agent-injector-8d9cfcd47-skklw 1/1 Running 0 121m oc get po -n vault | grep vault-0 | awk '{ print $2, $3 }' 2>/dev/null } @@ -70,6 +69,12 @@ vault_init() # The vault is ready to be initialized when it is "Running" but not "ready". Unsealing it makes it ready rdy_check=`get_vault_ready` + + if [ "$rdy_check" = "1/1 Running" ]; then + echo "Vault is already ready, exiting" + exit 0 + fi + until [ "$rdy_check" = "0/1 Running" ] do echo $rdy_check @@ -81,10 +86,13 @@ vault_init() vault_unseal $file vault_login $file - vault_pki_init $file - vault_kubernetes_init $file vault_secrets_init $file + vault_kubernetes_init $file vault_policy_init $file + + # Do not need pki init by default + # But this is how you could call it if you need it + #vault_pki_init $file } # Retrieves the root token specified in the file $1 @@ -169,23 +177,25 @@ vault_kubernetes_init() { file="$1" - vault_token=$(oc sa get-token -n vault vault) - k8s_host='kubernetes_host=https://$KUBERNETES_PORT_443_TCP_ADDR:443' - - vault_exec $file "vault auth enable kubernetes" - vault_exec $file "vault write auth/kubernetes/config token_reviewer_jwt=$vault_token $k8s_host kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc" + vault_exec $file "vault auth enable --path=hub kubernetes" } vault_policy_init() { file="$1" - vault_exec $file 'vault policy write hub-policy - << EOF -path "secret/*" - { capabilities = ["read"] + k8s_host='https://$KUBERNETES_PORT_443_TCP_ADDR:443' + secret_name="$(oc get -n k8s-external-secrets serviceaccount k8s-external-secrets-kubernetes-external-secrets -o jsonpath='{.secrets}' | jq -r '.[] | select(.name | test ("k8s-external-secrets-kubernetes-external-secrets-token-")).name')" + sa_token="$(oc get secret -n k8s-external-secrets ${secret_name} -o go-template='{{ .data.token | base64decode }}')" + + vault_exec $file "vault write auth/hub/config token_reviewer_jwt=$sa_token kubernetes_host=$k8s_host kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc" + vault_exec $file 'vault policy write hub-secret - << EOF +path "secret/data/hub/*" + { capabilities = ["create", "read", "update", "delete", "list"] } EOF ' + vault_exec $file 'vault write auth/hub/role/hub-role bound_service_account_names="k8s-external-secrets-kubernetes-external-secrets" bound_service_account_namespaces="k8s-external-secrets" policies="default,hub-secret" ttl="15m"' } vault_secrets_init() From 32a5ac428c002733f2f608f397edf72375fcef54 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 17 Feb 2022 19:50:35 +0100 Subject: [PATCH 0222/1263] Rename the namespace and serviceaccounts to the name of the new golang-based external secrets operator We're moving to the newer golang-based external secrets operator at https://external-secrets.io/ To be more explicit about our intention we name namespaces and serviceaccounts golang-external-secrets. Let's rename it in the hub config as well. Tested with the other related golang changes and everything worked as expected. --- scripts/vault-utils.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index b6c27af8..df4980c9 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -185,8 +185,8 @@ vault_policy_init() file="$1" k8s_host='https://$KUBERNETES_PORT_443_TCP_ADDR:443' - secret_name="$(oc get -n k8s-external-secrets serviceaccount k8s-external-secrets-kubernetes-external-secrets -o jsonpath='{.secrets}' | jq -r '.[] | select(.name | test ("k8s-external-secrets-kubernetes-external-secrets-token-")).name')" - sa_token="$(oc get secret -n k8s-external-secrets ${secret_name} -o go-template='{{ .data.token | base64decode }}')" + secret_name="$(oc get -n golang-external-secrets serviceaccount golang-external-secrets -o jsonpath='{.secrets}' | jq -r '.[] | select(.name | test ("golang-external-secrets-token-")).name')" + sa_token="$(oc get secret -n golang-external-secrets ${secret_name} -o go-template='{{ .data.token | base64decode }}')" vault_exec $file "vault write auth/hub/config token_reviewer_jwt=$sa_token kubernetes_host=$k8s_host kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc" vault_exec $file 'vault policy write hub-secret - << EOF @@ -195,7 +195,7 @@ path "secret/data/hub/*" } EOF ' - vault_exec $file 'vault write auth/hub/role/hub-role bound_service_account_names="k8s-external-secrets-kubernetes-external-secrets" bound_service_account_namespaces="k8s-external-secrets" policies="default,hub-secret" ttl="15m"' + vault_exec $file 'vault write auth/hub/role/hub-role bound_service_account_names="golang-external-secrets" bound_service_account_namespaces="golang-external-secrets" policies="default,hub-secret" ttl="15m"' } vault_secrets_init() From 6f6941928d0d012e22ef4468e0e63a7bf44ad334 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 17 Feb 2022 13:22:35 -0600 Subject: [PATCH 0223/1263] Add golang-external-secrets chart --- golang-external-secrets/Chart.yaml | 11 +++++++++ ...ternal-secrets-hub-clusterrolebinding.yaml | 14 +++++++++++ ...lang-external-secrets-hub-secretstore.yaml | 24 +++++++++++++++++++ golang-external-secrets/values.yaml | 3 +++ 4 files changed, 52 insertions(+) create mode 100644 golang-external-secrets/Chart.yaml create mode 100644 golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml create mode 100644 golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml create mode 100644 golang-external-secrets/values.yaml diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml new file mode 100644 index 00000000..141bf915 --- /dev/null +++ b/golang-external-secrets/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +description: A Helm chart to configure the golang-based external-secrets +keywords: +- pattern +name: golang-external-secrets +version: 0.0.1 +dependencies: + - name: external-secrets + version: "0.4.2" + repository: "https://charts.external-secrets.io" + #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml new file mode 100644 index 00000000..17fd4835 --- /dev/null +++ b/golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: role-tokenreview-binding + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: golang-external-secrets + namespace: golang-external-secrets diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml new file mode 100644 index 00000000..9980ca9a --- /dev/null +++ b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml @@ -0,0 +1,24 @@ +apiVersion: external-secrets.io/v1alpha1 +kind: ClusterSecretStore +metadata: + name: vault-backend + namespace: golang-external-secrets +spec: + provider: + vault: + server: https://vault-vault.{{ .Values.global.hubClusterDomain }} + path: secret + # Version of KV backend + version: v2 + caProvider: + type: ConfigMap + name: kube-root-ca.crt + key: ca.crt + namespace: golang-external-secrets + auth: + kubernetes: + mountPath: {{ .Values.mountPath }} + role: {{ .Values.mountRole }} + serviceAccountRef: + name: golang-external-secrets + namespace: golang-external-secrets diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml new file mode 100644 index 00000000..7724e9f4 --- /dev/null +++ b/golang-external-secrets/values.yaml @@ -0,0 +1,3 @@ +--- +mountPath: "hub" +mountRole: "hub-role" From ec21295c270057dfdc2154f6a79faaa7d8fb684a Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 22 Feb 2022 08:08:51 -0600 Subject: [PATCH 0224/1263] Add script to push vault secrets --- scripts/ansible-push-vault-secrets.sh | 120 ++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100755 scripts/ansible-push-vault-secrets.sh diff --git a/scripts/ansible-push-vault-secrets.sh b/scripts/ansible-push-vault-secrets.sh new file mode 100755 index 00000000..e75d651c --- /dev/null +++ b/scripts/ansible-push-vault-secrets.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env ansible-playbook +- name: Secret injection of validated-patterns + hosts: localhost + connection: local + gather_facts: no + vars: + values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" + kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" + vault_ns: "vault" + vault_pod: "vault-0" + vault_path: "secret/hub" + debug: False + + tasks: + - name: Check for existence of "{{ values_secret }}" + ansible.builtin.stat: + path: "{{ values_secret }}" + register: result + failed_when: not result.stat.exists + + - name: Check that KUBECONFIG is correctly set + fail: + msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" + failed_when: kubeconfig is not defined or kubeconfig | length == 0 + when: not debug | bool + + - name: Parse "{{ values_secret }}" + ansible.builtin.set_fact: + all_values: "{{ lookup('file', values_secret) | from_yaml }}" + + - name: Set secrets fact + ansible.builtin.set_fact: + secrets: "{{ all_values['secrets'] }}" + + - name: Verify we have any secrets at all + ansible.builtin.fail: + msg: "Was not able to parse any secrets from file {{ values_secret }}: {{ all_values }}" + failed_when: + secrets is not defined or secrets | length == 0 + + # Detect here if we have only the following two keys under a password + # s3.accessKey: + # s3.secretKey: + # If we do, then detect it and calculate the b64 s3Secret token + # Note: the vars: line is due to https://github.com/ansible/ansible/issues/40239 + - name: Check if any of the passwords has only s3.[accessKey,secretKey] and generate the combined s3Secret in that case + ansible.builtin.set_fact: + s3keys: "{{ s3keys | default({}) | combine({ item.key: {'s3Secret': s3secret | b64encode } }) }}" + vars: + s3secret: "{{ 's3.accessKey: ' + item.value['s3.accessKey'] + '\ns3.secretKey: ' + item.value['s3.secretKey'] }}" + when: + - '"s3.accessKey" in item.value.keys()' + - '"s3.secretKey" in item.value.keys()' + - '"s3Secret" not in item.value.keys()' + loop: + "{{ secrets | dict2items }}" + loop_control: + label: "{{ item.key }}" + + - name: Merge any s3Secret into the secrets dictionary if we have any + ansible.builtin.set_fact: + secrets: "{{ secrets | combine(s3keys) }}" + when: + s3keys is defined and s3keys | length > 0 + + - name: Check for vault namespace + kubernetes.core.k8s_info: + kind: Namespace + name: "{{ vault_ns }}" + register: vault_ns_rc + failed_when: vault_ns_rc.resources | length == 0 + when: not debug | bool + + - name: Check if the vault pod is present + kubernetes.core.k8s_info: + kind: Pod + namespace: "{{ vault_ns }}" + name: "{{ vault_pod }}" + register: vault_pod_rc + failed_when: vault_pod_rc.resources | length == 0 + when: not debug | bool + + # vault status returns 1 on error and 2 on sealed + # so we can bail out when sealed + - name: Check if the vault is unsealed + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault status + register: vault_status + failed_when: vault_status.rc|int == 1 + when: not debug | bool + + - name: Check vault status return + ansible.builtin.fail: + msg: The vault is still sealed. Please run "make vault-init" first with KUBECONFIG pointing to the HUB cluster + when: + - not debug | bool + - vault_status.rc | int > 0 + + - name: Debug + debug: + msg: "vault kv put {{ vault_path }}/{{ item.key }} -> {{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}" + loop: + "{{ secrets | dict2items }}" + loop_control: + label: "{{ item.key }}" + when: debug | bool + + - name: Add the actual secrets to the vault + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: | + sh -c "vault kv put {{ vault_path }}/{{ item.key }} {{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}" + loop: + "{{ secrets | dict2items }}" + loop_control: + label: "{{ item.key }}" + when: not debug | bool From c85985a9c2cbcda999a1666f0b198c5f3cdf952d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 27 Feb 2022 19:38:19 +0100 Subject: [PATCH 0225/1263] Fix test error in clustergroup example This fixed the following: Testing clustergroup chart (naked) --- tests/clustergroup-naked.expected.yaml 2022-02-27 18:06:11.474410537 +0100 +++ tests/.clustergroup-naked.expected.yaml 2022-02-27 19:29:17.534554450 +0100 @@ -61,6 +61,7 @@ # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: example-gitops + namespace: common-example annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: FAIL on clustergroup naked with opts [] make: *** [Makefile:34: test] Error 1 --- tests/clustergroup-naked.expected.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 1db4e47d..6245b702 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -61,6 +61,7 @@ metadata: # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: example-gitops + namespace: common-example annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: From 091695234590b2ffe8388a98fd772a79a6efca63 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 27 Feb 2022 19:39:08 +0100 Subject: [PATCH 0226/1263] Fix acm naked example test Currently errors out with: Testing acm chart (naked) --- tests/acm-naked.expected.yaml 2022-02-27 18:06:11.474410537 +0100 +++ tests/.acm-naked.expected.yaml 2022-02-27 19:38:40.898341311 +0100 @@ -2,13 +2,6 @@ # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io --- -# Source: acm/templates/policies/hub-certificate-authority.yaml -# We only push the hub CA to the regional clusters when the user explicitely tells us so -# This template fetches "ca.crt" from the "kube-root-ca.crt" configMap from the hub -# (this configmap is present in all namespaces) and puts it in the vault-ca secret inside -# the k8s-external-secrets namespace so the external-secrets pod knows how to trust -# the https://vault-vault.apps.hub-domain... endpoint ---- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub FAIL on acm naked with opts [] make: *** [Makefile:34: test] Error 1 This was just a leftover for when we removed the hub-ca app that pushed it around to managed clusters. --- tests/acm-naked.expected.yaml | 7 ------- tests/acm-normal.expected.yaml | 7 ------- 2 files changed, 14 deletions(-) diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index a1514518..2cd4858e 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -2,13 +2,6 @@ # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io --- -# Source: acm/templates/policies/hub-certificate-authority.yaml -# We only push the hub CA to the regional clusters when the user explicitely tells us so -# This template fetches "ca.crt" from the "kube-root-ca.crt" configMap from the hub -# (this configmap is present in all namespaces) and puts it in the vault-ca secret inside -# the k8s-external-secrets namespace so the external-secrets pod knows how to trust -# the https://vault-vault.apps.hub-domain... endpoint ---- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 3fe4b287..0bbe76bc 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -1,11 +1,4 @@ --- -# Source: acm/templates/policies/hub-certificate-authority.yaml -# We only push the hub CA to the regional clusters when the user explicitely tells us so -# This template fetches "ca.crt" from the "kube-root-ca.crt" configMap from the hub -# (this configmap is present in all namespaces) and puts it in the vault-ca secret inside -# the k8s-external-secrets namespace so the external-secrets pod knows how to trust -# the https://vault-vault.apps.hub-domain... endpoint ---- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub From 6efacc0fa36b7c827e0eedf4cc7ec436a853dd94 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 27 Feb 2022 20:10:46 +0100 Subject: [PATCH 0227/1263] Fix tests/clustergroup-normal.expected.yaml test --- tests/clustergroup-normal.expected.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 4ed6b6f1..25983b1a 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -189,6 +189,7 @@ metadata: # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: example-gitops + namespace: mypattern-example annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: From 4d51473db2c241e56be71c740855f6e4fc648d81 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 27 Feb 2022 20:46:40 +0100 Subject: [PATCH 0228/1263] Add initial checking github action on every pull/push At the moment this only runs "make test". We'll later expand this to do some additional linting. --- .github/linters/.markdown-lint.yml | 6 ++++ .github/workflows/linter.yml | 56 ++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 .github/linters/.markdown-lint.yml create mode 100644 .github/workflows/linter.yml diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml new file mode 100644 index 00000000..a0bc47d1 --- /dev/null +++ b/.github/linters/.markdown-lint.yml @@ -0,0 +1,6 @@ +{ + "default": true, + "MD003": false, + "MD013": false, + "MD033": false +} \ No newline at end of file diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml new file mode 100644 index 00000000..de70a301 --- /dev/null +++ b/.github/workflows/linter.yml @@ -0,0 +1,56 @@ +--- +name: Unit test common + +# +# Documentation: +# https://help.github.com/en/articles/workflow-syntax-for-github-actions +# + +############################# +# Start the job on all push # +############################# +on: [push, pull_request] + +############### +# Set the Job # +############### +jobs: + build: + # Name the Job + name: Unit common/ Code Base + # Set the agent to run on + runs-on: ubuntu-latest + + ################## + # Load all steps # + ################## + steps: + ########################## + # Checkout the code base # + ########################## + - name: Checkout Code + uses: actions/checkout@v2 + with: + # Full git history is needed to get a proper list of changed files within `super-linter` + fetch-depth: 0 + - name: Setup helm + uses: azure/setup-helm@v1 + # with: + # version: '' # default is latest stable + id: install + + ################################ + # Run Linter against code base # + ################################ + # - name: Lint Code Base + # uses: github/super-linter@v4 + # env: + # VALIDATE_ALL_CODEBASE: false + # DEFAULT_BRANCH: main + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ################################ + # Run make test # + ################################ + - name: Run make test + run: | + make test From 852fd51cfb00b7946512f6a1ff5a445d2683e4ba Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 28 Feb 2022 08:01:22 +0100 Subject: [PATCH 0229/1263] Add a helmlint target that runs helm lint over the charts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ❯ make helmlint ==> Linting install [INFO] Chart.yaml: icon is recommended 1 chart(s) linted, 0 chart(s) failed ==> Linting clustergroup [INFO] Chart.yaml: icon is recommended 1 chart(s) linted, 0 chart(s) failed ==> Linting acm [INFO] Chart.yaml: icon is recommended 1 chart(s) linted, 0 chart(s) failed --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 4ea46be9..6c323a5b 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,9 @@ test: # Test the charts as the pattern would drive them @for t in $(CHARTS); do common/scripts/test.sh $$t normal "$(TEST_OPTS) $(PATTERN_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done +helmlint: + @for t in $(CHARTS); do helm lint $$t; if [ $$? != 0 ]; then exit 1; fi; done + validate-origin: git ls-remote $(TARGET_REPO) From b143d5a5d6569e1019e5b858f4404cbd06b76717 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 28 Feb 2022 08:01:52 +0100 Subject: [PATCH 0230/1263] Run make helmlint on every push/pull request --- .github/workflows/linter.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index de70a301..6f261728 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -48,9 +48,9 @@ jobs: # VALIDATE_ALL_CODEBASE: false # DEFAULT_BRANCH: main # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ################################ - # Run make test # - ################################ - name: Run make test run: | make test + - name: Run make helmlint + run: | + make helmlint From b7415b85a7090a6ba79304ea72a10edc6f4ccf15 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 28 Feb 2022 08:06:02 +0100 Subject: [PATCH 0231/1263] Add golang-external-secrets to the charts being tested --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6c323a5b..8c356952 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ argosecret: show: helm template common/install/ --name-template $(NAME) $(HELM_OPTS) -CHARTS=install clustergroup acm +CHARTS=install clustergroup acm golang-external-secrets test: # Test that all values used by the chart are in values.yaml with the same defaults as the pattern From 7bf3f70ccf5717fc384a7f7d1d71849b9a07f4fb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 28 Feb 2022 08:13:34 +0100 Subject: [PATCH 0232/1263] Add right params to helmlint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ❯ make helmlint ==> Linting install [INFO] Chart.yaml: icon is recommended 1 chart(s) linted, 0 chart(s) failed ==> Linting clustergroup [INFO] Chart.yaml: icon is recommended 1 chart(s) linted, 0 chart(s) failed ==> Linting acm [INFO] Chart.yaml: icon is recommended 1 chart(s) linted, 0 chart(s) failed ==> Linting golang-external-secrets [INFO] Chart.yaml: icon is recommended [WARNING] /home/michele/Engineering/cloud-patterns/common/golang-external-secrets: chart directory is missing these dependencies: external-secrets 1 chart(s) linted, 0 chart(s) failed --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8c356952..f88449ec 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ test: @for t in $(CHARTS); do common/scripts/test.sh $$t normal "$(TEST_OPTS) $(PATTERN_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done helmlint: - @for t in $(CHARTS); do helm lint $$t; if [ $$? != 0 ]; then exit 1; fi; done + @for t in $(CHARTS); do helm lint $(TEST_OPTS) $(PATTERN_OPTS) $$t; if [ $$? != 0 ]; then exit 1; fi; done validate-origin: git ls-remote $(TARGET_REPO) From ce584302ed54268fc8ede598a53073c20e94cf0d Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 28 Feb 2022 10:23:14 -0600 Subject: [PATCH 0233/1263] Move make_common_subtree.sh to common from multicloud-gitops --- scripts/make_common_subtree.sh | 76 ++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100755 scripts/make_common_subtree.sh diff --git a/scripts/make_common_subtree.sh b/scripts/make_common_subtree.sh new file mode 100755 index 00000000..a5e406d8 --- /dev/null +++ b/scripts/make_common_subtree.sh @@ -0,0 +1,76 @@ +#!/bin/sh + +if [ "$1" = "-h" ]; then + echo "This script will convert common into a subtree and add a remote to help manage it." + echo "The script takes three positional arguments, as follows:" + echo + echo "$0 " + echo + echo "Run without arguments, the script would run as if these arguments had been passed:" + echo "$0 https://github.com/hybrid-cloud-patterns/common.git main common-subtree" + echo + echo "Please ensure the git subtree command is available. On RHEL/Fedora, the git subtree command" + echo "is in a separate package called git-subtree" + exit 1 +fi + +if [ -f '/etc/redhat-release' ]; then + rpm -qa | grep git-subtree 2>&1 + if [ ! $? = 0 ]; then + echo "you need to install git-subtree" + echo "would you like to install it now?" + select ANS in yes no + do + case $ANS in + yes) + sudo dnf install git-subtree -y + break + ;; + no) + exit + break + ;; + *) + echo "You must enter yes or no" + ;; + esac + done + fi +fi + +if [ "$1" ]; then + subtree_repo=$1 +else + subtree_repo=https://github.com/hybrid-cloud-patterns/common.git +fi + +if [ "$2" ]; then + subtree_branch=$2 +else + subtree_branch=main +fi + +if [ "$3" ]; then + subtree_remote=$3 +else + subtree_remote=common-subtree +fi + +git diff --quiet || (echo "This script must be run on a clean working tree" && exit 1) + +echo "Changing directory to project root" +cd `git rev-parse --show-toplevel` + +echo "Removing existing common and replacing it with subtree from $subtree_repo $subtree_remote" +rm -rf common + +echo "Committing removal of common" +(git add -A :/ && git commit -m "Removed previous version of common to convert to subtree from $subtree_repo $subtree_branch") || exit 1 + +echo "Adding (possibly replacing) subtree remote $subtree_remote" +git remote rm "$subtree_remote" +git remote add -f "$subtree_remote" "$subtree_repo" || exit 1 +git subtree add --prefix=common "$subtree_remote" "$subtree_branch" || exit 1 + +echo "Complete. You may now push these results if you are satisfied" +exit 0 From dc2106e3d694db2e000469a7924ee4e10a5ba394 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 7 Mar 2022 21:47:50 +0100 Subject: [PATCH 0234/1263] Find out charts dynamically --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index f88449ec..ee6520b8 100644 --- a/Makefile +++ b/Makefile @@ -27,8 +27,7 @@ argosecret: show: helm template common/install/ --name-template $(NAME) $(HELM_OPTS) -CHARTS=install clustergroup acm golang-external-secrets - +CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | sed -e 's/.\///') test: # Test that all values used by the chart are in values.yaml with the same defaults as the pattern @for t in $(CHARTS); do common/scripts/test.sh $$t naked ""; if [ $$? != 0 ]; then exit 1; fi; done From 4d582fa1ed4b2e51b7714caac37d77a499ae5fe4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 8 Mar 2022 11:33:22 +0100 Subject: [PATCH 0235/1263] Fix examples values to use imageregistry This avoids the following helm error: Error: template: example/templates/environment.yaml:6:28: executing "example/templates/environment.yaml" at <.Values.global.imageregistry.hostname>: nil pointer evaluating interface {}.hostname --- examples/kustomize-renderer/values.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/kustomize-renderer/values.yaml b/examples/kustomize-renderer/values.yaml index e713c393..cb80a03a 100644 --- a/examples/kustomize-renderer/values.yaml +++ b/examples/kustomize-renderer/values.yaml @@ -6,6 +6,7 @@ global: email: SOMEWHERE@EXAMPLE.COM dev_revision: main - quay: + imageregistry: provider: quay.io account: PLAINTEXT + From a1d404ae5494c84dc41980ec5a6072e1e07d4f1a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 8 Mar 2022 11:38:22 +0100 Subject: [PATCH 0236/1263] Add naked and normal expected.yaml files for examples-kustomize-renderer chart Now that we find charts dynamically we need to make sure this chart is covered as well --- ...les-kustomize-renderer-naked.expected.yaml | 36 +++++++++++++++++++ ...es-kustomize-renderer-normal.expected.yaml | 36 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 tests/examples-kustomize-renderer-naked.expected.yaml create mode 100644 tests/examples-kustomize-renderer-normal.expected.yaml diff --git a/tests/examples-kustomize-renderer-naked.expected.yaml b/tests/examples-kustomize-renderer-naked.expected.yaml new file mode 100644 index 00000000..0aa7ee5d --- /dev/null +++ b/tests/examples-kustomize-renderer-naked.expected.yaml @@ -0,0 +1,36 @@ +--- +# Source: example/templates/environment.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: environment +data: + IMAGE_PROVIDER: + IMAGE_ACCOUNT: PLAINTEXT + GIT_EMAIL: SOMEWHERE@EXAMPLE.COM + GIT_DEV_REPO_URL: https:///PLAINTEXT/manuela-dev.git + GIT_DEV_REPO_REVISION: main + GIT_OPS_REPO_TEST_URL: + GIT_OPS_REPO_TEST_REVISION: + GIT_OPS_REPO_PROD_URL: + GIT_OPS_REPO_PROD_REVISION: + IOT_CONSUMER_IMAGE: iot-consumer + IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag + IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml + IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml + IOT_FRONTEND_IMAGE: iot-frontend + IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag + IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml + IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml + IOT_SWSENSOR_IMAGE: iot-software-sensor + IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag + IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml + IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml + IOT_ANOMALY_IMAGE: iot-anomaly-detection + IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag + IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml + IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/tests/examples-kustomize-renderer-normal.expected.yaml b/tests/examples-kustomize-renderer-normal.expected.yaml new file mode 100644 index 00000000..09e04b22 --- /dev/null +++ b/tests/examples-kustomize-renderer-normal.expected.yaml @@ -0,0 +1,36 @@ +--- +# Source: example/templates/environment.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: environment +data: + IMAGE_PROVIDER: + IMAGE_ACCOUNT: PLAINTEXT + GIT_EMAIL: someone@somewhere.com + GIT_DEV_REPO_URL: https://github.com/hybrid-cloud-patterns/manuela-dev.git + GIT_DEV_REPO_REVISION: main + GIT_OPS_REPO_TEST_URL: https://github.com/pattern-clone/mypattern + GIT_OPS_REPO_TEST_REVISION: + GIT_OPS_REPO_PROD_URL: https://github.com/pattern-clone/mypattern + GIT_OPS_REPO_PROD_REVISION: + IOT_CONSUMER_IMAGE: iot-consumer + IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag + IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml + IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml + IOT_FRONTEND_IMAGE: iot-frontend + IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag + IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml + IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml + IOT_SWSENSOR_IMAGE: iot-software-sensor + IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag + IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml + IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml + IOT_ANOMALY_IMAGE: iot-anomaly-detection + IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag + IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml + IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml From d3e64196ea9fb0ab8cb8500d31da612592717c18 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 8 Mar 2022 11:58:33 +0100 Subject: [PATCH 0237/1263] Add proper naked+normal golang-secrets expected yaml files --- ...olang-external-secrets-naked.expected.yaml | 2924 +++++++++++++++++ ...lang-external-secrets-normal.expected.yaml | 2924 +++++++++++++++++ 2 files changed, 5848 insertions(+) create mode 100644 tests/golang-external-secrets-naked.expected.yaml create mode 100644 tests/golang-external-secrets-normal.expected.yaml diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml new file mode 100644 index 00000000..fc9fb287 --- /dev/null +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -0,0 +1,2924 @@ +--- +# Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: golang-external-secrets + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/external-secrets.io_clustersecretstores.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: clustersecretstores.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ClusterSecretStore + listKind: ClusterSecretStoreList + plural: clustersecretstores + shortNames: + - css + singular: clustersecretstore + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterSecretStore represents a secure external location for + storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) + The KES controller is instantiated with a specific controller name + and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may + be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using + Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to + be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates + with Akeyless. + properties: + secretRef: + description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: + AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within + a Secret resource, In some instances, `key` is a + required field. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within + a Secret resource, In some instances, `key` is a + required field. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using + Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references + for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS + Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate + against AWS if not set aws sdk will infer credentials from + your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account + tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource + being referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references + for AWS credentials both AccessKeyID and SecretAccessKey + must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider + will assume + type: string + service: + description: Service defines which service should be used + to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using + Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates + with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle + used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle + used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecret + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the + keyvault service. Valid values are: - "ServicePrincipal" + (default): Using a service principal (tenantId, clientId, + clientSecret) - "ManagedIdentity": Using Managed Identity + assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the + pod, you can select the one to be used + type: string + tenantId: + description: TenantID configures the Azure Tenant to send + requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched + from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using + Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate + against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource + being referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: GItlab configures this store to sync secrets using + Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are + located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults + to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM + Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific + to the Secrets Manager service instance + type: string + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using + Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with the Oracle Vault. If empty, use the instance principal, + otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the + API private key. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing + Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is + located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault + where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using + Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the + App Role auth mechanism, with the role and secret stored + in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication + backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication + backend when setting up the authentication backend + in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains + the App Role secret used to authenticate with Vault. + The `key` field must be specified and denotes which + entry within the Secret resource is used as the + app role secret. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates + by passing client certificate, private key and ca certificate + Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate + using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource + containing client private key to authenticate with + Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role + and JWT token using the JWT/OIDC authentication method + properties: + path: + default: jwt + description: 'Path where the JWT authentication backend + is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using + the JWT/OIDC Vault authentication method + type: string + secretRef: + description: SecretRef to a key in a Secret resource + containing JWT token to authenticate with Vault + using the JWT/OIDC authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing + the ServiceAccount token stored in the named Secret + resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication + backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault + Role to assume. A Role binds a Kubernetes ServiceAccount + with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes + ServiceAccount JWT used for authenticating with + Vault. If a name is specified without a key, `token` + is the default. If one is not specified, the one + bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing + the name of a kubernetes ServiceAccount. If the + service account is specified, the service account + secret token JWT will be used for authenticating + with Vault. If the service account selector is not + supplied, the secretRef will be used instead. + properties: + name: + description: The name of the ServiceAccount resource + being referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing + username/password pair using the LDAP authentication + method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend + is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource + containing password for the LDAP user used to authenticate + with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to + authenticate using the LDAP Vault authentication + method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by + presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault + server certificate. Only used if the Server URL is using + HTTPS protocol. This parameter is ignored for plain HTTP + protocol connection. If not set the system root certificates + are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate + Vault server certificate. + properties: + key: + description: The key the value inside of the provider + type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider + type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", + or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write + requests to the Vault leader instead of simply retrying + within a loop. This can increase performance if the option + is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a + set of features within Vault Enterprise that allows Vault + environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend + endpoint, e.g: "secret". The v2 KV secret engine version + specific "/data" path suffix for fetching secrets from Vault + is optional and will be appended if not present in specified + path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write + semantics by providing discovered cluster replication states + in each request. More information about eventual consistency + in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault + server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. + This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using + a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook + server certificate. Only used if the Server URL is using + HTTPS protocol. This parameter is ignored for plain HTTP + protocol connection. If not set the system root certificates + are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate + webhook server certificate. + properties: + key: + description: The key the value inside of the provider + type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider + type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", + or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will + be passed to the templating function as key value pairs + under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it may + be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of the + referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets + using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate + against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate + Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a + Secret resource, In some instances, `key` is a required + field. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/external-secrets.io_externalsecrets.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: externalsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ExternalSecret + listKind: ExternalSecretList + plural: externalsecrets + shortNames: + - es + singular: externalsecret + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. + properties: + data: + description: Data defines the connection between the Kubernetes Secret + keys and the Provider data + items: + description: ExternalSecretData defines the connection between the + Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data + location. + properties: + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider + value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider + value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific + Provider data If multiple entries are specified, the Secret keys + are merged in the specified order + items: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider + value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider + value, if supported + type: string + required: + - key + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values + are read again from the SecretStore provider Valid time units are + "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to + fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the + ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or + ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + description: ExternalSecretTarget defines the Kubernetes Secret to + be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the + resulting Secret Defaults to 'Owner' + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be + managed This field is immutable Defaults to the .metadata.name + of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret + resource. + properties: + data: + additionalProperties: + type: string + type: object + metadata: + description: ExternalSecretTemplateMetadata defines metadata + fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + - target + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret + was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced + version + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/external-secrets.io_secretstores.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: secretstores.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: SecretStore + listKind: SecretStoreList + plural: secretstores + shortNames: + - ss + singular: secretstore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: SecretStore represents a secure external location for storing + secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) + The KES controller is instantiated with a specific controller name + and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may + be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using + Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to + be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates + with Akeyless. + properties: + secretRef: + description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: + AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within + a Secret resource, In some instances, `key` is a + required field. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within + a Secret resource, In some instances, `key` is a + required field. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using + Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references + for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS + Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate + against AWS if not set aws sdk will infer credentials from + your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account + tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource + being referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references + for AWS credentials both AccessKeyID and SecretAccessKey + must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider + will assume + type: string + service: + description: Service defines which service should be used + to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using + Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates + with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle + used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle + used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecret + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the + keyvault service. Valid values are: - "ServicePrincipal" + (default): Using a service principal (tenantId, clientId, + clientSecret) - "ManagedIdentity": Using Managed Identity + assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the + pod, you can select the one to be used + type: string + tenantId: + description: TenantID configures the Azure Tenant to send + requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched + from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using + Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate + against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource + being referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: GItlab configures this store to sync secrets using + Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are + located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults + to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM + Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific + to the Secrets Manager service instance + type: string + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using + Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with the Oracle Vault. If empty, use the instance principal, + otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the + API private key. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing + Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is + located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault + where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using + Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the + App Role auth mechanism, with the role and secret stored + in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication + backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication + backend when setting up the authentication backend + in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains + the App Role secret used to authenticate with Vault. + The `key` field must be specified and denotes which + entry within the Secret resource is used as the + app role secret. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates + by passing client certificate, private key and ca certificate + Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate + using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource + containing client private key to authenticate with + Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role + and JWT token using the JWT/OIDC authentication method + properties: + path: + default: jwt + description: 'Path where the JWT authentication backend + is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using + the JWT/OIDC Vault authentication method + type: string + secretRef: + description: SecretRef to a key in a Secret resource + containing JWT token to authenticate with Vault + using the JWT/OIDC authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing + the ServiceAccount token stored in the named Secret + resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication + backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault + Role to assume. A Role binds a Kubernetes ServiceAccount + with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes + ServiceAccount JWT used for authenticating with + Vault. If a name is specified without a key, `token` + is the default. If one is not specified, the one + bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing + the name of a kubernetes ServiceAccount. If the + service account is specified, the service account + secret token JWT will be used for authenticating + with Vault. If the service account selector is not + supplied, the secretRef will be used instead. + properties: + name: + description: The name of the ServiceAccount resource + being referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing + username/password pair using the LDAP authentication + method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend + is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource + containing password for the LDAP user used to authenticate + with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to + authenticate using the LDAP Vault authentication + method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by + presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault + server certificate. Only used if the Server URL is using + HTTPS protocol. This parameter is ignored for plain HTTP + protocol connection. If not set the system root certificates + are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate + Vault server certificate. + properties: + key: + description: The key the value inside of the provider + type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider + type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", + or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write + requests to the Vault leader instead of simply retrying + within a loop. This can increase performance if the option + is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a + set of features within Vault Enterprise that allows Vault + environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend + endpoint, e.g: "secret". The v2 KV secret engine version + specific "/data" path suffix for fetching secrets from Vault + is optional and will be appended if not present in specified + path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write + semantics by providing discovered cluster replication states + in each request. More information about eventual consistency + in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault + server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. + This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using + a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook + server certificate. Only used if the Server URL is using + HTTPS protocol. This parameter is ignored for plain HTTP + protocol connection. If not set the system root certificates + are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate + webhook server certificate. + properties: + key: + description: The key the value inside of the provider + type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider + type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", + or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will + be passed to the templating function as key value pairs + under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it may + be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of the + referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets + using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate + against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate + Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a + Secret resource, In some instances, `key` is a required + field. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-controller + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "secretstores" + - "clustersecretstores" + - "externalsecrets" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "externalsecrets/status" + - "externalsecrets/finalizers" + - "secretstores" + - "secretstores/status" + - "secretstores/finalizers" + - "clustersecretstores" + - "clustersecretstores/status" + - "clustersecretstores/finalizers" + verbs: + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "serviceaccounts" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "list" + - "watch" + - "create" + - "update" + - "delete" + - "patch" + - apiGroups: + - "" + resources: + - "serviceaccounts/token" + verbs: + - "create" + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-view + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm + rbac.authorization.k8s.io/aggregate-to-view: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "secretstores" + - "clustersecretstores" + verbs: + - "get" + - "watch" + - "list" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-edit + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "secretstores" + - "clustersecretstores" + verbs: + - "create" + - "delete" + - "deletecollection" + - "patch" + - "update" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: golang-external-secrets-controller + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: golang-external-secrets-controller +subjects: + - name: golang-external-secrets + namespace: "default" + kind: ServiceAccount +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: role-tokenreview-binding + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: golang-external-secrets + namespace: golang-external-secrets +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: golang-external-secrets-leaderelection + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "" + resources: + - "configmaps" + resourceNames: + - "external-secrets-controller" + verbs: + - "get" + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "create" + - apiGroups: + - "coordination.k8s.io" + resources: + - "leases" + verbs: + - "get" + - "create" + - "update" + - "patch" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: golang-external-secrets-leaderelection + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: golang-external-secrets-leaderelection +subjects: + - kind: ServiceAccount + name: golang-external-secrets + namespace: "default" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + spec: + serviceAccountName: golang-external-secrets + containers: + - name: external-secrets + image: "ghcr.io/external-secrets/external-secrets:v0.4.2" + imagePullPolicy: IfNotPresent + args: + - --concurrent=1 + ports: + - containerPort: 8080 + protocol: TCP + name: metrics +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +apiVersion: external-secrets.io/v1alpha1 +kind: ClusterSecretStore +metadata: + name: vault-backend + namespace: golang-external-secrets +spec: + provider: + vault: + server: https://vault-vault.hub.example.com + path: secret + # Version of KV backend + version: v2 + caProvider: + type: ConfigMap + name: kube-root-ca.crt + key: ca.crt + namespace: golang-external-secrets + auth: + kubernetes: + mountPath: hub + role: hub-role + serviceAccountRef: + name: golang-external-secrets + namespace: golang-external-secrets diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml new file mode 100644 index 00000000..fc9fb287 --- /dev/null +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -0,0 +1,2924 @@ +--- +# Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: golang-external-secrets + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/external-secrets.io_clustersecretstores.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: clustersecretstores.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ClusterSecretStore + listKind: ClusterSecretStoreList + plural: clustersecretstores + shortNames: + - css + singular: clustersecretstore + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterSecretStore represents a secure external location for + storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) + The KES controller is instantiated with a specific controller name + and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may + be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using + Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to + be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates + with Akeyless. + properties: + secretRef: + description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: + AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within + a Secret resource, In some instances, `key` is a + required field. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within + a Secret resource, In some instances, `key` is a + required field. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using + Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references + for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS + Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate + against AWS if not set aws sdk will infer credentials from + your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account + tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource + being referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references + for AWS credentials both AccessKeyID and SecretAccessKey + must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider + will assume + type: string + service: + description: Service defines which service should be used + to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using + Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates + with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle + used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle + used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecret + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the + keyvault service. Valid values are: - "ServicePrincipal" + (default): Using a service principal (tenantId, clientId, + clientSecret) - "ManagedIdentity": Using Managed Identity + assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the + pod, you can select the one to be used + type: string + tenantId: + description: TenantID configures the Azure Tenant to send + requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched + from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using + Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate + against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource + being referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: GItlab configures this store to sync secrets using + Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are + located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults + to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM + Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific + to the Secrets Manager service instance + type: string + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using + Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with the Oracle Vault. If empty, use the instance principal, + otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the + API private key. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing + Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is + located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault + where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using + Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the + App Role auth mechanism, with the role and secret stored + in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication + backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication + backend when setting up the authentication backend + in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains + the App Role secret used to authenticate with Vault. + The `key` field must be specified and denotes which + entry within the Secret resource is used as the + app role secret. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates + by passing client certificate, private key and ca certificate + Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate + using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource + containing client private key to authenticate with + Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role + and JWT token using the JWT/OIDC authentication method + properties: + path: + default: jwt + description: 'Path where the JWT authentication backend + is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using + the JWT/OIDC Vault authentication method + type: string + secretRef: + description: SecretRef to a key in a Secret resource + containing JWT token to authenticate with Vault + using the JWT/OIDC authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing + the ServiceAccount token stored in the named Secret + resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication + backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault + Role to assume. A Role binds a Kubernetes ServiceAccount + with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes + ServiceAccount JWT used for authenticating with + Vault. If a name is specified without a key, `token` + is the default. If one is not specified, the one + bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing + the name of a kubernetes ServiceAccount. If the + service account is specified, the service account + secret token JWT will be used for authenticating + with Vault. If the service account selector is not + supplied, the secretRef will be used instead. + properties: + name: + description: The name of the ServiceAccount resource + being referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing + username/password pair using the LDAP authentication + method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend + is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource + containing password for the LDAP user used to authenticate + with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to + authenticate using the LDAP Vault authentication + method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by + presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault + server certificate. Only used if the Server URL is using + HTTPS protocol. This parameter is ignored for plain HTTP + protocol connection. If not set the system root certificates + are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate + Vault server certificate. + properties: + key: + description: The key the value inside of the provider + type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider + type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", + or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write + requests to the Vault leader instead of simply retrying + within a loop. This can increase performance if the option + is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a + set of features within Vault Enterprise that allows Vault + environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend + endpoint, e.g: "secret". The v2 KV secret engine version + specific "/data" path suffix for fetching secrets from Vault + is optional and will be appended if not present in specified + path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write + semantics by providing discovered cluster replication states + in each request. More information about eventual consistency + in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault + server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. + This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using + a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook + server certificate. Only used if the Server URL is using + HTTPS protocol. This parameter is ignored for plain HTTP + protocol connection. If not set the system root certificates + are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate + webhook server certificate. + properties: + key: + description: The key the value inside of the provider + type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider + type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", + or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will + be passed to the templating function as key value pairs + under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it may + be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of the + referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets + using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate + against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate + Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a + Secret resource, In some instances, `key` is a required + field. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/external-secrets.io_externalsecrets.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: externalsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ExternalSecret + listKind: ExternalSecretList + plural: externalsecrets + shortNames: + - es + singular: externalsecret + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. + properties: + data: + description: Data defines the connection between the Kubernetes Secret + keys and the Provider data + items: + description: ExternalSecretData defines the connection between the + Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data + location. + properties: + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider + value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider + value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific + Provider data If multiple entries are specified, the Secret keys + are merged in the specified order + items: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider + value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider + value, if supported + type: string + required: + - key + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values + are read again from the SecretStore provider Valid time units are + "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to + fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the + ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or + ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + description: ExternalSecretTarget defines the Kubernetes Secret to + be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the + resulting Secret Defaults to 'Owner' + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be + managed This field is immutable Defaults to the .metadata.name + of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret + resource. + properties: + data: + additionalProperties: + type: string + type: object + metadata: + description: ExternalSecretTemplateMetadata defines metadata + fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + - target + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret + was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced + version + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/external-secrets.io_secretstores.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: secretstores.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: SecretStore + listKind: SecretStoreList + plural: secretstores + shortNames: + - ss + singular: secretstore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: SecretStore represents a secure external location for storing + secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) + The KES controller is instantiated with a specific controller name + and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may + be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using + Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to + be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates + with Akeyless. + properties: + secretRef: + description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: + AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within + a Secret resource, In some instances, `key` is a + required field. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within + a Secret resource, In some instances, `key` is a + required field. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using + Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references + for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS + Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate + against AWS if not set aws sdk will infer credentials from + your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account + tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource + being referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references + for AWS credentials both AccessKeyID and SecretAccessKey + must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider + will assume + type: string + service: + description: Service defines which service should be used + to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using + Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates + with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle + used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle + used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecret + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the + keyvault service. Valid values are: - "ServicePrincipal" + (default): Using a service principal (tenantId, clientId, + clientSecret) - "ManagedIdentity": Using Managed Identity + assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the + pod, you can select the one to be used + type: string + tenantId: + description: TenantID configures the Azure Tenant to send + requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched + from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using + Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate + against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource + being referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: GItlab configures this store to sync secrets using + Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are + located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults + to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM + Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific + to the Secrets Manager service instance + type: string + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using + Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with the Oracle Vault. If empty, use the instance principal, + otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the + API private key. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing + Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is + located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault + where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using + Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates + with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the + App Role auth mechanism, with the role and secret stored + in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication + backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication + backend when setting up the authentication backend + in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains + the App Role secret used to authenticate with Vault. + The `key` field must be specified and denotes which + entry within the Secret resource is used as the + app role secret. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates + by passing client certificate, private key and ca certificate + Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate + using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource + containing client private key to authenticate with + Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role + and JWT token using the JWT/OIDC authentication method + properties: + path: + default: jwt + description: 'Path where the JWT authentication backend + is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using + the JWT/OIDC Vault authentication method + type: string + secretRef: + description: SecretRef to a key in a Secret resource + containing JWT token to authenticate with Vault + using the JWT/OIDC authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing + the ServiceAccount token stored in the named Secret + resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication + backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault + Role to assume. A Role binds a Kubernetes ServiceAccount + with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes + ServiceAccount JWT used for authenticating with + Vault. If a name is specified without a key, `token` + is the default. If one is not specified, the one + bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing + the name of a kubernetes ServiceAccount. If the + service account is specified, the service account + secret token JWT will be used for authenticating + with Vault. If the service account selector is not + supplied, the secretRef will be used instead. + properties: + name: + description: The name of the ServiceAccount resource + being referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing + username/password pair using the LDAP authentication + method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend + is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource + containing password for the LDAP user used to authenticate + with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it + may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of + the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to + authenticate using the LDAP Vault authentication + method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by + presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault + server certificate. Only used if the Server URL is using + HTTPS protocol. This parameter is ignored for plain HTTP + protocol connection. If not set the system root certificates + are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate + Vault server certificate. + properties: + key: + description: The key the value inside of the provider + type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider + type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", + or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write + requests to the Vault leader instead of simply retrying + within a loop. This can increase performance if the option + is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a + set of features within Vault Enterprise that allows Vault + environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend + endpoint, e.g: "secret". The v2 KV secret engine version + specific "/data" path suffix for fetching secrets from Vault + is optional and will be appended if not present in specified + path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write + semantics by providing discovered cluster replication states + in each request. More information about eventual consistency + in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault + server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. + This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using + a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook + server certificate. Only used if the Server URL is using + HTTPS protocol. This parameter is ignored for plain HTTP + protocol connection. If not set the system root certificates + are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate + webhook server certificate. + properties: + key: + description: The key the value inside of the provider + type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider + type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", + or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will + be passed to the templating function as key value pairs + under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret + resource's `data` field to be used. Some instances + of this field may be defaulted, in others it may + be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. + cluster-scoped defaults to the namespace of the + referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets + using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate + against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate + Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a + Secret resource, In some instances, `key` is a required + field. + properties: + key: + description: The key of the entry in the Secret resource's + `data` field to be used. Some instances of this + field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being + referred to. + type: string + namespace: + description: Namespace of the resource being referred + to. Ignored if referent is not cluster-scoped. cluster-scoped + defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-controller + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "secretstores" + - "clustersecretstores" + - "externalsecrets" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "externalsecrets/status" + - "externalsecrets/finalizers" + - "secretstores" + - "secretstores/status" + - "secretstores/finalizers" + - "clustersecretstores" + - "clustersecretstores/status" + - "clustersecretstores/finalizers" + verbs: + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "serviceaccounts" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "list" + - "watch" + - "create" + - "update" + - "delete" + - "patch" + - apiGroups: + - "" + resources: + - "serviceaccounts/token" + verbs: + - "create" + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-view + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm + rbac.authorization.k8s.io/aggregate-to-view: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "secretstores" + - "clustersecretstores" + verbs: + - "get" + - "watch" + - "list" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-edit + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "secretstores" + - "clustersecretstores" + verbs: + - "create" + - "delete" + - "deletecollection" + - "patch" + - "update" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: golang-external-secrets-controller + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: golang-external-secrets-controller +subjects: + - name: golang-external-secrets + namespace: "default" + kind: ServiceAccount +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: role-tokenreview-binding + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: golang-external-secrets + namespace: golang-external-secrets +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: golang-external-secrets-leaderelection + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "" + resources: + - "configmaps" + resourceNames: + - "external-secrets-controller" + verbs: + - "get" + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "create" + - apiGroups: + - "coordination.k8s.io" + resources: + - "leases" + verbs: + - "get" + - "create" + - "update" + - "patch" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: golang-external-secrets-leaderelection + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: golang-external-secrets-leaderelection +subjects: + - kind: ServiceAccount + name: golang-external-secrets + namespace: "default" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.4.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.4.2" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + spec: + serviceAccountName: golang-external-secrets + containers: + - name: external-secrets + image: "ghcr.io/external-secrets/external-secrets:v0.4.2" + imagePullPolicy: IfNotPresent + args: + - --concurrent=1 + ports: + - containerPort: 8080 + protocol: TCP + name: metrics +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +apiVersion: external-secrets.io/v1alpha1 +kind: ClusterSecretStore +metadata: + name: vault-backend + namespace: golang-external-secrets +spec: + provider: + vault: + server: https://vault-vault.hub.example.com + path: secret + # Version of KV backend + version: v2 + caProvider: + type: ConfigMap + name: kube-root-ca.crt + key: ca.crt + namespace: golang-external-secrets + auth: + kubernetes: + mountPath: hub + role: hub-role + serviceAccountRef: + name: golang-external-secrets + namespace: golang-external-secrets From ec614f7895d695e5189f86aee0e7fc9034f09f09 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 8 Mar 2022 11:59:28 +0100 Subject: [PATCH 0238/1263] Add external-secrets dependency chart to golang-external-secrets Otherwise any linting will complain and/or fail. We add external-secrets-0.4.2.tgz to golang-external-secrets/charts/ This was done with: helm dependency update golang-external-secrets --- .../charts/external-secrets-0.4.2.tgz | Bin 0 -> 18405 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 golang-external-secrets/charts/external-secrets-0.4.2.tgz diff --git a/golang-external-secrets/charts/external-secrets-0.4.2.tgz b/golang-external-secrets/charts/external-secrets-0.4.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..7ce50a244500e03f754f22205902d557ac45b679 GIT binary patch literal 18405 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 Date: Tue, 8 Mar 2022 12:00:54 +0100 Subject: [PATCH 0239/1263] Make sure we fail if helm template errors out We need to explicitely error out when helm template fails so we catch any errors during templating itself --- scripts/test.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/test.sh b/scripts/test.sh index 6e400fc0..f426ef01 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -12,6 +12,11 @@ OUTPUT=${TESTDIR}/.${name}-${TEST_VARIANT}.expected.yaml echo "Testing $1 chart (${TEST_VARIANT})" >&2 helm template $target --name-template $name ${CHART_OPTS} > ${OUTPUT} +rc=$? +if [ $rc -ne 0 ]; then + echo "FAIL on helm template $target --name-template $name ${CHART_OPTS}" + exit 1 +fi #cp ${OUTPUT} ${REFERENCE} if [ ! -e ${REFERENCE} ]; then touch ${REFERENCE} From f4ac53aa2fcf7214fa9aaa01af2b0634db31849a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 8 Mar 2022 12:01:41 +0100 Subject: [PATCH 0240/1263] Add global hubClusterDomain to golang-external-secrets values This makes it so that we have a default value and helm templating won't barf --- golang-external-secrets/values.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index 7724e9f4..b4656f3b 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -1,3 +1,6 @@ --- mountPath: "hub" mountRole: "hub-role" + +global: + hubClusterDomain: hub.example.com From 30ab3dcb7d66915ccf535f8ec710161150b31735 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 8 Mar 2022 12:03:22 +0100 Subject: [PATCH 0241/1263] Remove Makefile.toplevel I have not seen it referenced in any other file nor repo in our org. --- Makefile.toplevel | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 Makefile.toplevel diff --git a/Makefile.toplevel b/Makefile.toplevel deleted file mode 100644 index 1317d8b9..00000000 --- a/Makefile.toplevel +++ /dev/null @@ -1,16 +0,0 @@ -BOOTSTRAP=1 -ARGO_TARGET_NAMESPACE=replaceme - -.PHONY: default -default: show - -%: - make -f common/Makefile $* - -install: deploy -ifeq ($(BOOTSTRAP),1) - make -f common/Makefile TARGET_NAMESPACE=$(ARGO_TARGET_NAMESPACE) argosecret -endif - -secret: - make -f common/Makefile TARGET_NAMESPACE=$(ARGO_TARGET_NAMESPACE) argosecret From 41b0d23d3db34ba8976fa0f5bf4a9426eb28bf4a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 13 Mar 2022 22:49:04 +0100 Subject: [PATCH 0242/1263] Update secret.sh from Industrial Edge Via 465b1c40 in Industrial Edge we updated common/scripts/secret.sh in the IE repo only. Let's bring those changes back into common and while we're at it, let's make the indenting more consistent. --- scripts/secret.sh | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/scripts/secret.sh b/scripts/secret.sh index fc9bde81..554a2a8a 100755 --- a/scripts/secret.sh +++ b/scripts/secret.sh @@ -17,32 +17,51 @@ src_ns="${PATTERN}-${COMPONENT}" ns=0 gitops=0 +# Function log +# Arguments: +# $1 are for the options for echo +# $2 is for the message +# \033[0K\r - Trailing escape sequence to leave output on the same line +function log { + if [ -z "$2" ]; then + echo -e "\033[0K\r\033[1;36m$1\033[0m" + else + echo -e $1 "\033[0K\r\033[1;36m$2" + fi +} + # Check for Namespaces and Secrets to be ready (it takes the cluster a few minutes to deploy them) +spin='-\|/' +i=0 while [ 1 ] ; do - echo -n "Checking for namespace $TARGET_NAMESPACE to exist..." + i=$(( (i+1) %4 )) + log -n "Checking for namespace $TARGET_NAMESPACE to exist: ${spin:$i:1}" if [ oc get namespace $TARGET_NAMESPACE >/dev/null 2>/dev/null ]; then - echo "not yet" ns=0 sleep 2 continue else - echo "OK" + log "Checking for namespace $TARGET_NAMESPACE to exist: OK" ns=1 + break fi +done - echo -n "Checking for $passwd_resource to be populated in $src_ns..." +i=0 +while [ 1 ] ; do + i=$(( (i+1) %4 )) + log -n "Checking for $passwd_resource to be populated in $src_ns: ${spin:$i:1}" pw=`oc -n $src_ns extract $passwd_resource --to=- 2>/dev/null` if [ "$?" = 0 ] && [ -n "$pw" ]; then - echo "OK" + log "Checking for $passwd_resource to be populated in $src_ns: OK" gitops=1 else - echo "not yet" gitops=0 sleep 2 continue fi - echo "Conditions met, managing secret $SECRET_NAME in $TARGET_NAMESPACE" + log "Conditions met, managing secret $SECRET_NAME in $TARGET_NAMESPACE" break done From f34f3d5194bb827ebfb9462101b89c0130714522 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 13 Mar 2022 22:54:36 +0100 Subject: [PATCH 0243/1263] Remove old vault-token.sh script It is not used anywhere any longer: $ grep -ir vault-token industrial-edge multicloud-gitops common medical-diagnosis $ --- scripts/vault-token.sh | 64 ------------------------------------------ 1 file changed, 64 deletions(-) delete mode 100755 scripts/vault-token.sh diff --git a/scripts/vault-token.sh b/scripts/vault-token.sh deleted file mode 100755 index 8a141fd5..00000000 --- a/scripts/vault-token.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/sh - -#This script is written to be POSIX-compliant, as not all sh are created equal - many are bash but -#Debian-derivatives use dash which doesn't support some bash syntax -# -#TARGET_NAMESPACE=manuela-ci - -TARGET_NAMESPACE="external-secrets-deployable" -token_resource="vault" -src_ns="vault" - -ns=0 -ok=0 - -# Check for Namespaces and Secrets to be ready (it takes the cluster a few minutes to deploy them) -while [ 1 ] ; do - echo -n "Checking for namespace $TARGET_NAMESPACE to exist..." - if [ oc get namespace $TARGET_NAMESPACE >/dev/null 2>/dev/null ]; then - echo "not yet" - ns=0 - sleep 2 - continue - else - echo "OK" - ns=1 - fi - - echo -n "Checking for $token_resource to be populated in $src_ns..." - tok=`oc sa get-token -n $src_ns $token_resource 2>/dev/null` - if [ "$?" = 0 ] && [ -n "$tok" ]; then - echo "OK" - ok=1 - else - echo "not yet" - ok=0 - sleep 2 - continue - fi - - echo "Conditions met, managing sa token $token_resource in $TARGET_NAMESPACE" - break -done - -TOKEN=$(echo $tok | base64 -w 0) -cat << HERE | oc apply -f- -apiVersion: apps.open-cluster-management.io/v1 -kind: Deployable -metadata: - name: custom-kubernetes-token - namespace: external-secrets-deployable -spec: - channels: - - external-secrets-ns - template: - apiVersion: v1 - kind: Secret - metadata: - name: custom-kubernetes-token - data: - token: ${TOKEN} - type: Opaque -HERE - -#echo "{ \"apiVersion\": \"apps.open-cluster-management.io/v1\", \"kind\": \"Deployable\", \"metadata\": { \"name\": \"custom-kubernetes-token\", \"namespace\": \"external-secrets-deployable\" }, { spec: { \"channels\": \[\"data\": { \"ARGOCD_PASSWORD\": \"$password\", \"ARGOCD_USERNAME\": \"$user\" }, \"type\": \"Opaque\" }" | oc apply -f- From ed592b33ba044b579b4d92f448d5abbc493dc447 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 18 Mar 2022 08:12:32 +0100 Subject: [PATCH 0244/1263] Make the default policy for hub/* secrets more restrictive Leave only the read one for the time being as it seems to be a saner default. If more granularity is needed customizations are qreuired in any case. Tested and I can still correctly access the secret on the config-demo app on the regional cluster. --- scripts/vault-utils.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index df4980c9..b6a71df2 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -191,7 +191,7 @@ vault_policy_init() vault_exec $file "vault write auth/hub/config token_reviewer_jwt=$sa_token kubernetes_host=$k8s_host kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc" vault_exec $file 'vault policy write hub-secret - << EOF path "secret/data/hub/*" - { capabilities = ["create", "read", "update", "delete", "list"] + { capabilities = ["read"] } EOF ' From 2c1df91f066ad5b8952fc924586af166983c1285 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 18 Mar 2022 12:45:50 +0100 Subject: [PATCH 0245/1263] Remove init target Common is not a submodule any longer. No point in keeping this around --- Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Makefile b/Makefile index ee6520b8..4fb0327c 100644 --- a/Makefile +++ b/Makefile @@ -40,9 +40,6 @@ helmlint: validate-origin: git ls-remote $(TARGET_REPO) -init: - git submodule update --init --recursive - deploy: validate-origin helm install $(NAME) common/install/ $(HELM_OPTS) From 9991186a954cf08ca2a2a9c13321e209b51ffd67 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 18 Mar 2022 11:10:34 -0500 Subject: [PATCH 0246/1263] Add load-secrets target --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index f88449ec..eedf72a9 100644 --- a/Makefile +++ b/Makefile @@ -59,4 +59,7 @@ vault-init: vault-unseal: common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init +load-secrets: + common/scripts/ansible-push-vault-secrets.sh + .phony: install test From dc7ff55f3cabdb49b1b5bf5ef3d587d930666317 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Mar 2022 10:48:25 +0100 Subject: [PATCH 0247/1263] Switch to mustonlyhave complianceType in templates pushed out by ACM As somewhat specified in [1] there are different complianceTypes and 'musthave' does not imply that the object being applied is *identical* to what is specified in the policy. Namely the docs for 'musthave' state: "The other fields in the template are a subset of what exists in the object." The actual consequence on real deployment of 'musthave' is the following: * Existing object - foo bar: - a - b If the above template gets changed in ACM: - foo bar: - d - e The end result in case of 'musthave' complianceType will be: - foo bar: - a - b - d - e So let's switch to complianceType 'mustonlyhave' which actually enforces the full object: Indicates that an object must exist with the exact name and relevant fields. Tested this on my multicloud environment and I could observe that changes to the ACM policy templates started properly replacing objects on the remote clusters. [1] https://access.redhat.com/documentation/en-us/red_hat_advanced_cluster_management_for_kubernetes/2.4/html-single/governance/index#configuration-policy-yaml-table Closes: MBP-199 --- acm/templates/policies/application-policies.yaml | 2 +- acm/templates/policies/ocp-gitops-policy.yaml | 2 +- tests/acm-naked.expected.yaml | 2 +- tests/acm-normal.expected.yaml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 5ab3d8b8..39486a6c 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -25,7 +25,7 @@ spec: include: - default object-templates: - - complianceType: musthave + - complianceType: mustonlyhave objectDefinition: apiVersion: argoproj.io/v1alpha1 kind: Application diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index e6f7013b..65159159 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -26,7 +26,7 @@ spec: include: - default object-templates: - - complianceType: musthave + - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index 2cd4858e..864cacc4 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -73,7 +73,7 @@ spec: include: - default object-templates: - - complianceType: musthave + - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 0bbe76bc..961d03dd 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -105,7 +105,7 @@ spec: include: - default object-templates: - - complianceType: musthave + - complianceType: mustonlyhave objectDefinition: apiVersion: argoproj.io/v1alpha1 kind: Application @@ -187,7 +187,7 @@ spec: include: - default object-templates: - - complianceType: musthave + - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT apiVersion: operators.coreos.com/v1alpha1 From 3be221a78e1fb60b312b215c72f97e99be43a3f4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 24 Mar 2022 15:03:48 +0100 Subject: [PATCH 0248/1263] Add a make help target to list all existing targets This improves the UX discoverability This will give the following output when run from inside common: $ make help help This help message argosecret creates the argo secret show show the starting template without installing it test run helm tests helmlint run helm lint validate-origin verify the git origin is available deploy deploys the pattern upgrade runs helm upgrade uninstall runs helm uninstall vault-init inits, unseals and configured the vault vault-unseal unseals the vault load-secrets loads the secrets into the vault Tested on Linux (bash+zsh) and on Mac OSX (bash+zsh) --- Makefile | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 00c926d2..25a37242 100644 --- a/Makefile +++ b/Makefile @@ -13,49 +13,54 @@ HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_R TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set main.options.bootstrap=$(BOOTSTRAP) --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com PATTERN_OPTS=-f common/examples/values-example.yaml + +.PHONY: help +help: ## This help message + @printf "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)\n" + # Makefiles that use this target must provide: # PATTERN: The name of the pattern that is using it. This will be used programmatically for the source namespace # TARGET_NAMESPACE: target namespace to install the secret into # COMPONENT: The component of the target namespace. In industrial edge, factory or datacenter - and for the secret # it needs to be datacenter because that's where the CI components run. # SECRET_NAME: The name of the secret to manage -argosecret: +argosecret: ## creates the argo secret PATTERN="$(PATTERN)" TARGET_NAMESPACE="$(TARGET_NAMESPACE)" COMPONENT="$(COMPONENT)" SECRET_NAME="$(SECRET_NAME)" common/scripts/secret.sh # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show -show: +show: ## show the starting template without installing it helm template common/install/ --name-template $(NAME) $(HELM_OPTS) CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | sed -e 's/.\///') -test: +test: ## run helm tests # Test that all values used by the chart are in values.yaml with the same defaults as the pattern @for t in $(CHARTS); do common/scripts/test.sh $$t naked ""; if [ $$? != 0 ]; then exit 1; fi; done # Test the charts as the pattern would drive them @for t in $(CHARTS); do common/scripts/test.sh $$t normal "$(TEST_OPTS) $(PATTERN_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done -helmlint: +helmlint: ## run helm lint @for t in $(CHARTS); do helm lint $(TEST_OPTS) $(PATTERN_OPTS) $$t; if [ $$? != 0 ]; then exit 1; fi; done -validate-origin: +validate-origin: ## verify the git origin is available git ls-remote $(TARGET_REPO) -deploy: validate-origin +deploy: validate-origin ## deploys the pattern helm install $(NAME) common/install/ $(HELM_OPTS) -upgrade: validate-origin +upgrade: validate-origin ## runs helm upgrade helm upgrade $(NAME) common/install/ $(HELM_OPTS) -uninstall: +uninstall: ## runs helm uninstall helm uninstall $(NAME) -vault-init: +vault-init: ## inits, unseals and configured the vault common/scripts/vault-utils.sh vault_init common/pattern-vault.init -vault-unseal: +vault-unseal: ## unseals the vault common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init -load-secrets: +load-secrets: ## loads the secrets into the vault common/scripts/ansible-push-vault-secrets.sh .phony: install test From 3e936f423a4ca1984a84ef51aa608974e1626357 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 29 Mar 2022 20:17:26 +0200 Subject: [PATCH 0249/1263] Do not bail out when KUBECONFIG is unset We first check for ~/.kube/config and if it also does not exist then we bail out. There should be no need to set anything else in the playbook as ~/.kube/config is automatically read by the ansible kubernetes.core collection [1] [1] https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.kubectl_connection.rst --- scripts/ansible-push-vault-secrets.sh | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/scripts/ansible-push-vault-secrets.sh b/scripts/ansible-push-vault-secrets.sh index e75d651c..2ea0f23a 100755 --- a/scripts/ansible-push-vault-secrets.sh +++ b/scripts/ansible-push-vault-secrets.sh @@ -4,8 +4,9 @@ connection: local gather_facts: no vars: - values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" + kubeconfig_backup: "{{ lookup('env', 'HOME') }}/.kube/config" + values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" vault_ns: "vault" vault_pod: "vault-0" vault_path: "secret/hub" @@ -18,11 +19,20 @@ register: result failed_when: not result.stat.exists - - name: Check that KUBECONFIG is correctly set - fail: - msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" - failed_when: kubeconfig is not defined or kubeconfig | length == 0 - when: not debug | bool + - name: Check if KUBECONFIG is correctly set + debug: + msg: "KUBECONFIG is not set, falling back to ~/.kube/config" + when: kubeconfig is not defined or kubeconfig | length == 0 + + - name: Check if ~/.kube/config exists + ansible.builtin.stat: + path: "{{ kubeconfig_backup }}" + register: kubeconfig_result + + - name: Fail if both KUBECONFIG and ~/.kube/config do not exist + ansible.builtin.fail: + msg: "{{ kubeconfig_backup }} not found and KUBECONFIG unset. Bailing out." + failed_when: not kubeconfig_result.stat.exists - name: Parse "{{ values_secret }}" ansible.builtin.set_fact: From d9246aad69a28cf97f71f5c0e1068eaf7f8c035e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 30 Mar 2022 16:53:18 +0200 Subject: [PATCH 0250/1263] Add a kubeval makefile target This runs helm kubeval on the CHARTS found. It will discover a certain class of errors in templates which might make it useful (it does pretty basic checks). So it will detect stuff like: The file config-demo/templates/config-demo-deployment.yaml contains an invalid Deployment ---> spec.replicas: Invalid type. Expected: [integer,null], given: string E.g. a non valid replica gets detected. --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 25a37242..0b7a34b9 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,9 @@ test: ## run helm tests helmlint: ## run helm lint @for t in $(CHARTS); do helm lint $(TEST_OPTS) $(PATTERN_OPTS) $$t; if [ $$? != 0 ]; then exit 1; fi; done +kubeval: ## run helm kubeval + @for t in $(CHARTS); do helm kubeval --ignore-missing-schemas $$t; if [ $$? != 0 ]; then exit 1; fi; done + validate-origin: ## verify the git origin is available git ls-remote $(TARGET_REPO) From 9e4c2c5d3cdd7cb4a616342cb3daf16f970b6a76 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 30 Mar 2022 16:56:13 +0200 Subject: [PATCH 0251/1263] Add helm kubeval task to linter This will run the 'make kubeval' target in the linter and produce an output like the following: See https://kubeval.instrumenta.dev for help getting started. Installed plugin: kubeval make -f common/Makefile CHARTS="charts/all/config-demo" kubeval make[1]: Entering directory '/home/runner/work/multicloud-gitops/multicloud-gitops' Warning: Set to ignore missing schemas ... The file config-demo/templates/config-demo-cm.yaml contains a valid ConfigMap The file config-demo/templates/config-demo-svc.yaml contains a valid Service The file config-demo/templates/config-demo-deployment.yaml contains a valid Deployment The file config-demo/templates/config-demo-secret.yaml contains an empty YAML document The file config-demo/templates/config-demo-external-secret.yaml containing a ExternalSecret was not validated against a schema The file config-demo/templates/config-demo-is.yaml containing a ImageStream was not validated against a schema The file config-demo/templates/config-demo-secret.yaml containing a PlacementBinding was not validated against a schema The file config-demo/templates/config-demo-secret.yaml containing a PlacementRule was not validated against a schema The file config-demo/templates/config-demo-secret.yaml containing a Policy was not validated against a schema The file config-demo/templates/config-demo-route.yaml containing a Route was not validated against a schema make[1]: Leaving directory '/home/runner/work/multicloud-gitops/multicloud-gitops' make -f common/Makefile CHARTS="charts/hub/golang-external-secrets" kubeval --- .github/workflows/linter.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 6f261728..5c5f48dd 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -51,6 +51,15 @@ jobs: - name: Run make test run: | make test + - name: Run make helmlint run: | make helmlint + + - name: Run make helm kubeval + run: | + curl -L -O https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz + tar xf kubeval-linux-amd64.tar.gz + sudo mv -v kubeval /usr/local/bin + helm plugin install https://github.com/instrumenta/helm-kubeval + make kubeval From b2e89f3413cf1dd0655a16ea9b826778dd67f0e8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 31 Mar 2022 13:00:25 +0200 Subject: [PATCH 0252/1263] Allow to override the remote name Currently we hard-code origin as the remote to use for git. Allow the user to override this via TARGET_ORIGIN variable (which defaults to 'origin' like before) Tested with: make upgrade make TARGET_ORIGIN=fork upgrade And the commands used the 'origin' remote and the 'fork' one respectively. Reason for this is that I hate calling my forks 'origin' and I rarely even have an 'origin' remote. I call my remotes 'fork' so I do not push branches upstream accidentally. --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0b7a34b9..98a5341e 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,8 @@ SECRETS=~/values-secret.yaml NAME=$(shell basename `pwd`) # This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL # This is because we expect to use tokens for repo authentication as opposed to SSH keys -TARGET_REPO=$(shell git remote show origin | grep Push | sed -e 's/.*URL:[[:space:]]*//' -e 's%^git@%%' -e 's%^https://%%' -e 's%:%/%' -e 's%^%https://%') +TARGET_ORIGIN ?= origin +TARGET_REPO=$(shell git remote show $(TARGET_ORIGIN) | grep Push | sed -e 's/.*URL:[[:space:]]*//' -e 's%^git@%%' -e 's%^https://%%' -e 's%:%/%' -e 's%^%https://%') # git branch --show-current is also available as of git 2.22, but we will use this for compatibility TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spec.domain}) From 6550ecf22cd2ecb577ace5ecf2c6919f284974fd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 31 Mar 2022 15:20:16 +0200 Subject: [PATCH 0253/1263] Update external-secrets to 0.4.4 Tested on an OCP 4.9 cluster via multicloud-gitops: - Correctly obtained the config-demo secret once the vault was unsealed and the secret loaded --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.4.2.tgz | Bin 18405 -> 0 bytes .../charts/external-secrets-0.4.4.tgz | Bin 0 -> 18517 bytes ...olang-external-secrets-naked.expected.yaml | 43 +++++++++++------- ...lang-external-secrets-normal.expected.yaml | 43 +++++++++++------- 5 files changed, 53 insertions(+), 35 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.4.2.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.4.4.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 141bf915..f257fcd7 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.4.2" + version: "0.4.4" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.4.2.tgz b/golang-external-secrets/charts/external-secrets-0.4.2.tgz deleted file mode 100644 index 7ce50a244500e03f754f22205902d557ac45b679..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18405 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 Date: Thu, 31 Mar 2022 21:54:46 +0200 Subject: [PATCH 0254/1263] Move to kubeconform Note that this depends on the generated static files that we push out into our ocp-schemas repo. Example CI run: stdin - PlacementBinding edge-placement-binding is valid stdin - PlacementBinding openshift-gitops-placement-binding is valid stdin - PlacementRule edge-placement is valid stdin - PlacementRule openshift-gitops-placement is valid stdin - MultiClusterHub multiclusterhub is valid stdin - Policy openshift-gitops-policy is valid stdin - Policy edge-clustergroup-policy is valid stdin - ConfigMap environment is valid stdin - clustersecretstores.external-secrets.io CustomResourceDefinition skipped stdin - externalsecrets.external-secrets.io CustomResourceDefinition skipped stdin - secretstores.external-secrets.io CustomResourceDefinition skipped stdin - ClusterRole release-name-external-secrets-view is valid stdin - ClusterRole release-name-external-secrets-controller is valid stdin - ClusterRole release-name-external-secrets-edit is valid stdin - ServiceAccount release-name-external-secrets is valid stdin - Role release-name-external-secrets-leaderelection is valid stdin - ClusterRoleBinding release-name-external-secrets-controller is valid stdin - ClusterRoleBinding role-tokenreview-binding is valid stdin - RoleBinding release-name-external-secrets-leaderelection is valid stdin - ClusterSecretStore vault-backend is valid stdin - Deployment release-name-external-secrets is valid stdin - Namespace openshift-gitops is valid stdin - Subscription openshift-gitops-operator is valid stdin - Application release-name-example is valid stdin - ClusterRoleBinding openshift-gitops-cluster-admin-rolebinding is valid stdin - ClusterRoleBinding mypattern-example-cluster-admin-rolebinding is valid stdin - Namespace mypattern-example is valid stdin - Namespace application-ci is valid stdin - Namespace open-cluster-management is valid stdin - Application acm is valid stdin - Application pipelines is valid stdin - AppProject datacenter is valid stdin - OperatorGroup application-ci-operator-group is valid stdin - OperatorGroup open-cluster-management-operator-group is valid stdin - ArgoCD example-gitops is valid stdin - Subscription openshift-pipelines-operator-rh is valid stdin - Subscription advanced-cluster-management is valid --- .github/workflows/linter.yml | 11 +++++------ Makefile | 6 ++++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 5c5f48dd..81eee65b 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -56,10 +56,9 @@ jobs: run: | make helmlint - - name: Run make helm kubeval + - name: Run make helm kubeconform run: | - curl -L -O https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz - tar xf kubeval-linux-amd64.tar.gz - sudo mv -v kubeval /usr/local/bin - helm plugin install https://github.com/instrumenta/helm-kubeval - make kubeval + curl -L -O https://github.com/yannh/kubeconform/releases/download/v0.4.13/kubeconform-linux-amd64.tar.gz + tar xf kubeconform-linux-amd64.tar.gz + sudo mv -v kubeconform /usr/local/bin + make kubeconform diff --git a/Makefile b/Makefile index 98a5341e..db583a1b 100644 --- a/Makefile +++ b/Makefile @@ -43,8 +43,10 @@ test: ## run helm tests helmlint: ## run helm lint @for t in $(CHARTS); do helm lint $(TEST_OPTS) $(PATTERN_OPTS) $$t; if [ $$? != 0 ]; then exit 1; fi; done -kubeval: ## run helm kubeval - @for t in $(CHARTS); do helm kubeval --ignore-missing-schemas $$t; if [ $$? != 0 ]; then exit 1; fi; done +API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.9/ +# We need to skip 'CustomResourceDefinition' as openapi2jsonschema seems to be unable to generate them ATM +kubeconform: ## run helm kubeconform + @for t in $(CHARTS); do helm template $(TEST_OPTS) $(PATTERN_OPTS) $$t | kubeconform -strict -skip 'CustomResourceDefinition' -verbose -schema-location $(API_URL); if [ $$? != 0 ]; then exit 1; fi; done validate-origin: ## verify the git origin is available git ls-remote $(TARGET_REPO) From d4e65bae87786515a036872cf1e63456fcdbca33 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 6 Apr 2022 20:52:04 +0200 Subject: [PATCH 0255/1263] Bring in some change that was missed In multicloud-gitops via e5c6fb68 (Remove some obsolete options and add needed settings for isHubCluster) it was missed to backport the common hunk to the common/ proper tree. Fix this so proper common/ and multicloud-gitops/common/ do not differ at all. --- clustergroup/values.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index aec0fcd4..58643cc2 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -8,10 +8,7 @@ global: clusterGroup: name: example - - proposedOptions: - manageGitops: True - isHubCluster: True + isHubCluster: true # managedClusterGroups: # - name: factory From 6ab87aa7e4c85717c4ab79ab8cec70f4bde04046 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 8 Apr 2022 09:47:41 +0200 Subject: [PATCH 0256/1263] Allow skipped resource types to be customized Of course openapi2json is broken with Tekton Pipelines and Tasks, so we need to be able to skip resources on a per-project basis. --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index db583a1b..8cc874bb 100644 --- a/Makefile +++ b/Makefile @@ -44,9 +44,10 @@ helmlint: ## run helm lint @for t in $(CHARTS); do helm lint $(TEST_OPTS) $(PATTERN_OPTS) $$t; if [ $$? != 0 ]; then exit 1; fi; done API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.9/ +KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition' # We need to skip 'CustomResourceDefinition' as openapi2jsonschema seems to be unable to generate them ATM kubeconform: ## run helm kubeconform - @for t in $(CHARTS); do helm template $(TEST_OPTS) $(PATTERN_OPTS) $$t | kubeconform -strict -skip 'CustomResourceDefinition' -verbose -schema-location $(API_URL); if [ $$? != 0 ]; then exit 1; fi; done + @for t in $(CHARTS); do helm template $(TEST_OPTS) $(PATTERN_OPTS) $$t | kubeconform -strict $(KUBECONFORM_SKIP) -verbose -schema-location $(API_URL); if [ $$? != 0 ]; then exit 1; fi; done validate-origin: ## verify the git origin is available git ls-remote $(TARGET_REPO) From 1d3befcbbaa833abb3553835e252a55eadadb096 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 15 Apr 2022 10:16:39 +0200 Subject: [PATCH 0257/1263] Make sure we only bail out when both KUBECONFIG and ~/.kube/config are not set Currently there is an error in the logic there and it bails out when ~/.kube/config does not exist even though KUBECONFIG is set. Let's fix that --- scripts/ansible-push-vault-secrets.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ansible-push-vault-secrets.sh b/scripts/ansible-push-vault-secrets.sh index 2ea0f23a..6b36e66f 100755 --- a/scripts/ansible-push-vault-secrets.sh +++ b/scripts/ansible-push-vault-secrets.sh @@ -32,7 +32,7 @@ - name: Fail if both KUBECONFIG and ~/.kube/config do not exist ansible.builtin.fail: msg: "{{ kubeconfig_backup }} not found and KUBECONFIG unset. Bailing out." - failed_when: not kubeconfig_result.stat.exists + failed_when: not kubeconfig_result.stat.exists and (kubeconfig is not defined or kubeconfig | length == 0) - name: Parse "{{ values_secret }}" ansible.builtin.set_fact: From 557ef3f1e3e9ce926cb4ff930a085760c8e5b4e7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 15 Apr 2022 09:34:45 +0200 Subject: [PATCH 0258/1263] Stop bailing out if the vault file exists The rationale for this change is the following: - We recently made it so that make install, will also init the vault and add the secrets to it - Now if I run 'make install' but had an older common/pattern-vault.init lying around, make install will do the deploy and as soon as we call vault init the process will error out due to the file already existing. This is suboptimal because at this point a user won't be sure what to do and what steps are needed to complete the installation. Let's just move the old file to a .bak one. If one already existed we overwrite it (we cannot use mv --backup pattern as it does not exist on Mac OSX). This should be a reasonable compromise between usability and avoiding to overwrite the last version of the vault init output. Tested this and correctly got: make[2]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' common/scripts/vault-utils.sh vault_init common/pattern-vault.init common/pattern-vault.init already exists and contains seal secrets. We're moving it away to common/pattern-vault.init.bak renamed 'common/pattern-vault.init' -> 'common/pattern-vault.init.bak' No resources found in vault namespace. No resources found in vault namespace. ... 0/1 ContainerCreating 0/1 ContainerCreating Unseal Key 1: HfIACkOmMyR/jOdHU1ykILbsp83h1AX9JPqgN06uk/p0 Unseal Key 2: HYiV+NULGiSZDdQZMMdlGnK6CypX0DE7X7nMJ8dpW9+Z Unseal Key 3: l9hYpMttX7b/y1eBCaysLB3QSUVnEymCZw5uewZVGUSo ... --- scripts/vault-utils.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index df4980c9..9f0ab4e9 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -63,8 +63,8 @@ vault_init() fi if [ -f "$file" ] && grep -q -e '^Unseal' "$file"; then - echo "$file already exists and contains seal secrets. If this is what you really wanted, move that file away first or call vault_delete" - exit 1 + echo "$file already exists and contains seal secrets. We're moving it away to ${file}.bak" + mv -vf "${file}" "${file}.bak" fi # The vault is ready to be initialized when it is "Running" but not "ready". Unsealing it makes it ready From d3878c44172e16eafbc9feb9651521ae7617acc7 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 18 Apr 2022 13:31:53 -0500 Subject: [PATCH 0259/1263] Add .bak file to .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 765c0dda..29de39ee 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ values-secret.yaml .*.expected.yaml pattern-vault.init -vault.init +pattern-vault.init.bak From 02cac7e35357d2d71f60fa2517943cad4d2a674a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 19 Apr 2022 20:07:12 +0200 Subject: [PATCH 0260/1263] Remove the bootstrap concept from common/ It used to be for deploying multiple patterns. Let's drop it for the time being: Nowhere do we document how and when to use it anyways. --- Makefile | 5 ++--- install/templates/argocd/namespace.yaml | 2 -- install/templates/argocd/subscription.yaml | 2 -- install/values.yaml | 1 - 4 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 8cc874bb..021403de 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,3 @@ -BOOTSTRAP=1 SECRETS=~/values-secret.yaml NAME=$(shell basename `pwd`) # This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL @@ -10,8 +9,8 @@ TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spec.domain}) # --set values always take precedence over the contents of -f -HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set main.options.bootstrap=$(BOOTSTRAP) --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) -TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set main.options.bootstrap=$(BOOTSTRAP) --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com +HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) +TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com PATTERN_OPTS=-f common/examples/values-example.yaml diff --git a/install/templates/argocd/namespace.yaml b/install/templates/argocd/namespace.yaml index f852fe1b..9e9fafb2 100644 --- a/install/templates/argocd/namespace.yaml +++ b/install/templates/argocd/namespace.yaml @@ -1,8 +1,6 @@ # Pre-create so we can create our argo app for keeping subscriptions in sync # Do it here so that we don't try to sync it in the future -{{- if .Values.main.options.bootstrap }} apiVersion: v1 kind: Namespace metadata: name: openshift-gitops -{{- end }} diff --git a/install/templates/argocd/subscription.yaml b/install/templates/argocd/subscription.yaml index c1fb9a4e..22837b9a 100644 --- a/install/templates/argocd/subscription.yaml +++ b/install/templates/argocd/subscription.yaml @@ -1,4 +1,3 @@ -{{- if .Values.main.options.bootstrap }} apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -19,4 +18,3 @@ spec: {{- if .Values.main.options.useCSV }} startingCSV: openshift-gitops-operator.{{ .Values.main.gitops.csv }} {{- end }} -{{- end }} diff --git a/install/values.yaml b/install/values.yaml index 05d57466..b0507225 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -8,7 +8,6 @@ main: syncPolicy: Automatic installPlanApproval: Automatic useCSV: False - bootstrap: True gitops: channel: stable From a59de6bba8aa8f1d4d9c495631f669ed4a7e871c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 19 Apr 2022 20:39:03 +0200 Subject: [PATCH 0261/1263] Remove argosecret target from common/ Only IE is currently using it, so let's remove the target and the shell script from common/. A separate PR will add them to IE --- Makefile | 9 ------ scripts/secret.sh | 71 ----------------------------------------------- 2 files changed, 80 deletions(-) delete mode 100755 scripts/secret.sh diff --git a/Makefile b/Makefile index 8cc874bb..663313d1 100644 --- a/Makefile +++ b/Makefile @@ -19,15 +19,6 @@ PATTERN_OPTS=-f common/examples/values-example.yaml help: ## This help message @printf "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)\n" -# Makefiles that use this target must provide: -# PATTERN: The name of the pattern that is using it. This will be used programmatically for the source namespace -# TARGET_NAMESPACE: target namespace to install the secret into -# COMPONENT: The component of the target namespace. In industrial edge, factory or datacenter - and for the secret -# it needs to be datacenter because that's where the CI components run. -# SECRET_NAME: The name of the secret to manage -argosecret: ## creates the argo secret - PATTERN="$(PATTERN)" TARGET_NAMESPACE="$(TARGET_NAMESPACE)" COMPONENT="$(COMPONENT)" SECRET_NAME="$(SECRET_NAME)" common/scripts/secret.sh - # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show show: ## show the starting template without installing it diff --git a/scripts/secret.sh b/scripts/secret.sh deleted file mode 100755 index 554a2a8a..00000000 --- a/scripts/secret.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/sh - -#This script must receive arguments for the following parameters; it is easiest to pass them as environment variables from the command line. -#Example values from the first use for it, in industrial-edge -# -#This script is written to be POSIX-compliant, as not all sh are created equal - many are bash but -#Debian-derivatives use dash which doesn't support some bash syntax -# -#PATTERN=industrial-edge -#TARGET_NAMESPACE=manuela-ci -#COMPONENT=datacenter -#SECRET_NAME='argocd-env' - -passwd_resource="secrets/${COMPONENT}-gitops-cluster" -src_ns="${PATTERN}-${COMPONENT}" - -ns=0 -gitops=0 - -# Function log -# Arguments: -# $1 are for the options for echo -# $2 is for the message -# \033[0K\r - Trailing escape sequence to leave output on the same line -function log { - if [ -z "$2" ]; then - echo -e "\033[0K\r\033[1;36m$1\033[0m" - else - echo -e $1 "\033[0K\r\033[1;36m$2" - fi -} - -# Check for Namespaces and Secrets to be ready (it takes the cluster a few minutes to deploy them) -spin='-\|/' -i=0 -while [ 1 ] ; do - i=$(( (i+1) %4 )) - log -n "Checking for namespace $TARGET_NAMESPACE to exist: ${spin:$i:1}" - if [ oc get namespace $TARGET_NAMESPACE >/dev/null 2>/dev/null ]; then - ns=0 - sleep 2 - continue - else - log "Checking for namespace $TARGET_NAMESPACE to exist: OK" - ns=1 - break - fi -done - -i=0 -while [ 1 ] ; do - i=$(( (i+1) %4 )) - log -n "Checking for $passwd_resource to be populated in $src_ns: ${spin:$i:1}" - pw=`oc -n $src_ns extract $passwd_resource --to=- 2>/dev/null` - if [ "$?" = 0 ] && [ -n "$pw" ]; then - log "Checking for $passwd_resource to be populated in $src_ns: OK" - gitops=1 - else - gitops=0 - sleep 2 - continue - fi - - log "Conditions met, managing secret $SECRET_NAME in $TARGET_NAMESPACE" - break -done - -user=$(echo admin | base64) -password=$(echo $pw | base64) - -echo "{ \"apiVersion\": \"v1\", \"kind\": \"Secret\", \"metadata\": { \"name\": \"$SECRET_NAME\", \"namespace\": \"$TARGET_NAMESPACE\" }, \"data\": { \"ARGOCD_PASSWORD\": \"$password\", \"ARGOCD_USERNAME\": \"$user\" }, \"type\": \"Opaque\" }" | oc apply -f- From d209a9231be75d03e534466625c0fc5b5757d441 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 20 Apr 2022 17:12:12 +0200 Subject: [PATCH 0262/1263] Improve error message when values-secret.yaml is not formatted as expected The format we expect is: secrets: group1: secret1: value1 secret2: value2 group2: secret1: value1 This will generate the following vault injections: vault kv put secret/hub/group1 -> secret1=value1 secret2=value2 vault kv put secret/hub/group2 -> secret1=value1 Now if the file has a format that we do not really recognize, for example like: secrets: foo: bar group1: secret1: value1 We will error out as follows: TASK [Check if any of the passwords has only s3.[accessKey,secretKey] and generate the combined s3Secret in that case] fatal: [localhost]: FAILED! => {"msg": "The conditional check '\"s3.accessKey\" in item.value.keys()' failed. The error was: error while evaluating conditional (\"s3.accessKey\" in item.value.keys()): 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'keys'\n\nThe error appears to be in '/home/michele/Engineering/cloud-patterns/multicloud-gitops/common/scripts/ansible-push-vault-secrets.sh': line 56, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n # Note: the vars: line is due to https://github.com/ansible/ansible/issues/40239\n - name: Check if any of the passwords has only s3.[accessKey,secretKey] and generate the combined s3Secret in that case\n ^ here\n"} Let's error out more nicely in such a case. With this patch we get: TASK [Check the value-secret.yaml file for errors] failed: [localhost] (item=foo) => {"ansible_loop_var": "item", "changed": false, "item": {"key": "foo", "value": "bar"}, "msg": "\"{'key': 'foo', 'value': 'bar'}\" is not properly formatted. Each key under 'secrets:' needs to point to a dictionary of key, value pairs. See values-secret.yaml.template.\n"} skipping: [localhost] => (item=aws) Reported-By: Ilkka Tengvall --- scripts/ansible-push-vault-secrets.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scripts/ansible-push-vault-secrets.sh b/scripts/ansible-push-vault-secrets.sh index 6b36e66f..4795d67c 100755 --- a/scripts/ansible-push-vault-secrets.sh +++ b/scripts/ansible-push-vault-secrets.sh @@ -48,6 +48,19 @@ failed_when: secrets is not defined or secrets | length == 0 + - name: Check the value-secret.yaml file for errors + ansible.builtin.fail: + msg: > + "{{ item }}" is not properly formatted. Each key under 'secrets:' + needs to point to a dictionary of key, value pairs. See values-secret.yaml.template. + when: > + item.key | length == 0 or + item.value is not mapping + loop: + "{{ secrets | dict2items }}" + loop_control: + label: "{{ item.key }}" + # Detect here if we have only the following two keys under a password # s3.accessKey: # s3.secretKey: From d973b0e791ed87c65a61ed4172dc7b6d961920fb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 21 Apr 2022 10:38:29 +0200 Subject: [PATCH 0263/1263] ServiceAccount hub-gitops-argocd-dex-server needs more permissions Since gitops 1.5.0 the secondary argocd instance's web interface is erroring out with: Failed to query provider "https://hub-gitops-server-multicloud-gitops-hub.apps.bandini-dc.blueprints.rhecoeng.com/api/dex": Get "http://hub-gitops-dex-server.multicloud-gitops-hub.svc.cluster.local:5556/api/dex/.well-known/openid-configuration": dial tcp 172.30.229.125:5556: connect: connection refused The exact problem is: W0421 07:32:56.239809 1 reflector.go:324] pkg/mod/k8s.io/client-go@v0.23.1/tools/cache/reflector.go:167: failed to list *v1.Secret: secrets is forbidden: User "system:serviceaccount:multicloud-gitops-hub:hub-gitops-argocd-dex-server" cannot list resource "secrets" in API group "" in the namespace "multicloud-gitops-hub" E0421 07:32:56.239838 1 reflector.go:138] pkg/mod/k8s.io/client-go@v0.23.1/tools/cache/reflector.go:167: Failed to watch *v1.Secret: failed to list *v1.Secret: secrets is forbidden: User "system:serviceaccount:multicloud-gitops-hub:hub-gitops-argocd-dex-server" cannot list resource "secrets" in API group "" in the namespace "multicloud-gitops-hub" W0421 08:14:28.521492 1 reflector.go:324] pkg/mod/k8s.io/client-go@v0.23.1/tools/cache/reflector.go:167: failed to list *v1.ConfigMap: configmaps is forbidden: User "system:serviceaccount:multicloud-gitops-hub:hub-gitops-argocd-dex-server" cannot list resource "configmaps" in API group "" in the namespace "multicloud-gitops-hub" E0421 08:14:28.521517 1 reflector.go:138] pkg/mod/k8s.io/client-go@v0.23.1/tools/cache/reflector.go:167: Failed to watch *v1.ConfigMap: failed to list *v1.ConfigMap: configmaps is forbidden: User "system:serviceaccount:multicloud-gitops-hub:hub-gitops-argocd-dex-server" cannot list resource "configmaps" in API group "" in the namespace "multicloud-gitops-hub" Let's give the 'system:serviceaccount:multicloud-gitops-hub:hub-gitops-argocd-dex-server' user the same permissions we give hub-gitops-argocd-server and hub-gitops-argocd-application-controller. We should try and revisit these permission and try and lessen them. They are too high currently. Tested by applying this to a running cluster and the secondary argo instance was now available through the web interface. Fixes: https://github.com/hybrid-cloud-patterns/common/issues/76 --- clustergroup/templates/argocd-super-role.yaml | 4 ++++ tests/clustergroup-naked.expected.yaml | 4 ++++ tests/clustergroup-normal.expected.yaml | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/clustergroup/templates/argocd-super-role.yaml b/clustergroup/templates/argocd-super-role.yaml index f3c885f5..78af462d 100644 --- a/clustergroup/templates/argocd-super-role.yaml +++ b/clustergroup/templates/argocd-super-role.yaml @@ -35,3 +35,7 @@ subjects: # This is the {ArgoCD.name}-argocd-server name: {{ .Values.clusterGroup.name }}-gitops-argocd-server namespace: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} + # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) + - kind: ServiceAccount + name: {{ .Values.clusterGroup.name }}-gitops-argocd-dex-server + namespace: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 6245b702..f00e7f21 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -51,6 +51,10 @@ subjects: # This is the {ArgoCD.name}-argocd-server name: example-gitops-argocd-server namespace: common-example + # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) + - kind: ServiceAccount + name: example-gitops-argocd-dex-server + namespace: common-example --- # Source: pattern-clustergroup/templates/argocd.yaml apiVersion: argoproj.io/v1alpha1 diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 25983b1a..4696b60e 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -71,6 +71,10 @@ subjects: # This is the {ArgoCD.name}-argocd-server name: example-gitops-argocd-server namespace: mypattern-example + # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) + - kind: ServiceAccount + name: example-gitops-argocd-dex-server + namespace: mypattern-example --- # Source: pattern-clustergroup/templates/projects.yaml apiVersion: argoproj.io/v1alpha1 From ade3b3e26d4d8b2b9373add9dd0a61f5d792290c Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 21 Apr 2022 15:10:28 -0500 Subject: [PATCH 0264/1263] Add links for clustergroup argo(s) --- clustergroup/templates/argocd.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/clustergroup/templates/argocd.yaml b/clustergroup/templates/argocd.yaml index 3423ecc0..33484a1c 100644 --- a/clustergroup/templates/argocd.yaml +++ b/clustergroup/templates/argocd.yaml @@ -96,3 +96,16 @@ spec: tls: ca: {} status: +--- +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: {{ .Values.clusterGroup.name }}-gitops-link + namespace: {{ $namespace }} +spec: + applicationMenu: + section: OpenShift GitOps + imageURL: data:image/png;base64,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 + href: 'https://{{ .Values.clusterGroup.name }}-gitops-server-{{ $namespace }}.{{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }}' + location: ApplicationMenu + text: '{{ title .Values.clusterGroup.name }} ArgoCD' From 0760c943db1beac868c6d74e256a940fc3fe3aae Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 22 Apr 2022 09:20:56 +0200 Subject: [PATCH 0265/1263] Fix tests This is the fixup from the menu addition to get CI happy again --- tests/clustergroup-naked.expected.yaml | 14 ++++++++++++++ tests/clustergroup-normal.expected.yaml | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index f00e7f21..42e91fed 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -154,3 +154,17 @@ spec: tls: ca: {} status: +--- +# Source: pattern-clustergroup/templates/argocd.yaml +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: example-gitops-link + namespace: common-example +spec: + applicationMenu: + section: OpenShift GitOps + imageURL: data:image/png;base64,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 + href: 'https://example-gitops-server-common-example.' + location: ApplicationMenu + text: 'Example ArgoCD' diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 4696b60e..c246bc3b 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -283,6 +283,20 @@ spec: ca: {} status: --- +# Source: pattern-clustergroup/templates/argocd.yaml +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: example-gitops-link + namespace: mypattern-example +spec: + applicationMenu: + section: OpenShift GitOps + imageURL: data:image/png;base64,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 + href: 'https://example-gitops-server-mypattern-example.region.example.com' + location: ApplicationMenu + text: 'Example ArgoCD' +--- # Source: pattern-clustergroup/templates/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup From acefe882ef6067095b7f0dc360c5d36bfd80aa62 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 22 Apr 2022 09:49:52 +0200 Subject: [PATCH 0266/1263] Remove wrong line in examples/values-secret.yaml Ilkka mentioned this and it's quite annoying as load-secret will barf due to the wrong line. This is just a quick removal to avoid having people hit this issue. Maybe we should just drop this entirely and have only custom top-level values-secret.yaml.template files like we do in multicloud-gitops. --- examples/values-secret.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/values-secret.yaml b/examples/values-secret.yaml index 89904c63..1794f60a 100644 --- a/examples/values-secret.yaml +++ b/examples/values-secret.yaml @@ -4,7 +4,6 @@ main: secrets: # NEVER COMMIT THESE VALUES TO GIT - enabled: true imageregistry: # Quay -> Robot Accounts -> Robot Login account: test-account From 964e751db42d69b624b5dd0a7b3d8ed398345d6a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 22 Apr 2022 10:38:25 +0200 Subject: [PATCH 0267/1263] Quote key and value when uploading a secret Currently we break horribly when a user adds a multiline secret in yaml (via the | operator): failed: [localhost] (item=cert-test) => {"ansible_loop_var": "item", "changed": true, "item": {"key": "cert-test", "value": {"ca.crt": "-----BEGIN CERTIFICATE-----\nMIIEgjCCA uqgAwIBAgIBATANBgkqhkiG9w0BAQsFADAzMREwDwYDVQQKDAhDT09M\nLkxBQjEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTIyMDEwNTEz\nfoo\nbar\nbaz\n"}}, "rc": 127, "return_code": 127, "stderr": "Failed to parse K=V data: invalid key/value pair \"CERTIFICATE-----\": format must be key=value\nsh: line 1: MIIEgjCCAuqgAwIBAgIBATANBgkqhkiG9w0BAQsFADAzMREwDwYDVQ QKDAhDT09M: command not found\nsh: line 2: LkxBQjEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTIyMDEwNTEz: command not found Let's try to escape key and value and let the yaml parsers do their job. Tested with: secrets: cert-test: ca.crt: | -----BEGIN CERTIFICATE----- MIIEgjCCAuqgAwIBAgIBATANBgkqhkiG9w0BAQsFADAzMREwDwYDVQQKDAhDT09M LkxBQjEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTIyMDEwNTEz foo bar baz And got the following: $ oc exec -it -n vault vault-0 -- bash -c 'vault kv get secret/hub/cert-test' ======= Metadata ======= Key Value --- ----- created_time 2022-04-22T08:36:34.624976106Z custom_metadata deletion_time n/a destroyed false version 2 ===== Data ===== Key Value --- ----- ca.crt -----BEGIN CERTIFICATE----- MIIEgjCCAuqgAwIBAgIBATANBgkqhkiG9w0BAQsFADAzMREwDwYDVQQKDAhDT09M LkxBQjEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTIyMDEwNTEz foo bar baz This should fix (at least a chunk of) https://github.com/hybrid-cloud-patterns/multicloud-gitops/issues/62 --- scripts/ansible-push-vault-secrets.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ansible-push-vault-secrets.sh b/scripts/ansible-push-vault-secrets.sh index 4795d67c..0cf66e26 100755 --- a/scripts/ansible-push-vault-secrets.sh +++ b/scripts/ansible-push-vault-secrets.sh @@ -123,7 +123,7 @@ - name: Debug debug: - msg: "vault kv put {{ vault_path }}/{{ item.key }} -> {{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}" + msg: "vault kv put '{{ vault_path }}/{{ item.key }}' -> '{{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}'" loop: "{{ secrets | dict2items }}" loop_control: @@ -135,7 +135,7 @@ namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" command: | - sh -c "vault kv put {{ vault_path }}/{{ item.key }} {{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}" + sh -c "vault kv put '{{ vault_path }}/{{ item.key }}' '{{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}'" loop: "{{ secrets | dict2items }}" loop_control: From 7c795c9967380e3f63b073905ac1ca211320c94d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 26 Apr 2022 15:21:30 +0200 Subject: [PATCH 0268/1263] Fix quoting in case of multiple secrets per-key When we merged 964e751db (Quote key and value when uploading a secret) we partially fixed the quoting which was useful in case of multiline strings, but in doing so we broke multiple secrets because now we do the following: vault kv put 'secret/hub/group1' 'secret1=value1 secret2=value2' This will end up with a single secret1 having the value of 'value1 secret2=value2' which is clearly undesirable. Let's revisit the approach a bit and make sure we quote each value only so that when we deal with multiple secrets in one group/key we A) fix this issue and B) work correctly in case of multi-line secrets. Lacking a nice and simple multi-line quoting mechanism, we resort to regex_replace called on all values with the multiline flag and the dotall flagged enabled directly in the regex (?ms) as ansible does not allow for those flags to be passed directly. Tested with Ilkka's reproducer: secrets: ldap: bindPassword: foooooooo bindDN: "uid=aap_ldap,cn=sysaccounts,cn=etc,dc=cool,dc=lab" url: "ldaps://rh-idm-01.cool.lab" groupsBaseDN: "cn=groups,cn=accounts,dc=cool,dc=lab" usersBaseDN: "cn=users,cn=accounts,dc=cool,dc=lab" ca_crt: | -----BEGIN CERTIFICATE----- MIIEgjCCAuqgAwIBAgIBATANBgkqhkiG9w0BAQsFADAzMREwDwYDVQQKDAhDT09M LkxBQjEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTIyMDEwNTEz -----END CERTIFICATE----- And have gotten the following output: $ oc exec -it -n vault vault-0 -- bash -c 'vault kv get secret/hub/ldap' ... ======== Data ======== Key Value --- ----- bindDN uid=aap_ldap,cn=sysaccounts,cn=etc,dc=cool,dc=lab bindPassword foooooooo ca_crt -----BEGIN CERTIFICATE----- MIIEgjCCAuqgAwIBAgIBATANBgkqhkiG9w0BAQsFADAzMREwDwYDVQQKDAhDT09M LkxBQjEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTIyMDEwNTEz -----END CERTIFICATE----- groupsBaseDN cn=groups,cn=accounts,dc=cool,dc=lab url ldaps://rh-idm-01.cool.lab usersBaseDN cn=users,cn=accounts,dc=cool,dc=lab Fixes: https://github.com/hybrid-cloud-patterns/multicloud-gitops/issues/62 --- scripts/ansible-push-vault-secrets.sh | 38 +++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/scripts/ansible-push-vault-secrets.sh b/scripts/ansible-push-vault-secrets.sh index 0cf66e26..262a66c1 100755 --- a/scripts/ansible-push-vault-secrets.sh +++ b/scripts/ansible-push-vault-secrets.sh @@ -121,13 +121,41 @@ - not debug | bool - vault_status.rc | int > 0 - - name: Debug - debug: - msg: "vault kv put '{{ vault_path }}/{{ item.key }}' -> '{{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}'" +# The values-secret.yaml file is in the fairly loose form of: +# secrets: +# group1: +# key1: value1 +# key2: value2 +# group2: +# key1: valueA +# key4: valueC +# The above will generate: +# vault kv put 'secret/hub/group1' key1='value1' key2='value2' +# vault kv put 'secret/hub/group2' key1='valueA' key4='valueC' +# Below we loop on the top level keys (group1, group2) and for each +# sub-top-level key (key1, key2) we create a single 'key1=value1 key2=value2' string +# +# Special note is to be given to the regex_replace which is run against each value aka password: +# A. The need for it is to quote the passwords +# B. Since it needs to cater to multiline strings (certs) and ansible offers no way +# to specify re.DOTALL and re.MULTILINE we need to use (?ms) at the beginning +# See https://docs.python.org/3/library/re.html and (?aiLmsux) section +# and https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/filter/core.py#L124 +# C. The \\A and \\Z match the beginning and end of a string (in case of multiline strings +# do not want to match every line) + - name: Set vault commands fact + ansible.builtin.set_fact: + vault_cmds: "{{ vault_cmds | default({}) | combine({ item.key: vault_cmd}) }}" + vars: + vault_cmd: "vault kv put '{{ vault_path }}/{{ item.key }}' {{ item.value.keys() | zip(item.value.values() | map('regex_replace', '(?ms)\\A(.*)\\Z', \"'\\1'\")) | map('join', '=') | list | join(' ') }}" loop: "{{ secrets | dict2items }}" loop_control: label: "{{ item.key }}" + + - name: Debug vault commands + ansible.builtin.debug: + msg: "{{ vault_cmds }}" when: debug | bool - name: Add the actual secrets to the vault @@ -135,9 +163,9 @@ namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" command: | - sh -c "vault kv put '{{ vault_path }}/{{ item.key }}' '{{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}'" + sh -c "{{ item.value }}" loop: - "{{ secrets | dict2items }}" + "{{ vault_cmds | dict2items }}" loop_control: label: "{{ item.key }}" when: not debug | bool From c7254a8225e01548109a839292dbed7321e6b0c8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 27 Apr 2022 15:34:04 +0200 Subject: [PATCH 0269/1263] Remove useless exclude: kube-* from ACM policies The namespaceSelector example: namespaceSelector: include: ["default"] exclude: ["kube-*"] shows up in many places in the documentation. Yet it is not particularly relevant. Full explanation can be found in https://bugzilla.redhat.com/show_bug.cgi?id=2079393 Let's just drop the exclude, since we already define an include without a regex, there is no point in excluding anything really. Tested by redeploying a full multicloud-gitops pattern across two clusters. --- acm/templates/policies/application-policies.yaml | 2 -- acm/templates/policies/ocp-gitops-policy.yaml | 2 -- tests/acm-naked.expected.yaml | 2 -- tests/acm-normal.expected.yaml | 4 ---- 4 files changed, 10 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 39486a6c..57de4fca 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -20,8 +20,6 @@ spec: remediationAction: enforce severity: med namespaceSelector: - exclude: - - kube-* include: - default object-templates: diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index 65159159..b371ba3a 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -21,8 +21,6 @@ spec: remediationAction: enforce severity: med namespaceSelector: - exclude: - - kube-* include: - default object-templates: diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index 864cacc4..e752f149 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -68,8 +68,6 @@ spec: remediationAction: enforce severity: med namespaceSelector: - exclude: - - kube-* include: - default object-templates: diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 961d03dd..e5371c29 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -100,8 +100,6 @@ spec: remediationAction: enforce severity: med namespaceSelector: - exclude: - - kube-* include: - default object-templates: @@ -182,8 +180,6 @@ spec: remediationAction: enforce severity: med namespaceSelector: - exclude: - - kube-* include: - default object-templates: From d026fa8b6439b7789eb9d67eac6f42f3da31b807 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 28 Apr 2022 18:03:34 +0200 Subject: [PATCH 0270/1263] Add a small precheck to make sure python kubernetes is importable As noted by Johnny 'ansible-galaxy collection install community.kubernetes' does not care about lower-level dependencies whatsoever, so let's check if the python modules is importable first as the error otherwise is quite non-obvious. --- scripts/ansible-push-vault-secrets.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/ansible-push-vault-secrets.sh b/scripts/ansible-push-vault-secrets.sh index 262a66c1..dde02923 100755 --- a/scripts/ansible-push-vault-secrets.sh +++ b/scripts/ansible-push-vault-secrets.sh @@ -13,6 +13,9 @@ debug: False tasks: + - name: Check if the kubernetes python module is usable from ansible + ansible.builtin.shell: "{{ ansible_python_interpreter }} -c 'import kubernetes'" + - name: Check for existence of "{{ values_secret }}" ansible.builtin.stat: path: "{{ values_secret }}" From aa9063fb732c305c96f257b8aab0a2b9199c0be8 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 6 May 2022 12:18:26 +1000 Subject: [PATCH 0271/1263] Convert the test example to non-anonymous lists --- examples/values-example.yaml | 49 +++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 47d79e34..7d3168c9 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -12,32 +12,41 @@ clusterGroup: - application-ci subscriptions: - - name: advanced-cluster-management - namespace: open-cluster-management - channel: release-2.3 - csv: advanced-cluster-management.v2.3.2 + acm: + name: advanced-cluster-management + namespace: open-cluster-management + channel: release-2.4 + csv: advanced-cluster-management.v2.4.1 + + odh: + name: opendatahub-operator + source: community-operators + csv: opendatahub-operator.v1.1.0 + disabled: true - - name: openshift-pipelines-operator-rh - csv: redhat-openshift-pipelines.v1.5.1 + pipelines: + name: openshift-pipelines-operator-rh + csv: redhat-openshift-pipelines.v1.5.2 projects: - datacenter applications: - - name: acm - namespace: open-cluster-management - project: datacenter - path: common/acm - ignoreDifferences: - - group: internal.open-cluster-management.io - kind: ManagedClusterInfo - jsonPointers: - - /spec/loggingCA - - - name: pipelines - namespace: application-ci - project: datacenter - path: charts/datacenter/pipelines + acm: + name: acm + namespace: open-cluster-management + project: datacenter + path: common/acm + ignoreDifferences: + - group: internal.open-cluster-management.io + kind: ManagedClusterInfo + jsonPointers: + - /spec/loggingCA + pipe: + name: pipelines + namespace: application-ci + project: datacenter + path: charts/datacenter/pipelines managedClusterGroups: - name: edge From d2c898c5ceeb1b71faaba187c59e3629602ad03d Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 6 May 2022 12:19:28 +1000 Subject: [PATCH 0272/1263] Support easily disabling subscriptions --- clustergroup/templates/subscriptions.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/subscriptions.yaml b/clustergroup/templates/subscriptions.yaml index cf295bb2..2a0b2586 100644 --- a/clustergroup/templates/subscriptions.yaml +++ b/clustergroup/templates/subscriptions.yaml @@ -8,7 +8,7 @@ apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: {{ $subs.name }} - namespace: {{ default "openshift-operators" . }} + namespace: {{ . }} spec: name: {{ $subs.name }} source: {{ default "redhat-operators" $subs.source }} @@ -20,7 +20,7 @@ spec: {{- end }} --- {{- end }} -{{- else }} +{{- else if not $subs.disabled }} apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -38,3 +38,4 @@ spec: --- {{- end }} {{- end }} +--- From 90c6c06f245f735b5e7e9ace4debe69ea01192aa Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 6 May 2022 10:08:02 +0200 Subject: [PATCH 0273/1263] Fix up helm tests After merging (from IE) back to common: a17f02fb422291d8c0e4d1b49674dbf798be45e1 Convert the test example to non-anonymous lists 1629dac0e3da00d1572f62f97042e7663a0cad1b Support easily disabling subscriptions We need to fix up some tests --- tests/clustergroup-normal.expected.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index c246bc3b..da80615a 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -76,6 +76,9 @@ subjects: name: example-gitops-argocd-dex-server namespace: mypattern-example --- +# Source: pattern-clustergroup/templates/subscriptions.yaml +--- +--- # Source: pattern-clustergroup/templates/projects.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject @@ -327,7 +330,7 @@ spec: name: advanced-cluster-management source: redhat-operators sourceNamespace: openshift-marketplace - channel: release-2.3 + channel: release-2.4 installPlanApproval: Automatic --- # Source: pattern-clustergroup/templates/subscriptions.yaml From 2a6f6ca629693c90b64d1a9f8d53a79970844244 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 9 Jun 2022 11:00:58 +0200 Subject: [PATCH 0274/1263] Change all ACM policies' severity definition The error with ACM 2.5 is the following: Failed to create policy template ConfigurationPolicy.policy.open-cluster-management.io "config-demo-secret" is invalid: spec.severity: Unsupported value: "med": supported values: "low", "Low", "medium", "Medium", "high", "High", "critical", "Critical" Use "medium" as severity. Tested with ACM 2.5 and the policies come up correctly. I also tested this on ACM 2.4.4 to make sure that this does not introduce any regressions and it all worked correctly. --- acm/templates/policies/application-policies.yaml | 2 +- acm/templates/policies/ocp-gitops-policy.yaml | 2 +- tests/acm-naked.expected.yaml | 2 +- tests/acm-normal.expected.yaml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 57de4fca..b1c4c842 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -18,7 +18,7 @@ spec: name: {{ .name }}-clustergroup-config spec: remediationAction: enforce - severity: med + severity: medium namespaceSelector: include: - default diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index b371ba3a..7ca61b0f 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -19,7 +19,7 @@ spec: name: openshift-gitops-config spec: remediationAction: enforce - severity: med + severity: medium namespaceSelector: include: - default diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index e752f149..e94c6a51 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -66,7 +66,7 @@ spec: name: openshift-gitops-config spec: remediationAction: enforce - severity: med + severity: medium namespaceSelector: include: - default diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index e5371c29..79f16634 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -98,7 +98,7 @@ spec: name: edge-clustergroup-config spec: remediationAction: enforce - severity: med + severity: medium namespaceSelector: include: - default @@ -178,7 +178,7 @@ spec: name: openshift-gitops-config spec: remediationAction: enforce - severity: med + severity: medium namespaceSelector: include: - default From fb7b41f00807f0dc88fcfed95de6f60eea9bac30 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 13 Jun 2022 10:08:37 -0500 Subject: [PATCH 0275/1263] Improve installation resilience for vault --- scripts/ansible-push-vault-secrets.sh | 3 +++ scripts/vault-utils.sh | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/ansible-push-vault-secrets.sh b/scripts/ansible-push-vault-secrets.sh index dde02923..a29b5e2d 100755 --- a/scripts/ansible-push-vault-secrets.sh +++ b/scripts/ansible-push-vault-secrets.sh @@ -95,6 +95,9 @@ name: "{{ vault_ns }}" register: vault_ns_rc failed_when: vault_ns_rc.resources | length == 0 + until: vault_ns_rc is success + retries: 30 + delay: 5 when: not debug | bool - name: Check if the vault pod is present diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 5a023d18..a36a69a3 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -62,11 +62,6 @@ vault_init() file=common/vault.init fi - if [ -f "$file" ] && grep -q -e '^Unseal' "$file"; then - echo "$file already exists and contains seal secrets. We're moving it away to ${file}.bak" - mv -vf "${file}" "${file}.bak" - fi - # The vault is ready to be initialized when it is "Running" but not "ready". Unsealing it makes it ready rdy_check=`get_vault_ready` @@ -75,6 +70,11 @@ vault_init() exit 0 fi + if [ -f "$file" ] && grep -q -e '^Unseal' "$file"; then + echo "$file already exists and contains seal secrets. We're moving it away to ${file}.bak" + mv -vf "${file}" "${file}.bak" + fi + until [ "$rdy_check" = "0/1 Running" ] do echo $rdy_check From 39a1022b0e55752bbcad0e66dd00ab3478d0c5b8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 3 Jun 2022 18:04:26 +0200 Subject: [PATCH 0276/1263] Add initial support for imperative jobs This creates a CronJob (by default runs every 10mins) which will run all the playbooks listed in imperativeJobs: in a values-*.yaml file. The jobs will be run sequentially and need to be idempotent. They are currently using a rhel9-ubi container with ansible and some additional tools in them. Note that sometimes when changing values for the imperative nested dict you might stumble into https://github.com/argoproj/argo-cd/issues/9214 making a hard-refresh necessary to get things working. An example values-file would be: clusterGroup: imperative: # Override the */10 default schedule schedule: "*/5 * * * *" # NOTE: We *must* use lists and not hashes. As hashes lose ordering once parsed by helm jobs: - name: regional-ca playbook: ansible/playbooks/on-hub-get-regional-ca.yml timeout: 234 --- .../templates/imperative/clusterrole.yaml | 21 +++++ .../templates/imperative/configmap.yaml | 12 +++ clustergroup/templates/imperative/job.yaml | 87 +++++++++++++++++++ .../templates/imperative/namespace.yaml | 10 +++ clustergroup/templates/imperative/rbac.yaml | 30 +++++++ clustergroup/templates/imperative/role.yaml | 20 +++++ .../templates/imperative/serviceaccount.yaml | 10 +++ clustergroup/values.yaml | 19 ++++ 8 files changed, 209 insertions(+) create mode 100644 clustergroup/templates/imperative/clusterrole.yaml create mode 100644 clustergroup/templates/imperative/configmap.yaml create mode 100644 clustergroup/templates/imperative/job.yaml create mode 100644 clustergroup/templates/imperative/namespace.yaml create mode 100644 clustergroup/templates/imperative/rbac.yaml create mode 100644 clustergroup/templates/imperative/role.yaml create mode 100644 clustergroup/templates/imperative/serviceaccount.yaml diff --git a/clustergroup/templates/imperative/clusterrole.yaml b/clustergroup/templates/imperative/clusterrole.yaml new file mode 100644 index 00000000..ca2015a8 --- /dev/null +++ b/clustergroup/templates/imperative/clusterrole.yaml @@ -0,0 +1,21 @@ +{{/* Only define this if there are any imperativejobs defined */}} +{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ $.Values.clusterGroup.imperative.clusterRoleName }} +rules: +{{- if $.Values.clusterGroup.imperative.clusterRoleYaml -}} + {{ toYaml $.Values.clusterGroup.imperative.clusterRoleYaml | nindent 2 }} +{{- else }} + - apiGroups: + - '*' + resources: + - '*' + verbs: + - get + - list + - watch +{{- end }} +{{- end }} diff --git a/clustergroup/templates/imperative/configmap.yaml b/clustergroup/templates/imperative/configmap.yaml new file mode 100644 index 00000000..b9f91682 --- /dev/null +++ b/clustergroup/templates/imperative/configmap.yaml @@ -0,0 +1,12 @@ +{{/* Only define this if there are any imperativejobs defined */}} +{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +{{- $valuesyaml := toYaml $.Values -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }} + namespace: {{ $.Values.clusterGroup.imperative.namespace}} +data: + values.yaml: | +{{ tpl $valuesyaml . | indent 4 }} +{{ end }} diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml new file mode 100644 index 00000000..625883d5 --- /dev/null +++ b/clustergroup/templates/imperative/job.yaml @@ -0,0 +1,87 @@ +{{/* Only define this if there are any imperativejobs defined */}} +{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ $.Values.clusterGroup.imperative.cronJobName }} + namespace: {{ $.Values.clusterGroup.imperative.namespace}} +spec: + schedule: {{ $.Values.clusterGroup.imperative.schedule | quote }} + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: {{ $.Values.clusterGroup.imperative.activeDeadlineSeconds }} + template: + metadata: + name: {{ $.Values.clusterGroup.imperative.jobName }} + spec: + serviceAccountName: {{ $.Values.clusterGroup.imperative.serviceAccountName }} + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: {{ $.Values.clusterGroup.imperative.image }} + imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- {{ $.Values.global.repoURL }} /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" + {{- range $.Values.clusterGroup.imperative.jobs }} + - name: {{ .name }} + image: {{ .image | default $.Values.clusterGroup.imperative.image }} + imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - {{ .timeout | default "600" | quote }} + - ansible-playbook + {{- if $.Values.clusterGroup.imperative.verbosity }} + - {{ $.Values.clusterGroup.imperative.verbosity }} + {{- end }} + - -e + - "@/values/values.yaml" + - {{ .playbook }} + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + {{- end }} + containers: + - name: {{ $.Values.clusterGroup.imperative.jobName }}-done + image: {{ $.Values.clusterGroup.imperative.image }} + imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} + command: + - 'sh' + - '-c' + - echo + - {{ range $.Values.clusterGroup.imperative.jobs }}{{ .name }}{{ end }} + - '\n' + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }} + restartPolicy: Never +{{ end }} diff --git a/clustergroup/templates/imperative/namespace.yaml b/clustergroup/templates/imperative/namespace.yaml new file mode 100644 index 00000000..5f7778d4 --- /dev/null +++ b/clustergroup/templates/imperative/namespace.yaml @@ -0,0 +1,10 @@ +{{/* Only define this if there are any imperativejobs defined */}} +{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: {{ $.Values.clusterGroup.imperative.namespace }} + argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} + name: {{ $.Values.clusterGroup.imperative.namespace }} +{{ end }} diff --git a/clustergroup/templates/imperative/rbac.yaml b/clustergroup/templates/imperative/rbac.yaml new file mode 100644 index 00000000..4b83b124 --- /dev/null +++ b/clustergroup/templates/imperative/rbac.yaml @@ -0,0 +1,30 @@ +{{/* Only define this if there are any imperativejobs defined */}} +{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ $.Values.clusterGroup.imperative.namespace }}-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ $.Values.clusterGroup.imperative.clusterRoleName }} +subjects: + - kind: ServiceAccount + name: {{ $.Values.clusterGroup.imperative.serviceAccountName }} + namespace: {{ $.Values.clusterGroup.imperative.namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ $.Values.clusterGroup.imperative.namespace }}-admin-rolebinding + namespace: {{ $.Values.clusterGroup.imperative.namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ $.Values.clusterGroup.imperative.roleName }} +subjects: + - kind: ServiceAccount + name: {{ $.Values.clusterGroup.imperative.serviceAccountName }} + namespace: {{ $.Values.clusterGroup.imperative.namespace }} +{{ end }} diff --git a/clustergroup/templates/imperative/role.yaml b/clustergroup/templates/imperative/role.yaml new file mode 100644 index 00000000..3c495c8f --- /dev/null +++ b/clustergroup/templates/imperative/role.yaml @@ -0,0 +1,20 @@ +{{/* Only define this if there are any imperativejobs defined */}} +{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ $.Values.clusterGroup.imperative.roleName }} + namespace: {{ $.Values.clusterGroup.imperative.namespace }} +rules: +{{- if $.Values.clusterGroup.imperative.roleYaml -}} + {{ toYaml $.Values.clusterGroup.imperative.roleYaml | nindent 2 }} +{{- else }} + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +{{- end }} +{{- end }} diff --git a/clustergroup/templates/imperative/serviceaccount.yaml b/clustergroup/templates/imperative/serviceaccount.yaml new file mode 100644 index 00000000..af1ccba5 --- /dev/null +++ b/clustergroup/templates/imperative/serviceaccount.yaml @@ -0,0 +1,10 @@ +{{/* Only define this if there are any imperativejobs defined */}} +{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +{{- if $.Values.clusterGroup.imperative.serviceAccountCreate -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ $.Values.clusterGroup.imperative.serviceAccountName }} + namespace: {{ $.Values.clusterGroup.imperative.namespace }} +{{- end }} +{{- end }} diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 58643cc2..1b572fbe 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -6,10 +6,29 @@ global: syncPolicy: Automatic installPlanApproval: Automatic +# Note that sometimes changing helm values might require a hard refresh (https://github.com/helm/helm/issues/3486) clusterGroup: name: example isHubCluster: true + imperative: + jobs: [] + image: quay.io/hybridcloudpatterns/utility-container:latest + namespace: "imperative" + valuesConfigMap: "helm-values-configmap" + cronJobName: "imperative-cronjob" + jobName: "imperative-job" + imagePullPolicy: Always + activeDeadlineSeconds: 3600 + schedule: "*/10 * * * *" + verbosity: "" + serviceAccountCreate: true + serviceAccountName: imperative-sa + clusterRoleName: imperative-cluster-role + clusterRoleYaml: "" + roleName: imperative-role + roleYaml: "" + # managedClusterGroups: # - name: factory # # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git From c16f89e50b7a0faab64c5f880379a89446cf7a44 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 14 Jun 2022 10:02:29 +0200 Subject: [PATCH 0277/1263] Add some helm tests for imperative jobs Add a sample job to test helm in CI for imperative jobs --- Makefile | 2 +- tests/clustergroup-normal.expected.yaml | 262 ++++++++++++++++++++++++ 2 files changed, 263 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 724cb16b..003aa1bf 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spe # --set values always take precedence over the contents of -f HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) -TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com +TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" PATTERN_OPTS=-f common/examples/values-example.yaml diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index da80615a..40825d6e 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -12,6 +12,15 @@ metadata: name: mypattern-example spec: {} --- +# Source: pattern-clustergroup/templates/imperative/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: imperative + argocd.argoproj.io/managed-by: mypattern-example + name: imperative +--- # Source: pattern-clustergroup/templates/namespaces.yaml apiVersion: v1 kind: Namespace @@ -32,6 +41,136 @@ metadata: name: application-ci spec: --- +# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/imperative/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: helm-values-configmap + namespace: imperative +data: + values.yaml: | + clusterGroup: + applications: + acm: + ignoreDifferences: + - group: internal.open-cluster-management.io + jsonPointers: + - /spec/loggingCA + kind: ManagedClusterInfo + name: acm + namespace: open-cluster-management + path: common/acm + project: datacenter + pipe: + name: pipelines + namespace: application-ci + path: charts/datacenter/pipelines + project: datacenter + imperative: + activeDeadlineSeconds: 3600 + clusterRoleName: imperative-cluster-role + clusterRoleYaml: "" + cronJobName: imperative-cronjob + image: quay.io/hybridcloudpatterns/utility-container:latest + imagePullPolicy: Always + jobName: imperative-job + jobs: + - name: test + playbook: ansible/test.yml + namespace: imperative + roleName: imperative-role + roleYaml: "" + schedule: '*/10 * * * *' + serviceAccountCreate: true + serviceAccountName: imperative-sa + valuesConfigMap: helm-values-configmap + verbosity: "" + isHubCluster: true + managedClusterGroups: + - clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift + helmOverrides: + - name: clusterGroup.isHubCluster + value: "false" + name: edge + targetRevision: main + name: example + namespaces: + - open-cluster-management + - application-ci + projects: + - datacenter + subscriptions: + acm: + channel: release-2.4 + csv: advanced-cluster-management.v2.4.1 + name: advanced-cluster-management + namespace: open-cluster-management + odh: + csv: opendatahub-operator.v1.1.0 + disabled: true + name: opendatahub-operator + source: community-operators + pipelines: + csv: redhat-openshift-pipelines.v1.5.2 + name: openshift-pipelines-operator-rh + global: + git: + account: hybrid-cloud-patterns + dev_revision: main + email: someone@somewhere.com + hostname: github.com + hubClusterDomain: hub.example.com + localClusterDomain: region.example.com + namespace: pattern-namespace + options: + installPlanApproval: Automatic + syncPolicy: Automatic + useCSV: false + pattern: mypattern + repoURL: https://github.com/pattern-clone/mypattern + valuesDirectoryURL: https://github.com/pattern-clone/mypattern/raw/main + main: + clusterGroupName: example + git: + repoURL: https://github.com/pattern-clone/mypattern + revision: main + secrets: + aws: + s3Secret: test-secret + git: + token: test-git-token + username: test-user + imageregistry: + account: test-account + token: test-quay-token +--- +# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: imperative-cluster-role +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - get + - list + - watch +--- # Source: pattern-clustergroup/templates/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 @@ -76,6 +215,129 @@ subjects: name: example-gitops-argocd-dex-server namespace: mypattern-example --- +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: imperative-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: imperative-cluster-role +subjects: + - kind: ServiceAccount + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/imperative/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: imperative-role + namespace: imperative +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: imperative-admin-rolebinding + namespace: imperative +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: imperative-role +subjects: + - kind: ServiceAccount + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/imperative/job.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: imperative-cronjob + namespace: imperative +spec: + schedule: "*/10 * * * *" + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: 3600 + template: + metadata: + name: imperative-job + spec: + serviceAccountName: imperative-sa + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: quay.io/hybridcloudpatterns/utility-container:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" + - name: test + image: quay.io/hybridcloudpatterns/utility-container:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - "600" + - ansible-playbook + - -e + - "@/values/values.yaml" + - ansible/test.yml + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: imperative-job-done + image: quay.io/hybridcloudpatterns/utility-container:latest + imagePullPolicy: Always + command: + - 'sh' + - '-c' + - echo + - test + - '\n' + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap + restartPolicy: Never +--- # Source: pattern-clustergroup/templates/subscriptions.yaml --- --- From 605820fd331257b881d7098f04a402d0470d3e20 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 16 Jun 2022 14:56:09 +0200 Subject: [PATCH 0278/1263] Change image and add some additional comments --- clustergroup/values.yaml | 8 +++++++- tests/clustergroup-normal.expected.yaml | 8 ++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 1b572fbe..1151505c 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -13,16 +13,22 @@ clusterGroup: imperative: jobs: [] - image: quay.io/hybridcloudpatterns/utility-container:latest + # This image contains ansible + kubernetes.core by default and is used to run the jobs + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest namespace: "imperative" + # configmap name in the namespace that will contain all helm values valuesConfigMap: "helm-values-configmap" cronJobName: "imperative-cronjob" jobName: "imperative-job" imagePullPolicy: Always + # This is the maximum timeout of all the jobs (1h) activeDeadlineSeconds: 3600 + # By default we run this every 10minutes schedule: "*/10 * * * *" + # Increase ansible verbosity with '-v' or '-vv..' verbosity: "" serviceAccountCreate: true + # service account to be used to run the cron pods serviceAccountName: imperative-sa clusterRoleName: imperative-cluster-role clusterRoleYaml: "" diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 40825d6e..0dd6195a 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -78,7 +78,7 @@ data: clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob - image: quay.io/hybridcloudpatterns/utility-container:latest + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest imagePullPolicy: Always jobName: imperative-job jobs: @@ -280,7 +280,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: quay.io/hybridcloudpatterns/utility-container:latest + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -293,7 +293,7 @@ spec: - name: git mountPath: "/git" - name: test - image: quay.io/hybridcloudpatterns/utility-container:latest + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -316,7 +316,7 @@ spec: subPath: values.yaml containers: - name: imperative-job-done - image: quay.io/hybridcloudpatterns/utility-container:latest + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest imagePullPolicy: Always command: - 'sh' From af5bf8d26538aaa59d2f750112106471ab73d30c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 17 Jun 2022 22:04:51 +0200 Subject: [PATCH 0279/1263] Remove SECRETS from the initial helm chart args passing We're using vault + the load-secrets target to inject secrets in it. Let's not pass the secret values via helm anywhere as this really defeats the whole purpose of uploading the secrets out of band. --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 003aa1bf..91efef7c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,3 @@ -SECRETS=~/values-secret.yaml NAME=$(shell basename `pwd`) # This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL # This is because we expect to use tokens for repo authentication as opposed to SSH keys @@ -9,7 +8,7 @@ TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spec.domain}) # --set values always take precedence over the contents of -f -HELM_OPTS=-f values-global.yaml -f $(SECRETS) --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) +HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" PATTERN_OPTS=-f common/examples/values-example.yaml From 17c633ac0eeaa590a1b9bb4c545941bb8139edb2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 22 Jun 2022 12:07:06 +0200 Subject: [PATCH 0280/1263] Update external-secrets to 0.5.6 Done via: helm dependency update golang-external-secrets Note that this change will require tweaks in the externalsecrets usage when using dataFrom. See https://external-secrets.io/v0.5.0/guides-v1beta1/ for more information. --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.4.4.tgz | Bin 18517 -> 0 bytes .../charts/external-secrets-0.5.6.tgz | Bin 0 -> 39237 bytes ...olang-external-secrets-naked.expected.yaml | 7801 +++++++++++------ ...lang-external-secrets-normal.expected.yaml | 7801 +++++++++++------ 5 files changed, 10665 insertions(+), 4939 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.4.4.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.5.6.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index f257fcd7..91e9c720 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.4.4" + version: "0.5.6" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.4.4.tgz b/golang-external-secrets/charts/external-secrets-0.4.4.tgz deleted file mode 100644 index 364fd25664d84fe110a95cee70c3b437aa7b7732..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18517 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 M|H-luGyon3010pkcmMzZ literal 0 HcmV?d00001 diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 0010c13a..b77cc46a 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -1,4 +1,17 @@ --- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-secrets-cert-controller + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm +--- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount @@ -6,2642 +19,5266 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm --- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/external-secrets.io_clustersecretstores.yaml +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml +apiVersion: v1 +kind: Secret +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm + external-secrets.io/component : webhook +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.9.0 creationTimestamp: null - name: clustersecretstores.external-secrets.io + name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io names: categories: - - externalsecrets - kind: ClusterSecretStore - listKind: ClusterSecretStoreList - plural: clustersecretstores + - externalsecrets + kind: ClusterExternalSecret + listKind: ClusterExternalSecretList + plural: clusterexternalsecrets shortNames: - - css - singular: clustersecretstore + - ces + singular: clusterexternalsecret scope: Cluster versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: ClusterSecretStore represents a secure external location for - storing secrets, which can be referenced as part of `storeRef` fields. - 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: SecretStoreSpec defines the desired state of SecretStore. - properties: - controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) - The KES controller is instantiated with a specific controller name - and filters ES based on this property' - type: string - provider: - description: Used to configure the provider. Only one provider may - be set - maxProperties: 1 - minProperties: 1 - properties: - akeyless: - description: Akeyless configures this store to sync secrets using - Akeyless Vault provider - properties: - akeylessGWApiURL: - description: Akeyless GW API Url from which the secrets to - be fetched from. - type: string - authSecretRef: - description: Auth configures how the operator authenticates - with Akeyless. - properties: - secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: - AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' - properties: - accessID: - description: The SecretAccessID is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - accessType: - description: A reference to a specific 'key' within - a Secret resource, In some instances, `key` is a - required field. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - accessTypeParam: - description: A reference to a specific 'key' within - a Secret resource, In some instances, `key` is a - required field. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - required: - - akeylessGWApiURL - - authSecretRef - type: object - alibaba: - description: Alibaba configures this store to sync secrets using - Alibaba Cloud provider - properties: - auth: - description: AlibabaAuth contains a secretRef for credentials. + - name: v1beta1 + schema: + openAPIV3Schema: + description: ClusterExternalSecret is the Schema for the clusterexternalsecrets 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: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret. + properties: + externalSecretName: + description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret + type: string + externalSecretSpec: + description: The spec for the ExternalSecrets to be created + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: - secretRef: - description: AlibabaAuthSecretRef holds secret references - for Alibaba credentials. + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - accessKeySecretSecretRef: - description: The AccessKeySecret is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string required: - - accessKeyIDSecretRef - - accessKeySecretSecretRef + - key type: object + secretKey: + type: string required: - - secretRef - type: object - endpoint: - type: string - regionID: - description: Alibaba Region to be used for the provider - type: string - required: - - auth - - regionID - type: object - aws: - description: AWS configures this store to sync secrets using AWS - Secret Manager provider - properties: - auth: - description: 'Auth defines the information necessary to authenticate - against AWS if not set aws sdk will infer credentials from - your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' - properties: - jwt: - description: Authenticate against AWS using service account - tokens. - properties: - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource - being referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - required: - - name - type: object - type: object - secretRef: - description: AWSAuthSecretRef holds secret references - for AWS credentials both AccessKeyID and SecretAccessKey - must be defined in order to properly authenticate. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object + - remoteRef + - secretKey type: object - region: - description: AWS Region to be used for the provider - type: string - role: - description: Role is a Role ARN which the SecretManager provider - will assume - type: string - service: - description: Service defines which service should be used - to fetch the secrets - enum: - - SecretsManager - - ParameterStore - type: string - required: - - region - - service - type: object - azurekv: - description: AzureKV configures this store to sync secrets using - Azure Key Vault provider - properties: - authSecretRef: - description: Auth configures how the operator authenticates - with Azure. Required for ServicePrincipal auth type. + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + maxProperties: 1 + minProperties: 1 properties: - clientId: - description: The Azure clientId of the service principle - used for authentication. + extract: + description: Used to extract multiple key/value pairs from one secret properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. + description: Key is the key used in the Provider, mandatory type: string - name: - description: The name of the Secret resource being - referred to. + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. + version: + description: Used to select a specific version of the Provider value, if supported type: string + required: + - key type: object - clientSecret: - description: The Azure ClientSecret of the service principle - used for authentication. + find: + description: Used to find secrets based on tags or regular expressions properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. + conversionStrategy: + default: Default + description: Used to define a conversion Strategy type: string name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. + description: Finds secrets based on the name. + properties: + regexp: + description: Finds secrets base + type: string + type: object + path: + description: A root path to start the find operations. type: string + tags: + additionalProperties: + type: string + description: Find secrets based on tags. + type: object type: object - required: - - clientId - - clientSecret type: object - authType: - default: ServicePrincipal - description: 'Auth type defines how to authenticate to the - keyvault service. Valid values are: - "ServicePrincipal" - (default): Using a service principal (tenantId, clientId, - clientSecret) - "ManagedIdentity": Using Managed Identity - assigned to the pod (see aad-pod-identity)' - enum: - - ServicePrincipal - - ManagedIdentity - type: string - identityId: - description: If multiple Managed Identity is assigned to the - pod, you can select the one to be used - type: string - tenantId: - description: TenantID configures the Azure Tenant to send - requests to. Required for ServicePrincipal auth type. - type: string - vaultUrl: - description: Vault Url from which the secrets to be fetched - from. - type: string - required: - - vaultUrl - type: object - fake: - description: Fake configures a store with static key/value pairs - properties: - data: - items: + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Orphan + - Merge + - None + type: string + deletionPolicy: + default: Retain + description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + enum: + - Delete + - Merge + - Retain + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. properties: - key: - type: string - value: - type: string - valueMap: + data: additionalProperties: type: string type: object - version: + engineVersion: + default: v2 type: string - required: - - key - type: object - type: array - required: - - data - type: object - gcpsm: - description: GCPSM configures this store to sync secrets using - Google Cloud Platform Secret Manager provider - properties: - auth: - description: Auth defines the information necessary to authenticate - against GCP - properties: - secretRef: - properties: - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. + type: object + labels: + additionalProperties: type: string - type: object - type: object - workloadIdentity: - properties: - clusterLocation: - type: string - clusterName: - type: string - serviceAccountRef: - description: A reference to a ServiceAccount resource. + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 properties: - name: - description: The name of the ServiceAccount resource - being referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - required: - - name + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object type: object - required: - - clusterLocation - - clusterName - - serviceAccountRef - type: object - type: object - projectID: - description: ProjectID project where secret is located - type: string - type: object - gitlab: - description: GItlab configures this store to sync secrets using - Gitlab Variables provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with a GitLab instance. + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + type: object + namespaceSelector: + description: The labels to select by to find the Namespaces to create the ExternalSecrets in. + 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: - SecretRef: - properties: - accessToken: - description: AccessToken is used for authentication. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object + 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 required: - - SecretRef + - key + - operator type: object - projectID: - description: ProjectID specifies a project where secrets are - located. + type: array + 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 + refreshTime: + description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. + type: string + required: + - externalSecretSpec + - namespaceSelector + type: object + status: + description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret. + properties: + conditions: + items: + properties: + message: type: string - url: - description: URL configures the GitLab instance URL. Defaults - to https://gitlab.com/. + status: + type: string + type: type: string required: - - auth + - status + - type type: object - ibm: - description: IBM configures this store to sync secrets using IBM - Cloud provider + type: array + failedNamespaces: + description: Failed namespaces are the namespaces that failed to apply an ExternalSecret + items: + description: ClusterExternalSecretNamespaceFailure represents a failed namespace deployment and it's reason. properties: - auth: - description: Auth configures how secret-manager authenticates - with the IBM secrets manager. - properties: - secretRef: - properties: - secretApiKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - serviceUrl: - description: ServiceURL is the Endpoint URL that is specific - to the Secrets Manager service instance + namespace: + description: Namespace is the namespace that failed when trying to apply an ExternalSecret + type: string + reason: + description: Reason is why the ExternalSecret failed to apply to the namespace type: string required: - - auth + - namespace type: object - oracle: - description: Oracle configures this store to sync secrets using - Oracle Vault provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with the Oracle Vault. If empty, use the instance principal, - otherwise the user credentials specified in Auth. - properties: - secretRef: - description: SecretRef to pass through sensitive information. - properties: - fingerprint: - description: Fingerprint is the fingerprint of the - API private key. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - privatekey: - description: PrivateKey is the user's API Signing - Key in PEM format, used for authentication. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - fingerprint - - privatekey - type: object - tenancy: - description: Tenancy is the tenancy OCID where user is - located. - type: string - user: - description: User is an access OCID specific to the account. - type: string - required: - - secretRef - - tenancy - - user - type: object - region: - description: Region is the region where vault is located. - type: string - vault: - description: Vault is the vault's OCID of the specific vault - where secret is located. - type: string - required: - - region - - vault - type: object - vault: - description: Vault configures this store to sync secrets using - Hashi provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with the Vault server. - properties: - appRole: - description: AppRole authenticates with Vault using the - App Role auth mechanism, with the role and secret stored - in a Kubernetes Secret resource. - properties: - path: - default: approle - description: 'Path where the App Role authentication - backend is mounted in Vault, e.g: "approle"' - type: string - roleId: - description: RoleID configured in the App Role authentication - backend when setting up the authentication backend - in Vault. - type: string - secretRef: - description: Reference to a key in a Secret that contains - the App Role secret used to authenticate with Vault. - The `key` field must be specified and denotes which - entry within the Secret resource is used as the - app role secret. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - path - - roleId - - secretRef - type: object - cert: - description: Cert authenticates with TLS Certificates - by passing client certificate, private key and ca certificate - Cert authentication method - properties: - clientCert: - description: ClientCert is a certificate to authenticate - using the Cert Vault authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - secretRef: - description: SecretRef to a key in a Secret resource - containing client private key to authenticate with - Vault using the Cert authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - jwt: - description: Jwt authenticates with Vault by passing role - and JWT token using the JWT/OIDC authentication method - properties: - path: - default: jwt - description: 'Path where the JWT authentication backend - is mounted in Vault, e.g: "jwt"' - type: string - role: - description: Role is a JWT role to authenticate using - the JWT/OIDC Vault authentication method - type: string - secretRef: - description: SecretRef to a key in a Secret resource - containing JWT token to authenticate with Vault - using the JWT/OIDC authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - path - type: object - kubernetes: - description: Kubernetes authenticates with Vault by passing - the ServiceAccount token stored in the named Secret - resource to the Vault server. - properties: - mountPath: - default: kubernetes - description: 'Path where the Kubernetes authentication - backend is mounted in Vault, e.g: "kubernetes"' - type: string - role: - description: A required field containing the Vault - Role to assume. A Role binds a Kubernetes ServiceAccount - with a set of Vault policies. - type: string - secretRef: - description: Optional secret field containing a Kubernetes - ServiceAccount JWT used for authenticating with - Vault. If a name is specified without a key, `token` - is the default. If one is not specified, the one - bound to the controller will be used. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - serviceAccountRef: - description: Optional service account field containing - the name of a kubernetes ServiceAccount. If the - service account is specified, the service account - secret token JWT will be used for authenticating - with Vault. If the service account selector is not - supplied, the secretRef will be used instead. - properties: - name: - description: The name of the ServiceAccount resource - being referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - required: - - name - type: object - required: - - mountPath - - role - type: object - ldap: - description: Ldap authenticates with Vault by passing - username/password pair using the LDAP authentication - method - properties: - path: - default: ldap - description: 'Path where the LDAP authentication backend - is mounted in Vault, e.g: "ldap"' - type: string - secretRef: - description: SecretRef to a key in a Secret resource - containing password for the LDAP user used to authenticate - with Vault using the LDAP authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - username: - description: Username is a LDAP user name used to - authenticate using the LDAP Vault authentication - method - type: string - required: - - path - - username - type: object - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by - presenting a token. - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - type: object - caBundle: - description: PEM encoded CA bundle used to validate Vault - server certificate. Only used if the Server URL is using - HTTPS protocol. This parameter is ignored for plain HTTP - protocol connection. If not set the system root certificates - are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate - Vault server certificate. - properties: - key: - description: The key the value inside of the provider - type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider - type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", - or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write - requests to the Vault leader instead of simply retrying - within a loop. This can increase performance if the option - is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header - type: boolean - namespace: - description: 'Name of the vault namespace. Namespaces is a - set of features within Vault Enterprise that allows Vault - environments to support Secure Multi-tenancy. e.g: "ns1". - More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' - type: string - path: - description: 'Path is the mount path of the Vault KV backend - endpoint, e.g: "secret". The v2 KV secret engine version - specific "/data" path suffix for fetching secrets from Vault - is optional and will be appended if not present in specified - path.' - type: string - readYourWrites: - description: ReadYourWrites ensures isolated read-after-write - semantics by providing discovered cluster replication states - in each request. More information about eventual consistency - in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency - type: boolean - server: - description: 'Server is the connection address for the Vault - server, e.g: "https://vault.example.com:8200".' - type: string - version: - default: v2 - description: Version is the Vault KV secret engine version. - This can be either "v1" or "v2". Version defaults to "v2". - enum: - - v1 - - v2 - type: string - required: - - auth - - server - type: object - webhook: - description: Webhook configures this store to sync secrets using - a generic templated webhook - properties: - body: - description: Body - type: string - caBundle: - description: PEM encoded CA bundle used to validate webhook - server certificate. Only used if the Server URL is using - HTTPS protocol. This parameter is ignored for plain HTTP - protocol connection. If not set the system root certificates - are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate - webhook server certificate. - properties: - key: - description: The key the value inside of the provider - type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider - type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", - or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - headers: - additionalProperties: - type: string - description: Headers - type: object - method: - description: Webhook Method - type: string - result: - description: Result formatting - properties: - jsonPath: - description: Json path of return value - type: string - type: object - secrets: - description: Secrets to fill in templates These secrets will - be passed to the templating function as key value pairs - under the given name - items: - properties: - name: - description: Name of this secret in templates - type: string - secretRef: - description: Secret ref to fill in credentials - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it may - be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of the - referent. - type: string - type: object - required: - - name - - secretRef - type: object - type: array - timeout: - description: Timeout - type: string - url: - description: Webhook url to call - type: string - required: - - result - - url - type: object - yandexlockbox: - description: YandexLockbox configures this store to sync secrets - using Yandex Lockbox provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate - against Yandex Lockbox - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate - Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a - Secret resource, In some instances, `key` is a required - field. - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - type: object - retrySettings: - description: Used to configure http retries if failed - properties: - maxRetries: - format: int32 - type: integer - retryInterval: + type: array + provisionedNamespaces: + description: ProvisionedNamespaces are the namespaces where the ClusterExternalSecret has secrets + items: type: string - type: object - required: - - provider - type: object - status: - description: SecretStoreStatus defines the observed state of the SecretStore. - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert --- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/external-secrets.io_externalsecrets.yaml +# Source: golang-external-secrets/charts/external-secrets/templates/crds/clustersecretstore.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.9.0 creationTimestamp: null - name: externalsecrets.external-secrets.io + name: clustersecretstores.external-secrets.io spec: group: external-secrets.io names: categories: - - externalsecrets - kind: ExternalSecret - listKind: ExternalSecretList - plural: externalsecrets + - externalsecrets + kind: ClusterSecretStore + listKind: ClusterSecretStoreList + plural: clustersecretstores shortNames: - - es - singular: externalsecret - scope: Namespaced + - css + singular: clustersecretstore + scope: Cluster versions: - - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name - name: Store - type: string - - jsonPath: .spec.refreshInterval - name: Refresh Interval - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. - properties: - data: - description: Data defines the connection between the Kubernetes Secret - keys and the Provider data - items: - description: ExternalSecretData defines the connection between the - Kubernetes Secret key (spec.data.) and the Provider data. + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 properties: - remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data - location. + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider properties: - key: - description: Key is the key used in the Provider, mandatory + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. type: string - property: - description: Used to select a specific property of the Provider - value (if a map), if supported + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + secretRef: + description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: type: string - version: - description: Used to select a specific version of the Provider - value, if supported + regionID: + description: Alibaba Region to be used for the provider type: string required: - - key + - auth + - regionID type: object - secretKey: - type: string - required: - - remoteRef - - secretKey - type: object - type: array - dataFrom: - description: DataFrom is used to fetch all properties from a specific - Provider data If multiple entries are specified, the Secret keys - are merged in the specified order - items: - description: ExternalSecretDataRemoteRef defines Provider data location. - properties: - key: - description: Key is the key used in the Provider, mandatory - type: string - property: - description: Used to select a specific property of the Provider - value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider - value, if supported - type: string - required: - - key - type: object - type: array - refreshInterval: - default: 1h - description: RefreshInterval is the amount of time before the values - are read again from the SecretStore provider Valid time units are - "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to - fetch and create it once. Defaults to 1h. - type: string - secretStoreRef: - description: SecretStoreRef defines which SecretStore to fetch the - ExternalSecret data. - properties: - kind: - description: Kind of the SecretStore resource (SecretStore or - ClusterSecretStore) Defaults to `SecretStore` - type: string - name: - description: Name of the SecretStore resource - type: string - required: - - name - type: object - target: - description: ExternalSecretTarget defines the Kubernetes Secret to - be created There can be only one target per ExternalSecret. - properties: - creationPolicy: - default: Owner - description: CreationPolicy defines rules on how to create the - resulting Secret Defaults to 'Owner' - type: string - immutable: - description: Immutable defines if the final secret will be immutable - type: boolean - name: - description: Name defines the name of the Secret resource to be - managed This field is immutable Defaults to the .metadata.name - of the ExternalSecret resource - type: string - template: - description: Template defines a blueprint for the created Secret - resource. - properties: - data: - additionalProperties: + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider type: string - type: object - engineVersion: - default: v1 - description: EngineVersion specifies the template engine version - that should be used to compile/execute the template specified - in .data and .templateFrom[]. - type: string - metadata: - description: ExternalSecretTemplateMetadata defines metadata - fields for the Secret blueprint. - properties: - annotations: - additionalProperties: + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + name: + description: The name of the ServiceAccount resource being referred to. type: string - type: object - labels: - additionalProperties: + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key type: object - type: object - templateFrom: - items: + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. maxProperties: 1 minProperties: 1 properties: - configMap: + cert: + description: has both clientCert and clientKey as secretKeySelector properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap type: string required: - - items - - name + - name + - type type: object - secret: + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. properties: - items: - items: - properties: - key: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: type: string - required: - - key - type: object - type: array - name: + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object required: - - items - - name + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string type: object type: object - type: array - type: - type: string - type: object - type: object - required: - - secretStoreRef - - target - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - refreshTime: - description: refreshTime is the time and date the external secret - was fetched and the target secret updated - format: date-time - nullable: true - type: string - syncedResourceVersion: - description: SyncedResourceVersion keeps track of the last synced - version - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/external-secrets.io_secretstores.yaml -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: secretstores.external-secrets.io -spec: - group: external-secrets.io - names: - categories: - - externalsecrets - kind: SecretStore - listKind: SecretStoreList - plural: secretstores - shortNames: - - ss - singular: secretstore - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: SecretStore represents a secure external location for storing - secrets, which can be referenced as part of `storeRef` fields. - 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: SecretStoreSpec defines the desired state of SecretStore. - properties: - controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) - The KES controller is instantiated with a specific controller name - and filters ES based on this property' - type: string - provider: - description: Used to configure the provider. Only one provider may - be set - maxProperties: 1 - minProperties: 1 - properties: - akeyless: - description: Akeyless configures this store to sync secrets using - Akeyless Vault provider - properties: - akeylessGWApiURL: - description: Akeyless GW API Url from which the secrets to - be fetched from. - type: string - authSecretRef: - description: Auth configures how the operator authenticates - with Akeyless. - properties: - secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: - AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: properties: - accessID: - description: The SecretAccessID is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - accessType: - description: A reference to a specific 'key' within - a Secret resource, In some instances, `key` is a - required field. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - accessTypeParam: - description: A reference to a specific 'key' within - a Secret resource, In some instances, `key` is a - required field. + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. type: string name: - description: The name of the Secret resource being - referred to. + description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + required: + - name + - secretRef type: object - required: - - secretRef - type: object - required: - - akeylessGWApiURL - - authSecretRef - type: object - alibaba: - description: Alibaba configures this store to sync secrets using - Alibaba Cloud provider - properties: - auth: - description: AlibabaAuth contains a secretRef for credentials. - properties: - secretRef: - description: AlibabaAuthSecretRef holds secret references - for Alibaba credentials. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - accessKeySecretSecretRef: - description: The AccessKeySecret is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - accessKeyIDSecretRef - - accessKeySecretSecretRef - type: object - required: - - secretRef - type: object - endpoint: - type: string - regionID: - description: Alibaba Region to be used for the provider - type: string - required: - - auth - - regionID - type: object - aws: - description: AWS configures this store to sync secrets using AWS - Secret Manager provider + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: properties: - auth: - description: 'Auth defines the information necessary to authenticate - against AWS if not set aws sdk will infer credentials from - your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' - properties: - jwt: - description: Authenticate against AWS using service account - tokens. - properties: - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource - being referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - required: - - name - type: object - type: object - secretRef: - description: AWSAuthSecretRef holds secret references - for AWS credentials both AccessKeyID and SecretAccessKey - must be defined in order to properly authenticate. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - type: object - region: - description: AWS Region to be used for the provider + lastTransitionTime: + format: date-time type: string - role: - description: Role is a Role ARN which the SecretManager provider - will assume - type: string - service: - description: Service defines which service should be used - to fetch the secrets - enum: - - SecretsManager - - ParameterStore - type: string - required: - - region - - service - type: object - azurekv: - description: AzureKV configures this store to sync secrets using - Azure Key Vault provider - properties: - authSecretRef: - description: Auth configures how the operator authenticates - with Azure. Required for ServicePrincipal auth type. - properties: - clientId: - description: The Azure clientId of the service principle - used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - clientSecret: - description: The Azure ClientSecret of the service principle - used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - required: - - clientId - - clientSecret - type: object - authType: - default: ServicePrincipal - description: 'Auth type defines how to authenticate to the - keyvault service. Valid values are: - "ServicePrincipal" - (default): Using a service principal (tenantId, clientId, - clientSecret) - "ManagedIdentity": Using Managed Identity - assigned to the pod (see aad-pod-identity)' - enum: - - ServicePrincipal - - ManagedIdentity + message: type: string - identityId: - description: If multiple Managed Identity is assigned to the - pod, you can select the one to be used + reason: type: string - tenantId: - description: TenantID configures the Azure Tenant to send - requests to. Required for ServicePrincipal auth type. + status: type: string - vaultUrl: - description: Vault Url from which the secrets to be fetched - from. + type: type: string required: - - vaultUrl + - status + - type type: object - fake: - description: Fake configures a store with static key/value pairs - properties: - data: - items: + type: array + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. properties: - key: - type: string - value: - type: string - valueMap: - additionalProperties: - type: string + secretRef: + description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string type: object - version: + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string required: - - key + - name type: object - type: array - required: - - data - type: object - gcpsm: - description: GCPSM configures this store to sync secrets using - Google Cloud Platform Secret Manager provider - properties: - auth: - description: Auth defines the information necessary to authenticate - against GCP - properties: - secretRef: - properties: - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - workloadIdentity: + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: properties: - clusterLocation: + key: type: string - clusterName: + value: type: string - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource - being referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - required: - - name + valueMap: + additionalProperties: + type: string type: object + version: + type: string required: - - clusterLocation - - clusterName - - serviceAccountRef - type: object - type: object - projectID: - description: ProjectID project where secret is located - type: string - type: object - gitlab: - description: GItlab configures this store to sync secrets using - Gitlab Variables provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with a GitLab instance. - properties: - SecretRef: - properties: - accessToken: - description: AccessToken is used for authentication. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - required: - - SecretRef - type: object - projectID: - description: ProjectID specifies a project where secrets are - located. - type: string - url: - description: URL configures the GitLab instance URL. Defaults - to https://gitlab.com/. - type: string - required: - - auth - type: object - ibm: - description: IBM configures this store to sync secrets using IBM - Cloud provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with the IBM secrets manager. - properties: - secretRef: - properties: - secretApiKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object + - key type: object - required: - - secretRef - type: object - serviceUrl: - description: ServiceURL is the Endpoint URL that is specific - to the Secrets Manager service instance - type: string - required: - - auth - type: object - oracle: - description: Oracle configures this store to sync secrets using - Oracle Vault provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with the Oracle Vault. If empty, use the instance principal, - otherwise the user credentials specified in Auth. - properties: - secretRef: - description: SecretRef to pass through sensitive information. - properties: - fingerprint: - description: Fingerprint is the fingerprint of the - API private key. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - privatekey: - description: PrivateKey is the user's API Signing - Key in PEM format, used for authentication. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - fingerprint - - privatekey - type: object - tenancy: - description: Tenancy is the tenancy OCID where user is - located. - type: string - user: - description: User is an access OCID specific to the account. - type: string - required: - - secretRef - - tenancy - - user - type: object - region: - description: Region is the region where vault is located. - type: string - vault: - description: Vault is the vault's OCID of the specific vault - where secret is located. - type: string - required: - - region - - vault - type: object - vault: - description: Vault configures this store to sync secrets using - Hashi provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with the Vault server. - properties: - appRole: - description: AppRole authenticates with Vault using the - App Role auth mechanism, with the role and secret stored - in a Kubernetes Secret resource. - properties: - path: - default: approle - description: 'Path where the App Role authentication - backend is mounted in Vault, e.g: "approle"' - type: string - roleId: - description: RoleID configured in the App Role authentication - backend when setting up the authentication backend - in Vault. - type: string - secretRef: - description: Reference to a key in a Secret that contains - the App Role secret used to authenticate with Vault. - The `key` field must be specified and denotes which - entry within the Secret resource is used as the - app role secret. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - path - - roleId + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: - secretRef - type: object - cert: - description: Cert authenticates with TLS Certificates - by passing client certificate, private key and ca certificate - Cert authentication method - properties: - clientCert: - description: ClientCert is a certificate to authenticate - using the Cert Vault authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - secretRef: - description: SecretRef to a key in a Secret resource - containing client private key to authenticate with - Vault using the Cert authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - jwt: - description: Jwt authenticates with Vault by passing role - and JWT token using the JWT/OIDC authentication method - properties: - path: - default: jwt - description: 'Path where the JWT authentication backend - is mounted in Vault, e.g: "jwt"' - type: string - role: - description: Role is a JWT role to authenticate using - the JWT/OIDC Vault authentication method - type: string - secretRef: - description: SecretRef to a key in a Secret resource - containing JWT token to authenticate with Vault - using the JWT/OIDC authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - path - type: object - kubernetes: - description: Kubernetes authenticates with Vault by passing - the ServiceAccount token stored in the named Secret - resource to the Vault server. + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + onepassword: + description: OnePassword configures this store to sync secrets using the 1Password Cloud provider + properties: + auth: + description: Auth defines the information necessary to authenticate against OnePassword Connect Server + properties: + secretRef: + description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. + properties: + connectTokenSecretRef: + description: The ConnectToken is used for authentication to a 1Password Connect Server. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - connectTokenSecretRef + type: object + required: + - secretRef + type: object + connectHost: + description: ConnectHost defines the OnePassword Connect Server to connect to + type: string + vaults: + additionalProperties: + type: integer + description: Vaults defines which OnePassword vaults to search in which order + type: object + required: + - auth + - connectHost + - vaults + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + senhasegura: + description: Senhasegura configures this store to sync secrets using senhasegura provider + properties: + auth: + description: Auth defines parameters to authenticate in senhasegura + properties: + clientId: + type: string + clientSecretSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecretSecretRef + type: object + ignoreSslCertificate: + default: false + description: IgnoreSslCertificate defines if SSL certificate must be ignored + type: boolean + module: + description: Module defines which senhasegura module should be used to get secrets + type: string + url: + description: URL of senhasegura + type: string + required: + - auth + - module + - url + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: + type: string + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: properties: - mountPath: - default: kubernetes - description: 'Path where the Kubernetes authentication - backend is mounted in Vault, e.g: "kubernetes"' - type: string - role: - description: A required field containing the Vault - Role to assume. A Role binds a Kubernetes ServiceAccount - with a set of Vault policies. + name: + description: Name of this secret in templates type: string secretRef: - description: Optional secret field containing a Kubernetes - ServiceAccount JWT used for authenticating with - Vault. If a name is specified without a key, `token` - is the default. If one is not specified, the one - bound to the controller will be used. + description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - serviceAccountRef: - description: Optional service account field containing - the name of a kubernetes ServiceAccount. If the - service account is specified, the service account - secret token JWT will be used for authenticating - with Vault. If the service account selector is not - supplied, the secretRef will be used instead. - properties: name: - description: The name of the ServiceAccount resource - being referred to. + description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string - required: - - name type: object required: - - mountPath - - role + - name + - secretRef type: object - ldap: - description: Ldap authenticates with Vault by passing - username/password pair using the LDAP authentication - method - properties: - path: - default: ldap - description: 'Path where the LDAP authentication backend - is mounted in Vault, e.g: "ldap"' + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexcertificatemanager: + description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Certificate Manager + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + refreshInterval: + description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. + type: integer + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/externalsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.0 + creationTimestamp: null + name: externalsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ExternalSecret + listKind: ExternalSecretList + plural: externalsecrets + shortNames: + - es + singular: externalsecret + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v1 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: type: string - secretRef: - description: SecretRef to a key in a Secret resource - containing password for the LDAP user used to authenticate - with Vault using the LDAP authentication method + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array name: - description: The name of the Secret resource being - referred to. type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: type: string + required: + - items + - name type: object - username: - description: Username is a LDAP user name used to - authenticate using the LDAP Vault authentication - method - type: string - required: - - path - - username - type: object - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by - presenting a token. - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string type: object - type: object - caBundle: - description: PEM encoded CA bundle used to validate Vault - server certificate. Only used if the Server URL is using - HTTPS protocol. This parameter is ignored for plain HTTP - protocol connection. If not set the system root certificates - are used to validate the TLS connection. - format: byte + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + - target + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: type: string - caProvider: - description: The provider for the CA bundle to use to validate - Vault server certificate. + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string key: - description: The key the value inside of the provider - type to use, only used with "Secret" type + description: Key is the key used in the Provider, mandatory type: string - name: - description: The name of the object located at the provider - type. + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None type: string - namespace: - description: The namespace the Provider type is in. + property: + description: Used to select a specific property of the Provider value (if a map), if supported type: string - type: - description: The type of provider to use such as "Secret", - or "ConfigMap". - enum: - - Secret - - ConfigMap + version: + description: Used to select a specific version of the Provider value, if supported type: string required: - - name - - type + - key type: object - forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write - requests to the Vault leader instead of simply retrying - within a loop. This can increase performance if the option - is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header - type: boolean - namespace: - description: 'Name of the vault namespace. Namespaces is a - set of features within Vault Enterprise that allows Vault - environments to support Secure Multi-tenancy. e.g: "ns1". - More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' - type: string - path: - description: 'Path is the mount path of the Vault KV backend - endpoint, e.g: "secret". The v2 KV secret engine version - specific "/data" path suffix for fetching secrets from Vault - is optional and will be appended if not present in specified - path.' - type: string - readYourWrites: - description: ReadYourWrites ensures isolated read-after-write - semantics by providing discovered cluster replication states - in each request. More information about eventual consistency - in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency - type: boolean - server: - description: 'Server is the connection address for the Vault - server, e.g: "https://vault.example.com:8200".' - type: string - version: - default: v2 - description: Version is the Vault KV secret engine version. - This can be either "v1" or "v2". Version defaults to "v2". - enum: - - v1 - - v2 + secretKey: type: string required: - - auth - - server + - remoteRef + - secretKey type: object - webhook: - description: Webhook configures this store to sync secrets using - a generic templated webhook + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + maxProperties: 1 + minProperties: 1 properties: - body: - description: Body - type: string - caBundle: - description: PEM encoded CA bundle used to validate webhook - server certificate. Only used if the Server URL is using - HTTPS protocol. This parameter is ignored for plain HTTP - protocol connection. If not set the system root certificates - are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate - webhook server certificate. + extract: + description: Used to extract multiple key/value pairs from one secret properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string key: - description: The key the value inside of the provider - type to use, only used with "Secret" type + description: Key is the key used in the Provider, mandatory type: string - name: - description: The name of the object located at the provider - type. + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None type: string - namespace: - description: The namespace the Provider type is in. + property: + description: Used to select a specific property of the Provider value (if a map), if supported type: string - type: - description: The type of provider to use such as "Secret", - or "ConfigMap". - enum: - - Secret - - ConfigMap + version: + description: Used to select a specific version of the Provider value, if supported type: string required: - - name - - type + - key + type: object + find: + description: Used to find secrets based on tags or regular expressions + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + name: + description: Finds secrets based on the name. + properties: + regexp: + description: Finds secrets base + type: string + type: object + path: + description: A root path to start the find operations. + type: string + tags: + additionalProperties: + type: string + description: Find secrets based on tags. + type: object type: object - headers: - additionalProperties: + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Orphan + - Merge + - None + type: string + deletionPolicy: + default: Retain + description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + enum: + - Delete + - Merge + - Retain + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v2 + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/secretstore.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.0 + creationTimestamp: null + name: secretstores.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: SecretStore + listKind: SecretStoreList + plural: secretstores + shortNames: + - ss + singular: secretstore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + secretRef: + description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: + type: string + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + secretRef: + description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + onepassword: + description: OnePassword configures this store to sync secrets using the 1Password Cloud provider + properties: + auth: + description: Auth defines the information necessary to authenticate against OnePassword Connect Server + properties: + secretRef: + description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. + properties: + connectTokenSecretRef: + description: The ConnectToken is used for authentication to a 1Password Connect Server. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - connectTokenSecretRef + type: object + required: + - secretRef + type: object + connectHost: + description: ConnectHost defines the OnePassword Connect Server to connect to + type: string + vaults: + additionalProperties: + type: integer + description: Vaults defines which OnePassword vaults to search in which order + type: object + required: + - auth + - connectHost + - vaults + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + senhasegura: + description: Senhasegura configures this store to sync secrets using senhasegura provider + properties: + auth: + description: Auth defines parameters to authenticate in senhasegura + properties: + clientId: + type: string + clientSecretSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecretSecretRef + type: object + ignoreSslCertificate: + default: false + description: IgnoreSslCertificate defines if SSL certificate must be ignored + type: boolean + module: + description: Module defines which senhasegura module should be used to get secrets + type: string + url: + description: URL of senhasegura + type: string + required: + - auth + - module + - url + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: + type: string + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 type: string - description: Headers - type: object - method: - description: Webhook Method - type: string - result: - description: Result formatting - properties: - jsonPath: - description: Json path of return value - type: string - type: object - secrets: - description: Secrets to fill in templates These secrets will - be passed to the templating function as key value pairs - under the given name - items: + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string name: - description: Name of this secret in templates + description: The name of the object located at the provider type. type: string - secretRef: - description: Secret ref to fill in credentials + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexcertificatemanager: + description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Certificate Manager + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it may - be required. + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. type: string name: - description: The name of the Secret resource being - referred to. + description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of the - referent. + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object - required: - - name - - secretRef type: object - type: array - timeout: - description: Timeout + required: + - auth + type: object + type: object + refreshInterval: + description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. + type: integer + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time type: string - url: - description: Webhook url to call + message: type: string - required: - - result - - url - type: object - yandexlockbox: - description: YandexLockbox configures this store to sync secrets - using Yandex Lockbox provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + reason: + type: string + status: + type: string + type: type: string - auth: - description: Auth defines the information necessary to authenticate - against Yandex Lockbox - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate - Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a - Secret resource, In some instances, `key` is a required - field. - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - type: object required: - - auth + - status + - type type: object - type: object - retrySettings: - description: Used to configure http retries if failed - properties: - maxRetries: - format: int32 - type: integer - retryInterval: - type: string - type: object - required: - - provider - type: object - status: - description: SecretStoreStatus defines the observed state of the SecretStore. - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-cert-controller + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "apiextensions.k8s.io" + resources: + - "customresourcedefinitions" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" + - apiGroups: + - "admissionregistration.k8s.io" + resources: + - "validatingwebhookconfigurations" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "endpoints" + verbs: + - "list" + - "get" + - "watch" + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -2649,10 +5286,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -2661,6 +5298,7 @@ rules: - "secretstores" - "clustersecretstores" - "externalsecrets" + - "clusterexternalsecrets" verbs: - "get" - "list" @@ -2677,6 +5315,9 @@ rules: - "clustersecretstores" - "clustersecretstores/status" - "clustersecretstores/finalizers" + - "clusterexternalsecrets" + - "clusterexternalsecrets/status" + - "clusterexternalsecrets/finalizers" verbs: - "update" - "patch" @@ -2684,6 +5325,7 @@ rules: - "" resources: - "serviceaccounts" + - "namespaces" verbs: - "get" - "list" @@ -2721,6 +5363,13 @@ rules: verbs: - "create" - "patch" + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + verbs: + - "create" + - "update" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -2728,10 +5377,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -2754,10 +5403,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -2775,16 +5424,36 @@ rules: - "patch" - "update" --- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: golang-external-secrets-cert-controller + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: golang-external-secrets-cert-controller +subjects: + - name: external-secrets-cert-controller + namespace: "default" + kind: ServiceAccount +--- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -2817,10 +5486,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -2856,10 +5525,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -2870,6 +5539,77 @@ subjects: name: golang-external-secrets namespace: "default" --- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm + external-secrets.io/component : webhook +spec: + type: ClusterIP + ports: + - port: 443 + targetPort: 10250 + protocol: TCP + name: webhook + selector: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets-cert-controller + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + spec: + serviceAccountName: external-secrets-cert-controller + containers: + - name: cert-controller + image: "ghcr.io/external-secrets/external-secrets:v0.5.6" + imagePullPolicy: IfNotPresent + args: + - certcontroller + - --crd-requeue-interval=5m + - --service-name=golang-external-secrets-webhook + - --service-namespace=default + - --secret-name=golang-external-secrets-webhook + - --secret-namespace=default + ports: + - containerPort: 8080 + protocol: TCP + name: metrics + readinessProbe: + httpGet: + port: 8081 + path: /readyz + initialDelaySeconds: 20 + periodSeconds: 5 +--- # Source: golang-external-secrets/charts/external-secrets/templates/deployment.yaml apiVersion: apps/v1 kind: Deployment @@ -2877,10 +5617,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -2897,7 +5637,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.4.4" + image: "ghcr.io/external-secrets/external-secrets:v0.5.6" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -2906,6 +5646,64 @@ spec: protocol: TCP name: metrics --- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + spec: + hostNetwork: false + serviceAccountName: external-secrets-webhook + containers: + - name: webhook + image: "ghcr.io/external-secrets/external-secrets:v0.5.6" + imagePullPolicy: IfNotPresent + args: + - webhook + - --port=10250 + - --dns-name=golang-external-secrets-webhook.default.svc + - --cert-dir=/tmp/certs + - --check-interval=5m + ports: + - containerPort: 8080 + protocol: TCP + name: metrics + - containerPort: 10250 + protocol: TCP + name: webhook + readinessProbe: + httpGet: + port: 8081 + path: /readyz + initialDelaySeconds: 20 + periodSeconds: 5 + volumeMounts: + - name: certs + mountPath: /tmp/certs + readOnly: true + volumes: + - name: certs + secret: + secretName: golang-external-secrets-webhook +--- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1alpha1 kind: ClusterSecretStore @@ -2931,3 +5729,68 @@ spec: serviceAccountRef: name: golang-external-secrets namespace: golang-external-secrets +--- +# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: secretstore-validate + labels: + external-secrets.io/component: webhook +webhooks: +- name: "validate.secretstore.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["secretstores"] + scope: "Namespaced" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-secretstore + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 + +- name: "validate.clustersecretstore.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["clustersecretstores"] + scope: "Cluster" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-clustersecretstore + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 +--- +# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: externalsecret-validate + labels: + external-secrets.io/component: webhook +webhooks: +- name: "validate.externalsecret.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["externalsecrets"] + scope: "Namespaced" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-externalsecret + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 + failurePolicy: Fail diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 0010c13a..b77cc46a 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -1,4 +1,17 @@ --- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-secrets-cert-controller + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm +--- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount @@ -6,2642 +19,5266 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm --- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/external-secrets.io_clustersecretstores.yaml +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml +apiVersion: v1 +kind: Secret +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm + external-secrets.io/component : webhook +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.9.0 creationTimestamp: null - name: clustersecretstores.external-secrets.io + name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io names: categories: - - externalsecrets - kind: ClusterSecretStore - listKind: ClusterSecretStoreList - plural: clustersecretstores + - externalsecrets + kind: ClusterExternalSecret + listKind: ClusterExternalSecretList + plural: clusterexternalsecrets shortNames: - - css - singular: clustersecretstore + - ces + singular: clusterexternalsecret scope: Cluster versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: ClusterSecretStore represents a secure external location for - storing secrets, which can be referenced as part of `storeRef` fields. - 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: SecretStoreSpec defines the desired state of SecretStore. - properties: - controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) - The KES controller is instantiated with a specific controller name - and filters ES based on this property' - type: string - provider: - description: Used to configure the provider. Only one provider may - be set - maxProperties: 1 - minProperties: 1 - properties: - akeyless: - description: Akeyless configures this store to sync secrets using - Akeyless Vault provider - properties: - akeylessGWApiURL: - description: Akeyless GW API Url from which the secrets to - be fetched from. - type: string - authSecretRef: - description: Auth configures how the operator authenticates - with Akeyless. - properties: - secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: - AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' - properties: - accessID: - description: The SecretAccessID is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - accessType: - description: A reference to a specific 'key' within - a Secret resource, In some instances, `key` is a - required field. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - accessTypeParam: - description: A reference to a specific 'key' within - a Secret resource, In some instances, `key` is a - required field. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - required: - - akeylessGWApiURL - - authSecretRef - type: object - alibaba: - description: Alibaba configures this store to sync secrets using - Alibaba Cloud provider - properties: - auth: - description: AlibabaAuth contains a secretRef for credentials. + - name: v1beta1 + schema: + openAPIV3Schema: + description: ClusterExternalSecret is the Schema for the clusterexternalsecrets 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: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret. + properties: + externalSecretName: + description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret + type: string + externalSecretSpec: + description: The spec for the ExternalSecrets to be created + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: - secretRef: - description: AlibabaAuthSecretRef holds secret references - for Alibaba credentials. + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - accessKeySecretSecretRef: - description: The AccessKeySecret is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string required: - - accessKeyIDSecretRef - - accessKeySecretSecretRef + - key type: object + secretKey: + type: string required: - - secretRef - type: object - endpoint: - type: string - regionID: - description: Alibaba Region to be used for the provider - type: string - required: - - auth - - regionID - type: object - aws: - description: AWS configures this store to sync secrets using AWS - Secret Manager provider - properties: - auth: - description: 'Auth defines the information necessary to authenticate - against AWS if not set aws sdk will infer credentials from - your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' - properties: - jwt: - description: Authenticate against AWS using service account - tokens. - properties: - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource - being referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - required: - - name - type: object - type: object - secretRef: - description: AWSAuthSecretRef holds secret references - for AWS credentials both AccessKeyID and SecretAccessKey - must be defined in order to properly authenticate. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object + - remoteRef + - secretKey type: object - region: - description: AWS Region to be used for the provider - type: string - role: - description: Role is a Role ARN which the SecretManager provider - will assume - type: string - service: - description: Service defines which service should be used - to fetch the secrets - enum: - - SecretsManager - - ParameterStore - type: string - required: - - region - - service - type: object - azurekv: - description: AzureKV configures this store to sync secrets using - Azure Key Vault provider - properties: - authSecretRef: - description: Auth configures how the operator authenticates - with Azure. Required for ServicePrincipal auth type. + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + maxProperties: 1 + minProperties: 1 properties: - clientId: - description: The Azure clientId of the service principle - used for authentication. + extract: + description: Used to extract multiple key/value pairs from one secret properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. + description: Key is the key used in the Provider, mandatory type: string - name: - description: The name of the Secret resource being - referred to. + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. + version: + description: Used to select a specific version of the Provider value, if supported type: string + required: + - key type: object - clientSecret: - description: The Azure ClientSecret of the service principle - used for authentication. + find: + description: Used to find secrets based on tags or regular expressions properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. + conversionStrategy: + default: Default + description: Used to define a conversion Strategy type: string name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. + description: Finds secrets based on the name. + properties: + regexp: + description: Finds secrets base + type: string + type: object + path: + description: A root path to start the find operations. type: string + tags: + additionalProperties: + type: string + description: Find secrets based on tags. + type: object type: object - required: - - clientId - - clientSecret type: object - authType: - default: ServicePrincipal - description: 'Auth type defines how to authenticate to the - keyvault service. Valid values are: - "ServicePrincipal" - (default): Using a service principal (tenantId, clientId, - clientSecret) - "ManagedIdentity": Using Managed Identity - assigned to the pod (see aad-pod-identity)' - enum: - - ServicePrincipal - - ManagedIdentity - type: string - identityId: - description: If multiple Managed Identity is assigned to the - pod, you can select the one to be used - type: string - tenantId: - description: TenantID configures the Azure Tenant to send - requests to. Required for ServicePrincipal auth type. - type: string - vaultUrl: - description: Vault Url from which the secrets to be fetched - from. - type: string - required: - - vaultUrl - type: object - fake: - description: Fake configures a store with static key/value pairs - properties: - data: - items: + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Orphan + - Merge + - None + type: string + deletionPolicy: + default: Retain + description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + enum: + - Delete + - Merge + - Retain + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. properties: - key: - type: string - value: - type: string - valueMap: + data: additionalProperties: type: string type: object - version: + engineVersion: + default: v2 type: string - required: - - key - type: object - type: array - required: - - data - type: object - gcpsm: - description: GCPSM configures this store to sync secrets using - Google Cloud Platform Secret Manager provider - properties: - auth: - description: Auth defines the information necessary to authenticate - against GCP - properties: - secretRef: - properties: - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. + type: object + labels: + additionalProperties: type: string - type: object - type: object - workloadIdentity: - properties: - clusterLocation: - type: string - clusterName: - type: string - serviceAccountRef: - description: A reference to a ServiceAccount resource. + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 properties: - name: - description: The name of the ServiceAccount resource - being referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - required: - - name + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object type: object - required: - - clusterLocation - - clusterName - - serviceAccountRef - type: object - type: object - projectID: - description: ProjectID project where secret is located - type: string - type: object - gitlab: - description: GItlab configures this store to sync secrets using - Gitlab Variables provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with a GitLab instance. + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + type: object + namespaceSelector: + description: The labels to select by to find the Namespaces to create the ExternalSecrets in. + 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: - SecretRef: - properties: - accessToken: - description: AccessToken is used for authentication. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object + 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 required: - - SecretRef + - key + - operator type: object - projectID: - description: ProjectID specifies a project where secrets are - located. + type: array + 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 + refreshTime: + description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. + type: string + required: + - externalSecretSpec + - namespaceSelector + type: object + status: + description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret. + properties: + conditions: + items: + properties: + message: type: string - url: - description: URL configures the GitLab instance URL. Defaults - to https://gitlab.com/. + status: + type: string + type: type: string required: - - auth + - status + - type type: object - ibm: - description: IBM configures this store to sync secrets using IBM - Cloud provider + type: array + failedNamespaces: + description: Failed namespaces are the namespaces that failed to apply an ExternalSecret + items: + description: ClusterExternalSecretNamespaceFailure represents a failed namespace deployment and it's reason. properties: - auth: - description: Auth configures how secret-manager authenticates - with the IBM secrets manager. - properties: - secretRef: - properties: - secretApiKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - serviceUrl: - description: ServiceURL is the Endpoint URL that is specific - to the Secrets Manager service instance + namespace: + description: Namespace is the namespace that failed when trying to apply an ExternalSecret + type: string + reason: + description: Reason is why the ExternalSecret failed to apply to the namespace type: string required: - - auth + - namespace type: object - oracle: - description: Oracle configures this store to sync secrets using - Oracle Vault provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with the Oracle Vault. If empty, use the instance principal, - otherwise the user credentials specified in Auth. - properties: - secretRef: - description: SecretRef to pass through sensitive information. - properties: - fingerprint: - description: Fingerprint is the fingerprint of the - API private key. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - privatekey: - description: PrivateKey is the user's API Signing - Key in PEM format, used for authentication. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - fingerprint - - privatekey - type: object - tenancy: - description: Tenancy is the tenancy OCID where user is - located. - type: string - user: - description: User is an access OCID specific to the account. - type: string - required: - - secretRef - - tenancy - - user - type: object - region: - description: Region is the region where vault is located. - type: string - vault: - description: Vault is the vault's OCID of the specific vault - where secret is located. - type: string - required: - - region - - vault - type: object - vault: - description: Vault configures this store to sync secrets using - Hashi provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with the Vault server. - properties: - appRole: - description: AppRole authenticates with Vault using the - App Role auth mechanism, with the role and secret stored - in a Kubernetes Secret resource. - properties: - path: - default: approle - description: 'Path where the App Role authentication - backend is mounted in Vault, e.g: "approle"' - type: string - roleId: - description: RoleID configured in the App Role authentication - backend when setting up the authentication backend - in Vault. - type: string - secretRef: - description: Reference to a key in a Secret that contains - the App Role secret used to authenticate with Vault. - The `key` field must be specified and denotes which - entry within the Secret resource is used as the - app role secret. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - path - - roleId - - secretRef - type: object - cert: - description: Cert authenticates with TLS Certificates - by passing client certificate, private key and ca certificate - Cert authentication method - properties: - clientCert: - description: ClientCert is a certificate to authenticate - using the Cert Vault authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - secretRef: - description: SecretRef to a key in a Secret resource - containing client private key to authenticate with - Vault using the Cert authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - jwt: - description: Jwt authenticates with Vault by passing role - and JWT token using the JWT/OIDC authentication method - properties: - path: - default: jwt - description: 'Path where the JWT authentication backend - is mounted in Vault, e.g: "jwt"' - type: string - role: - description: Role is a JWT role to authenticate using - the JWT/OIDC Vault authentication method - type: string - secretRef: - description: SecretRef to a key in a Secret resource - containing JWT token to authenticate with Vault - using the JWT/OIDC authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - path - type: object - kubernetes: - description: Kubernetes authenticates with Vault by passing - the ServiceAccount token stored in the named Secret - resource to the Vault server. - properties: - mountPath: - default: kubernetes - description: 'Path where the Kubernetes authentication - backend is mounted in Vault, e.g: "kubernetes"' - type: string - role: - description: A required field containing the Vault - Role to assume. A Role binds a Kubernetes ServiceAccount - with a set of Vault policies. - type: string - secretRef: - description: Optional secret field containing a Kubernetes - ServiceAccount JWT used for authenticating with - Vault. If a name is specified without a key, `token` - is the default. If one is not specified, the one - bound to the controller will be used. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - serviceAccountRef: - description: Optional service account field containing - the name of a kubernetes ServiceAccount. If the - service account is specified, the service account - secret token JWT will be used for authenticating - with Vault. If the service account selector is not - supplied, the secretRef will be used instead. - properties: - name: - description: The name of the ServiceAccount resource - being referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - required: - - name - type: object - required: - - mountPath - - role - type: object - ldap: - description: Ldap authenticates with Vault by passing - username/password pair using the LDAP authentication - method - properties: - path: - default: ldap - description: 'Path where the LDAP authentication backend - is mounted in Vault, e.g: "ldap"' - type: string - secretRef: - description: SecretRef to a key in a Secret resource - containing password for the LDAP user used to authenticate - with Vault using the LDAP authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - username: - description: Username is a LDAP user name used to - authenticate using the LDAP Vault authentication - method - type: string - required: - - path - - username - type: object - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by - presenting a token. - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - type: object - caBundle: - description: PEM encoded CA bundle used to validate Vault - server certificate. Only used if the Server URL is using - HTTPS protocol. This parameter is ignored for plain HTTP - protocol connection. If not set the system root certificates - are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate - Vault server certificate. - properties: - key: - description: The key the value inside of the provider - type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider - type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", - or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write - requests to the Vault leader instead of simply retrying - within a loop. This can increase performance if the option - is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header - type: boolean - namespace: - description: 'Name of the vault namespace. Namespaces is a - set of features within Vault Enterprise that allows Vault - environments to support Secure Multi-tenancy. e.g: "ns1". - More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' - type: string - path: - description: 'Path is the mount path of the Vault KV backend - endpoint, e.g: "secret". The v2 KV secret engine version - specific "/data" path suffix for fetching secrets from Vault - is optional and will be appended if not present in specified - path.' - type: string - readYourWrites: - description: ReadYourWrites ensures isolated read-after-write - semantics by providing discovered cluster replication states - in each request. More information about eventual consistency - in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency - type: boolean - server: - description: 'Server is the connection address for the Vault - server, e.g: "https://vault.example.com:8200".' - type: string - version: - default: v2 - description: Version is the Vault KV secret engine version. - This can be either "v1" or "v2". Version defaults to "v2". - enum: - - v1 - - v2 - type: string - required: - - auth - - server - type: object - webhook: - description: Webhook configures this store to sync secrets using - a generic templated webhook - properties: - body: - description: Body - type: string - caBundle: - description: PEM encoded CA bundle used to validate webhook - server certificate. Only used if the Server URL is using - HTTPS protocol. This parameter is ignored for plain HTTP - protocol connection. If not set the system root certificates - are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate - webhook server certificate. - properties: - key: - description: The key the value inside of the provider - type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider - type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", - or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - headers: - additionalProperties: - type: string - description: Headers - type: object - method: - description: Webhook Method - type: string - result: - description: Result formatting - properties: - jsonPath: - description: Json path of return value - type: string - type: object - secrets: - description: Secrets to fill in templates These secrets will - be passed to the templating function as key value pairs - under the given name - items: - properties: - name: - description: Name of this secret in templates - type: string - secretRef: - description: Secret ref to fill in credentials - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it may - be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of the - referent. - type: string - type: object - required: - - name - - secretRef - type: object - type: array - timeout: - description: Timeout - type: string - url: - description: Webhook url to call - type: string - required: - - result - - url - type: object - yandexlockbox: - description: YandexLockbox configures this store to sync secrets - using Yandex Lockbox provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate - against Yandex Lockbox - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate - Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a - Secret resource, In some instances, `key` is a required - field. - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - type: object - retrySettings: - description: Used to configure http retries if failed - properties: - maxRetries: - format: int32 - type: integer - retryInterval: + type: array + provisionedNamespaces: + description: ProvisionedNamespaces are the namespaces where the ClusterExternalSecret has secrets + items: type: string - type: object - required: - - provider - type: object - status: - description: SecretStoreStatus defines the observed state of the SecretStore. - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert --- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/external-secrets.io_externalsecrets.yaml +# Source: golang-external-secrets/charts/external-secrets/templates/crds/clustersecretstore.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.9.0 creationTimestamp: null - name: externalsecrets.external-secrets.io + name: clustersecretstores.external-secrets.io spec: group: external-secrets.io names: categories: - - externalsecrets - kind: ExternalSecret - listKind: ExternalSecretList - plural: externalsecrets + - externalsecrets + kind: ClusterSecretStore + listKind: ClusterSecretStoreList + plural: clustersecretstores shortNames: - - es - singular: externalsecret - scope: Namespaced + - css + singular: clustersecretstore + scope: Cluster versions: - - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name - name: Store - type: string - - jsonPath: .spec.refreshInterval - name: Refresh Interval - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. - properties: - data: - description: Data defines the connection between the Kubernetes Secret - keys and the Provider data - items: - description: ExternalSecretData defines the connection between the - Kubernetes Secret key (spec.data.) and the Provider data. + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 properties: - remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data - location. + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider properties: - key: - description: Key is the key used in the Provider, mandatory + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. type: string - property: - description: Used to select a specific property of the Provider - value (if a map), if supported + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + secretRef: + description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: type: string - version: - description: Used to select a specific version of the Provider - value, if supported + regionID: + description: Alibaba Region to be used for the provider type: string required: - - key + - auth + - regionID type: object - secretKey: - type: string - required: - - remoteRef - - secretKey - type: object - type: array - dataFrom: - description: DataFrom is used to fetch all properties from a specific - Provider data If multiple entries are specified, the Secret keys - are merged in the specified order - items: - description: ExternalSecretDataRemoteRef defines Provider data location. - properties: - key: - description: Key is the key used in the Provider, mandatory - type: string - property: - description: Used to select a specific property of the Provider - value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider - value, if supported - type: string - required: - - key - type: object - type: array - refreshInterval: - default: 1h - description: RefreshInterval is the amount of time before the values - are read again from the SecretStore provider Valid time units are - "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to - fetch and create it once. Defaults to 1h. - type: string - secretStoreRef: - description: SecretStoreRef defines which SecretStore to fetch the - ExternalSecret data. - properties: - kind: - description: Kind of the SecretStore resource (SecretStore or - ClusterSecretStore) Defaults to `SecretStore` - type: string - name: - description: Name of the SecretStore resource - type: string - required: - - name - type: object - target: - description: ExternalSecretTarget defines the Kubernetes Secret to - be created There can be only one target per ExternalSecret. - properties: - creationPolicy: - default: Owner - description: CreationPolicy defines rules on how to create the - resulting Secret Defaults to 'Owner' - type: string - immutable: - description: Immutable defines if the final secret will be immutable - type: boolean - name: - description: Name defines the name of the Secret resource to be - managed This field is immutable Defaults to the .metadata.name - of the ExternalSecret resource - type: string - template: - description: Template defines a blueprint for the created Secret - resource. - properties: - data: - additionalProperties: + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider type: string - type: object - engineVersion: - default: v1 - description: EngineVersion specifies the template engine version - that should be used to compile/execute the template specified - in .data and .templateFrom[]. - type: string - metadata: - description: ExternalSecretTemplateMetadata defines metadata - fields for the Secret blueprint. - properties: - annotations: - additionalProperties: + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + name: + description: The name of the ServiceAccount resource being referred to. type: string - type: object - labels: - additionalProperties: + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key type: object - type: object - templateFrom: - items: + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. maxProperties: 1 minProperties: 1 properties: - configMap: + cert: + description: has both clientCert and clientKey as secretKeySelector properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap type: string required: - - items - - name + - name + - type type: object - secret: + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. properties: - items: - items: - properties: - key: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: type: string - required: - - key - type: object - type: array - name: + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object required: - - items - - name + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string type: object type: object - type: array - type: - type: string - type: object - type: object - required: - - secretStoreRef - - target - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - refreshTime: - description: refreshTime is the time and date the external secret - was fetched and the target secret updated - format: date-time - nullable: true - type: string - syncedResourceVersion: - description: SyncedResourceVersion keeps track of the last synced - version - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/external-secrets.io_secretstores.yaml -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: secretstores.external-secrets.io -spec: - group: external-secrets.io - names: - categories: - - externalsecrets - kind: SecretStore - listKind: SecretStoreList - plural: secretstores - shortNames: - - ss - singular: secretstore - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: SecretStore represents a secure external location for storing - secrets, which can be referenced as part of `storeRef` fields. - 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: SecretStoreSpec defines the desired state of SecretStore. - properties: - controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) - The KES controller is instantiated with a specific controller name - and filters ES based on this property' - type: string - provider: - description: Used to configure the provider. Only one provider may - be set - maxProperties: 1 - minProperties: 1 - properties: - akeyless: - description: Akeyless configures this store to sync secrets using - Akeyless Vault provider - properties: - akeylessGWApiURL: - description: Akeyless GW API Url from which the secrets to - be fetched from. - type: string - authSecretRef: - description: Auth configures how the operator authenticates - with Akeyless. - properties: - secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: - AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: properties: - accessID: - description: The SecretAccessID is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - accessType: - description: A reference to a specific 'key' within - a Secret resource, In some instances, `key` is a - required field. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - accessTypeParam: - description: A reference to a specific 'key' within - a Secret resource, In some instances, `key` is a - required field. + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. type: string name: - description: The name of the Secret resource being - referred to. + description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + required: + - name + - secretRef type: object - required: - - secretRef - type: object - required: - - akeylessGWApiURL - - authSecretRef - type: object - alibaba: - description: Alibaba configures this store to sync secrets using - Alibaba Cloud provider - properties: - auth: - description: AlibabaAuth contains a secretRef for credentials. - properties: - secretRef: - description: AlibabaAuthSecretRef holds secret references - for Alibaba credentials. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - accessKeySecretSecretRef: - description: The AccessKeySecret is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - accessKeyIDSecretRef - - accessKeySecretSecretRef - type: object - required: - - secretRef - type: object - endpoint: - type: string - regionID: - description: Alibaba Region to be used for the provider - type: string - required: - - auth - - regionID - type: object - aws: - description: AWS configures this store to sync secrets using AWS - Secret Manager provider + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: properties: - auth: - description: 'Auth defines the information necessary to authenticate - against AWS if not set aws sdk will infer credentials from - your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' - properties: - jwt: - description: Authenticate against AWS using service account - tokens. - properties: - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource - being referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - required: - - name - type: object - type: object - secretRef: - description: AWSAuthSecretRef holds secret references - for AWS credentials both AccessKeyID and SecretAccessKey - must be defined in order to properly authenticate. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - type: object - region: - description: AWS Region to be used for the provider + lastTransitionTime: + format: date-time type: string - role: - description: Role is a Role ARN which the SecretManager provider - will assume - type: string - service: - description: Service defines which service should be used - to fetch the secrets - enum: - - SecretsManager - - ParameterStore - type: string - required: - - region - - service - type: object - azurekv: - description: AzureKV configures this store to sync secrets using - Azure Key Vault provider - properties: - authSecretRef: - description: Auth configures how the operator authenticates - with Azure. Required for ServicePrincipal auth type. - properties: - clientId: - description: The Azure clientId of the service principle - used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - clientSecret: - description: The Azure ClientSecret of the service principle - used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - required: - - clientId - - clientSecret - type: object - authType: - default: ServicePrincipal - description: 'Auth type defines how to authenticate to the - keyvault service. Valid values are: - "ServicePrincipal" - (default): Using a service principal (tenantId, clientId, - clientSecret) - "ManagedIdentity": Using Managed Identity - assigned to the pod (see aad-pod-identity)' - enum: - - ServicePrincipal - - ManagedIdentity + message: type: string - identityId: - description: If multiple Managed Identity is assigned to the - pod, you can select the one to be used + reason: type: string - tenantId: - description: TenantID configures the Azure Tenant to send - requests to. Required for ServicePrincipal auth type. + status: type: string - vaultUrl: - description: Vault Url from which the secrets to be fetched - from. + type: type: string required: - - vaultUrl + - status + - type type: object - fake: - description: Fake configures a store with static key/value pairs - properties: - data: - items: + type: array + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. properties: - key: - type: string - value: - type: string - valueMap: - additionalProperties: - type: string + secretRef: + description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string type: object - version: + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string required: - - key + - name type: object - type: array - required: - - data - type: object - gcpsm: - description: GCPSM configures this store to sync secrets using - Google Cloud Platform Secret Manager provider - properties: - auth: - description: Auth defines the information necessary to authenticate - against GCP - properties: - secretRef: - properties: - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - workloadIdentity: + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: properties: - clusterLocation: + key: type: string - clusterName: + value: type: string - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource - being referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - required: - - name + valueMap: + additionalProperties: + type: string type: object + version: + type: string required: - - clusterLocation - - clusterName - - serviceAccountRef - type: object - type: object - projectID: - description: ProjectID project where secret is located - type: string - type: object - gitlab: - description: GItlab configures this store to sync secrets using - Gitlab Variables provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with a GitLab instance. - properties: - SecretRef: - properties: - accessToken: - description: AccessToken is used for authentication. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - required: - - SecretRef - type: object - projectID: - description: ProjectID specifies a project where secrets are - located. - type: string - url: - description: URL configures the GitLab instance URL. Defaults - to https://gitlab.com/. - type: string - required: - - auth - type: object - ibm: - description: IBM configures this store to sync secrets using IBM - Cloud provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with the IBM secrets manager. - properties: - secretRef: - properties: - secretApiKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object + - key type: object - required: - - secretRef - type: object - serviceUrl: - description: ServiceURL is the Endpoint URL that is specific - to the Secrets Manager service instance - type: string - required: - - auth - type: object - oracle: - description: Oracle configures this store to sync secrets using - Oracle Vault provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with the Oracle Vault. If empty, use the instance principal, - otherwise the user credentials specified in Auth. - properties: - secretRef: - description: SecretRef to pass through sensitive information. - properties: - fingerprint: - description: Fingerprint is the fingerprint of the - API private key. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - privatekey: - description: PrivateKey is the user's API Signing - Key in PEM format, used for authentication. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - fingerprint - - privatekey - type: object - tenancy: - description: Tenancy is the tenancy OCID where user is - located. - type: string - user: - description: User is an access OCID specific to the account. - type: string - required: - - secretRef - - tenancy - - user - type: object - region: - description: Region is the region where vault is located. - type: string - vault: - description: Vault is the vault's OCID of the specific vault - where secret is located. - type: string - required: - - region - - vault - type: object - vault: - description: Vault configures this store to sync secrets using - Hashi provider - properties: - auth: - description: Auth configures how secret-manager authenticates - with the Vault server. - properties: - appRole: - description: AppRole authenticates with Vault using the - App Role auth mechanism, with the role and secret stored - in a Kubernetes Secret resource. - properties: - path: - default: approle - description: 'Path where the App Role authentication - backend is mounted in Vault, e.g: "approle"' - type: string - roleId: - description: RoleID configured in the App Role authentication - backend when setting up the authentication backend - in Vault. - type: string - secretRef: - description: Reference to a key in a Secret that contains - the App Role secret used to authenticate with Vault. - The `key` field must be specified and denotes which - entry within the Secret resource is used as the - app role secret. - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - path - - roleId + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: - secretRef - type: object - cert: - description: Cert authenticates with TLS Certificates - by passing client certificate, private key and ca certificate - Cert authentication method - properties: - clientCert: - description: ClientCert is a certificate to authenticate - using the Cert Vault authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - secretRef: - description: SecretRef to a key in a Secret resource - containing client private key to authenticate with - Vault using the Cert authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - type: object - jwt: - description: Jwt authenticates with Vault by passing role - and JWT token using the JWT/OIDC authentication method - properties: - path: - default: jwt - description: 'Path where the JWT authentication backend - is mounted in Vault, e.g: "jwt"' - type: string - role: - description: Role is a JWT role to authenticate using - the JWT/OIDC Vault authentication method - type: string - secretRef: - description: SecretRef to a key in a Secret resource - containing JWT token to authenticate with Vault - using the JWT/OIDC authentication method - properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - required: - - path - type: object - kubernetes: - description: Kubernetes authenticates with Vault by passing - the ServiceAccount token stored in the named Secret - resource to the Vault server. + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + onepassword: + description: OnePassword configures this store to sync secrets using the 1Password Cloud provider + properties: + auth: + description: Auth defines the information necessary to authenticate against OnePassword Connect Server + properties: + secretRef: + description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. + properties: + connectTokenSecretRef: + description: The ConnectToken is used for authentication to a 1Password Connect Server. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - connectTokenSecretRef + type: object + required: + - secretRef + type: object + connectHost: + description: ConnectHost defines the OnePassword Connect Server to connect to + type: string + vaults: + additionalProperties: + type: integer + description: Vaults defines which OnePassword vaults to search in which order + type: object + required: + - auth + - connectHost + - vaults + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + senhasegura: + description: Senhasegura configures this store to sync secrets using senhasegura provider + properties: + auth: + description: Auth defines parameters to authenticate in senhasegura + properties: + clientId: + type: string + clientSecretSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecretSecretRef + type: object + ignoreSslCertificate: + default: false + description: IgnoreSslCertificate defines if SSL certificate must be ignored + type: boolean + module: + description: Module defines which senhasegura module should be used to get secrets + type: string + url: + description: URL of senhasegura + type: string + required: + - auth + - module + - url + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: + type: string + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: properties: - mountPath: - default: kubernetes - description: 'Path where the Kubernetes authentication - backend is mounted in Vault, e.g: "kubernetes"' - type: string - role: - description: A required field containing the Vault - Role to assume. A Role binds a Kubernetes ServiceAccount - with a set of Vault policies. + name: + description: Name of this secret in templates type: string secretRef: - description: Optional secret field containing a Kubernetes - ServiceAccount JWT used for authenticating with - Vault. If a name is specified without a key, `token` - is the default. If one is not specified, the one - bound to the controller will be used. + description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string - name: - description: The name of the Secret resource being - referred to. + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. - type: string - type: object - serviceAccountRef: - description: Optional service account field containing - the name of a kubernetes ServiceAccount. If the - service account is specified, the service account - secret token JWT will be used for authenticating - with Vault. If the service account selector is not - supplied, the secretRef will be used instead. - properties: name: - description: The name of the ServiceAccount resource - being referred to. + description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string - required: - - name type: object required: - - mountPath - - role + - name + - secretRef type: object - ldap: - description: Ldap authenticates with Vault by passing - username/password pair using the LDAP authentication - method - properties: - path: - default: ldap - description: 'Path where the LDAP authentication backend - is mounted in Vault, e.g: "ldap"' + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexcertificatemanager: + description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Certificate Manager + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + refreshInterval: + description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. + type: integer + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/externalsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.0 + creationTimestamp: null + name: externalsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ExternalSecret + listKind: ExternalSecretList + plural: externalsecrets + shortNames: + - es + singular: externalsecret + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v1 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: type: string - secretRef: - description: SecretRef to a key in a Secret resource - containing password for the LDAP user used to authenticate - with Vault using the LDAP authentication method + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: properties: - key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it - may be required. - type: string + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array name: - description: The name of the Secret resource being - referred to. type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of - the referent. + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: type: string + required: + - items + - name type: object - username: - description: Username is a LDAP user name used to - authenticate using the LDAP Vault authentication - method - type: string - required: - - path - - username - type: object - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by - presenting a token. - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string type: object - type: object - caBundle: - description: PEM encoded CA bundle used to validate Vault - server certificate. Only used if the Server URL is using - HTTPS protocol. This parameter is ignored for plain HTTP - protocol connection. If not set the system root certificates - are used to validate the TLS connection. - format: byte + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + - target + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: type: string - caProvider: - description: The provider for the CA bundle to use to validate - Vault server certificate. + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string key: - description: The key the value inside of the provider - type to use, only used with "Secret" type + description: Key is the key used in the Provider, mandatory type: string - name: - description: The name of the object located at the provider - type. + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None type: string - namespace: - description: The namespace the Provider type is in. + property: + description: Used to select a specific property of the Provider value (if a map), if supported type: string - type: - description: The type of provider to use such as "Secret", - or "ConfigMap". - enum: - - Secret - - ConfigMap + version: + description: Used to select a specific version of the Provider value, if supported type: string required: - - name - - type + - key type: object - forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write - requests to the Vault leader instead of simply retrying - within a loop. This can increase performance if the option - is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header - type: boolean - namespace: - description: 'Name of the vault namespace. Namespaces is a - set of features within Vault Enterprise that allows Vault - environments to support Secure Multi-tenancy. e.g: "ns1". - More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' - type: string - path: - description: 'Path is the mount path of the Vault KV backend - endpoint, e.g: "secret". The v2 KV secret engine version - specific "/data" path suffix for fetching secrets from Vault - is optional and will be appended if not present in specified - path.' - type: string - readYourWrites: - description: ReadYourWrites ensures isolated read-after-write - semantics by providing discovered cluster replication states - in each request. More information about eventual consistency - in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency - type: boolean - server: - description: 'Server is the connection address for the Vault - server, e.g: "https://vault.example.com:8200".' - type: string - version: - default: v2 - description: Version is the Vault KV secret engine version. - This can be either "v1" or "v2". Version defaults to "v2". - enum: - - v1 - - v2 + secretKey: type: string required: - - auth - - server + - remoteRef + - secretKey type: object - webhook: - description: Webhook configures this store to sync secrets using - a generic templated webhook + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + maxProperties: 1 + minProperties: 1 properties: - body: - description: Body - type: string - caBundle: - description: PEM encoded CA bundle used to validate webhook - server certificate. Only used if the Server URL is using - HTTPS protocol. This parameter is ignored for plain HTTP - protocol connection. If not set the system root certificates - are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate - webhook server certificate. + extract: + description: Used to extract multiple key/value pairs from one secret properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string key: - description: The key the value inside of the provider - type to use, only used with "Secret" type + description: Key is the key used in the Provider, mandatory type: string - name: - description: The name of the object located at the provider - type. + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None type: string - namespace: - description: The namespace the Provider type is in. + property: + description: Used to select a specific property of the Provider value (if a map), if supported type: string - type: - description: The type of provider to use such as "Secret", - or "ConfigMap". - enum: - - Secret - - ConfigMap + version: + description: Used to select a specific version of the Provider value, if supported type: string required: - - name - - type + - key + type: object + find: + description: Used to find secrets based on tags or regular expressions + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + name: + description: Finds secrets based on the name. + properties: + regexp: + description: Finds secrets base + type: string + type: object + path: + description: A root path to start the find operations. + type: string + tags: + additionalProperties: + type: string + description: Find secrets based on tags. + type: object type: object - headers: - additionalProperties: + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Orphan + - Merge + - None + type: string + deletionPolicy: + default: Retain + description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + enum: + - Delete + - Merge + - Retain + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v2 + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/secretstore.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.0 + creationTimestamp: null + name: secretstores.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: SecretStore + listKind: SecretStoreList + plural: secretstores + shortNames: + - ss + singular: secretstore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + secretRef: + description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: + type: string + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + secretRef: + description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + onepassword: + description: OnePassword configures this store to sync secrets using the 1Password Cloud provider + properties: + auth: + description: Auth defines the information necessary to authenticate against OnePassword Connect Server + properties: + secretRef: + description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. + properties: + connectTokenSecretRef: + description: The ConnectToken is used for authentication to a 1Password Connect Server. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - connectTokenSecretRef + type: object + required: + - secretRef + type: object + connectHost: + description: ConnectHost defines the OnePassword Connect Server to connect to + type: string + vaults: + additionalProperties: + type: integer + description: Vaults defines which OnePassword vaults to search in which order + type: object + required: + - auth + - connectHost + - vaults + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + senhasegura: + description: Senhasegura configures this store to sync secrets using senhasegura provider + properties: + auth: + description: Auth defines parameters to authenticate in senhasegura + properties: + clientId: + type: string + clientSecretSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecretSecretRef + type: object + ignoreSslCertificate: + default: false + description: IgnoreSslCertificate defines if SSL certificate must be ignored + type: boolean + module: + description: Module defines which senhasegura module should be used to get secrets + type: string + url: + description: URL of senhasegura + type: string + required: + - auth + - module + - url + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: + type: string + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 type: string - description: Headers - type: object - method: - description: Webhook Method - type: string - result: - description: Result formatting - properties: - jsonPath: - description: Json path of return value - type: string - type: object - secrets: - description: Secrets to fill in templates These secrets will - be passed to the templating function as key value pairs - under the given name - items: + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string name: - description: Name of this secret in templates + description: The name of the object located at the provider type. type: string - secretRef: - description: Secret ref to fill in credentials + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexcertificatemanager: + description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Certificate Manager + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret - resource's `data` field to be used. Some instances - of this field may be defaulted, in others it may - be required. + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. type: string name: - description: The name of the Secret resource being - referred to. + description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. - cluster-scoped defaults to the namespace of the - referent. + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object - required: - - name - - secretRef type: object - type: array - timeout: - description: Timeout + required: + - auth + type: object + type: object + refreshInterval: + description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. + type: integer + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time type: string - url: - description: Webhook url to call + message: type: string - required: - - result - - url - type: object - yandexlockbox: - description: YandexLockbox configures this store to sync secrets - using Yandex Lockbox provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + reason: + type: string + status: + type: string + type: type: string - auth: - description: Auth defines the information necessary to authenticate - against Yandex Lockbox - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate - Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a - Secret resource, In some instances, `key` is a required - field. - properties: - key: - description: The key of the entry in the Secret resource's - `data` field to be used. Some instances of this - field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being - referred to. - type: string - namespace: - description: Namespace of the resource being referred - to. Ignored if referent is not cluster-scoped. cluster-scoped - defaults to the namespace of the referent. - type: string - type: object - type: object required: - - auth + - status + - type type: object - type: object - retrySettings: - description: Used to configure http retries if failed - properties: - maxRetries: - format: int32 - type: integer - retryInterval: - type: string - type: object - required: - - provider - type: object - status: - description: SecretStoreStatus defines the observed state of the SecretStore. - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-cert-controller + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "apiextensions.k8s.io" + resources: + - "customresourcedefinitions" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" + - apiGroups: + - "admissionregistration.k8s.io" + resources: + - "validatingwebhookconfigurations" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "endpoints" + verbs: + - "list" + - "get" + - "watch" + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -2649,10 +5286,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -2661,6 +5298,7 @@ rules: - "secretstores" - "clustersecretstores" - "externalsecrets" + - "clusterexternalsecrets" verbs: - "get" - "list" @@ -2677,6 +5315,9 @@ rules: - "clustersecretstores" - "clustersecretstores/status" - "clustersecretstores/finalizers" + - "clusterexternalsecrets" + - "clusterexternalsecrets/status" + - "clusterexternalsecrets/finalizers" verbs: - "update" - "patch" @@ -2684,6 +5325,7 @@ rules: - "" resources: - "serviceaccounts" + - "namespaces" verbs: - "get" - "list" @@ -2721,6 +5363,13 @@ rules: verbs: - "create" - "patch" + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + verbs: + - "create" + - "update" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -2728,10 +5377,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -2754,10 +5403,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -2775,16 +5424,36 @@ rules: - "patch" - "update" --- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: golang-external-secrets-cert-controller + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: golang-external-secrets-cert-controller +subjects: + - name: external-secrets-cert-controller + namespace: "default" + kind: ServiceAccount +--- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -2817,10 +5486,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -2856,10 +5525,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -2870,6 +5539,77 @@ subjects: name: golang-external-secrets namespace: "default" --- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm + external-secrets.io/component : webhook +spec: + type: ClusterIP + ports: + - port: 443 + targetPort: 10250 + protocol: TCP + name: webhook + selector: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets-cert-controller + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + spec: + serviceAccountName: external-secrets-cert-controller + containers: + - name: cert-controller + image: "ghcr.io/external-secrets/external-secrets:v0.5.6" + imagePullPolicy: IfNotPresent + args: + - certcontroller + - --crd-requeue-interval=5m + - --service-name=golang-external-secrets-webhook + - --service-namespace=default + - --secret-name=golang-external-secrets-webhook + - --secret-namespace=default + ports: + - containerPort: 8080 + protocol: TCP + name: metrics + readinessProbe: + httpGet: + port: 8081 + path: /readyz + initialDelaySeconds: 20 + periodSeconds: 5 +--- # Source: golang-external-secrets/charts/external-secrets/templates/deployment.yaml apiVersion: apps/v1 kind: Deployment @@ -2877,10 +5617,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.4.4 + helm.sh/chart: external-secrets-0.5.6 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.4.4" + app.kubernetes.io/version: "v0.5.6" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -2897,7 +5637,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.4.4" + image: "ghcr.io/external-secrets/external-secrets:v0.5.6" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -2906,6 +5646,64 @@ spec: protocol: TCP name: metrics --- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.5.6 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + spec: + hostNetwork: false + serviceAccountName: external-secrets-webhook + containers: + - name: webhook + image: "ghcr.io/external-secrets/external-secrets:v0.5.6" + imagePullPolicy: IfNotPresent + args: + - webhook + - --port=10250 + - --dns-name=golang-external-secrets-webhook.default.svc + - --cert-dir=/tmp/certs + - --check-interval=5m + ports: + - containerPort: 8080 + protocol: TCP + name: metrics + - containerPort: 10250 + protocol: TCP + name: webhook + readinessProbe: + httpGet: + port: 8081 + path: /readyz + initialDelaySeconds: 20 + periodSeconds: 5 + volumeMounts: + - name: certs + mountPath: /tmp/certs + readOnly: true + volumes: + - name: certs + secret: + secretName: golang-external-secrets-webhook +--- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1alpha1 kind: ClusterSecretStore @@ -2931,3 +5729,68 @@ spec: serviceAccountRef: name: golang-external-secrets namespace: golang-external-secrets +--- +# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: secretstore-validate + labels: + external-secrets.io/component: webhook +webhooks: +- name: "validate.secretstore.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["secretstores"] + scope: "Namespaced" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-secretstore + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 + +- name: "validate.clustersecretstore.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["clustersecretstores"] + scope: "Cluster" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-clustersecretstore + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 +--- +# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: externalsecret-validate + labels: + external-secrets.io/component: webhook +webhooks: +- name: "validate.externalsecret.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["externalsecrets"] + scope: "Namespaced" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-externalsecret + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 + failurePolicy: Fail From 4f4d98f771c6e385816a8756ce54a8b985500feb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 27 Jun 2022 09:26:26 +0200 Subject: [PATCH 0281/1263] Move to eso-0.5.7 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.5.6.tgz | Bin 39237 -> 0 bytes .../charts/external-secrets-0.5.7.tgz | Bin 0 -> 39371 bytes ...olang-external-secrets-naked.expected.yaml | 110 ++++++++---------- ...lang-external-secrets-normal.expected.yaml | 110 ++++++++---------- 5 files changed, 103 insertions(+), 119 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.5.6.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.5.7.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 91e9c720..d988604c 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.5.6" + version: "0.5.7" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.5.6.tgz b/golang-external-secrets/charts/external-secrets-0.5.6.tgz deleted file mode 100644 index fb40f1189ab9f7efe80ea869557addae7ba9387d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39237 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 M|H-luGyon3010pkcmMzZ diff --git a/golang-external-secrets/charts/external-secrets-0.5.7.tgz b/golang-external-secrets/charts/external-secrets-0.5.7.tgz new file mode 100644 index 0000000000000000000000000000000000000000..47356aa69dfdda93d754ad0fe0796c969373a372 GIT binary patch literal 39371 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

QzOHocYPA`p?4@$@ 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 a=u2Pv(wDzh`F{Zb0RR7MQHBiw9tHsG>&of? literal 0 HcmV?d00001 diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index b77cc46a..fe85949f 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook --- @@ -1863,18 +1863,14 @@ spec: serviceAccount: description: points to a service account that should be used for authentication properties: - serviceAccount: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name type: object token: description: use static token to authenticate with @@ -4537,18 +4533,14 @@ spec: serviceAccount: description: points to a service account that should be used for authentication properties: - serviceAccount: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name type: object token: description: use static token to authenticate with @@ -5228,10 +5220,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5286,10 +5278,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5377,10 +5369,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -5403,10 +5395,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -5430,10 +5422,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5450,10 +5442,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5486,10 +5478,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5525,10 +5517,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5546,10 +5538,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook spec: @@ -5570,10 +5562,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5590,7 +5582,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.5.6" + image: "ghcr.io/external-secrets/external-secrets:v0.5.7" imagePullPolicy: IfNotPresent args: - certcontroller @@ -5617,10 +5609,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5637,7 +5629,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.5.6" + image: "ghcr.io/external-secrets/external-secrets:v0.5.7" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -5653,10 +5645,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5674,7 +5666,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.5.6" + image: "ghcr.io/external-secrets/external-secrets:v0.5.7" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index b77cc46a..fe85949f 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook --- @@ -1863,18 +1863,14 @@ spec: serviceAccount: description: points to a service account that should be used for authentication properties: - serviceAccount: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name type: object token: description: use static token to authenticate with @@ -4537,18 +4533,14 @@ spec: serviceAccount: description: points to a service account that should be used for authentication properties: - serviceAccount: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name type: object token: description: use static token to authenticate with @@ -5228,10 +5220,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5286,10 +5278,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5377,10 +5369,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -5403,10 +5395,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -5430,10 +5422,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5450,10 +5442,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5486,10 +5478,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5525,10 +5517,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5546,10 +5538,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook spec: @@ -5570,10 +5562,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5590,7 +5582,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.5.6" + image: "ghcr.io/external-secrets/external-secrets:v0.5.7" imagePullPolicy: IfNotPresent args: - certcontroller @@ -5617,10 +5609,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5637,7 +5629,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.5.6" + image: "ghcr.io/external-secrets/external-secrets:v0.5.7" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -5653,10 +5645,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.6 + helm.sh/chart: external-secrets-0.5.7 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.6" + app.kubernetes.io/version: "v0.5.7" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5674,7 +5666,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.5.6" + image: "ghcr.io/external-secrets/external-secrets:v0.5.7" imagePullPolicy: IfNotPresent args: - webhook From d930d63f08ad31a10512855cdd03620e3f3a1e1a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 27 Jun 2022 17:46:20 +0200 Subject: [PATCH 0282/1263] Add super linter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We disable the following linters to begin with: VALIDATE_BASH: false VALIDATE_JSCPD: false VALIDATE_KUBERNETES_KUBEVAL: false VALIDATE_YAML: false Kubeval is already covered by us and we cannot use this one because it does not have all of our OCP-specific schemas. JSCP needs to be disabled because our is not yaml for the most part but helm templates which are not recognized. Same with the YAML validator. We can debate if we reenable shell later. In any case we should to it only after we merge the vault utils ansible port. Our main goal is to always have gitleaks running to be notified if a key/password gets pushed accidentally. We can see that it is indeed running: 2022-06-27 15:47:57 [INFO] --------------------------- 2022-06-27 15:47:57 [INFO] File:[/github/workspace/clustergroup/templates/namespaces.yaml] 2022-06-27 15:47:57 [INFO] - File:[namespaces.yaml] was linted with [gitleaks] successfully 2022-06-27 15:47:57 [INFO] - Command output: ------ ○ │╲ │ ○ ○ ░ ░ gitleaks 3:47PMINF scan completed in 404.005µs 3:47PMINF no leaks found Also add a 'super-linter' Makefile target to test linting locally easily. --- .github/workflows/superlinter.yml | 38 +++++++++++++++++++++++++++++++ Makefile | 9 ++++++++ 2 files changed, 47 insertions(+) create mode 100644 .github/workflows/superlinter.yml diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml new file mode 100644 index 00000000..0141598c --- /dev/null +++ b/.github/workflows/superlinter.yml @@ -0,0 +1,38 @@ +--- +name: Super linter + +on: [push, pull_request] + +jobs: + build: + # Name the Job + name: Super linter + # Set the agent to run on + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + with: + # Full git history is needed to get a proper list of changed files within `super-linter` + fetch-depth: 0 + + ################################ + # Run Linter against code base # + ################################ + - name: Lint Code Base + uses: github/super-linter/slim@v4 + env: + VALIDATE_ALL_CODEBASE: true + DEFAULT_BRANCH: main + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # These are the validation we disable atm + VALIDATE_BASH: false + VALIDATE_JSCPD: false + VALIDATE_KUBERNETES_KUBEVAL: false + VALIDATE_YAML: false + # VALIDATE_ANSIBLE: false + # VALIDATE_DOCKERFILE_HADOLINT: false + # VALIDATE_MARKDOWN: false + # VALIDATE_NATURAL_LANGUAGE: false + # VALIDATE_TEKTON: false diff --git a/Makefile b/Makefile index 91efef7c..f7b9dd58 100644 --- a/Makefile +++ b/Makefile @@ -59,4 +59,13 @@ vault-unseal: ## unseals the vault load-secrets: ## loads the secrets into the vault common/scripts/ansible-push-vault-secrets.sh +super-linter: ## Runs super linter locally + podman run -e RUN_LOCAL=true -e USE_FIND_ALGORITHM=true \ + -e VALIDATE_BASH=false \ + -e VALIDATE_JSCPD=false \ + -e VALIDATE_KUBERNETES_KUBEVAL=false \ + -e VALIDATE_YAML=false \ + -v $(PWD):/tmp/lint:rw,z docker.io/github/super-linter:slim-v4 + .phony: install test + From 46b6c13aef7eaf9dad36c23da9f1e4de8403942c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 28 Jun 2022 12:01:10 +0200 Subject: [PATCH 0283/1263] Switch kubeconform to use OCP 4.10 + ACM 2.5 + ESO 0.5.x --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 91efef7c..639a568c 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ test: ## run helm tests helmlint: ## run helm lint @for t in $(CHARTS); do helm lint $(TEST_OPTS) $(PATTERN_OPTS) $$t; if [ $$? != 0 ]; then exit 1; fi; done -API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.9/ +API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.10/ KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition' # We need to skip 'CustomResourceDefinition' as openapi2jsonschema seems to be unable to generate them ATM kubeconform: ## run helm kubeconform From badeba1f03e70ae831b9c6f5a8e6df99a6f23c19 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 28 Jun 2022 15:09:46 +0200 Subject: [PATCH 0284/1263] Switch to external-secrets.io/v1beta1 for the clusterstore This way we align with the v1beta1 externalsecrets we have already switched to. --- .../templates/golang-external-secrets-hub-secretstore.yaml | 2 +- tests/golang-external-secrets-naked.expected.yaml | 2 +- tests/golang-external-secrets-normal.expected.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml index 9980ca9a..30483a16 100644 --- a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +++ b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml @@ -1,4 +1,4 @@ -apiVersion: external-secrets.io/v1alpha1 +apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: name: vault-backend diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index fe85949f..4070d8ae 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -5697,7 +5697,7 @@ spec: secretName: golang-external-secrets-webhook --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml -apiVersion: external-secrets.io/v1alpha1 +apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: name: vault-backend diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index fe85949f..4070d8ae 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -5697,7 +5697,7 @@ spec: secretName: golang-external-secrets-webhook --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml -apiVersion: external-secrets.io/v1alpha1 +apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: name: vault-backend From 10b631516a746b0fc3b0913786edb568f5e848bf Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 29 Jun 2022 16:45:03 +0200 Subject: [PATCH 0285/1263] Add support for .disabled in imperative jobs If a job was the "disabled: true" key in its definition it won't be rendered in the CronJob definition. The use case here is to be able to simply noop some jobs without having to remove them fully. Tested with: imperative: jobs: - name: foo playbook: ansible/playbooks/foo.yml - name: bar playbook: ansible/playbooks/bar.yml disabled: true - name: baz playbook: ansible/playbooks/baz.yml disabled: false The above generates the following initContainers in the CronJob definition (note how bar is skipped): initContainers: ... - name: foo image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest command: ... - ansible/playbooks/foo.yml - name: baz command: ... - ansible/playbooks/test.yml --- clustergroup/templates/imperative/job.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index 625883d5..a6b9d25d 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -35,6 +35,7 @@ spec: - name: git mountPath: "/git" {{- range $.Values.clusterGroup.imperative.jobs }} + {{- if ne (.disabled | default "false" | toString | lower ) "true" }} - name: {{ .name }} image: {{ .image | default $.Values.clusterGroup.imperative.image }} imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} @@ -60,6 +61,7 @@ spec: - name: values-volume mountPath: /values/values.yaml subPath: values.yaml + {{- end }} {{- end }} containers: - name: {{ $.Values.clusterGroup.imperative.jobName }}-done From 5f069b0c168a3b791052dd0aa03b8b577257f323 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 29 Jun 2022 17:07:15 +0200 Subject: [PATCH 0286/1263] Fix verbosity job attribute I initially had planned to have a global .clusterGroup.imperative.verbosity helm value, but then in the example I committed to multicloud-gitops I made it per job (in the comments). Let's switch to a per-comment verbosity as it makes sense anyways and is more intuitive --- clustergroup/templates/imperative/job.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index a6b9d25d..d4b4bebf 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -49,8 +49,8 @@ spec: - timeout - {{ .timeout | default "600" | quote }} - ansible-playbook - {{- if $.Values.clusterGroup.imperative.verbosity }} - - {{ $.Values.clusterGroup.imperative.verbosity }} + {{- if .verbosity }} + - {{ .verbosity }} {{- end }} - -e - "@/values/values.yaml" From 6251d6a40e44489d48b43359e9640170fe0ec53a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 29 Jun 2022 17:12:12 +0200 Subject: [PATCH 0287/1263] Add support for tags in imperative jobs Let's add a .tag key in the job definition. This allows us to run playbooks with different tags if needed. Tested with: - name: foo playbook: ansible/playbooks/foo.yml verbosity: "-v" tags: "vault_init,vault_unseal" - name: bar playbook: ansible/playbooks/bar.yml disabled: true - name: baz playbook: ansible/playbooks/baz.yml disabled: false Obtained the following: initContainers: - name: foo image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest command: - timeout - "600" - ansible-playbook - -v - -t - vault_init,vault_unseal - -e - "@/values/values.yaml" - ansible/playbooks/foo.yml ... - name: baz command: - timeout - "600" - ansible-playbook - -e - "@/values/values.yaml" - ansible/playbooks/baz.yml --- clustergroup/templates/imperative/job.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index d4b4bebf..ba5b2819 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -52,6 +52,10 @@ spec: {{- if .verbosity }} - {{ .verbosity }} {{- end }} + {{- if .tags }} + - -t + - {{ .tags }} + {{- end }} - -e - "@/values/values.yaml" - {{ .playbook }} From fbceae2929eb29c02957dd87377af538d08793f3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 29 Jun 2022 17:17:07 +0200 Subject: [PATCH 0288/1263] Add super-linter.log to .gitignore That is generated locally by 'make super-linter' --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 29de39ee..34ec269e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ values-secret.yaml .*.expected.yaml pattern-vault.init pattern-vault.init.bak +super-linter.log From ba1327831eb05f268ef3d2ddf804e8d080b56650 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 29 Jun 2022 18:34:05 +0200 Subject: [PATCH 0289/1263] Add support for extravars in imperative jobs Tested with the following: - name: vault-unseal playbook: common/ansible/playbooks/vault/vault.yaml tags: "vault_init,vault_unseal,vault_secrets_init" extravars: - '{"file_unseal": false}' disabled: true --- clustergroup/templates/imperative/job.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index ba5b2819..86881c13 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -58,6 +58,10 @@ spec: {{- end }} - -e - "@/values/values.yaml" + {{- range .extravars }} + - -e + - {{ . | quote }} + {{- end }} - {{ .playbook }} volumeMounts: - name: git From b285436365c271e5ca308901140ccacd24b78e1c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 09:23:02 +0100 Subject: [PATCH 0290/1263] Move vault push secrets task into a separate role We'd like to keep any of our imperative steps in ansible. We want to avoid shell as it is just too fragile for most of our use-cases. The main goal is that this stuff is fully reusable. Otherwise we end up with fixes landing in one tree (say common) but never in the other repos. This initial pass implements the following: - Add common/ansible/ folder with roles and tasks for each imperative thing we need to do. - We'll start off with common/ansible/roles/vault_utils and the task to push secrets - So common/ansible/roles/vault_utils/ will be a role with no main.yaml task but instead a bunch of other specific tasks - So injecting secrets will be something like: - name: Run tasks/other.yaml instead of 'main' include_role: name: vault_utils tasks_from: push_secrets - In industrial edge and other instead of duplicating that code we can import specific tasks like done above --- ansible/roles/vault_utils/README.md | 38 ++++ ansible/roles/vault_utils/defaults/main.yml | 8 + ansible/roles/vault_utils/handlers/main.yml | 2 + ansible/roles/vault_utils/meta/main.yml | 52 +++++ ansible/roles/vault_utils/tasks/main.yml | 2 + .../roles/vault_utils/tasks/push_secrets.yaml | 106 +++++++++++ ansible/roles/vault_utils/tests/inventory | 2 + ansible/roles/vault_utils/tests/test.yml | 5 + ansible/roles/vault_utils/vars/main.yml | 2 + scripts/ansible-push-vault-secrets.sh | 180 +----------------- scripts/ansible.cfg | 3 + scripts/ansible_push_vault_secrets.yaml | 18 ++ 12 files changed, 244 insertions(+), 174 deletions(-) create mode 100644 ansible/roles/vault_utils/README.md create mode 100644 ansible/roles/vault_utils/defaults/main.yml create mode 100644 ansible/roles/vault_utils/handlers/main.yml create mode 100644 ansible/roles/vault_utils/meta/main.yml create mode 100644 ansible/roles/vault_utils/tasks/main.yml create mode 100644 ansible/roles/vault_utils/tasks/push_secrets.yaml create mode 100644 ansible/roles/vault_utils/tests/inventory create mode 100644 ansible/roles/vault_utils/tests/test.yml create mode 100644 ansible/roles/vault_utils/vars/main.yml create mode 100644 scripts/ansible.cfg create mode 100644 scripts/ansible_push_vault_secrets.yaml diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md new file mode 100644 index 00000000..225dd44b --- /dev/null +++ b/ansible/roles/vault_utils/README.md @@ -0,0 +1,38 @@ +Role Name +========= + +A brief description of the role goes here. + +Requirements +------------ + +Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. + +Role Variables +-------------- + +A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. + +Dependencies +------------ + +A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. + +Example Playbook +---------------- + +Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: + + - hosts: servers + roles: + - { role: username.rolename, x: 42 } + +License +------- + +BSD + +Author Information +------------------ + +An optional section for the role authors to include contact information, or a website (HTML is not allowed). diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml new file mode 100644 index 00000000..4d0f18c2 --- /dev/null +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -0,0 +1,8 @@ +--- +# defaults file for vault_utils +values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" +kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" +vault_ns: "vault" +vault_pod: "vault-0" +vault_path: "secret/hub" +debug: False diff --git a/ansible/roles/vault_utils/handlers/main.yml b/ansible/roles/vault_utils/handlers/main.yml new file mode 100644 index 00000000..a983544d --- /dev/null +++ b/ansible/roles/vault_utils/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for vault_utils diff --git a/ansible/roles/vault_utils/meta/main.yml b/ansible/roles/vault_utils/meta/main.yml new file mode 100644 index 00000000..c572acc9 --- /dev/null +++ b/ansible/roles/vault_utils/meta/main.yml @@ -0,0 +1,52 @@ +galaxy_info: + author: your name + description: your role description + company: your company (optional) + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + # issue_tracker_url: http://example.com/issue/tracker + + # Choose a valid license ID from https://spdx.org - some suggested licenses: + # - BSD-3-Clause (default) + # - MIT + # - GPL-2.0-or-later + # - GPL-3.0-only + # - Apache-2.0 + # - CC-BY-4.0 + license: license (GPL-2.0-or-later, MIT, etc) + + min_ansible_version: 2.1 + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + # + # Provide a list of supported platforms, and for each platform a list of versions. + # If you don't wish to enumerate all versions for a particular platform, use 'all'. + # To view available platforms and versions (or releases), visit: + # https://galaxy.ansible.com/api/v1/platforms/ + # + # platforms: + # - name: Fedora + # versions: + # - all + # - 25 + # - name: SomePlatform + # versions: + # - all + # - 1.0 + # - 7 + # - 99.99 + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] + # List your role dependencies here, one per line. Be sure to remove the '[]' above, + # if you add dependencies to this list. diff --git a/ansible/roles/vault_utils/tasks/main.yml b/ansible/roles/vault_utils/tasks/main.yml new file mode 100644 index 00000000..31c8f701 --- /dev/null +++ b/ansible/roles/vault_utils/tasks/main.yml @@ -0,0 +1,2 @@ +--- +# tasks file for vault_utils diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml new file mode 100644 index 00000000..3b6c896a --- /dev/null +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -0,0 +1,106 @@ +- name: Check for existence of "{{ values_secret }}" + ansible.builtin.stat: + path: "{{ values_secret }}" + register: result + failed_when: not result.stat.exists + +- name: Check that KUBECONFIG is correctly set + fail: + msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" + failed_when: kubeconfig is not defined or kubeconfig | length == 0 + when: not debug | bool + +- name: Parse "{{ values_secret }}" + ansible.builtin.set_fact: + all_values: "{{ lookup('file', values_secret) | from_yaml }}" + +- name: Set secrets fact + ansible.builtin.set_fact: + secrets: "{{ all_values['secrets'] }}" + +- name: Verify we have any secrets at all + ansible.builtin.fail: + msg: "Was not able to parse any secrets from file {{ values_secret }}: {{ all_values }}" + failed_when: + secrets is not defined or secrets | length == 0 + +# Detect here if we have only the following two keys under a password +# s3.accessKey: +# s3.secretKey: +# If we do, then detect it and calculate the b64 s3Secret token +# Note: the vars: line is due to https://github.com/ansible/ansible/issues/40239 +- name: Check if any of the passwords has only s3.[accessKey,secretKey] and generate the combined s3Secret in that case + ansible.builtin.set_fact: + s3keys: "{{ s3keys | default({}) | combine({ item.key: {'s3Secret': s3secret | b64encode } }) }}" + vars: + s3secret: "{{ 's3.accessKey: ' + item.value['s3.accessKey'] + '\ns3.secretKey: ' + item.value['s3.secretKey'] }}" + when: + - '"s3.accessKey" in item.value.keys()' + - '"s3.secretKey" in item.value.keys()' + - '"s3Secret" not in item.value.keys()' + loop: + "{{ secrets | dict2items }}" + loop_control: + label: "{{ item.key }}" + +- name: Merge any s3Secret into the secrets dictionary if we have any + ansible.builtin.set_fact: + secrets: "{{ secrets | combine(s3keys) }}" + when: + s3keys is defined and s3keys | length > 0 + +- name: Check for vault namespace + kubernetes.core.k8s_info: + kind: Namespace + name: "{{ vault_ns }}" + register: vault_ns_rc + failed_when: vault_ns_rc.resources | length == 0 + when: not debug | bool + +- name: Check if the vault pod is present + kubernetes.core.k8s_info: + kind: Pod + namespace: "{{ vault_ns }}" + name: "{{ vault_pod }}" + register: vault_pod_rc + failed_when: vault_pod_rc.resources | length == 0 + when: not debug | bool + +# vault status returns 1 on error and 2 on sealed +# so we can bail out when sealed +- name: Check if the vault is unsealed + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault status + register: vault_status + failed_when: vault_status.rc|int == 1 + when: not debug | bool + +- name: Check vault status return + ansible.builtin.fail: + msg: The vault is still sealed. Please run "make vault-init" first with KUBECONFIG pointing to the HUB cluster + when: + - not debug | bool + - vault_status.rc | int > 0 + +- name: Debug + debug: + msg: "vault kv put {{ vault_path }}/{{ item.key }} -> {{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}" + loop: + "{{ secrets | dict2items }}" + loop_control: + label: "{{ item.key }}" + when: debug | bool + +- name: Add the actual secrets to the vault + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: | + sh -c "vault kv put {{ vault_path }}/{{ item.key }} {{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}" + loop: + "{{ secrets | dict2items }}" + loop_control: + label: "{{ item.key }}" + when: not debug | bool diff --git a/ansible/roles/vault_utils/tests/inventory b/ansible/roles/vault_utils/tests/inventory new file mode 100644 index 00000000..878877b0 --- /dev/null +++ b/ansible/roles/vault_utils/tests/inventory @@ -0,0 +1,2 @@ +localhost + diff --git a/ansible/roles/vault_utils/tests/test.yml b/ansible/roles/vault_utils/tests/test.yml new file mode 100644 index 00000000..0998beb6 --- /dev/null +++ b/ansible/roles/vault_utils/tests/test.yml @@ -0,0 +1,5 @@ +--- +- hosts: localhost + remote_user: root + roles: + - vault_utils diff --git a/ansible/roles/vault_utils/vars/main.yml b/ansible/roles/vault_utils/vars/main.yml new file mode 100644 index 00000000..f6e02b93 --- /dev/null +++ b/ansible/roles/vault_utils/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for vault_utils diff --git a/scripts/ansible-push-vault-secrets.sh b/scripts/ansible-push-vault-secrets.sh index a29b5e2d..9b1ef8a9 100755 --- a/scripts/ansible-push-vault-secrets.sh +++ b/scripts/ansible-push-vault-secrets.sh @@ -1,177 +1,9 @@ -#!/usr/bin/env ansible-playbook -- name: Secret injection of validated-patterns - hosts: localhost - connection: local - gather_facts: no - vars: - kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" - kubeconfig_backup: "{{ lookup('env', 'HOME') }}/.kube/config" - values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" - vault_ns: "vault" - vault_pod: "vault-0" - vault_path: "secret/hub" - debug: False +#!/usr/bin/env bash +set -ex - tasks: - - name: Check if the kubernetes python module is usable from ansible - ansible.builtin.shell: "{{ ansible_python_interpreter }} -c 'import kubernetes'" +SCRIPT=$(readlink -f "$0") +SCRIPTPATH=$(dirname "$SCRIPT") - - name: Check for existence of "{{ values_secret }}" - ansible.builtin.stat: - path: "{{ values_secret }}" - register: result - failed_when: not result.stat.exists +export ANSIBLE_CONFIG="${SCRIPTPATH}/ansible.cfg" - - name: Check if KUBECONFIG is correctly set - debug: - msg: "KUBECONFIG is not set, falling back to ~/.kube/config" - when: kubeconfig is not defined or kubeconfig | length == 0 - - - name: Check if ~/.kube/config exists - ansible.builtin.stat: - path: "{{ kubeconfig_backup }}" - register: kubeconfig_result - - - name: Fail if both KUBECONFIG and ~/.kube/config do not exist - ansible.builtin.fail: - msg: "{{ kubeconfig_backup }} not found and KUBECONFIG unset. Bailing out." - failed_when: not kubeconfig_result.stat.exists and (kubeconfig is not defined or kubeconfig | length == 0) - - - name: Parse "{{ values_secret }}" - ansible.builtin.set_fact: - all_values: "{{ lookup('file', values_secret) | from_yaml }}" - - - name: Set secrets fact - ansible.builtin.set_fact: - secrets: "{{ all_values['secrets'] }}" - - - name: Verify we have any secrets at all - ansible.builtin.fail: - msg: "Was not able to parse any secrets from file {{ values_secret }}: {{ all_values }}" - failed_when: - secrets is not defined or secrets | length == 0 - - - name: Check the value-secret.yaml file for errors - ansible.builtin.fail: - msg: > - "{{ item }}" is not properly formatted. Each key under 'secrets:' - needs to point to a dictionary of key, value pairs. See values-secret.yaml.template. - when: > - item.key | length == 0 or - item.value is not mapping - loop: - "{{ secrets | dict2items }}" - loop_control: - label: "{{ item.key }}" - - # Detect here if we have only the following two keys under a password - # s3.accessKey: - # s3.secretKey: - # If we do, then detect it and calculate the b64 s3Secret token - # Note: the vars: line is due to https://github.com/ansible/ansible/issues/40239 - - name: Check if any of the passwords has only s3.[accessKey,secretKey] and generate the combined s3Secret in that case - ansible.builtin.set_fact: - s3keys: "{{ s3keys | default({}) | combine({ item.key: {'s3Secret': s3secret | b64encode } }) }}" - vars: - s3secret: "{{ 's3.accessKey: ' + item.value['s3.accessKey'] + '\ns3.secretKey: ' + item.value['s3.secretKey'] }}" - when: - - '"s3.accessKey" in item.value.keys()' - - '"s3.secretKey" in item.value.keys()' - - '"s3Secret" not in item.value.keys()' - loop: - "{{ secrets | dict2items }}" - loop_control: - label: "{{ item.key }}" - - - name: Merge any s3Secret into the secrets dictionary if we have any - ansible.builtin.set_fact: - secrets: "{{ secrets | combine(s3keys) }}" - when: - s3keys is defined and s3keys | length > 0 - - - name: Check for vault namespace - kubernetes.core.k8s_info: - kind: Namespace - name: "{{ vault_ns }}" - register: vault_ns_rc - failed_when: vault_ns_rc.resources | length == 0 - until: vault_ns_rc is success - retries: 30 - delay: 5 - when: not debug | bool - - - name: Check if the vault pod is present - kubernetes.core.k8s_info: - kind: Pod - namespace: "{{ vault_ns }}" - name: "{{ vault_pod }}" - register: vault_pod_rc - failed_when: vault_pod_rc.resources | length == 0 - when: not debug | bool - - # vault status returns 1 on error and 2 on sealed - # so we can bail out when sealed - - name: Check if the vault is unsealed - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault status - register: vault_status - failed_when: vault_status.rc|int == 1 - when: not debug | bool - - - name: Check vault status return - ansible.builtin.fail: - msg: The vault is still sealed. Please run "make vault-init" first with KUBECONFIG pointing to the HUB cluster - when: - - not debug | bool - - vault_status.rc | int > 0 - -# The values-secret.yaml file is in the fairly loose form of: -# secrets: -# group1: -# key1: value1 -# key2: value2 -# group2: -# key1: valueA -# key4: valueC -# The above will generate: -# vault kv put 'secret/hub/group1' key1='value1' key2='value2' -# vault kv put 'secret/hub/group2' key1='valueA' key4='valueC' -# Below we loop on the top level keys (group1, group2) and for each -# sub-top-level key (key1, key2) we create a single 'key1=value1 key2=value2' string -# -# Special note is to be given to the regex_replace which is run against each value aka password: -# A. The need for it is to quote the passwords -# B. Since it needs to cater to multiline strings (certs) and ansible offers no way -# to specify re.DOTALL and re.MULTILINE we need to use (?ms) at the beginning -# See https://docs.python.org/3/library/re.html and (?aiLmsux) section -# and https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/filter/core.py#L124 -# C. The \\A and \\Z match the beginning and end of a string (in case of multiline strings -# do not want to match every line) - - name: Set vault commands fact - ansible.builtin.set_fact: - vault_cmds: "{{ vault_cmds | default({}) | combine({ item.key: vault_cmd}) }}" - vars: - vault_cmd: "vault kv put '{{ vault_path }}/{{ item.key }}' {{ item.value.keys() | zip(item.value.values() | map('regex_replace', '(?ms)\\A(.*)\\Z', \"'\\1'\")) | map('join', '=') | list | join(' ') }}" - loop: - "{{ secrets | dict2items }}" - loop_control: - label: "{{ item.key }}" - - - name: Debug vault commands - ansible.builtin.debug: - msg: "{{ vault_cmds }}" - when: debug | bool - - - name: Add the actual secrets to the vault - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: | - sh -c "{{ item.value }}" - loop: - "{{ vault_cmds | dict2items }}" - loop_control: - label: "{{ item.key }}" - when: not debug | bool +ansible-playbook "$SCRIPTPATH/ansible_push_vault_secrets.yaml" diff --git a/scripts/ansible.cfg b/scripts/ansible.cfg new file mode 100644 index 00000000..a163814b --- /dev/null +++ b/scripts/ansible.cfg @@ -0,0 +1,3 @@ +[defaults] +display_skipped_hosts=False +roles_path=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:../ansible/roles diff --git a/scripts/ansible_push_vault_secrets.yaml b/scripts/ansible_push_vault_secrets.yaml new file mode 100644 index 00000000..9ef1653e --- /dev/null +++ b/scripts/ansible_push_vault_secrets.yaml @@ -0,0 +1,18 @@ +- name: Secret injection of validated-patterns + hosts: localhost + connection: local + gather_facts: no + vars: + values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" + kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" + vault_ns: "vault" + vault_pod: "vault-0" + vault_path: "secret/hub" + debug: False + + tasks: + - name: Run push secrets task + include_role: + name: vault_utils + tasks_from: push_secrets + From 6c681df8513d0d688dbc72c20324619ae5d50163 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 10:05:14 +0100 Subject: [PATCH 0291/1263] Shuffle files around to keep the ansible tree structure cleaner This way we keep all ansible stuff under ansible/ and leave the tiny shell wrappers in scripts/ --- ansible/ansible.cfg | 3 +++ ansible/playbooks/vault/push_secrets.yaml | 18 ++++++++++++++++++ scripts/ansible-push-vault-secrets.sh | 9 +++++---- 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 ansible/ansible.cfg create mode 100644 ansible/playbooks/vault/push_secrets.yaml diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg new file mode 100644 index 00000000..0a189294 --- /dev/null +++ b/ansible/ansible.cfg @@ -0,0 +1,3 @@ +[defaults] +display_skipped_hosts=False +roles_path=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:./roles diff --git a/ansible/playbooks/vault/push_secrets.yaml b/ansible/playbooks/vault/push_secrets.yaml new file mode 100644 index 00000000..9ef1653e --- /dev/null +++ b/ansible/playbooks/vault/push_secrets.yaml @@ -0,0 +1,18 @@ +- name: Secret injection of validated-patterns + hosts: localhost + connection: local + gather_facts: no + vars: + values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" + kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" + vault_ns: "vault" + vault_pod: "vault-0" + vault_path: "secret/hub" + debug: False + + tasks: + - name: Run push secrets task + include_role: + name: vault_utils + tasks_from: push_secrets + diff --git a/scripts/ansible-push-vault-secrets.sh b/scripts/ansible-push-vault-secrets.sh index 9b1ef8a9..98652dd0 100755 --- a/scripts/ansible-push-vault-secrets.sh +++ b/scripts/ansible-push-vault-secrets.sh @@ -1,9 +1,10 @@ #!/usr/bin/env bash -set -ex +set -e SCRIPT=$(readlink -f "$0") SCRIPTPATH=$(dirname "$SCRIPT") +ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" +PLAYBOOKPATH="${ANSIBLEPATH}/playbooks" +export ANSIBLE_CONFIG="${ANSIBLEPATH}/ansible.cfg" -export ANSIBLE_CONFIG="${SCRIPTPATH}/ansible.cfg" - -ansible-playbook "$SCRIPTPATH/ansible_push_vault_secrets.yaml" +ansible-playbook "$PLAYBOOKPATH/vault/push_secrets.yaml" From 98863a759154c27cc66bbb112b04bf733d611ae3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 10:31:25 +0100 Subject: [PATCH 0292/1263] Remove files from their old place --- scripts/ansible.cfg | 3 --- scripts/ansible_push_vault_secrets.yaml | 18 ------------------ 2 files changed, 21 deletions(-) delete mode 100644 scripts/ansible.cfg delete mode 100644 scripts/ansible_push_vault_secrets.yaml diff --git a/scripts/ansible.cfg b/scripts/ansible.cfg deleted file mode 100644 index a163814b..00000000 --- a/scripts/ansible.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[defaults] -display_skipped_hosts=False -roles_path=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:../ansible/roles diff --git a/scripts/ansible_push_vault_secrets.yaml b/scripts/ansible_push_vault_secrets.yaml deleted file mode 100644 index 9ef1653e..00000000 --- a/scripts/ansible_push_vault_secrets.yaml +++ /dev/null @@ -1,18 +0,0 @@ -- name: Secret injection of validated-patterns - hosts: localhost - connection: local - gather_facts: no - vars: - values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" - kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" - vault_ns: "vault" - vault_pod: "vault-0" - vault_path: "secret/hub" - debug: False - - tasks: - - name: Run push secrets task - include_role: - name: vault_utils - tasks_from: push_secrets - From 3d91598a81ac78fd4d9488ff15da55c98599733c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 10:55:50 +0100 Subject: [PATCH 0293/1263] Initial work on vault_init --- ansible/playbooks/vault/push_secrets.yaml | 10 +--- ansible/playbooks/vault/vault_init.yaml | 12 +++++ ansible/roles/vault_utils/defaults/main.yml | 1 + .../roles/vault_utils/tasks/vault_delete.yaml | 13 +++++ .../roles/vault_utils/tasks/vault_init.yaml | 50 +++++++++++++++++++ scripts/vault-utils.sh | 34 ++++++++++++- 6 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 ansible/playbooks/vault/vault_init.yaml create mode 100644 ansible/roles/vault_utils/tasks/vault_delete.yaml create mode 100644 ansible/roles/vault_utils/tasks/vault_init.yaml diff --git a/ansible/playbooks/vault/push_secrets.yaml b/ansible/playbooks/vault/push_secrets.yaml index 9ef1653e..d4241582 100644 --- a/ansible/playbooks/vault/push_secrets.yaml +++ b/ansible/playbooks/vault/push_secrets.yaml @@ -1,18 +1,10 @@ +--- - name: Secret injection of validated-patterns hosts: localhost connection: local gather_facts: no - vars: - values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" - kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" - vault_ns: "vault" - vault_pod: "vault-0" - vault_path: "secret/hub" - debug: False - tasks: - name: Run push secrets task include_role: name: vault_utils tasks_from: push_secrets - diff --git a/ansible/playbooks/vault/vault_init.yaml b/ansible/playbooks/vault/vault_init.yaml new file mode 100644 index 00000000..45928315 --- /dev/null +++ b/ansible/playbooks/vault/vault_init.yaml @@ -0,0 +1,12 @@ +--- +- name: Vault init + hosts: localhost + connection: local + gather_facts: no + tasks: + - name: Vault init + include_role: + name: vault_utils + tasks_from: vault_init + + diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 4d0f18c2..0f4e4f3d 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -6,3 +6,4 @@ vault_ns: "vault" vault_pod: "vault-0" vault_path: "secret/hub" debug: False +output_file: "common/vault.init" diff --git a/ansible/roles/vault_utils/tasks/vault_delete.yaml b/ansible/roles/vault_utils/tasks/vault_delete.yaml new file mode 100644 index 00000000..9d153c70 --- /dev/null +++ b/ansible/roles/vault_utils/tasks/vault_delete.yaml @@ -0,0 +1,13 @@ +--- +- name: Check that KUBECONFIG is correctly set + fail: + msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" + failed_when: kubeconfig is not defined or kubeconfig | length == 0 + when: not debug | bool + +# FIXME +- name: Delete pods + shell: | + oc -n vault delete pod vault-0 & + oc -n vault delete pvc data-vault-0 & + sleep 10 diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml new file mode 100644 index 00000000..b96fb04d --- /dev/null +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -0,0 +1,50 @@ +--- +- name: Check that KUBECONFIG is correctly set + fail: + msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" + failed_when: kubeconfig is not defined or kubeconfig | length == 0 + when: not debug | bool + +- name: Check for existence of "{{ output_file }}" + ansible.builtin.stat: + path: "{{ output_file }}" + register: result + +- name: Fail if "{{ output_file }} exists" + fail: + msg: "{{ output_file }} already exists, not overwriting. Please move it away before proceeding" + failed_when: result.stat.exists + +- name: Check for vault namespace + kubernetes.core.k8s_info: + kind: Namespace + name: "{{ vault_ns }}" + register: vault_ns_rc + failed_when: vault_ns_rc.resources | length == 0 + when: not debug | bool + +- name: Check if the vault pod is present + kubernetes.core.k8s_info: + kind: Pod + namespace: "{{ vault_ns }}" + name: "{{ vault_pod }}" + register: vault_pod_rc + failed_when: vault_pod_rc.resources | length == 0 + when: not debug | bool + +- name: Init vault operator + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: | + vault operator init -format=json + register: vault_init_json + +- name: debug + debug: + msg: "{{ vault_init_json }}" + +- name: Save vault operator output + ansible.builtin.copy: + dest: "{{ output_file }}" + content: "{{ vault_init_json.stdout | to_nice_json }}" diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index a36a69a3..87b4adcb 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -1,4 +1,36 @@ -#!/bin/sh +#!/usr/bin/env bash +set -e + +SCRIPT=$(readlink -f "$0") +SCRIPTPATH=$(dirname "$SCRIPT") +COMMONPATH=$(dirname "$SCRIPTPATH") +ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" +PLAYBOOKPATH="${ANSIBLEPATH}/playbooks" +export ANSIBLE_CONFIG="${ANSIBLEPATH}/ansible.cfg" + +# Parse arguments +if [ $# -lt 1 ]; then + echo "Specify at least the command ($#): $*" + exit 1 +fi + +TASK="${1}" +OUTFILE=${2:-"$COMMONPATH"/vault.init} + +if [ -z ${TASK} ]; then + echo "Task is unset" + exit 1 +fi + +if [ ! -f "${PLAYBOOKPATH}/vault/${TASK}.yaml" ]; then + echo "${PLAYBOOKPATH}/vault/${TASK}.yaml does not exist, exiting" + exit 1 +fi + +ansible-playbook -e output_file="${OUTFILE}" "$PLAYBOOKPATH/vault/${TASK}.yaml" +exit $? + + # Assumptions - vault in the demo will be running in non-HA mode so there will only be a vault-0 # vault will be running in the "vault" namespace From 6cb7a4abe215481b002a001d270a8f5df8c3891f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 11:11:44 +0100 Subject: [PATCH 0294/1263] Avoid using all these playbooks --- ansible/playbooks/vault/push_secrets.yaml | 10 ---------- ansible/playbooks/vault/vault_init.yaml | 12 ------------ scripts/vault-utils.sh | 23 +++++++++++++++++++---- 3 files changed, 19 insertions(+), 26 deletions(-) delete mode 100644 ansible/playbooks/vault/push_secrets.yaml delete mode 100644 ansible/playbooks/vault/vault_init.yaml diff --git a/ansible/playbooks/vault/push_secrets.yaml b/ansible/playbooks/vault/push_secrets.yaml deleted file mode 100644 index d4241582..00000000 --- a/ansible/playbooks/vault/push_secrets.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Secret injection of validated-patterns - hosts: localhost - connection: local - gather_facts: no - tasks: - - name: Run push secrets task - include_role: - name: vault_utils - tasks_from: push_secrets diff --git a/ansible/playbooks/vault/vault_init.yaml b/ansible/playbooks/vault/vault_init.yaml deleted file mode 100644 index 45928315..00000000 --- a/ansible/playbooks/vault/vault_init.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -- name: Vault init - hosts: localhost - connection: local - gather_facts: no - tasks: - - name: Vault init - include_role: - name: vault_utils - tasks_from: vault_init - - diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 87b4adcb..15c27d87 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -5,7 +5,9 @@ SCRIPT=$(readlink -f "$0") SCRIPTPATH=$(dirname "$SCRIPT") COMMONPATH=$(dirname "$SCRIPTPATH") ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" -PLAYBOOKPATH="${ANSIBLEPATH}/playbooks" +ROLEPATH="${ANSIBLEPATH}/roles/vault_utils" +TMPDIR=${TMPDIR:-/tmp} +TMPFILE=$(mktemp "${TMPDIR}/vault-utils.XXXXXXX.yaml") export ANSIBLE_CONFIG="${ANSIBLEPATH}/ansible.cfg" # Parse arguments @@ -22,12 +24,25 @@ if [ -z ${TASK} ]; then exit 1 fi -if [ ! -f "${PLAYBOOKPATH}/vault/${TASK}.yaml" ]; then - echo "${PLAYBOOKPATH}/vault/${TASK}.yaml does not exist, exiting" +if [ ! -f "${ROLEPATH}/tasks/${TASK}.yaml" ]; then + echo "${ROLEPATH}/tasks/${TASK}.yaml does not exist, exiting" exit 1 fi -ansible-playbook -e output_file="${OUTFILE}" "$PLAYBOOKPATH/vault/${TASK}.yaml" +cat > "${TMPFILE}" < Date: Mon, 14 Mar 2022 13:04:20 +0100 Subject: [PATCH 0295/1263] Always pass absolute paths --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 3bace9ea..49d98235 100644 --- a/Makefile +++ b/Makefile @@ -51,10 +51,10 @@ uninstall: ## runs helm uninstall helm uninstall $(NAME) vault-init: ## inits, unseals and configured the vault - common/scripts/vault-utils.sh vault_init common/pattern-vault.init + common/scripts/vault-utils.sh vault_init $(shell readlink -f common/pattern-vault.init) vault-unseal: ## unseals the vault - common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init + common/scripts/vault-utils.sh vault_unseal $(shell readlink -f common/pattern-vault.init) load-secrets: ## loads the secrets into the vault common/scripts/ansible-push-vault-secrets.sh From 7f8856af6ec599ad666fa01a7783f0f3663a0fe2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 13:04:39 +0100 Subject: [PATCH 0296/1263] Implement most of vault_init and vault_unseal --- ansible/playbooks/vault/push_secrets.yaml | 10 +++++ ansible/playbooks/vault/vault_delete.yaml | 10 +++++ ansible/playbooks/vault/vault_init.yaml | 21 +++++++++ ansible/roles/vault_utils/defaults/main.yml | 2 + .../roles/vault_utils/tasks/vault_delete.yaml | 26 ++++++++--- .../roles/vault_utils/tasks/vault_init.yaml | 14 +++--- .../vault_utils/tasks/vault_secrets_init.yaml | 43 +++++++++++++++++++ .../roles/vault_utils/tasks/vault_unseal.yaml | 39 +++++++++++++++++ scripts/vault-utils.sh | 23 ++-------- 9 files changed, 155 insertions(+), 33 deletions(-) create mode 100644 ansible/playbooks/vault/push_secrets.yaml create mode 100644 ansible/playbooks/vault/vault_delete.yaml create mode 100644 ansible/playbooks/vault/vault_init.yaml create mode 100644 ansible/roles/vault_utils/tasks/vault_secrets_init.yaml create mode 100644 ansible/roles/vault_utils/tasks/vault_unseal.yaml diff --git a/ansible/playbooks/vault/push_secrets.yaml b/ansible/playbooks/vault/push_secrets.yaml new file mode 100644 index 00000000..d4241582 --- /dev/null +++ b/ansible/playbooks/vault/push_secrets.yaml @@ -0,0 +1,10 @@ +--- +- name: Secret injection of validated-patterns + hosts: localhost + connection: local + gather_facts: no + tasks: + - name: Run push secrets task + include_role: + name: vault_utils + tasks_from: push_secrets diff --git a/ansible/playbooks/vault/vault_delete.yaml b/ansible/playbooks/vault/vault_delete.yaml new file mode 100644 index 00000000..e42da16d --- /dev/null +++ b/ansible/playbooks/vault/vault_delete.yaml @@ -0,0 +1,10 @@ +--- +- name: Vault removal + hosts: localhost + connection: local + gather_facts: no + tasks: + - name: Run vault delete tasks + include_role: + name: vault_utils + tasks_from: vault_delete diff --git a/ansible/playbooks/vault/vault_init.yaml b/ansible/playbooks/vault/vault_init.yaml new file mode 100644 index 00000000..95b5bcfa --- /dev/null +++ b/ansible/playbooks/vault/vault_init.yaml @@ -0,0 +1,21 @@ +--- +- name: Vault initialization + hosts: localhost + connection: local + gather_facts: no + vars: + external_secrets_ns: golang-external-secrets + external_secrets_sa: golang-external-secrets + tasks: + - name: Run vault init tasks + include_role: + name: vault_utils + tasks_from: vault_init + - name: Unseal vault + include_role: + name: vault_utils + tasks_from: vault_unseal + - name: Vault secrets init + include_role: + name: vault_utils + tasks_from: vault_secrets_init diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 0f4e4f3d..7e2bd7a9 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -4,6 +4,8 @@ values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" vault_ns: "vault" vault_pod: "vault-0" +vault_hub: "hub" +vault_hub_kubernetes_host: 'https://$KUBERNETES_PORT_443_TCP_ADDR:443' vault_path: "secret/hub" debug: False output_file: "common/vault.init" diff --git a/ansible/roles/vault_utils/tasks/vault_delete.yaml b/ansible/roles/vault_utils/tasks/vault_delete.yaml index 9d153c70..645bed8c 100644 --- a/ansible/roles/vault_utils/tasks/vault_delete.yaml +++ b/ansible/roles/vault_utils/tasks/vault_delete.yaml @@ -1,13 +1,25 @@ --- - name: Check that KUBECONFIG is correctly set - fail: + ansible.builtin.fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 when: not debug | bool -# FIXME -- name: Delete pods - shell: | - oc -n vault delete pod vault-0 & - oc -n vault delete pvc data-vault-0 & - sleep 10 +# FIXME to use proper k8 ansible collection +- name: Delete vault pod + ansible.builtin.shell: | + oc -n vault delete pod vault-0 + register: delete_pod + async: 7200 + poll: 0 + +- name: Delete vault pvc + ansible.builtin.shell: | + oc -n vault delete pvc data-vault-0 + register: delete_pvc + async: 7200 + poll: 0 + +- name: Wait for tasks + wait_for: + timeout: 10 diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index b96fb04d..31ee5844 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -36,15 +36,15 @@ kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" - command: | - vault operator init -format=json - register: vault_init_json + command: vault operator init -format=json + register: vault_init_json_out -- name: debug - debug: - msg: "{{ vault_init_json }}" +- name: Set output json fact + set_fact: + vault_init_json: "{{ vault_init_json_out.stdout | from_json }}" - name: Save vault operator output ansible.builtin.copy: + follow: true dest: "{{ output_file }}" - content: "{{ vault_init_json.stdout | to_nice_json }}" + content: "{{ vault_init_json | to_nice_json }}" diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml new file mode 100644 index 00000000..0120067f --- /dev/null +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -0,0 +1,43 @@ +--- +- name: Check that KUBECONFIG is correctly set + fail: + msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" + failed_when: kubeconfig is not defined or kubeconfig | length == 0 + when: not debug | bool + +- name: Create secrets backend kv-v2 + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault secrets enable -path=secret kv-v2 + +- name: Enable kubernetes backend on hub + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: "vault auth enable -path={{ vault_hub }} kubernetes" + +- name: Fetch "{{ external_secrets_ns }}/{{ external_secrets_sa }} secret name" + ansible.builtin.shell: | + oc get -n "{{ external_secrets_ns }}" serviceaccount "{{ external_secrets_sa }}" -o jsonpath='{.secrets}' + register: secrets_out + +- name: Filter secrets of "{{ external_secrets_ns }}/{{ external_secrets_sa }}" + set_fact: + secret: "{{ secrets_out.stdout | from_json | json_query(\"[?starts_with(name, 'golang-external-secrets-token-')].name | [0]\") }}" + +- name: Get token of "{{ external_secrets_ns }}/{{ external_secrets_sa }}" + ansible.builtin.shell: | + oc get secret -n "{{external_secrets_ns }}" "{{ secret }}" -o go-template="{{ '{{' }} .data.token | base64decode {{ '}}' }}" + register: sa_token_out + +- name: Set sa_token fact + set_fact: + sa_token: "{{ sa_token_out.stdout }}" + +- name: Configure hub kubernetes backend + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: | + vault write auth/"{{ vault_hub }}"/config token_reviewer_jwt="{{ sa_token }}" kubernetes_host="{{ vault_hub_kubernetes_host }}" kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml new file mode 100644 index 00000000..87df162e --- /dev/null +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -0,0 +1,39 @@ +--- +- name: Check that KUBECONFIG is correctly set + fail: + msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" + failed_when: kubeconfig is not defined or kubeconfig | length == 0 + when: not debug | bool + +- name: Check for existence of "{{ output_file }}" + ansible.builtin.stat: + path: "{{ output_file }}" + register: result + +- name: Fail if "{{ output_file }}" does not exists + fail: + msg: "{{ output_file }} does not exist. Stopping here" + failed_when: not result.stat.exists + +# We reparse the json vault init file in case unseal was called without operator init before +- name: Parse "{{ output_file }}" + set_fact: + vault_init_json: "{{ lookup('file', output_file) | from_json }}" + +- name: Set root token and unseal_keys + set_fact: + root_token: "{{ vault_init_json['root_token'] }}" + unseal_keys: "{{ vault_init_json['unseal_keys_hex'] }}" + +- name: Unseal vault + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault operator unseal "{{ item }}" + loop: "{{ unseal_keys }}" + +- name: Login into vault + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault login "{{ root_token }}" diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 15c27d87..450fcb1b 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -5,9 +5,7 @@ SCRIPT=$(readlink -f "$0") SCRIPTPATH=$(dirname "$SCRIPT") COMMONPATH=$(dirname "$SCRIPTPATH") ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" -ROLEPATH="${ANSIBLEPATH}/roles/vault_utils" -TMPDIR=${TMPDIR:-/tmp} -TMPFILE=$(mktemp "${TMPDIR}/vault-utils.XXXXXXX.yaml") +PLAYBOOKPATH="${ANSIBLEPATH}/playbooks" export ANSIBLE_CONFIG="${ANSIBLEPATH}/ansible.cfg" # Parse arguments @@ -24,25 +22,12 @@ if [ -z ${TASK} ]; then exit 1 fi -if [ ! -f "${ROLEPATH}/tasks/${TASK}.yaml" ]; then - echo "${ROLEPATH}/tasks/${TASK}.yaml does not exist, exiting" +if [ ! -f "${PLAYBOOKPATH}/vault/${TASK}.yaml" ]; then + echo "${PLAYBOOKPATH}/vault/${TASK}.yaml does not exist, exiting" exit 1 fi -cat > "${TMPFILE}" < Date: Mon, 14 Mar 2022 13:33:11 +0100 Subject: [PATCH 0297/1263] Complete vault_secrets_init --- ansible/roles/vault_utils/defaults/main.yml | 1 + .../vault_utils/tasks/vault_secrets_init.yaml | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 7e2bd7a9..19b4cb1c 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -7,5 +7,6 @@ vault_pod: "vault-0" vault_hub: "hub" vault_hub_kubernetes_host: 'https://$KUBERNETES_PORT_443_TCP_ADDR:443' vault_path: "secret/hub" +vault_hub_ttl: "15m" debug: False output_file: "common/vault.init" diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 0120067f..28ef3851 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -41,3 +41,27 @@ pod: "{{ vault_pod }}" command: | vault write auth/"{{ vault_hub }}"/config token_reviewer_jwt="{{ sa_token }}" kubernetes_host="{{ vault_hub_kubernetes_host }}" kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc + +- name: Configure policy template for hub + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: > + bash -e -c "echo \"path \\\"secret/data/{{ vault_hub }}/*\\\" { + capabilities = [\\\"create\\\", \\\"read\\\", \\\"update\\\", \\\"delete\\\", \\\"list\\\"] }\" > /tmp/policy-{{ vault_hub }}.hcl" + +- name: Configure policy for regional cluster + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: "vault policy write {{ vault_hub }}-secret /tmp/policy-{{ vault_hub }}.hcl" + +- name: Configure kubernetes role for regional cluster + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: > + vault write auth/"{{ vault_hub }}"/role/"{{ vault_hub }}"-role + bound_service_account_names="{{ external_secrets_sa }}" + bound_service_account_namespaces="{{ external_secrets_ns }}" + policies="default,{{ vault_hub }}-secret" ttl="{{ vault_hub_ttl }}" From 5001de03cd71500c2c487816ed4b50ffcb5c5f34 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 13:35:29 +0100 Subject: [PATCH 0298/1263] Do not print unseal keys in log --- ansible/roles/vault_utils/tasks/vault_unseal.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index 87df162e..4f89e2b1 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -31,6 +31,9 @@ pod: "{{ vault_pod }}" command: vault operator unseal "{{ item }}" loop: "{{ unseal_keys }}" + loop_control: + extended: true + label: "Unsealing with key {{ ansible_loop.index }}" - name: Login into vault kubernetes.core.k8s_exec: From e2522054402876454758fd3c993621619ca346e6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 13:56:45 +0100 Subject: [PATCH 0299/1263] Added vault_pki_internal command --- ansible/playbooks/vault/vault_pki_init.yaml | 10 + ansible/roles/vault_utils/defaults/main.yml | 1 + .../vault_utils/tasks/vault_pki_init.yaml | 47 ++++ .../vault_utils/tasks/vault_secrets_init.yaml | 4 +- scripts/vault-utils.sh | 220 ------------------ 5 files changed, 60 insertions(+), 222 deletions(-) create mode 100644 ansible/playbooks/vault/vault_pki_init.yaml create mode 100644 ansible/roles/vault_utils/tasks/vault_pki_init.yaml diff --git a/ansible/playbooks/vault/vault_pki_init.yaml b/ansible/playbooks/vault/vault_pki_init.yaml new file mode 100644 index 00000000..a759e927 --- /dev/null +++ b/ansible/playbooks/vault/vault_pki_init.yaml @@ -0,0 +1,10 @@ +--- +- name: Vault PKI init + hosts: localhost + connection: local + gather_facts: no + tasks: + - name: Run vault pki init tasks + include_role: + name: vault_utils + tasks_from: vault_pki_init diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 19b4cb1c..46970632 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -8,5 +8,6 @@ vault_hub: "hub" vault_hub_kubernetes_host: 'https://$KUBERNETES_PORT_443_TCP_ADDR:443' vault_path: "secret/hub" vault_hub_ttl: "15m" +vault_pki_max_lease_ttl: "8760h" debug: False output_file: "common/vault.init" diff --git a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml new file mode 100644 index 00000000..15d5c8b8 --- /dev/null +++ b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml @@ -0,0 +1,47 @@ +--- +- name: Check that KUBECONFIG is correctly set + fail: + msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" + failed_when: kubeconfig is not defined or kubeconfig | length == 0 + when: not debug | bool + +# FIXME: this should be done a lot more elegantly +- name: Get cluster domain + shell: | + oc get ingresses.config/cluster -o jsonpath='{.spec.domain}' | cut -d. -f3- + register: ingress_domain_out + +- name: Set ingress domain + set_fact: + ingress_domain: "{{ ingress_domain_out.stdout }}" + cert_role: "{{ ingress_domain_out.stdout | replace('.', '_') }}" + +- name: Enable vault pki backend + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault secrets enable pki + +- name: Set vault pki max-lease-ttl + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault secrets tune --max-lease-ttl="{{ vault_pki_max_lease_ttl }}" pki + +- name: Set vault pki generate/internal CN + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault write pki/root/generate/internal common_name="{{ ingress_domain }}" ttl="{{ vault_pki_max_lease_ttl }}" + +- name: Set vault pki CRL + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl" + +- name: Set vault pki roles + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault write pki/roles/"{{ cert_role }}" allowed_domains="{{ ingress_domain }}" allow_subdomains=true max_ttl="{{ vault_pki_max_lease_ttl }}" diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 28ef3851..559a9276 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -50,13 +50,13 @@ bash -e -c "echo \"path \\\"secret/data/{{ vault_hub }}/*\\\" { capabilities = [\\\"create\\\", \\\"read\\\", \\\"update\\\", \\\"delete\\\", \\\"list\\\"] }\" > /tmp/policy-{{ vault_hub }}.hcl" -- name: Configure policy for regional cluster +- name: Configure policy for hub kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" command: "vault policy write {{ vault_hub }}-secret /tmp/policy-{{ vault_hub }}.hcl" -- name: Configure kubernetes role for regional cluster +- name: Configure kubernetes role for hub kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 450fcb1b..f3bd5482 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -29,223 +29,3 @@ fi ansible-playbook -e output_file="${OUTFILE}" "${PLAYBOOKPATH}/vault/${TASK}.yaml" exit $? - - - -# Assumptions - vault in the demo will be running in non-HA mode so there will only be a vault-0 -# vault will be running in the "vault" namespace - -vault_delete() -{ - oc -n vault delete pod vault-0 & - oc -n vault delete pvc data-vault-0 & -} - -vault_ready_check() -{ -#NAME READY STATUS RESTARTS AGE -#vault-0 1/1 Running 0 44m - oc get po -n vault | grep vault-0 | awk '{ print $2, $3 }' 2>/dev/null -} - -get_vault_ready() -{ - rdy_output=`vault_ready_check` - - # Things we may have to wait for: - # being assigned to a host - if [ "$?" ]; then - sleep 5 - until [ "$rdy_output" ] - do - sleep 5 - rdy_output=`vault_ready_check` - done - fi - - printf "%s" "$rdy_output" -} - -vault_unseal() -{ - # Argument is expected to be the text output of the vault operator init command which includes Unseal Keys - # (5 by default) and a root token. - if [ -n "$1" ]; then - file=$1 - else - file=common/vault.init - fi - - for unseal in `cat $file | grep "Unseal Key" | awk '{ print $4 }'` - do - oc -n vault exec vault-0 -- vault operator unseal $unseal - done - - vault_login $file -} - -vault_init() -{ - # Argument is expected to be the text output of the vault operator init command which includes Unseal Keys - # (5 by default) and a root token. - if [ -n "$1" ]; then - file=$1 - else - file=common/vault.init - fi - - # The vault is ready to be initialized when it is "Running" but not "ready". Unsealing it makes it ready - rdy_check=`get_vault_ready` - - if [ "$rdy_check" = "1/1 Running" ]; then - echo "Vault is already ready, exiting" - exit 0 - fi - - if [ -f "$file" ] && grep -q -e '^Unseal' "$file"; then - echo "$file already exists and contains seal secrets. We're moving it away to ${file}.bak" - mv -vf "${file}" "${file}.bak" - fi - - until [ "$rdy_check" = "0/1 Running" ] - do - echo $rdy_check - rdy_check=`get_vault_ready` - done - - oc -n vault exec vault-0 -- vault operator init | tee $file - - vault_unseal $file - vault_login $file - - vault_secrets_init $file - vault_kubernetes_init $file - vault_policy_init $file - - # Do not need pki init by default - # But this is how you could call it if you need it - #vault_pki_init $file -} - -# Retrieves the root token specified in the file $1 -vault_get_root_token() -{ - # Argument is expected to be the text output of the vault operator init command which includes Unseal Keys - # (5 by default) and a root token. - if [ -n "$1" ]; then - file=$1 - else - file=common/vault.init - fi - - token=`grep "Initial Root Token" $file | awk '{ print $4 }'` - printf "%s" "$token" -} - -# Exec a vault command wrapped with the vault root token specified in the file -# $1 -vault_token_exec() -{ - file="$1" - token=`vault_get_root_token $file` - shift - cmd="$@" - - vault_exec $file "VAULT_TOKEN=$token $cmd" -} - -vault_exec() -{ - file="$1" - token=`vault_get_root_token $file` - shift - cmd="$@" - - oc -n vault exec -i vault-0 -- sh -c "$cmd" -} - -vault_login() -{ - file="$1" - token=`vault_get_root_token $file` - shift - cmd="$@" - - vault_exec $file "vault login $token" -} - -oc_get_domain() -{ - oc get ingresses.config/cluster -o jsonpath={.spec.domain} -} - -oc_get_pki_domain() -{ - printf "%s" `oc_get_domain | cut -d. -f3-` -} - -oc_get_pki_role() -{ - pkidomain=`oc_get_pki_domain` - certrole=`printf "%s" "$pkidomain" | sed 's|\.|_|g'` - printf "%s" "$certrole" -} - -vault_pki_init() -{ - file="$1" - - pkidomain=`oc_get_pki_domain` - pkirole=`oc_get_pki_role` - - vault_exec $file "vault secrets enable pki" - vault_exec $file "vault secrets tune --max-lease-ttl=8760h pki" - vault_exec $file "vault write pki/root/generate/internal common_name=$pkidomain ttl=8760h" - vault_exec $file 'vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl"' - vault_exec $file "vault write pki/roles/$pkirole allowed_domains=$pkidomain allow_subdomains=true max_ttl=8760h" -} - -vault_kubernetes_init() -{ - file="$1" - - vault_exec $file "vault auth enable --path=hub kubernetes" -} - -vault_policy_init() -{ - file="$1" - - k8s_host='https://$KUBERNETES_PORT_443_TCP_ADDR:443' - secret_name="$(oc get -n golang-external-secrets serviceaccount golang-external-secrets -o jsonpath='{.secrets}' | jq -r '.[] | select(.name | test ("golang-external-secrets-token-")).name')" - sa_token="$(oc get secret -n golang-external-secrets ${secret_name} -o go-template='{{ .data.token | base64decode }}')" - - vault_exec $file "vault write auth/hub/config token_reviewer_jwt=$sa_token kubernetes_host=$k8s_host kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc" - vault_exec $file 'vault policy write hub-secret - << EOF -path "secret/data/hub/*" - { capabilities = ["read"] -} -EOF -' - vault_exec $file 'vault write auth/hub/role/hub-role bound_service_account_names="golang-external-secrets" bound_service_account_namespaces="golang-external-secrets" policies="default,hub-secret" ttl="15m"' -} - -vault_secrets_init() -{ - file="$1" - - vault_exec $file "vault secrets enable -path=secret kv-v2" -} - -vault_create_secret() -{ - file="$1" - shift - secret_path="$1" - shift - secret="$@" - - vault_exec $file "vault kv put $secret_path $secret" -} - -$@ From 90cba9e10cf14f2d881123ac6da6496f0fd90e58 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 18:07:36 +0100 Subject: [PATCH 0300/1263] Silence localhost warning --- ansible/ansible.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index 0a189294..51a6c098 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -1,3 +1,4 @@ [defaults] display_skipped_hosts=False +localhost_warning=False roles_path=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:./roles From 5f08adb465c4abe1faf09995d6ee23d21bcb1f99 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 18:07:47 +0100 Subject: [PATCH 0301/1263] Move to tags + cleanups Discussed this a bit with Johnny. Using tags seems to be a bit more concise (i.e. we avoid having too many playbooks). The drawback is that the ordering in roles/vault/tasks/main.yaml is very relevant. --- ansible/playbooks/vault/vault.yaml | 7 +++++ ansible/playbooks/vault/vault_delete.yaml | 10 ------- ansible/playbooks/vault/vault_init.yaml | 21 -------------- ansible/playbooks/vault/vault_pki_init.yaml | 10 ------- ansible/roles/vault_utils/defaults/main.yml | 3 +- ansible/roles/vault_utils/tasks/main.yml | 28 ++++++++++++++++++- .../roles/vault_utils/tasks/vault_delete.yaml | 1 - .../roles/vault_utils/tasks/vault_init.yaml | 3 -- .../vault_utils/tasks/vault_pki_init.yaml | 1 - .../vault_utils/tasks/vault_secrets_init.yaml | 1 - .../roles/vault_utils/tasks/vault_unseal.yaml | 1 - scripts/vault-utils.sh | 16 ++++++----- 12 files changed, 45 insertions(+), 57 deletions(-) create mode 100644 ansible/playbooks/vault/vault.yaml delete mode 100644 ansible/playbooks/vault/vault_delete.yaml delete mode 100644 ansible/playbooks/vault/vault_init.yaml delete mode 100644 ansible/playbooks/vault/vault_pki_init.yaml diff --git a/ansible/playbooks/vault/vault.yaml b/ansible/playbooks/vault/vault.yaml new file mode 100644 index 00000000..2c79f886 --- /dev/null +++ b/ansible/playbooks/vault/vault.yaml @@ -0,0 +1,7 @@ +--- +- name: Vault initialization + hosts: localhost + connection: local + gather_facts: no + roles: + - vault_utils diff --git a/ansible/playbooks/vault/vault_delete.yaml b/ansible/playbooks/vault/vault_delete.yaml deleted file mode 100644 index e42da16d..00000000 --- a/ansible/playbooks/vault/vault_delete.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Vault removal - hosts: localhost - connection: local - gather_facts: no - tasks: - - name: Run vault delete tasks - include_role: - name: vault_utils - tasks_from: vault_delete diff --git a/ansible/playbooks/vault/vault_init.yaml b/ansible/playbooks/vault/vault_init.yaml deleted file mode 100644 index 95b5bcfa..00000000 --- a/ansible/playbooks/vault/vault_init.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -- name: Vault initialization - hosts: localhost - connection: local - gather_facts: no - vars: - external_secrets_ns: golang-external-secrets - external_secrets_sa: golang-external-secrets - tasks: - - name: Run vault init tasks - include_role: - name: vault_utils - tasks_from: vault_init - - name: Unseal vault - include_role: - name: vault_utils - tasks_from: vault_unseal - - name: Vault secrets init - include_role: - name: vault_utils - tasks_from: vault_secrets_init diff --git a/ansible/playbooks/vault/vault_pki_init.yaml b/ansible/playbooks/vault/vault_pki_init.yaml deleted file mode 100644 index a759e927..00000000 --- a/ansible/playbooks/vault/vault_pki_init.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Vault PKI init - hosts: localhost - connection: local - gather_facts: no - tasks: - - name: Run vault pki init tasks - include_role: - name: vault_utils - tasks_from: vault_pki_init diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 46970632..3e53295d 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -9,5 +9,6 @@ vault_hub_kubernetes_host: 'https://$KUBERNETES_PORT_443_TCP_ADDR:443' vault_path: "secret/hub" vault_hub_ttl: "15m" vault_pki_max_lease_ttl: "8760h" -debug: False output_file: "common/vault.init" +external_secrets_ns: golang-external-secrets +external_secrets_sa: golang-external-secrets diff --git a/ansible/roles/vault_utils/tasks/main.yml b/ansible/roles/vault_utils/tasks/main.yml index 31c8f701..4052b09e 100644 --- a/ansible/roles/vault_utils/tasks/main.yml +++ b/ansible/roles/vault_utils/tasks/main.yml @@ -1,2 +1,28 @@ --- -# tasks file for vault_utils +- name: Delete vault + include_tasks: + file: vault_delete.yaml + apply: + tags: [never, vault_delete] + tags: [never, vault_delete] + +- name: Run vault init tasks + include_tasks: + file: vault_init.yaml + apply: + tags: [never, vault_init] + tags: [never, vault_init] + +- name: Unseal vault + include_tasks: + file: vault_unseal.yaml + apply: + tags: [never, vault_unseal] + tags: [never, vault_unseal] + +- name: Vault secrets init + include_tasks: + file: vault_secrets_init.yaml + apply: + tags: [never, vault_secrets_init] + tags: [never, vault_secrets_init] diff --git a/ansible/roles/vault_utils/tasks/vault_delete.yaml b/ansible/roles/vault_utils/tasks/vault_delete.yaml index 645bed8c..3e5b2434 100644 --- a/ansible/roles/vault_utils/tasks/vault_delete.yaml +++ b/ansible/roles/vault_utils/tasks/vault_delete.yaml @@ -3,7 +3,6 @@ ansible.builtin.fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 - when: not debug | bool # FIXME to use proper k8 ansible collection - name: Delete vault pod diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 31ee5844..0767503b 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -3,7 +3,6 @@ fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 - when: not debug | bool - name: Check for existence of "{{ output_file }}" ansible.builtin.stat: @@ -21,7 +20,6 @@ name: "{{ vault_ns }}" register: vault_ns_rc failed_when: vault_ns_rc.resources | length == 0 - when: not debug | bool - name: Check if the vault pod is present kubernetes.core.k8s_info: @@ -30,7 +28,6 @@ name: "{{ vault_pod }}" register: vault_pod_rc failed_when: vault_pod_rc.resources | length == 0 - when: not debug | bool - name: Init vault operator kubernetes.core.k8s_exec: diff --git a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml index 15d5c8b8..2e0ed5a3 100644 --- a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml @@ -3,7 +3,6 @@ fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 - when: not debug | bool # FIXME: this should be done a lot more elegantly - name: Get cluster domain diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 559a9276..4a5e9ae4 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -3,7 +3,6 @@ fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 - when: not debug | bool - name: Create secrets backend kv-v2 kubernetes.core.k8s_exec: diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index 4f89e2b1..986fdc1f 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -3,7 +3,6 @@ fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 - when: not debug | bool - name: Check for existence of "{{ output_file }}" ansible.builtin.stat: diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index f3bd5482..c44b3937 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -e +set -eu SCRIPT=$(readlink -f "$0") SCRIPTPATH=$(dirname "$SCRIPT") @@ -22,10 +22,12 @@ if [ -z ${TASK} ]; then exit 1 fi -if [ ! -f "${PLAYBOOKPATH}/vault/${TASK}.yaml" ]; then - echo "${PLAYBOOKPATH}/vault/${TASK}.yaml does not exist, exiting" - exit 1 -fi +case "${TASK}" in + "vault_init") + TAGS="vault_init,vault_unseal,vault_secrets_init" + ;; + *) + TAGS="${TASK}" +esac -ansible-playbook -e output_file="${OUTFILE}" "${PLAYBOOKPATH}/vault/${TASK}.yaml" -exit $? +ansible-playbook -t "${TAGS}" -e output_file="${OUTFILE}" "${PLAYBOOKPATH}/vault/vault.yaml" From de2cf6b51a5ee1f5c6c16739db0b530fe0eff940 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 18:28:52 +0100 Subject: [PATCH 0302/1263] Fix up metadata --- ansible/roles/vault_utils/meta/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/roles/vault_utils/meta/main.yml b/ansible/roles/vault_utils/meta/main.yml index c572acc9..5c5a1564 100644 --- a/ansible/roles/vault_utils/meta/main.yml +++ b/ansible/roles/vault_utils/meta/main.yml @@ -1,7 +1,6 @@ galaxy_info: - author: your name - description: your role description - company: your company (optional) + author: Validated Patterns Team https://github.com/hybrid-cloud-patterns/ + description: Utilities to manage vault in kubernetes (init, unseal, etc) # If the issue tracker for your role is not on github, uncomment the # next line and provide a value @@ -47,6 +46,7 @@ galaxy_info: # NOTE: A tag is limited to a single word comprised of alphanumeric characters. # Maximum 20 tags per role. -dependencies: [] +dependencies: + - community.kubernetes # List your role dependencies here, one per line. Be sure to remove the '[]' above, # if you add dependencies to this list. From da78e60ec42db7fc1e9a67b81648bbfb99ae7605 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 18:40:30 +0100 Subject: [PATCH 0303/1263] Clean up README a bit --- ansible/roles/vault_utils/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index 225dd44b..1f0396bc 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -1,22 +1,22 @@ Role Name ========= -A brief description of the role goes here. +Bunch of utilities to manage the vault inside k8s imperatively Requirements ------------ -Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. +ansible-galaxy collection install community.kubernetes Role Variables -------------- -A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. + Dependencies ------------ -A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. + Example Playbook ---------------- From fd6ec4e5eb02c656cbf5546c6794809e93b420e7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 18:40:47 +0100 Subject: [PATCH 0304/1263] Drop dependencies for now --- ansible/roles/vault_utils/meta/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/meta/main.yml b/ansible/roles/vault_utils/meta/main.yml index 5c5a1564..64348549 100644 --- a/ansible/roles/vault_utils/meta/main.yml +++ b/ansible/roles/vault_utils/meta/main.yml @@ -46,7 +46,6 @@ galaxy_info: # NOTE: A tag is limited to a single word comprised of alphanumeric characters. # Maximum 20 tags per role. -dependencies: - - community.kubernetes +dependencies: [] # List your role dependencies here, one per line. Be sure to remove the '[]' above, # if you add dependencies to this list. From 072b33fd8f1dbe1e4edac6110266de11129aa705 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 14 Mar 2022 18:41:08 +0100 Subject: [PATCH 0305/1263] Set debug to default(False) inside the play directly since we dropped the var --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 3b6c896a..c173a452 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -8,7 +8,7 @@ fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 - when: not debug | bool + when: not debug | default(False) | bool - name: Parse "{{ values_secret }}" ansible.builtin.set_fact: @@ -55,7 +55,7 @@ name: "{{ vault_ns }}" register: vault_ns_rc failed_when: vault_ns_rc.resources | length == 0 - when: not debug | bool + when: not debug | default(False) | bool - name: Check if the vault pod is present kubernetes.core.k8s_info: @@ -64,7 +64,7 @@ name: "{{ vault_pod }}" register: vault_pod_rc failed_when: vault_pod_rc.resources | length == 0 - when: not debug | bool + when: not debug | default(False) | bool # vault status returns 1 on error and 2 on sealed # so we can bail out when sealed @@ -75,13 +75,13 @@ command: vault status register: vault_status failed_when: vault_status.rc|int == 1 - when: not debug | bool + when: not debug | default(False) | bool - name: Check vault status return ansible.builtin.fail: msg: The vault is still sealed. Please run "make vault-init" first with KUBECONFIG pointing to the HUB cluster when: - - not debug | bool + - not debug | default(False) | bool - vault_status.rc | int > 0 - name: Debug @@ -91,7 +91,7 @@ "{{ secrets | dict2items }}" loop_control: label: "{{ item.key }}" - when: debug | bool + when: debug | default(False) | bool - name: Add the actual secrets to the vault kubernetes.core.k8s_exec: @@ -103,4 +103,4 @@ "{{ secrets | dict2items }}" loop_control: label: "{{ item.key }}" - when: not debug | bool + when: not debug | default(False) | bool From 28c701c47ed87fe086554c29ba1a395088467f40 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 21 Mar 2022 19:37:48 +0100 Subject: [PATCH 0306/1263] Error out nicely when secrets key is missing Let's error out properly in case the secrets key is missing --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index c173a452..403875bb 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -14,6 +14,12 @@ ansible.builtin.set_fact: all_values: "{{ lookup('file', values_secret) | from_yaml }}" +- name: Check for secrets keys + ansible.builtin.fail: + msg: "Was not able to parse any secrets from file {{ values_secret }}. 'secrets:' top-level key is missing" + failed_when: + 'secrets' not in all_values.keys() + - name: Set secrets fact ansible.builtin.set_fact: secrets: "{{ all_values['secrets'] }}" From ff3b4899594aedf82cbdd028525a7f4ccf05ab21 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 21 Mar 2022 19:59:08 +0100 Subject: [PATCH 0307/1263] Wait for vault NS and vault pod We wait for both resources up to 15mins each. --- ansible/roles/vault_utils/tasks/vault_init.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 0767503b..35f35e83 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -18,6 +18,9 @@ kubernetes.core.k8s_info: kind: Namespace name: "{{ vault_ns }}" + wait: yes + wait_sleep: 10 + wait_timeout: 900 register: vault_ns_rc failed_when: vault_ns_rc.resources | length == 0 @@ -26,6 +29,9 @@ kind: Pod namespace: "{{ vault_ns }}" name: "{{ vault_pod }}" + wait: yes + wait_sleep: 10 + wait_timeout: 900 register: vault_pod_rc failed_when: vault_pod_rc.resources | length == 0 From faa74dc69c8c2b5c2b04ef8802fdb8db1bcf288a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 21 Mar 2022 20:18:13 +0100 Subject: [PATCH 0308/1263] Make secrets parsing a little more robust Let's error out nicely if the top-level secrets key is missing from the yaml file. --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 403875bb..8671cd09 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -12,13 +12,12 @@ - name: Parse "{{ values_secret }}" ansible.builtin.set_fact: - all_values: "{{ lookup('file', values_secret) | from_yaml }}" + all_values: "{{ lookup('file', values_secret) | from_yaml | default({}, true) }}" - name: Check for secrets keys ansible.builtin.fail: msg: "Was not able to parse any secrets from file {{ values_secret }}. 'secrets:' top-level key is missing" - failed_when: - 'secrets' not in all_values.keys() + failed_when: "'secrets' not in all_values.keys()" - name: Set secrets fact ansible.builtin.set_fact: From dcc9e87b51bfb84e8f73abb0fbd678b9a2ff00ef Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 21 Mar 2022 20:18:52 +0100 Subject: [PATCH 0309/1263] Wait for vault ns+pod using until/retries This approach seems to work correctly, whereas I was unable to make the k8s native wait: yes & co work properly. --- ansible/roles/vault_utils/tasks/vault_init.yaml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 35f35e83..d313e8b5 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -18,22 +18,20 @@ kubernetes.core.k8s_info: kind: Namespace name: "{{ vault_ns }}" - wait: yes - wait_sleep: 10 - wait_timeout: 900 register: vault_ns_rc - failed_when: vault_ns_rc.resources | length == 0 + until: vault_ns_rc.resources | length > 0 + retries: 20 + delay: 45 - name: Check if the vault pod is present kubernetes.core.k8s_info: kind: Pod namespace: "{{ vault_ns }}" name: "{{ vault_pod }}" - wait: yes - wait_sleep: 10 - wait_timeout: 900 register: vault_pod_rc - failed_when: vault_pod_rc.resources | length == 0 + until: vault_ns_rc.resources | length > 0 + retries: 20 + delay: 45 - name: Init vault operator kubernetes.core.k8s_exec: From 5a623f8200bd0a07b934fde8090892ac09be239e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 21 Mar 2022 21:34:24 +0100 Subject: [PATCH 0310/1263] Make parsing of value-secret.yaml more robust We add a task checking that every key under secrets: has actually another dictionary under it. Spotted by Ilkka --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 8671cd09..8975ba57 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -29,6 +29,19 @@ failed_when: secrets is not defined or secrets | length == 0 +- name: Check the value-secret.yaml file for errors + fail: + msg: > + "{{ item }}" is not properly formatted. Each key under 'secrets:' + needs to point to a dictionary of key, value pairs. See values-secret.yaml.template. + when: > + item.key | length == 0 or + item.value is not mapping + loop: + "{{ secrets | dict2items }}" + loop_control: + label: "{{ item.key }}" + # Detect here if we have only the following two keys under a password # s3.accessKey: # s3.secretKey: From cdc72582ea66c78b4fe338d1fe1a06ca1b1bdddb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Mar 2022 10:18:11 +0100 Subject: [PATCH 0311/1263] Do not use readlink vault-init/vault-seal It's not needed. Tested both with running vault-init from common/ and from outside in the main project. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 49d98235..0dbee6d7 100644 --- a/Makefile +++ b/Makefile @@ -51,10 +51,10 @@ uninstall: ## runs helm uninstall helm uninstall $(NAME) vault-init: ## inits, unseals and configured the vault - common/scripts/vault-utils.sh vault_init $(shell readlink -f common/pattern-vault.init) + common/scripts/vault-utils.sh vault_init common/pattern-vault.init vault-unseal: ## unseals the vault - common/scripts/vault-utils.sh vault_unseal $(shell readlink -f common/pattern-vault.init) + common/scripts/vault-utils.sh vault_init common/pattern-vault.init load-secrets: ## loads the secrets into the vault common/scripts/ansible-push-vault-secrets.sh From e5d6bcffbe693d4820b17c6eb2fb56f7f30fb230 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Mar 2022 10:19:05 +0100 Subject: [PATCH 0312/1263] Add ansible linting job This should try and keep our ansible somewhat clean --- .github/workflows/ansible-lint.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/ansible-lint.yml diff --git a/.github/workflows/ansible-lint.yml b/.github/workflows/ansible-lint.yml new file mode 100644 index 00000000..e2d65751 --- /dev/null +++ b/.github/workflows/ansible-lint.yml @@ -0,0 +1,18 @@ +name: Ansible Lint # feel free to pick your own name + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + # Important: This sets up your GITHUB_WORKSPACE environment variable + - uses: actions/checkout@v2 + + - name: Lint Ansible Playbook + # Using the latest as of today (2022-03-23) v6.0.1 + uses: ansible/ansible-lint-action@v6.0.1 + # Let's point it to the path + with: + args: "ansible/" From 082517b3fe52033eed47b5408f8d7832327ea028 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Mar 2022 11:03:10 +0100 Subject: [PATCH 0313/1263] Address all ansible-lint warnings --- ansible/playbooks/vault/push_secrets.yaml | 2 +- ansible/playbooks/vault/vault.yaml | 2 +- ansible/roles/vault_utils/meta/main.yml | 21 +++++++++++++----- .../roles/vault_utils/tasks/push_secrets.yaml | 10 ++++----- .../roles/vault_utils/tasks/vault_delete.yaml | 8 ++++++- .../roles/vault_utils/tasks/vault_init.yaml | 7 +++--- .../vault_utils/tasks/vault_pki_init.yaml | 8 ++++--- .../vault_utils/tasks/vault_secrets_init.yaml | 22 ++++++++++++------- .../roles/vault_utils/tasks/vault_unseal.yaml | 8 +++---- 9 files changed, 56 insertions(+), 32 deletions(-) diff --git a/ansible/playbooks/vault/push_secrets.yaml b/ansible/playbooks/vault/push_secrets.yaml index d4241582..ed3cad61 100644 --- a/ansible/playbooks/vault/push_secrets.yaml +++ b/ansible/playbooks/vault/push_secrets.yaml @@ -2,7 +2,7 @@ - name: Secret injection of validated-patterns hosts: localhost connection: local - gather_facts: no + gather_facts: false tasks: - name: Run push secrets task include_role: diff --git a/ansible/playbooks/vault/vault.yaml b/ansible/playbooks/vault/vault.yaml index 2c79f886..64711e47 100644 --- a/ansible/playbooks/vault/vault.yaml +++ b/ansible/playbooks/vault/vault.yaml @@ -2,6 +2,6 @@ - name: Vault initialization hosts: localhost connection: local - gather_facts: no + gather_facts: false roles: - vault_utils diff --git a/ansible/roles/vault_utils/meta/main.yml b/ansible/roles/vault_utils/meta/main.yml index 64348549..ac9991de 100644 --- a/ansible/roles/vault_utils/meta/main.yml +++ b/ansible/roles/vault_utils/meta/main.yml @@ -13,7 +13,7 @@ galaxy_info: # - GPL-3.0-only # - Apache-2.0 # - CC-BY-4.0 - license: license (GPL-2.0-or-later, MIT, etc) + license: Apache-2.0 min_ansible_version: 2.1 @@ -26,11 +26,20 @@ galaxy_info: # To view available platforms and versions (or releases), visit: # https://galaxy.ansible.com/api/v1/platforms/ # - # platforms: - # - name: Fedora - # versions: - # - all - # - 25 + platforms: + - name: Fedora + versions: + - all + - name: Ubuntu + versions: + - all + - name: Debian + versions: + - all + - name: EL + versions: + - 8 + # # - name: SomePlatform # versions: # - all diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 8975ba57..df9105f4 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -5,7 +5,7 @@ failed_when: not result.stat.exists - name: Check that KUBECONFIG is correctly set - fail: + ansible.builtin.fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 when: not debug | default(False) | bool @@ -30,7 +30,7 @@ secrets is not defined or secrets | length == 0 - name: Check the value-secret.yaml file for errors - fail: + ansible.builtin.fail: msg: > "{{ item }}" is not properly formatted. Each key under 'secrets:' needs to point to a dictionary of key, value pairs. See values-secret.yaml.template. @@ -103,8 +103,8 @@ - vault_status.rc | int > 0 - name: Debug - debug: - msg: "vault kv put {{ vault_path }}/{{ item.key }} -> {{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}" + ansible.builtin.debug: + msg: "vault kv put {{ vault_path }}/{{ item.key }} -> {{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ') }}" loop: "{{ secrets | dict2items }}" loop_control: @@ -116,7 +116,7 @@ namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" command: | - sh -c "vault kv put {{ vault_path }}/{{ item.key }} {{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}" + sh -c "vault kv put {{ vault_path }}/{{ item.key }} {{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ') }}" loop: "{{ secrets | dict2items }}" loop_control: diff --git a/ansible/roles/vault_utils/tasks/vault_delete.yaml b/ansible/roles/vault_utils/tasks/vault_delete.yaml index 3e5b2434..7e06cf2d 100644 --- a/ansible/roles/vault_utils/tasks/vault_delete.yaml +++ b/ansible/roles/vault_utils/tasks/vault_delete.yaml @@ -11,6 +11,7 @@ register: delete_pod async: 7200 poll: 0 + changed_when: true - name: Delete vault pvc ansible.builtin.shell: | @@ -18,7 +19,12 @@ register: delete_pvc async: 7200 poll: 0 + changed_when: true - name: Wait for tasks - wait_for: + ansible.builtin.wait_for: timeout: 10 + +- name: Print out result from deletion + ansible.builtin.debug: + msg: "{{ delete_pod.stdout }} - {{ delete_pvc.stdout }}" diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index d313e8b5..8b951e5f 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -1,6 +1,6 @@ --- - name: Check that KUBECONFIG is correctly set - fail: + ansible.builtin.fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 @@ -10,7 +10,7 @@ register: result - name: Fail if "{{ output_file }} exists" - fail: + ansible.builtin.fail: msg: "{{ output_file }} already exists, not overwriting. Please move it away before proceeding" failed_when: result.stat.exists @@ -41,7 +41,7 @@ register: vault_init_json_out - name: Set output json fact - set_fact: + ansible.builtin.set_fact: vault_init_json: "{{ vault_init_json_out.stdout | from_json }}" - name: Save vault operator output @@ -49,3 +49,4 @@ follow: true dest: "{{ output_file }}" content: "{{ vault_init_json | to_nice_json }}" + mode: '0600' diff --git a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml index 2e0ed5a3..d8852e26 100644 --- a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml @@ -1,17 +1,19 @@ --- - name: Check that KUBECONFIG is correctly set - fail: + ansible.builtin.fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 # FIXME: this should be done a lot more elegantly - name: Get cluster domain - shell: | + ansible.builtin.shell: | + set -e -opipefail oc get ingresses.config/cluster -o jsonpath='{.spec.domain}' | cut -d. -f3- register: ingress_domain_out + changed_when: false - name: Set ingress domain - set_fact: + ansible.builtin.set_fact: ingress_domain: "{{ ingress_domain_out.stdout }}" cert_role: "{{ ingress_domain_out.stdout | replace('.', '_') }}" diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 4a5e9ae4..751f1cf5 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -1,6 +1,6 @@ --- - name: Check that KUBECONFIG is correctly set - fail: + ansible.builtin.fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 @@ -18,30 +18,36 @@ - name: Fetch "{{ external_secrets_ns }}/{{ external_secrets_sa }} secret name" ansible.builtin.shell: | - oc get -n "{{ external_secrets_ns }}" serviceaccount "{{ external_secrets_sa }}" -o jsonpath='{.secrets}' + oc get -n "{{ external_secrets_ns }}" serviceaccount "{{ external_secrets_sa }}" -o jsonpath='{.secrets}' register: secrets_out + changed_when: true - name: Filter secrets of "{{ external_secrets_ns }}/{{ external_secrets_sa }}" - set_fact: + ansible.builtin.set_fact: secret: "{{ secrets_out.stdout | from_json | json_query(\"[?starts_with(name, 'golang-external-secrets-token-')].name | [0]\") }}" - name: Get token of "{{ external_secrets_ns }}/{{ external_secrets_sa }}" ansible.builtin.shell: | - oc get secret -n "{{external_secrets_ns }}" "{{ secret }}" -o go-template="{{ '{{' }} .data.token | base64decode {{ '}}' }}" + set -e -opipefail + oc get secret -n "{{ external_secrets_ns }}" "{{ secret }}" -o go-template="{{ '{{' }} .data.token | base64decode {{ '}}' }}" register: sa_token_out + changed_when: true - name: Set sa_token fact - set_fact: + ansible.builtin.set_fact: sa_token: "{{ sa_token_out.stdout }}" - name: Configure hub kubernetes backend kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" - command: | - vault write auth/"{{ vault_hub }}"/config token_reviewer_jwt="{{ sa_token }}" kubernetes_host="{{ vault_hub_kubernetes_host }}" kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc + command: > + vault write auth/"{{ vault_hub }}"/config token_reviewer_jwt="{{ sa_token }}" + kubernetes_host="{{ vault_hub_kubernetes_host }}" + kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + issuer=https://kubernetes.default.svc -- name: Configure policy template for hub +- name: Configure policy template for hub kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index 986fdc1f..c61be829 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -1,6 +1,6 @@ --- - name: Check that KUBECONFIG is correctly set - fail: + ansible.builtin.fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 @@ -10,17 +10,17 @@ register: result - name: Fail if "{{ output_file }}" does not exists - fail: + ansible.builtin.fail: msg: "{{ output_file }} does not exist. Stopping here" failed_when: not result.stat.exists # We reparse the json vault init file in case unseal was called without operator init before - name: Parse "{{ output_file }}" - set_fact: + ansible.builtin.set_fact: vault_init_json: "{{ lookup('file', output_file) | from_json }}" - name: Set root token and unseal_keys - set_fact: + ansible.builtin.set_fact: root_token: "{{ vault_init_json['root_token'] }}" unseal_keys: "{{ vault_init_json['unseal_keys_hex'] }}" From bb63ec10f6cf57b0d8529f70f36cd8b560e5ee88 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Mar 2022 11:11:32 +0100 Subject: [PATCH 0314/1263] More linting fixes --- ansible/roles/vault_utils/meta/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/roles/vault_utils/meta/main.yml b/ansible/roles/vault_utils/meta/main.yml index ac9991de..d9fb4eff 100644 --- a/ansible/roles/vault_utils/meta/main.yml +++ b/ansible/roles/vault_utils/meta/main.yml @@ -29,16 +29,16 @@ galaxy_info: platforms: - name: Fedora versions: - - all + - all - name: Ubuntu versions: - - all + - all - name: Debian versions: - - all + - all - name: EL versions: - - 8 + - 8 # # - name: SomePlatform # versions: From 493fa8533b53269b5a81481d42774f66098b77ae Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Mar 2022 11:25:21 +0100 Subject: [PATCH 0315/1263] Retry the Init vault operator task It might happen that both the vault namespace and the vault-0 pod come up cleanly but the vault itself is still not ready. So we'd end up with the following: TASK [vault_utils : Check for vault namespace] ok: [localhost] TASK [vault_utils : Check if the vault pod is present] ok: [localhost] TASK [vault_utils : Init vault operator] fatal: [localhost]: FAILED! => {"changed": false, "msg": "Failed to execute on pod vault-0 due to : (0)\nReason: Handshake status 500 Internal Server Error\n"} Let's retry a few times --- ansible/roles/vault_utils/tasks/vault_init.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 8b951e5f..7270f7b5 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -39,6 +39,8 @@ pod: "{{ vault_pod }}" command: vault operator init -format=json register: vault_init_json_out + retries: 10 + delay: 30 - name: Set output json fact ansible.builtin.set_fact: From 8e96fdc60d71c62f848d09bc00f55fefc8f8ad66 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Mar 2022 11:34:06 +0100 Subject: [PATCH 0316/1263] Correct the debug statement in vault_delete --- ansible/roles/vault_utils/tasks/vault_delete.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/vault_delete.yaml b/ansible/roles/vault_utils/tasks/vault_delete.yaml index 7e06cf2d..d7997e37 100644 --- a/ansible/roles/vault_utils/tasks/vault_delete.yaml +++ b/ansible/roles/vault_utils/tasks/vault_delete.yaml @@ -27,4 +27,4 @@ - name: Print out result from deletion ansible.builtin.debug: - msg: "{{ delete_pod.stdout }} - {{ delete_pvc.stdout }}" + msg: "{{ delete_pod }} - {{ delete_pvc }}" From aaf4b9869c498e8cee4142a8250e84b145dd6436 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Mar 2022 13:41:44 +0100 Subject: [PATCH 0317/1263] Various fixes We: - Make sure we reference the output_file as an absolute file - Add some extra checks running vault status before trying to init it - Fix up a condition bug around waiting for ns vs pod --- .../roles/vault_utils/tasks/vault_init.yaml | 58 ++++++++++++++++--- .../roles/vault_utils/tasks/vault_unseal.yaml | 25 ++++++-- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 7270f7b5..59d4addc 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -4,14 +4,27 @@ msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 -- name: Check for existence of "{{ output_file }}" +# If the output_file is relative we need to make it absolute and we choose +# the current path which normally is the main pattern folder. Reason for this +# is that the copy task needs an absolute path because we're not guaranteed +- name: Set absolute path for output_file + set_fact: + output_file_abs: "{{ lookup('env', 'PWD') }}/{{ output_file }}" + when: "not output_file.startswith('/')" + +- name: Set output_file to new veriable + set_fact: + output_file_abs: "{{ output_file }}" + when: "output_file.startswith('/')" + +- name: Check for existence of "{{ output_file_abs }}" ansible.builtin.stat: - path: "{{ output_file }}" + path: "{{ output_file_abs }}" register: result -- name: Fail if "{{ output_file }} exists" +- name: Fail if "{{ output_file_abs }} exists" ansible.builtin.fail: - msg: "{{ output_file }} already exists, not overwriting. Please move it away before proceeding" + msg: "{{ output_file_abs }} already exists, not overwriting. Please move it away before proceeding" failed_when: result.stat.exists - name: Check for vault namespace @@ -29,26 +42,55 @@ namespace: "{{ vault_ns }}" name: "{{ vault_pod }}" register: vault_pod_rc - until: vault_ns_rc.resources | length > 0 + until: vault_pod_rc.resources | length > 0 retries: 20 delay: 45 +# This needs retrying because during startup we can just get +# Failed to execute on pod vault-0 due to : (0)\nReason: Handshake status 500 Internal Server Error +# In the above case there is no 'rc' in vault_status. So first we wait for 'rc' to show up and ignore +# any errors, and then we bail out if rc is 2 as it means the vault is already initialized +- name: Check for the vault status + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault status -format=json + register: vault_status_json + until: "'rc' in vault_status_json" + retries: 5 + delay: 10 + failed_when: false + +- name: Set vault status output json fact + ansible.builtin.set_fact: + vault_status: "{{ vault_status_json.stdout | from_json }}" + when: vault_status_json.stdout_lines | length > 0 + +- name: Fail when the vault status is sealed + fail: + msg: "The vault is already configured. Please unseal it with 'make vault-unseal'. Msg: {{ vault_status }}" + when: vault_status['initialized'] | bool == true + +# We need to retry here because the vault service might be starting +# and can return a 500 internal server until its state is fully ready - name: Init vault operator kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" command: vault operator init -format=json register: vault_init_json_out + until: vault_init_json_out is not failed retries: 10 - delay: 30 + delay: 15 -- name: Set output json fact +- name: Set vault init output json fact ansible.builtin.set_fact: vault_init_json: "{{ vault_init_json_out.stdout | from_json }}" +# Prefer the output_file_abs var if it is defined - name: Save vault operator output ansible.builtin.copy: follow: true - dest: "{{ output_file }}" + dest: "{{ output_file_abs }}" content: "{{ vault_init_json | to_nice_json }}" mode: '0600' diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index c61be829..e713c78f 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -4,20 +4,33 @@ msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 -- name: Check for existence of "{{ output_file }}" +# If the output_file is relative we need to make it absolute and we choose +# the current path which normally is the main pattern folder. Reason for this +# is that the copy task needs an absolute path because we're not guaranteed +- name: Set absolute path for output_file + set_fact: + output_file_abs: "{{ lookup('env', 'PWD') }}/{{ output_file }}" + when: "not output_file.startswith('/')" + +- name: Set output_file to new veriable + set_fact: + output_file_abs: "{{ output_file }}" + when: "output_file.startswith('/')" + +- name: Check for existence of "{{ output_file_abs }}" ansible.builtin.stat: - path: "{{ output_file }}" + path: "{{ output_file_abs }}" register: result -- name: Fail if "{{ output_file }}" does not exists +- name: Fail if "{{ output_file_abs }}" does not exists ansible.builtin.fail: - msg: "{{ output_file }} does not exist. Stopping here" + msg: "{{ output_file_abs }} does not exist. Stopping here" failed_when: not result.stat.exists # We reparse the json vault init file in case unseal was called without operator init before -- name: Parse "{{ output_file }}" +- name: Parse "{{ output_file_abs }}" ansible.builtin.set_fact: - vault_init_json: "{{ lookup('file', output_file) | from_json }}" + vault_init_json: "{{ lookup('file', output_file_abs) | from_json }}" - name: Set root token and unseal_keys ansible.builtin.set_fact: From e26747dcdc65d4efd2c53172eac7ea72c676a796 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Mar 2022 13:47:07 +0100 Subject: [PATCH 0318/1263] Spell out which tags in the makefile, make things clearer and code shorter --- Makefile | 2 ++ scripts/vault-utils.sh | 10 +--------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 0dbee6d7..118bcb2a 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,8 @@ uninstall: ## runs helm uninstall vault-init: ## inits, unseals and configured the vault common/scripts/vault-utils.sh vault_init common/pattern-vault.init + common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init + common/scripts/vault-utils.sh vault_secrets_init common/pattern-vault.init vault-unseal: ## unseals the vault common/scripts/vault-utils.sh vault_init common/pattern-vault.init diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index c44b3937..29d22542 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -22,12 +22,4 @@ if [ -z ${TASK} ]; then exit 1 fi -case "${TASK}" in - "vault_init") - TAGS="vault_init,vault_unseal,vault_secrets_init" - ;; - *) - TAGS="${TASK}" -esac - -ansible-playbook -t "${TAGS}" -e output_file="${OUTFILE}" "${PLAYBOOKPATH}/vault/vault.yaml" +ansible-playbook -t "${TASK}" -e output_file="${OUTFILE}" "${PLAYBOOKPATH}/vault/vault.yaml" From a2fa55b1e5ba6e278b57ff76cd6a0adb36e9188f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Mar 2022 13:54:32 +0100 Subject: [PATCH 0319/1263] Fix pipefail and indent --- ansible/roles/vault_utils/tasks/vault_pki_init.yaml | 2 +- ansible/roles/vault_utils/tasks/vault_secrets_init.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml index d8852e26..f996cac0 100644 --- a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml @@ -7,7 +7,7 @@ # FIXME: this should be done a lot more elegantly - name: Get cluster domain ansible.builtin.shell: | - set -e -opipefail + set -o pipefail oc get ingresses.config/cluster -o jsonpath='{.spec.domain}' | cut -d. -f3- register: ingress_domain_out changed_when: false diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 751f1cf5..3a510033 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -28,7 +28,7 @@ - name: Get token of "{{ external_secrets_ns }}/{{ external_secrets_sa }}" ansible.builtin.shell: | - set -e -opipefail + set -o pipefail oc get secret -n "{{ external_secrets_ns }}" "{{ secret }}" -o go-template="{{ '{{' }} .data.token | base64decode {{ '}}' }}" register: sa_token_out changed_when: true @@ -67,6 +67,6 @@ pod: "{{ vault_pod }}" command: > vault write auth/"{{ vault_hub }}"/role/"{{ vault_hub }}"-role - bound_service_account_names="{{ external_secrets_sa }}" - bound_service_account_namespaces="{{ external_secrets_ns }}" - policies="default,{{ vault_hub }}-secret" ttl="{{ vault_hub_ttl }}" + bound_service_account_names="{{ external_secrets_sa }}" + bound_service_account_namespaces="{{ external_secrets_ns }}" + policies="default,{{ vault_hub }}-secret" ttl="{{ vault_hub_ttl }}" From c4e1b9c1f2e4151354c39c0a5acf12859f377102 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Mar 2022 14:08:35 +0100 Subject: [PATCH 0320/1263] Use FQCN everywhere --- ansible/roles/vault_utils/tasks/vault_init.yaml | 6 +++--- ansible/roles/vault_utils/tasks/vault_unseal.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 59d4addc..9386f290 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -8,12 +8,12 @@ # the current path which normally is the main pattern folder. Reason for this # is that the copy task needs an absolute path because we're not guaranteed - name: Set absolute path for output_file - set_fact: + ansible.builtin.set_fact: output_file_abs: "{{ lookup('env', 'PWD') }}/{{ output_file }}" when: "not output_file.startswith('/')" - name: Set output_file to new veriable - set_fact: + ansible.builtin.set_fact: output_file_abs: "{{ output_file }}" when: "output_file.startswith('/')" @@ -67,7 +67,7 @@ when: vault_status_json.stdout_lines | length > 0 - name: Fail when the vault status is sealed - fail: + ansible.builtin.fail: msg: "The vault is already configured. Please unseal it with 'make vault-unseal'. Msg: {{ vault_status }}" when: vault_status['initialized'] | bool == true diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index e713c78f..fb8c1a1b 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -8,12 +8,12 @@ # the current path which normally is the main pattern folder. Reason for this # is that the copy task needs an absolute path because we're not guaranteed - name: Set absolute path for output_file - set_fact: + ansible.builtin.set_fact: output_file_abs: "{{ lookup('env', 'PWD') }}/{{ output_file }}" when: "not output_file.startswith('/')" - name: Set output_file to new veriable - set_fact: + ansible.builtin.set_fact: output_file_abs: "{{ output_file }}" when: "output_file.startswith('/')" From 626a520175726d340989c05bd596d3fe73dcda2c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Mar 2022 14:15:41 +0100 Subject: [PATCH 0321/1263] Fix boolean comparison --- ansible/roles/vault_utils/tasks/vault_init.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 9386f290..c0725064 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -69,7 +69,7 @@ - name: Fail when the vault status is sealed ansible.builtin.fail: msg: "The vault is already configured. Please unseal it with 'make vault-unseal'. Msg: {{ vault_status }}" - when: vault_status['initialized'] | bool == true + when: vault_status['initialized'] | bool # We need to retry here because the vault service might be starting # and can return a 500 internal server until its state is fully ready From 9c5099b218d9f9c6e5629b5650f4f3efa85b3f2e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Mar 2022 17:59:33 +0100 Subject: [PATCH 0322/1263] Keep default vault output file consistent: common/vault.init --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 118bcb2a..779f29dd 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,7 @@ upgrade: validate-origin ## runs helm upgrade uninstall: ## runs helm uninstall helm uninstall $(NAME) +<<<<<<< HEAD vault-init: ## inits, unseals and configured the vault common/scripts/vault-utils.sh vault_init common/pattern-vault.init common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init From 0f1babee77fd58e48b02c865ba9814f8824c46c8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 15:30:35 +0200 Subject: [PATCH 0323/1263] Ops forgot a merging tag leftover --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 779f29dd..118bcb2a 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,6 @@ upgrade: validate-origin ## runs helm upgrade uninstall: ## runs helm uninstall helm uninstall $(NAME) -<<<<<<< HEAD vault-init: ## inits, unseals and configured the vault common/scripts/vault-utils.sh vault_init common/pattern-vault.init common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init From 51441c666cc340865fc32dca3d7e974e99edd5c0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 15:52:01 +0200 Subject: [PATCH 0324/1263] Do not bail out when KUBECONFIG is unset We first check for ~/.kube/config and if it also does not exist then we bail out. There should be no need to set anything else in the playbook as ~/.kube/config is automatically read by the ansible kubernetes.core collection [1] This brings in 3e936f423a4ca1984a84ef51aa608974e1626357 and 1d3befcbbaa833abb3553835e252a55eadadb096 into the ansible porting branch [1] https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.kubectl_connection.rst --- ansible/roles/vault_utils/defaults/main.yml | 1 + ansible/roles/vault_utils/tasks/push_secrets.yaml | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 3e53295d..3a2c6086 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -2,6 +2,7 @@ # defaults file for vault_utils values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" +kubeconfig_backup: "{{ lookup('env', 'HOME') }}/.kube/config" vault_ns: "vault" vault_pod: "vault-0" vault_hub: "hub" diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index df9105f4..072ba071 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -4,6 +4,21 @@ register: result failed_when: not result.stat.exists +- name: Check if KUBECONFIG is correctly set + debug: + msg: "KUBECONFIG is not set, falling back to ~/.kube/config" + when: kubeconfig is not defined or kubeconfig | length == 0 + +- name: Check if ~/.kube/config exists + ansible.builtin.stat: + path: "{{ kubeconfig_backup }}" + register: kubeconfig_result + +- name: Fail if both KUBECONFIG and ~/.kube/config do not exist + ansible.builtin.fail: + msg: "{{ kubeconfig_backup }} not found and KUBECONFIG unset. Bailing out." + failed_when: not kubeconfig_result.stat.exists and (kubeconfig is not defined or kubeconfig | length == 0) + - name: Check that KUBECONFIG is correctly set ansible.builtin.fail: msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" From d0eed12e5328101227dbc126e1281e05d6a94672 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 15:54:23 +0200 Subject: [PATCH 0325/1263] Rename output_file to common/pattern-vault.init --- ansible/roles/vault_utils/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 3a2c6086..50150236 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -10,6 +10,6 @@ vault_hub_kubernetes_host: 'https://$KUBERNETES_PORT_443_TCP_ADDR:443' vault_path: "secret/hub" vault_hub_ttl: "15m" vault_pki_max_lease_ttl: "8760h" -output_file: "common/vault.init" +output_file: "common/pattern-vault.init" external_secrets_ns: golang-external-secrets external_secrets_sa: golang-external-secrets From 914871118e2bb7d7e84dd70500ad071535517911 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 15:58:38 +0200 Subject: [PATCH 0326/1263] Make the default policy for hub/* secrets more restrictive Leave only the read one for the time being as it seems to be a saner default. If more granularity is needed customizations are qreuired in any case. Tested and I can still correctly access the secret on the config-demo app on the regional cluster. This is the ansible port of ed592b33ba044b579b4d92f448d5abbc493dc447 --- ansible/roles/vault_utils/defaults/main.yml | 2 ++ ansible/roles/vault_utils/tasks/vault_secrets_init.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 50150236..abdf8c11 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -7,6 +7,8 @@ vault_ns: "vault" vault_pod: "vault-0" vault_hub: "hub" vault_hub_kubernetes_host: 'https://$KUBERNETES_PORT_443_TCP_ADDR:443' +# Needs extra escaping due to how it gets injected via shell in the vault +vault_hub_capabilities: "[\\\"read\\\"]" vault_path: "secret/hub" vault_hub_ttl: "15m" vault_pki_max_lease_ttl: "8760h" diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 3a510033..3145d940 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -53,7 +53,7 @@ pod: "{{ vault_pod }}" command: > bash -e -c "echo \"path \\\"secret/data/{{ vault_hub }}/*\\\" { - capabilities = [\\\"create\\\", \\\"read\\\", \\\"update\\\", \\\"delete\\\", \\\"list\\\"] }\" > /tmp/policy-{{ vault_hub }}.hcl" + capabilities = {{ vault_hub_capabilities }} }\" > /tmp/policy-{{ vault_hub }}.hcl" - name: Configure policy for hub kubernetes.core.k8s_exec: From 3d0b4531f63f9e1d7de2aa24c41c9ca07d33a965 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 16:29:49 +0200 Subject: [PATCH 0327/1263] Stop bailing out if the vault file exists This is the ansible port of 557ef3f1e3e9ce926cb4ff930a085760c8e5b4e7 The rationale for this change is the following: - We recently made it so that make install, will also init the vault and add the secrets to it - Now if I run 'make install' but had an older common/pattern-vault.init lying around, make install will do the deploy and as soon as we call vault init the process will error out due to the file already existing. This is suboptimal because at this point a user won't be sure what to do and what steps are needed to complete the installation. Let's just move the old file to a .bak one. If one already existed we overwrite it (we cannot use mv --backup pattern as it does not exist on Mac OSX). This should be a reasonable compromise between usability and avoiding to overwrite the last version of the vault init output. Tested this and correctly got: make[2]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' common/scripts/vault-utils.sh vault_init common/pattern-vault.init common/pattern-vault.init already exists and contains seal secrets. We're moving it away to common/pattern-vault.init.bak renamed 'common/pattern-vault.init' -> 'common/pattern-vault.init.bak' No resources found in vault namespace. No resources found in vault namespace. ... 0/1 ContainerCreating 0/1 ContainerCreating Unseal Key 1: HfIACkOmMyR/jOdHU1ykILbsp83h1AX9JPqgN06uk/p0 Unseal Key 2: HYiV+NULGiSZDdQZMMdlGnK6CypX0DE7X7nMJ8dpW9+Z Unseal Key 3: l9hYpMttX7b/y1eBCaysLB3QSUVnEymCZw5uewZVGUSo ... --- .../roles/vault_utils/tasks/vault_init.yaml | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index c0725064..df9d0c9e 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -4,27 +4,21 @@ msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 -# If the output_file is relative we need to make it absolute and we choose -# the current path which normally is the main pattern folder. Reason for this -# is that the copy task needs an absolute path because we're not guaranteed +# Note that the 'realpath' filter explicitely only resolves on the ansible/local box +# which is fine in our case - name: Set absolute path for output_file ansible.builtin.set_fact: - output_file_abs: "{{ lookup('env', 'PWD') }}/{{ output_file }}" - when: "not output_file.startswith('/')" - -- name: Set output_file to new veriable - ansible.builtin.set_fact: - output_file_abs: "{{ output_file }}" - when: "output_file.startswith('/')" + output_file_abs: "{{ output_file | realpath }}" - name: Check for existence of "{{ output_file_abs }}" ansible.builtin.stat: path: "{{ output_file_abs }}" register: result -- name: Fail if "{{ output_file_abs }} exists" - ansible.builtin.fail: - msg: "{{ output_file_abs }} already exists, not overwriting. Please move it away before proceeding" +- name: Rename "{{ output_file_abs }} if it exists" + ansible.builtin.copy: + src: "{{ output_file_abs }}" + dest: "{{ output_file_abs }}.bak" failed_when: result.stat.exists - name: Check for vault namespace From f2c2477bfa95587b7a7518dc61e2eb75d1e6c9c2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 16:55:35 +0200 Subject: [PATCH 0328/1263] Improve error message Backport d209a9231be75d03e534466625c0fc5b5757d441 to ansible --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 072ba071..2fe7468d 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -57,6 +57,19 @@ loop_control: label: "{{ item.key }}" +- name: Check the value-secret.yaml file for errors + ansible.builtin.fail: + msg: > + "{{ item }}" is not properly formatted. Each key under 'secrets:' + needs to point to a dictionary of key, value pairs. See values-secret.yaml.template. + when: > + item.key | length == 0 or + item.value is not mapping + loop: + "{{ secrets | dict2items }}" + loop_control: + label: "{{ item.key }}" + # Detect here if we have only the following two keys under a password # s3.accessKey: # s3.secretKey: From 3e84134525a512f03de3e9aa8cf43f878cffcdf4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 17:25:44 +0200 Subject: [PATCH 0329/1263] Backport quote key and value when uploading a secret Backporting 964e751db42d69b624b5dd0a7b3d8ed398345d6a to ansible --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 2fe7468d..54cab462 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -132,7 +132,8 @@ - name: Debug ansible.builtin.debug: - msg: "vault kv put {{ vault_path }}/{{ item.key }} -> {{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ') }}" + msg: "vault kv put '{{ vault_path }}/{{ item.key }}' -> '{{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')" + loop: "{{ secrets | dict2items }}" loop_control: @@ -144,7 +145,7 @@ namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" command: | - sh -c "vault kv put {{ vault_path }}/{{ item.key }} {{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ') }}" + sh -c "vault kv put '{{ vault_path }}/{{ item.key }}' '{{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}'" loop: "{{ secrets | dict2items }}" loop_control: From 25451933eb692d953089b6b7329900cbdf7e7bc0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 17:29:23 +0200 Subject: [PATCH 0330/1263] Backport more fixes to ansible Namely these two: 7c795c9967380e3f63b073905ac1ca211320c94d d026fa8b6439b7789eb9d67eac6f42f3da31b807 --- .../roles/vault_utils/tasks/push_secrets.yaml | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 54cab462..c87815a9 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -1,3 +1,6 @@ +- name: Check if the kubernetes python module is usable from ansible + ansible.builtin.shell: "{{ ansible_python_interpreter }} -c 'import kubernetes'" + - name: Check for existence of "{{ values_secret }}" ansible.builtin.stat: path: "{{ values_secret }}" @@ -130,24 +133,51 @@ - not debug | default(False) | bool - vault_status.rc | int > 0 -- name: Debug - ansible.builtin.debug: - msg: "vault kv put '{{ vault_path }}/{{ item.key }}' -> '{{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')" - +# The values-secret.yaml file is in the fairly loose form of: +# secrets: +# group1: +# key1: value1 +# key2: value2 +# group2: +# key1: valueA +# key4: valueC +# The above will generate: +# vault kv put 'secret/hub/group1' key1='value1' key2='value2' +# vault kv put 'secret/hub/group2' key1='valueA' key4='valueC' +# Below we loop on the top level keys (group1, group2) and for each +# sub-top-level key (key1, key2) we create a single 'key1=value1 key2=value2' string +# +# Special note is to be given to the regex_replace which is run against each value aka password: +# A. The need for it is to quote the passwords +# B. Since it needs to cater to multiline strings (certs) and ansible offers no way +# to specify re.DOTALL and re.MULTILINE we need to use (?ms) at the beginning +# See https://docs.python.org/3/library/re.html and (?aiLmsux) section +# and https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/filter/core.py#L124 +# C. The \\A and \\Z match the beginning and end of a string (in case of multiline strings +# do not want to match every line) +- name: Set vault commands fact + ansible.builtin.set_fact: + vault_cmds: "{{ vault_cmds | default({}) | combine({ item.key: vault_cmd}) }}" + vars: + vault_cmd: "vault kv put '{{ vault_path }}/{{ item.key }}' {{ item.value.keys() | zip(item.value.values() | map('regex_replace', '(?ms)\\A(.*)\\Z', \"'\\1'\")) | map('join', '=') | list | join(' ') }}" loop: "{{ secrets | dict2items }}" loop_control: label: "{{ item.key }}" when: debug | default(False) | bool +- name: Debug vault commands + ansible.builtin.debug: + msg: "{{ vault_cmds }}" + - name: Add the actual secrets to the vault kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" command: | - sh -c "vault kv put '{{ vault_path }}/{{ item.key }}' '{{ item.value.keys() | zip(item.value.values()) | map('join', '=') | list | join(' ')}}'" + sh -c "{{ item.value }}" loop: - "{{ secrets | dict2items }}" + "{{ vault_cmds | dict2items }}" loop_control: label: "{{ item.key }}" when: not debug | default(False) | bool From 4bf5fcf0d088eeabb8d0a39a1623e7512c95ff5b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 17:31:17 +0200 Subject: [PATCH 0331/1263] Backport more fixes to ansible fb7b41f00807f0dc88fcfed95de6f60eea9bac30 --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 3 +++ ansible/roles/vault_utils/tasks/vault_init.yaml | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index c87815a9..409ba304 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -104,6 +104,9 @@ name: "{{ vault_ns }}" register: vault_ns_rc failed_when: vault_ns_rc.resources | length == 0 + until: vault_ns_rc is success + retries: 30 + delay: 5 when: not debug | default(False) | bool - name: Check if the vault pod is present diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index df9d0c9e..1d3225c4 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -15,12 +15,6 @@ path: "{{ output_file_abs }}" register: result -- name: Rename "{{ output_file_abs }} if it exists" - ansible.builtin.copy: - src: "{{ output_file_abs }}" - dest: "{{ output_file_abs }}.bak" - failed_when: result.stat.exists - - name: Check for vault namespace kubernetes.core.k8s_info: kind: Namespace @@ -40,6 +34,12 @@ retries: 20 delay: 45 +- name: Rename "{{ output_file_abs }} if it exists" + ansible.builtin.copy: + src: "{{ output_file_abs }}" + dest: "{{ output_file_abs }}.bak" + failed_when: result.stat.exists + # This needs retrying because during startup we can just get # Failed to execute on pod vault-0 due to : (0)\nReason: Handshake status 500 Internal Server Error # In the above case there is no 'rc' in vault_status. So first we wait for 'rc' to show up and ignore From b480ddbfd31400953d8d6b07c3681cf464a6ad28 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 21:36:51 +0200 Subject: [PATCH 0332/1263] Update ansible-lint to latest version v.6.2.1 --- .github/workflows/ansible-lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ansible-lint.yml b/.github/workflows/ansible-lint.yml index e2d65751..2a15908e 100644 --- a/.github/workflows/ansible-lint.yml +++ b/.github/workflows/ansible-lint.yml @@ -11,8 +11,8 @@ jobs: - uses: actions/checkout@v2 - name: Lint Ansible Playbook - # Using the latest as of today (2022-03-23) v6.0.1 - uses: ansible/ansible-lint-action@v6.0.1 + # Using the latest as of today (2022-06-23) v6.2.1 + uses: ansible/ansible-lint-action@v6.2.1 # Let's point it to the path with: args: "ansible/" From cfbfafea7e2de999d02f06c9f148624a2a5ac9cd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 21:42:16 +0200 Subject: [PATCH 0333/1263] Add ansible-lint target --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 118bcb2a..3d6d3dcf 100644 --- a/Makefile +++ b/Makefile @@ -69,5 +69,8 @@ super-linter: ## Runs super linter locally -e VALIDATE_YAML=false \ -v $(PWD):/tmp/lint:rw,z docker.io/github/super-linter:slim-v4 +ansible-lint: ## run ansible lint on ansible/ folder + ansible-lint ansible/ + .phony: install test From 28bb3ff908c5bd7c4987abce9718a2fd94534d2f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 21:42:28 +0200 Subject: [PATCH 0334/1263] Fix some ansible linting errors --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 3 ++- ansible/roles/vault_utils/tasks/vault_init.yaml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 409ba304..3289e444 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -1,5 +1,6 @@ - name: Check if the kubernetes python module is usable from ansible - ansible.builtin.shell: "{{ ansible_python_interpreter }} -c 'import kubernetes'" + ansible.builtin.command: "{{ ansible_python_interpreter }} -c 'import kubernetes'" + changed_when: false - name: Check for existence of "{{ values_secret }}" ansible.builtin.stat: diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 1d3225c4..c7509cc4 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -38,6 +38,7 @@ ansible.builtin.copy: src: "{{ output_file_abs }}" dest: "{{ output_file_abs }}.bak" + mode: '0600' failed_when: result.stat.exists # This needs retrying because during startup we can just get From fff48ca7d975fcdfb636f44dc88eb4500d02f8c3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 21:44:06 +0200 Subject: [PATCH 0335/1263] Fix some more ansible linting errors --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 3289e444..6e371d9e 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -9,7 +9,7 @@ failed_when: not result.stat.exists - name: Check if KUBECONFIG is correctly set - debug: + ansible.builtin.debug: msg: "KUBECONFIG is not set, falling back to ~/.kube/config" when: kubeconfig is not defined or kubeconfig | length == 0 @@ -163,7 +163,9 @@ ansible.builtin.set_fact: vault_cmds: "{{ vault_cmds | default({}) | combine({ item.key: vault_cmd}) }}" vars: - vault_cmd: "vault kv put '{{ vault_path }}/{{ item.key }}' {{ item.value.keys() | zip(item.value.values() | map('regex_replace', '(?ms)\\A(.*)\\Z', \"'\\1'\")) | map('join', '=') | list | join(' ') }}" + vault_cmd: > + "vault kv put '{{ vault_path }}/{{ item.key }}' {{ item.value.keys() | zip(item.value.values() | + map('regex_replace', '(?ms)\\A(.*)\\Z', \"'\\1'\")) | map('join', '=') | list | join(' ') }}" loop: "{{ secrets | dict2items }}" loop_control: From 5c9a334a2e6cd4cc47cae61572dbfdf0901c8fff Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 21:46:20 +0200 Subject: [PATCH 0336/1263] Use path and not args and using ansible-lint --- .github/workflows/ansible-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ansible-lint.yml b/.github/workflows/ansible-lint.yml index 2a15908e..f0943b53 100644 --- a/.github/workflows/ansible-lint.yml +++ b/.github/workflows/ansible-lint.yml @@ -15,4 +15,4 @@ jobs: uses: ansible/ansible-lint-action@v6.2.1 # Let's point it to the path with: - args: "ansible/" + path: "ansible/" From d27e7ec38bb810bda0c54a2dce4847452af1457a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 23 Jun 2022 22:00:21 +0200 Subject: [PATCH 0337/1263] Use ansible-lint from a container --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3d6d3dcf..33e453ae 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,7 @@ super-linter: ## Runs super linter locally -v $(PWD):/tmp/lint:rw,z docker.io/github/super-linter:slim-v4 ansible-lint: ## run ansible lint on ansible/ folder - ansible-lint ansible/ + podman run -it -v $(PWD):/workspace:rw,z --workdir /workspace --entrypoint "/usr/local/bin/ansible-lint" quay.io/ansible/creator-ee:latest "-vvv" "ansible/" .phony: install test From 26d111169f6d9566726bb4edbc89f9705489656c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 10:27:07 +0200 Subject: [PATCH 0338/1263] Only rename file when it exists --- ansible/roles/vault_utils/tasks/vault_init.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index c7509cc4..1a4d3095 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -39,7 +39,7 @@ src: "{{ output_file_abs }}" dest: "{{ output_file_abs }}.bak" mode: '0600' - failed_when: result.stat.exists + when: result.stat.exists # This needs retrying because during startup we can just get # Failed to execute on pod vault-0 due to : (0)\nReason: Handshake status 500 Internal Server Error From a02948467622d9fb582bb3166c1fc3d189368296 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 10:50:58 +0200 Subject: [PATCH 0339/1263] Fix unseal target --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 33e453ae..654bcb77 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ vault-init: ## inits, unseals and configured the vault common/scripts/vault-utils.sh vault_secrets_init common/pattern-vault.init vault-unseal: ## unseals the vault - common/scripts/vault-utils.sh vault_init common/pattern-vault.init + common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init load-secrets: ## loads the secrets into the vault common/scripts/ansible-push-vault-secrets.sh From a9e35282eb31ca71badbebd2e027a4a34f380e37 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 10:51:28 +0200 Subject: [PATCH 0340/1263] Use the simpler realpath filter to get absolute path locally --- ansible/roles/vault_utils/tasks/vault_unseal.yaml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index fb8c1a1b..e19c6bf8 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -4,18 +4,11 @@ msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 -# If the output_file is relative we need to make it absolute and we choose -# the current path which normally is the main pattern folder. Reason for this -# is that the copy task needs an absolute path because we're not guaranteed +# Note that the 'realpath' filter explicitely only resolves on the ansible/local box +# which is fine in our case - name: Set absolute path for output_file ansible.builtin.set_fact: - output_file_abs: "{{ lookup('env', 'PWD') }}/{{ output_file }}" - when: "not output_file.startswith('/')" - -- name: Set output_file to new veriable - ansible.builtin.set_fact: - output_file_abs: "{{ output_file }}" - when: "output_file.startswith('/')" + output_file_abs: "{{ output_file | realpath }}" - name: Check for existence of "{{ output_file_abs }}" ansible.builtin.stat: From 422947e6e21c5733a1ea63bf55616d9ba68cc7ff Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 11:39:59 +0200 Subject: [PATCH 0341/1263] Be more idempotent and fix the vault config policies --- ansible/roles/vault_utils/defaults/main.yml | 5 ++-- .../vault_utils/tasks/vault_secrets_init.yaml | 26 ++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index abdf8c11..aab06d7e 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -8,8 +8,9 @@ vault_pod: "vault-0" vault_hub: "hub" vault_hub_kubernetes_host: 'https://$KUBERNETES_PORT_443_TCP_ADDR:443' # Needs extra escaping due to how it gets injected via shell in the vault -vault_hub_capabilities: "[\\\"read\\\"]" -vault_path: "secret/hub" +vault_hub_capabilities: '[\\\"read\\\"]' +vault_base_path: "secret" +vault_path: "{{ vault_base_path }}/{{ vault_hub }}" vault_hub_ttl: "15m" vault_pki_max_lease_ttl: "8760h" output_file: "common/pattern-vault.init" diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 3145d940..5970d1ef 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -4,17 +4,41 @@ msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" failed_when: kubeconfig is not defined or kubeconfig | length == 0 +- name: Is secrets backend already enabled + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: > + bash -e -c "vault secrets list | grep -e '^{{ vault_base_path }}'" + register: secrets_enabled + failed_when: false + - name: Create secrets backend kv-v2 kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" - command: vault secrets enable -path=secret kv-v2 + command: vault secrets enable -path="{{ vault_base_path }}" kv-v2 + when: secrets_enabled.rc != 0 + +- name: Is kubernetes backend already enabled + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: > + bash -e -c "vault auth list | grep -e '^{{ vault_hub }}'" + register: kubernetes_enabled + failed_when: false + +- name: Debug + ansible.builtin.debug: + msg: "{{ kubernetes_enabled }}" - name: Enable kubernetes backend on hub kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" command: "vault auth enable -path={{ vault_hub }} kubernetes" + when: kubernetes_enabled.rc != 0 - name: Fetch "{{ external_secrets_ns }}/{{ external_secrets_sa }} secret name" ansible.builtin.shell: | From 281bd8c4ff57787d2b9470172b3cbd9e211720cf Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 11:40:30 +0200 Subject: [PATCH 0342/1263] Break up too long line to fix linting And also move debug when in the right place --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 6e371d9e..f9862ddd 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -163,18 +163,17 @@ ansible.builtin.set_fact: vault_cmds: "{{ vault_cmds | default({}) | combine({ item.key: vault_cmd}) }}" vars: - vault_cmd: > - "vault kv put '{{ vault_path }}/{{ item.key }}' {{ item.value.keys() | zip(item.value.values() | - map('regex_replace', '(?ms)\\A(.*)\\Z', \"'\\1'\")) | map('join', '=') | list | join(' ') }}" + vault_cmd: "vault kv put '{{ vault_path }}/{{ item.key }}' + {{ item.value.keys() | zip(item.value.values() | map('regex_replace', '(?ms)\\A(.*)\\Z', \"'\\1'\")) | map('join', '=') | list | join(' ') }}" loop: "{{ secrets | dict2items }}" loop_control: label: "{{ item.key }}" - when: debug | default(False) | bool - name: Debug vault commands ansible.builtin.debug: msg: "{{ vault_cmds }}" + when: debug | default(False) | bool - name: Add the actual secrets to the vault kubernetes.core.k8s_exec: From 2d84e4b6e44d856b85d146e3bc14bd158b231865 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 11:41:08 +0200 Subject: [PATCH 0343/1263] Use the role to push secrets --- Makefile | 2 +- ansible/roles/vault_utils/tasks/main.yml | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 654bcb77..23c76744 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ vault-unseal: ## unseals the vault common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init load-secrets: ## loads the secrets into the vault - common/scripts/ansible-push-vault-secrets.sh + common/scripts/vault-utils.sh push_secrets common/pattern-vault.init super-linter: ## Runs super linter locally podman run -e RUN_LOCAL=true -e USE_FIND_ALGORITHM=true \ diff --git a/ansible/roles/vault_utils/tasks/main.yml b/ansible/roles/vault_utils/tasks/main.yml index 4052b09e..a8ab61b9 100644 --- a/ansible/roles/vault_utils/tasks/main.yml +++ b/ansible/roles/vault_utils/tasks/main.yml @@ -26,3 +26,10 @@ apply: tags: [never, vault_secrets_init] tags: [never, vault_secrets_init] + +- name: Load secrets + include_tasks: + file: push_secrets.yaml + apply: + tags: [never, push_secrets] + tags: [never, push_secrets] From f907fcc4cea1588b65c40b2a8b706e4357ba43a0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 11:41:23 +0200 Subject: [PATCH 0344/1263] Remove unused scripts/ansible-push-vault-secrets.sh --- scripts/ansible-push-vault-secrets.sh | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100755 scripts/ansible-push-vault-secrets.sh diff --git a/scripts/ansible-push-vault-secrets.sh b/scripts/ansible-push-vault-secrets.sh deleted file mode 100755 index 98652dd0..00000000 --- a/scripts/ansible-push-vault-secrets.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -set -e - -SCRIPT=$(readlink -f "$0") -SCRIPTPATH=$(dirname "$SCRIPT") -ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" -PLAYBOOKPATH="${ANSIBLEPATH}/playbooks" -export ANSIBLE_CONFIG="${ANSIBLEPATH}/ansible.cfg" - -ansible-playbook "$PLAYBOOKPATH/vault/push_secrets.yaml" From ab49e1e61c958fce6ba50c3bfa1e86aed8dc0dfd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 15:17:59 +0200 Subject: [PATCH 0345/1263] Split off all pre check tasks into a single file --- .../roles/vault_utils/tasks/push_secrets.yaml | 26 ++----------------- .../roles/vault_utils/tasks/vault_delete.yaml | 5 +--- .../roles/vault_utils/tasks/vault_init.yaml | 5 +--- .../vault_utils/tasks/vault_pki_init.yaml | 5 +--- .../vault_utils/tasks/vault_secrets_init.yaml | 5 +--- .../roles/vault_utils/tasks/vault_unseal.yaml | 5 +--- 6 files changed, 7 insertions(+), 44 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index f9862ddd..8b78f79e 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -1,6 +1,5 @@ -- name: Check if the kubernetes python module is usable from ansible - ansible.builtin.command: "{{ ansible_python_interpreter }} -c 'import kubernetes'" - changed_when: false +--- +- include_tasks: pre_check.yaml - name: Check for existence of "{{ values_secret }}" ansible.builtin.stat: @@ -8,27 +7,6 @@ register: result failed_when: not result.stat.exists -- name: Check if KUBECONFIG is correctly set - ansible.builtin.debug: - msg: "KUBECONFIG is not set, falling back to ~/.kube/config" - when: kubeconfig is not defined or kubeconfig | length == 0 - -- name: Check if ~/.kube/config exists - ansible.builtin.stat: - path: "{{ kubeconfig_backup }}" - register: kubeconfig_result - -- name: Fail if both KUBECONFIG and ~/.kube/config do not exist - ansible.builtin.fail: - msg: "{{ kubeconfig_backup }} not found and KUBECONFIG unset. Bailing out." - failed_when: not kubeconfig_result.stat.exists and (kubeconfig is not defined or kubeconfig | length == 0) - -- name: Check that KUBECONFIG is correctly set - ansible.builtin.fail: - msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" - failed_when: kubeconfig is not defined or kubeconfig | length == 0 - when: not debug | default(False) | bool - - name: Parse "{{ values_secret }}" ansible.builtin.set_fact: all_values: "{{ lookup('file', values_secret) | from_yaml | default({}, true) }}" diff --git a/ansible/roles/vault_utils/tasks/vault_delete.yaml b/ansible/roles/vault_utils/tasks/vault_delete.yaml index d7997e37..6c132b23 100644 --- a/ansible/roles/vault_utils/tasks/vault_delete.yaml +++ b/ansible/roles/vault_utils/tasks/vault_delete.yaml @@ -1,8 +1,5 @@ --- -- name: Check that KUBECONFIG is correctly set - ansible.builtin.fail: - msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" - failed_when: kubeconfig is not defined or kubeconfig | length == 0 +- include_tasks: pre_check.yaml # FIXME to use proper k8 ansible collection - name: Delete vault pod diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 1a4d3095..45d8b96e 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -1,8 +1,5 @@ --- -- name: Check that KUBECONFIG is correctly set - ansible.builtin.fail: - msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" - failed_when: kubeconfig is not defined or kubeconfig | length == 0 +- include_tasks: pre_check.yaml # Note that the 'realpath' filter explicitely only resolves on the ansible/local box # which is fine in our case diff --git a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml index f996cac0..79b40d5e 100644 --- a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml @@ -1,8 +1,5 @@ --- -- name: Check that KUBECONFIG is correctly set - ansible.builtin.fail: - msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" - failed_when: kubeconfig is not defined or kubeconfig | length == 0 +- include_tasks: pre_check.yaml # FIXME: this should be done a lot more elegantly - name: Get cluster domain diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 5970d1ef..491dd53f 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -1,8 +1,5 @@ --- -- name: Check that KUBECONFIG is correctly set - ansible.builtin.fail: - msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" - failed_when: kubeconfig is not defined or kubeconfig | length == 0 +- include_tasks: pre_check.yaml - name: Is secrets backend already enabled kubernetes.core.k8s_exec: diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index e19c6bf8..e69101c9 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -1,8 +1,5 @@ --- -- name: Check that KUBECONFIG is correctly set - ansible.builtin.fail: - msg: "KUBECONFIG is not set. Please set it so we can inject the secrets into the cluster's vault" - failed_when: kubeconfig is not defined or kubeconfig | length == 0 +- include_tasks: pre_check.yaml # Note that the 'realpath' filter explicitely only resolves on the ansible/local box # which is fine in our case From 14aef8e60ee303354a379e5e551d34606793943a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 15:23:19 +0200 Subject: [PATCH 0346/1263] Remove spurious debug task --- ansible/roles/vault_utils/tasks/vault_secrets_init.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 491dd53f..8c6a4397 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -26,10 +26,6 @@ register: kubernetes_enabled failed_when: false -- name: Debug - ansible.builtin.debug: - msg: "{{ kubernetes_enabled }}" - - name: Enable kubernetes backend on hub kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" From e7a720e6b614e5e274eca98b1e62ad569191086c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 15:23:49 +0200 Subject: [PATCH 0347/1263] Increase wait for vault status and bail out if k8s does not return stdout at all --- ansible/roles/vault_utils/tasks/vault_init.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 45d8b96e..2a514b37 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -49,9 +49,9 @@ command: vault status -format=json register: vault_status_json until: "'rc' in vault_status_json" - retries: 5 + retries: 15 delay: 10 - failed_when: false + failed_when: "'stdout_lines' not in vault_status_json" - name: Set vault status output json fact ansible.builtin.set_fact: From c57187e31288efcf9080ca151c548e07f8f32b5d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 17:41:24 +0200 Subject: [PATCH 0348/1263] Add short algorithm exaplanation --- ansible/roles/vault_utils/README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index 1f0396bc..e32c7eb2 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -27,12 +27,23 @@ Including an example of how to use your role (for instance, with variables passe roles: - { role: username.rolename, x: 42 } + +Internals +--------- +Here is the rough high-level algorithm used to unseal the vault: +1. Check vault status. If vault is not initialized go to 2. If initialized go to 3. +2. Initialize vault and store unseal keys + login token either on a local file + or inside a secret in k8s (file_unseal var controls this) +3. Check vault status. If vault is unsealed go to 5. else to to 4. +4. Unseal the vault using the secrets read from the file or the secret + (file_unseal controls this) +5. Configure the vault (should be idempotent) + License ------- -BSD +Apache Author Information ------------------ -An optional section for the role authors to include contact information, or a website (HTML is not allowed). From 59b7cf84a55155e4fee5bdcd19112a254bbcd267 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 17:41:33 +0200 Subject: [PATCH 0349/1263] Make things a lot more idempotent and add file_unseal var And save output on file when file_unseal is true but inside a secret if file_unseal is false. --- ansible/roles/vault_utils/defaults/main.yml | 6 ++ .../roles/vault_utils/tasks/vault_init.yaml | 83 +++++++++++++------ .../roles/vault_utils/tasks/vault_unseal.yaml | 80 ++++++++++++++++++ 3 files changed, 145 insertions(+), 24 deletions(-) diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index aab06d7e..2ff2dd49 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -16,3 +16,9 @@ vault_pki_max_lease_ttl: "8760h" output_file: "common/pattern-vault.init" external_secrets_ns: golang-external-secrets external_secrets_sa: golang-external-secrets +# Setting this to false makes the role store the vault unseal keys and root login +# token inside a secret in the cluster. +# *Note* that this is fundamentally unsafe +file_unseal: true +unseal_secret: "vaultkeys" +unseal_namespace: "imperative" diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 2a514b37..dda55784 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -1,17 +1,6 @@ --- - include_tasks: pre_check.yaml -# Note that the 'realpath' filter explicitely only resolves on the ansible/local box -# which is fine in our case -- name: Set absolute path for output_file - ansible.builtin.set_fact: - output_file_abs: "{{ output_file | realpath }}" - -- name: Check for existence of "{{ output_file_abs }}" - ansible.builtin.stat: - path: "{{ output_file_abs }}" - register: result - - name: Check for vault namespace kubernetes.core.k8s_info: kind: Namespace @@ -31,13 +20,6 @@ retries: 20 delay: 45 -- name: Rename "{{ output_file_abs }} if it exists" - ansible.builtin.copy: - src: "{{ output_file_abs }}" - dest: "{{ output_file_abs }}.bak" - mode: '0600' - when: result.stat.exists - # This needs retrying because during startup we can just get # Failed to execute on pod vault-0 due to : (0)\nReason: Handshake status 500 Internal Server Error # In the above case there is no 'rc' in vault_status. So first we wait for 'rc' to show up and ignore @@ -58,10 +40,37 @@ vault_status: "{{ vault_status_json.stdout | from_json }}" when: vault_status_json.stdout_lines | length > 0 -- name: Fail when the vault status is sealed - ansible.builtin.fail: - msg: "The vault is already configured. Please unseal it with 'make vault-unseal'. Msg: {{ vault_status }}" - when: vault_status['initialized'] | bool +# If the vault is already initialized we skip all the tasks below +- name: Is the vault initialized? + ansible.builtin.set_fact: + vault_initialized: vault_status['initialized'] | bool + +# Note that the 'realpath' filter explicitely only resolves on the ansible/local box +# which is fine in our case +- name: Set absolute path for output_file + ansible.builtin.set_fact: + output_file_abs: "{{ output_file | realpath }}" + when: + - not vault_initialized + - file_unseal + +- name: Check for existence of "{{ output_file_abs }}" + ansible.builtin.stat: + path: "{{ output_file_abs }}" + register: result + when: + - not vault_initialized + - file_unseal + +- name: Rename "{{ output_file_abs }} if it exists" + ansible.builtin.copy: + src: "{{ output_file_abs }}" + dest: "{{ output_file_abs }}.bak" + mode: '0600' + when: + - not vault_initialized + - file_unseal + - result.stat.exists # We need to retry here because the vault service might be starting # and can return a 500 internal server until its state is fully ready @@ -74,15 +83,41 @@ until: vault_init_json_out is not failed retries: 10 delay: 15 + when: not vault_initialized - name: Set vault init output json fact ansible.builtin.set_fact: vault_init_json: "{{ vault_init_json_out.stdout | from_json }}" + when: not vault_initialized -# Prefer the output_file_abs var if it is defined -- name: Save vault operator output +# We store the the operator unseal keys and root token to a file when +# the vault was not already initialized *and* when unseal_from_cluster +# is set to false +- name: Save vault operator output (local file) ansible.builtin.copy: follow: true dest: "{{ output_file_abs }}" content: "{{ vault_init_json | to_nice_json }}" mode: '0600' + when: + - not vault_initialized + - file_unseal + +# We store the the operator unseal keys and root token to a secret inside +# the cluster when the vault was not already initialized *and* when +# unseal_from_cluster is set to true +- name: Save vault operator output (into a secret inside the cluster) + kubernetes.core.k8s: + state: present + definition: + apiVersion: v1 + kind: Secret + type: Opaque + metadata: + name: "{{ unseal_secret }}" + namespace: "{{ unseal_namespace }}" + data: + vault_init_json: "{{ vault_init_json | to_nice_json | b64encode }}" + when: + - not vault_initialized + - not file_unseal diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index e69101c9..b8ee2c5e 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -1,31 +1,109 @@ --- - include_tasks: pre_check.yaml +- name: Check for vault namespace + kubernetes.core.k8s_info: + kind: Namespace + name: "{{ vault_ns }}" + register: vault_ns_rc + until: vault_ns_rc.resources | length > 0 + retries: 20 + delay: 45 + +- name: Check if the vault pod is present + kubernetes.core.k8s_info: + kind: Pod + namespace: "{{ vault_ns }}" + name: "{{ vault_pod }}" + register: vault_pod_rc + until: vault_pod_rc.resources | length > 0 + retries: 20 + delay: 45 + +# This needs retrying because during startup we can just get +# Failed to execute on pod vault-0 due to : (0)\nReason: Handshake status 500 Internal Server Error +# In the above case there is no 'rc' in vault_status. So first we wait for 'rc' to show up and ignore +# any errors, and then we bail out if rc is 2 as it means the vault is already initialized +- name: Check for the vault status + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault status -format=json + register: vault_status_json + until: "'rc' in vault_status_json" + retries: 15 + delay: 10 + failed_when: "'stdout_lines' not in vault_status_json" + +- name: Set vault status output json fact + ansible.builtin.set_fact: + vault_status: "{{ vault_status_json.stdout | from_json }}" + when: vault_status_json.stdout_lines | length > 0 + +# If the vault is already initialized we skip all the tasks below +- name: Is the vault sealed? + ansible.builtin.set_fact: + vault_sealed: vault_status['sealed'] | bool + # Note that the 'realpath' filter explicitely only resolves on the ansible/local box # which is fine in our case - name: Set absolute path for output_file ansible.builtin.set_fact: output_file_abs: "{{ output_file | realpath }}" + when: + - vault_sealed + - file_unseal - name: Check for existence of "{{ output_file_abs }}" ansible.builtin.stat: path: "{{ output_file_abs }}" register: result + when: + - vault_sealed + - file_unseal - name: Fail if "{{ output_file_abs }}" does not exists ansible.builtin.fail: msg: "{{ output_file_abs }} does not exist. Stopping here" failed_when: not result.stat.exists + when: + - vault_sealed + - file_unseal # We reparse the json vault init file in case unseal was called without operator init before +# and if file_unseal is true - name: Parse "{{ output_file_abs }}" ansible.builtin.set_fact: vault_init_json: "{{ lookup('file', output_file_abs) | from_json }}" + when: + - vault_sealed + - file_unseal + +# We reparse the json vault init secret in case unseal was called without operator init before +# and if file_unseal is false +- name: Parse "{{ output_file_abs }}" + kubernetes.core.k8s_info: + kind: Secret + namespace: "{{ unseal_namespace }}" + name: "{{ unseal_secret }}" + api_version: v1 + register: vault_init_data + when: + - vault_sealed + - not file_unseal + +- name: Set vault init json + ansible.builtin.set_fact: + vault_init_json: "{{ vault_init_data.resources[0].data.vault_data_json | b64decode | from_json }}" + when: + - vault_sealed + - not file_unseal - name: Set root token and unseal_keys ansible.builtin.set_fact: root_token: "{{ vault_init_json['root_token'] }}" unseal_keys: "{{ vault_init_json['unseal_keys_hex'] }}" + when: vault_sealed - name: Unseal vault kubernetes.core.k8s_exec: @@ -36,9 +114,11 @@ loop_control: extended: true label: "Unsealing with key {{ ansible_loop.index }}" + when: vault_sealed - name: Login into vault kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" command: vault login "{{ root_token }}" + when: vault_sealed From 83cf105546acd15607e308c59b55aa771f6fd43d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 17:44:21 +0200 Subject: [PATCH 0350/1263] Forgot to commit the pre_check.yaml file --- .../roles/vault_utils/tasks/pre_check.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 ansible/roles/vault_utils/tasks/pre_check.yaml diff --git a/ansible/roles/vault_utils/tasks/pre_check.yaml b/ansible/roles/vault_utils/tasks/pre_check.yaml new file mode 100644 index 00000000..9551d2b6 --- /dev/null +++ b/ansible/roles/vault_utils/tasks/pre_check.yaml @@ -0,0 +1,19 @@ +--- +- name: Check if the kubernetes python module is usable from ansible + ansible.builtin.command: "{{ ansible_python_interpreter }} -c 'import kubernetes'" + changed_when: false + +- name: Check if KUBECONFIG is correctly set + ansible.builtin.debug: + msg: "KUBECONFIG is not set, falling back to ~/.kube/config" + when: kubeconfig is not defined or kubeconfig | length == 0 + +- name: Check if ~/.kube/config exists + ansible.builtin.stat: + path: "{{ kubeconfig_backup }}" + register: kubeconfig_result + +- name: Fail if both KUBECONFIG and ~/.kube/config do not exist + ansible.builtin.fail: + msg: "{{ kubeconfig_backup }} not found and KUBECONFIG unset. Bailing out." + failed_when: not kubeconfig_result.stat.exists and (kubeconfig is not defined or kubeconfig | length == 0) From 65c44b6778c1f76293d6964d4f1d70fc88b0ae63 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 17:48:57 +0200 Subject: [PATCH 0351/1263] Consolidate vault status checking in a single file This way we can reuse it everywhere The vault_status.yaml will register a single fact called 'vault_status' containing the output of 'vault status -format=json' already parsed into a dictionary --- .../roles/vault_utils/tasks/push_secrets.yaml | 44 +++---------------- .../roles/vault_utils/tasks/vault_init.yaml | 40 +---------------- .../roles/vault_utils/tasks/vault_status.yaml | 42 ++++++++++++++++++ .../roles/vault_utils/tasks/vault_unseal.yaml | 42 +----------------- 4 files changed, 51 insertions(+), 117 deletions(-) create mode 100644 ansible/roles/vault_utils/tasks/vault_status.yaml diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 8b78f79e..aa0806e6 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -1,5 +1,11 @@ --- - include_tasks: pre_check.yaml +- include_tasks: vault_status.yaml + +- name: Fail if the vault is sealed + ansible.builtin.fail: + msg: "The vault is still sealed, cannot continue" + failed_when: vault_status['sealed'] | bool - name: Check for existence of "{{ values_secret }}" ansible.builtin.stat: @@ -77,44 +83,6 @@ when: s3keys is defined and s3keys | length > 0 -- name: Check for vault namespace - kubernetes.core.k8s_info: - kind: Namespace - name: "{{ vault_ns }}" - register: vault_ns_rc - failed_when: vault_ns_rc.resources | length == 0 - until: vault_ns_rc is success - retries: 30 - delay: 5 - when: not debug | default(False) | bool - -- name: Check if the vault pod is present - kubernetes.core.k8s_info: - kind: Pod - namespace: "{{ vault_ns }}" - name: "{{ vault_pod }}" - register: vault_pod_rc - failed_when: vault_pod_rc.resources | length == 0 - when: not debug | default(False) | bool - -# vault status returns 1 on error and 2 on sealed -# so we can bail out when sealed -- name: Check if the vault is unsealed - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault status - register: vault_status - failed_when: vault_status.rc|int == 1 - when: not debug | default(False) | bool - -- name: Check vault status return - ansible.builtin.fail: - msg: The vault is still sealed. Please run "make vault-init" first with KUBECONFIG pointing to the HUB cluster - when: - - not debug | default(False) | bool - - vault_status.rc | int > 0 - # The values-secret.yaml file is in the fairly loose form of: # secrets: # group1: diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index dda55784..ec2d57ba 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -1,44 +1,6 @@ --- - include_tasks: pre_check.yaml - -- name: Check for vault namespace - kubernetes.core.k8s_info: - kind: Namespace - name: "{{ vault_ns }}" - register: vault_ns_rc - until: vault_ns_rc.resources | length > 0 - retries: 20 - delay: 45 - -- name: Check if the vault pod is present - kubernetes.core.k8s_info: - kind: Pod - namespace: "{{ vault_ns }}" - name: "{{ vault_pod }}" - register: vault_pod_rc - until: vault_pod_rc.resources | length > 0 - retries: 20 - delay: 45 - -# This needs retrying because during startup we can just get -# Failed to execute on pod vault-0 due to : (0)\nReason: Handshake status 500 Internal Server Error -# In the above case there is no 'rc' in vault_status. So first we wait for 'rc' to show up and ignore -# any errors, and then we bail out if rc is 2 as it means the vault is already initialized -- name: Check for the vault status - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault status -format=json - register: vault_status_json - until: "'rc' in vault_status_json" - retries: 15 - delay: 10 - failed_when: "'stdout_lines' not in vault_status_json" - -- name: Set vault status output json fact - ansible.builtin.set_fact: - vault_status: "{{ vault_status_json.stdout | from_json }}" - when: vault_status_json.stdout_lines | length > 0 +- include_tasks: vault_status.yaml # If the vault is already initialized we skip all the tasks below - name: Is the vault initialized? diff --git a/ansible/roles/vault_utils/tasks/vault_status.yaml b/ansible/roles/vault_utils/tasks/vault_status.yaml new file mode 100644 index 00000000..53a33770 --- /dev/null +++ b/ansible/roles/vault_utils/tasks/vault_status.yaml @@ -0,0 +1,42 @@ +--- +# Registers a variable valled vault_status containing the vault's status json dict +- name: Check for vault namespace + kubernetes.core.k8s_info: + kind: Namespace + name: "{{ vault_ns }}" + register: vault_ns_rc + until: vault_ns_rc.resources | length > 0 + retries: 20 + delay: 45 + +- name: Check if the vault pod is present + kubernetes.core.k8s_info: + kind: Pod + namespace: "{{ vault_ns }}" + name: "{{ vault_pod }}" + register: vault_pod_rc + until: vault_pod_rc.resources | length > 0 + retries: 20 + delay: 45 + +# This needs retrying because during startup we can just get +# Failed to execute on pod vault-0 due to : (0)\nReason: Handshake status 500 Internal Server Error +# In the above case there is no 'rc' in vault_status. So first we wait for 'rc' to show up and ignore +# any errors, and then we bail out if rc is 2 as it means the vault is already initialized +- name: Check for the vault status + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault status -format=json + register: vault_status_json + until: "'rc' in vault_status_json" + retries: 15 + delay: 10 + failed_when: "'stdout_lines' not in vault_status_json" + +- name: Set vault status output json fact + ansible.builtin.set_fact: + vault_status: "{{ vault_status_json.stdout | from_json }}" + when: vault_status_json.stdout_lines | length > 0 + + diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index b8ee2c5e..5e414d5a 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -1,46 +1,8 @@ --- - include_tasks: pre_check.yaml +- include_tasks: vault_status.yaml -- name: Check for vault namespace - kubernetes.core.k8s_info: - kind: Namespace - name: "{{ vault_ns }}" - register: vault_ns_rc - until: vault_ns_rc.resources | length > 0 - retries: 20 - delay: 45 - -- name: Check if the vault pod is present - kubernetes.core.k8s_info: - kind: Pod - namespace: "{{ vault_ns }}" - name: "{{ vault_pod }}" - register: vault_pod_rc - until: vault_pod_rc.resources | length > 0 - retries: 20 - delay: 45 - -# This needs retrying because during startup we can just get -# Failed to execute on pod vault-0 due to : (0)\nReason: Handshake status 500 Internal Server Error -# In the above case there is no 'rc' in vault_status. So first we wait for 'rc' to show up and ignore -# any errors, and then we bail out if rc is 2 as it means the vault is already initialized -- name: Check for the vault status - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault status -format=json - register: vault_status_json - until: "'rc' in vault_status_json" - retries: 15 - delay: 10 - failed_when: "'stdout_lines' not in vault_status_json" - -- name: Set vault status output json fact - ansible.builtin.set_fact: - vault_status: "{{ vault_status_json.stdout | from_json }}" - when: vault_status_json.stdout_lines | length > 0 - -# If the vault is already initialized we skip all the tasks below +# If the vault is already unsealed we skip all the tasks below - name: Is the vault sealed? ansible.builtin.set_fact: vault_sealed: vault_status['sealed'] | bool From 1e45fcc1ea29ef21a064858891234cdee21b322d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 17:50:14 +0200 Subject: [PATCH 0352/1263] Remove too many blank lines and please linter --- ansible/roles/vault_utils/tasks/vault_status.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_status.yaml b/ansible/roles/vault_utils/tasks/vault_status.yaml index 53a33770..09ce24ec 100644 --- a/ansible/roles/vault_utils/tasks/vault_status.yaml +++ b/ansible/roles/vault_utils/tasks/vault_status.yaml @@ -38,5 +38,3 @@ ansible.builtin.set_fact: vault_status: "{{ vault_status_json.stdout | from_json }}" when: vault_status_json.stdout_lines | length > 0 - - From bb2758b5fcc637530b5a8d174685ea3a6ee4196c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 18:21:40 +0200 Subject: [PATCH 0353/1263] Fix fact parsing from json --- ansible/roles/vault_utils/tasks/vault_init.yaml | 2 +- ansible/roles/vault_utils/tasks/vault_unseal.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index ec2d57ba..dd0226ba 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -5,7 +5,7 @@ # If the vault is already initialized we skip all the tasks below - name: Is the vault initialized? ansible.builtin.set_fact: - vault_initialized: vault_status['initialized'] | bool + vault_initialized: "{{ vault_status['initialized'] | bool }}" # Note that the 'realpath' filter explicitely only resolves on the ansible/local box # which is fine in our case diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index 5e414d5a..88d98cee 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -5,7 +5,7 @@ # If the vault is already unsealed we skip all the tasks below - name: Is the vault sealed? ansible.builtin.set_fact: - vault_sealed: vault_status['sealed'] | bool + vault_sealed: "{{ vault_status['sealed'] | bool }}" # Note that the 'realpath' filter explicitely only resolves on the ansible/local box # which is fine in our case From 377d4ec5ed38548c73d70569b5c81a5b85e45800 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 24 Jun 2022 19:02:41 +0200 Subject: [PATCH 0354/1263] On my slow local cluster vault pod takes longer to come up --- ansible/roles/vault_utils/tasks/vault_status.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_status.yaml b/ansible/roles/vault_utils/tasks/vault_status.yaml index 09ce24ec..f54fa4a9 100644 --- a/ansible/roles/vault_utils/tasks/vault_status.yaml +++ b/ansible/roles/vault_utils/tasks/vault_status.yaml @@ -30,8 +30,8 @@ command: vault status -format=json register: vault_status_json until: "'rc' in vault_status_json" - retries: 15 - delay: 10 + retries: 20 + delay: 45 failed_when: "'stdout_lines' not in vault_status_json" - name: Set vault status output json fact From 994b0b4dac1b8c63a3e148a7beef1c55c620f7f0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 25 Jun 2022 18:56:45 +0200 Subject: [PATCH 0355/1263] Make sure we call bash when setting up the kubernetes backend This makes sure that 'https://$KUBERNETES_PORT_443_TCP_ADDR:443' gets resolved correctly as an environment variable. --- ansible/roles/vault_utils/tasks/vault_secrets_init.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 8c6a4397..48c5f659 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -59,10 +59,10 @@ namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" command: > - vault write auth/"{{ vault_hub }}"/config token_reviewer_jwt="{{ sa_token }}" + bash -c "vault write auth/{{ vault_hub }}/config token_reviewer_jwt={{ sa_token }} kubernetes_host="{{ vault_hub_kubernetes_host }}" kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt - issuer=https://kubernetes.default.svc + issuer=https://kubernetes.default.svc" - name: Configure policy template for hub kubernetes.core.k8s_exec: From 822c0525d236e000717f71a4f2bb81513ee36429 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 25 Jun 2022 19:38:09 +0200 Subject: [PATCH 0356/1263] Make sure we bail out in case of error --- ansible/roles/vault_utils/tasks/vault_secrets_init.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 48c5f659..4ff8606f 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -59,7 +59,7 @@ namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" command: > - bash -c "vault write auth/{{ vault_hub }}/config token_reviewer_jwt={{ sa_token }} + bash -e -c "vault write auth/{{ vault_hub }}/config token_reviewer_jwt={{ sa_token }} kubernetes_host="{{ vault_hub_kubernetes_host }}" kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc" From 790f30f57932fbc00bb4286cbfa23f4421fc9952 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 25 Jun 2022 19:45:03 +0200 Subject: [PATCH 0357/1263] Fix last escaping bug --- ansible/roles/vault_utils/defaults/main.yml | 2 +- ansible/roles/vault_utils/tasks/vault_secrets_init.yaml | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 2ff2dd49..e838a388 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -6,7 +6,7 @@ kubeconfig_backup: "{{ lookup('env', 'HOME') }}/.kube/config" vault_ns: "vault" vault_pod: "vault-0" vault_hub: "hub" -vault_hub_kubernetes_host: 'https://$KUBERNETES_PORT_443_TCP_ADDR:443' +vault_hub_kubernetes_host: https://$KUBERNETES_PORT_443_TCP_ADDR:443 # Needs extra escaping due to how it gets injected via shell in the vault vault_hub_capabilities: '[\\\"read\\\"]' vault_base_path: "secret" diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 4ff8606f..24e40190 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -58,9 +58,8 @@ kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" - command: > - bash -e -c "vault write auth/{{ vault_hub }}/config token_reviewer_jwt={{ sa_token }} - kubernetes_host="{{ vault_hub_kubernetes_host }}" + command: bash -e -c "vault write auth/{{ vault_hub }}/config token_reviewer_jwt={{ sa_token }} + kubernetes_host={{ vault_hub_kubernetes_host }} kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc" From f44ba5c40f04f67d27b0eb48614ac2d31afe2b2c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 25 Jun 2022 22:45:53 +0200 Subject: [PATCH 0358/1263] When using a secret store unseal material in a field called vault_data_json --- ansible/roles/vault_utils/tasks/vault_init.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index dd0226ba..e3ba9cb6 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -79,7 +79,7 @@ name: "{{ unseal_secret }}" namespace: "{{ unseal_namespace }}" data: - vault_init_json: "{{ vault_init_json | to_nice_json | b64encode }}" + vault_data_json: "{{ vault_init_json | to_nice_json | b64encode }}" when: - not vault_initialized - not file_unseal From a8a2f06347c72eff7c62a5e3a36045ffb6371f6e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 26 Jun 2022 20:14:31 +0200 Subject: [PATCH 0359/1263] Cleanup meta/main.yml --- ansible/roles/vault_utils/meta/main.yml | 37 +++---------------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/ansible/roles/vault_utils/meta/main.yml b/ansible/roles/vault_utils/meta/main.yml index d9fb4eff..c99eb3a9 100644 --- a/ansible/roles/vault_utils/meta/main.yml +++ b/ansible/roles/vault_utils/meta/main.yml @@ -2,30 +2,13 @@ galaxy_info: author: Validated Patterns Team https://github.com/hybrid-cloud-patterns/ description: Utilities to manage vault in kubernetes (init, unseal, etc) - # If the issue tracker for your role is not on github, uncomment the - # next line and provide a value - # issue_tracker_url: http://example.com/issue/tracker - - # Choose a valid license ID from https://spdx.org - some suggested licenses: - # - BSD-3-Clause (default) - # - MIT - # - GPL-2.0-or-later - # - GPL-3.0-only - # - Apache-2.0 - # - CC-BY-4.0 + issue_tracker_url: https://github.com/hybrid-cloud-patterns/common/issues license: Apache-2.0 - - min_ansible_version: 2.1 + min_ansible_version: "2.1" # If this a Container Enabled role, provide the minimum Ansible Container version. # min_ansible_container_version: - # - # Provide a list of supported platforms, and for each platform a list of versions. - # If you don't wish to enumerate all versions for a particular platform, use 'all'. - # To view available platforms and versions (or releases), visit: - # https://galaxy.ansible.com/api/v1/platforms/ - # platforms: - name: Fedora versions: @@ -38,22 +21,10 @@ galaxy_info: - all - name: EL versions: - - 8 - # - # - name: SomePlatform - # versions: - # - all - # - 1.0 - # - 7 - # - 99.99 + - "8" + - "9" galaxy_tags: [] - # List tags for your role here, one per line. A tag is a keyword that describes - # and categorizes the role. Users find roles by searching for tags. Be sure to - # remove the '[]' above, if you add tags to this list. - # - # NOTE: A tag is limited to a single word comprised of alphanumeric characters. - # Maximum 20 tags per role. dependencies: [] # List your role dependencies here, one per line. Be sure to remove the '[]' above, From 4765834f92607577710d2e319f0471a48be337dd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 27 Jun 2022 10:04:59 +0200 Subject: [PATCH 0360/1263] Cleanup README.md --- ansible/roles/vault_utils/README.md | 51 +++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index e32c7eb2..ab2d39f1 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -6,26 +6,51 @@ Bunch of utilities to manage the vault inside k8s imperatively Requirements ------------ -ansible-galaxy collection install community.kubernetes +ansible-galaxy collection install kubernetes.core (formerly known as community.kubernetes) Role Variables -------------- - +Defaults as to where the values-secret.yaml file is and the two ways to connect to a kubernetes cluster +(KUBERCONFIG and ~/.kube/config respectively): +``` +values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" +kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" +kubeconfig_backup: "{{ lookup('env', 'HOME') }}/.kube/config" +``` + +Default values for vault configuration: +``` +vault_ns: "vault" +vault_pod: "vault-0" +vault_hub: "hub" +vault_hub_kubernetes_host: https://$KUBERNETES_PORT_443_TCP_ADDR:443 +# Needs extra escaping due to how it gets injected via shell in the vault +vault_hub_capabilities: '[\\\"read\\\"]' +vault_base_path: "secret" +vault_path: "{{ vault_base_path }}/{{ vault_hub }}" +vault_hub_ttl: "15m" +vault_pki_max_lease_ttl: "8760h" +external_secrets_ns: golang-external-secrets +external_secrets_sa: golang-external-secrets +``` + +Use the local file system (output_file variable) to store the vault's unseal keys. +If set to false they will be stored inside a secret defined by `unseal_secret` +in the `unseal_namespace` namespace: +``` +file_unseal: true +# token inside a secret in the cluster. +# *Note* that this is fundamentally unsafe +output_file: "common/pattern-vault.init" +unseal_secret: "vaultkeys" +unseal_namespace: "imperative" +``` Dependencies ------------ - - -Example Playbook ----------------- - -Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: - - - hosts: servers - roles: - - { role: username.rolename, x: 42 } +This relies on https://docs.ansible.com/ansible/latest/collections/kubernetes/core/k8s_module.html Internals @@ -46,4 +71,4 @@ Apache Author Information ------------------ - +Michele Baldessari From b2c5e661f6e220b0d82f3e1225325cf4db1c202a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 27 Jun 2022 10:15:38 +0200 Subject: [PATCH 0361/1263] Use consistent output_file name. Move to a more portable absolute path function --- scripts/vault-utils.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 29d22542..2f014a45 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -1,7 +1,12 @@ #!/usr/bin/env bash set -eu -SCRIPT=$(readlink -f "$0") +get_abs_filename() { + # $1 : relative filename + echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" +} + +SCRIPT=$(get_abs_filename "$0") SCRIPTPATH=$(dirname "$SCRIPT") COMMONPATH=$(dirname "$SCRIPTPATH") ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" @@ -15,7 +20,7 @@ if [ $# -lt 1 ]; then fi TASK="${1}" -OUTFILE=${2:-"$COMMONPATH"/vault.init} +OUTFILE=${2:-"$COMMONPATH"/pattern-vault.init} if [ -z ${TASK} ]; then echo "Task is unset" From a759e3df673a6c55aa163e405f3b3026e0c6e86a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 28 Jun 2022 16:18:23 +0200 Subject: [PATCH 0362/1263] Fix some markdown to please the linters --- ansible/roles/vault_utils/README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index ab2d39f1..e83471b9 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -13,14 +13,16 @@ Role Variables Defaults as to where the values-secret.yaml file is and the two ways to connect to a kubernetes cluster (KUBERCONFIG and ~/.kube/config respectively): -``` + +```yaml values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" kubeconfig_backup: "{{ lookup('env', 'HOME') }}/.kube/config" ``` Default values for vault configuration: -``` + +```yaml vault_ns: "vault" vault_pod: "vault-0" vault_hub: "hub" @@ -38,7 +40,8 @@ external_secrets_sa: golang-external-secrets Use the local file system (output_file variable) to store the vault's unseal keys. If set to false they will be stored inside a secret defined by `unseal_secret` in the `unseal_namespace` namespace: -``` + +```yaml file_unseal: true # token inside a secret in the cluster. # *Note* that this is fundamentally unsafe @@ -50,12 +53,13 @@ unseal_namespace: "imperative" Dependencies ------------ -This relies on https://docs.ansible.com/ansible/latest/collections/kubernetes/core/k8s_module.html - +This relies on [kubernetes.core](https://docs.ansible.com/ansible/latest/collections/kubernetes/core/k8s_module.html) Internals --------- + Here is the rough high-level algorithm used to unseal the vault: + 1. Check vault status. If vault is not initialized go to 2. If initialized go to 3. 2. Initialize vault and store unseal keys + login token either on a local file or inside a secret in k8s (file_unseal var controls this) @@ -71,4 +75,5 @@ Apache Author Information ------------------ + Michele Baldessari From 8e4d31fe0ae7007284de5f286269b758b962980f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 29 Jun 2022 14:52:30 +0200 Subject: [PATCH 0363/1263] Remove all oc calls and allow for running from inside a cluster Loosen the KUBECONFIG + ~/.kube/config check and only fail it if both are missing *and* we're not running inside a pod in a cluster (i.e. when KUBERNETES_SERVICE_HOST env var is set) Also remove all remaining "oc" calls. Those are not really very ansible *and* the json_path query we do in ansible is only available when using the general.community collection which is not present in the ee container we use by default. --- .../roles/vault_utils/tasks/pre_check.yaml | 11 ++++-- .../vault_utils/tasks/vault_secrets_init.yaml | 36 ++++++++++++------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/pre_check.yaml b/ansible/roles/vault_utils/tasks/pre_check.yaml index 9551d2b6..1dc5f445 100644 --- a/ansible/roles/vault_utils/tasks/pre_check.yaml +++ b/ansible/roles/vault_utils/tasks/pre_check.yaml @@ -13,7 +13,14 @@ path: "{{ kubeconfig_backup }}" register: kubeconfig_result -- name: Fail if both KUBECONFIG and ~/.kube/config do not exist +- name: Check if we're running inside an OCP cluster directly + ansible.builtin.set_fact: + running_in_ocp: "{{ lookup('env', 'KUBERNETES_SERVICE_HOST') | length > 0 | bool }}" + +- name: Fail if both KUBECONFIG and ~/.kube/config do not exist but only when not running in a cluster ansible.builtin.fail: msg: "{{ kubeconfig_backup }} not found and KUBECONFIG unset. Bailing out." - failed_when: not kubeconfig_result.stat.exists and (kubeconfig is not defined or kubeconfig | length == 0) + failed_when: + - not running_in_ocp + - not kubeconfig_result.stat.exists + - kubeconfig is not defined or kubeconfig | length == 0 diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 24e40190..336746c3 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -34,25 +34,37 @@ when: kubernetes_enabled.rc != 0 - name: Fetch "{{ external_secrets_ns }}/{{ external_secrets_sa }} secret name" - ansible.builtin.shell: | - oc get -n "{{ external_secrets_ns }}" serviceaccount "{{ external_secrets_sa }}" -o jsonpath='{.secrets}' - register: secrets_out - changed_when: true + kubernetes.core.k8s_info: + kind: ServiceAccount + namespace: "{{ external_secrets_ns }}" + name: "{{ external_secrets_sa }}" + api_version: v1 + register: serviceaccount + +# FIXME: we could bail out nicely if secrets is empty +- name: Get "{{ external_secret_sa }} secrets" + ansible.builtin.set_fact: + secrets: "{{ serviceaccount.resources[0].secrets }}" + - name: Filter secrets of "{{ external_secrets_ns }}/{{ external_secrets_sa }}" ansible.builtin.set_fact: - secret: "{{ secrets_out.stdout | from_json | json_query(\"[?starts_with(name, 'golang-external-secrets-token-')].name | [0]\") }}" + secret: "{{ item.name }}" + when: '"golang-external-secrets-token-" in item.name' + loop: "{{ secrets }}" -- name: Get token of "{{ external_secrets_ns }}/{{ external_secrets_sa }}" - ansible.builtin.shell: | - set -o pipefail - oc get secret -n "{{ external_secrets_ns }}" "{{ secret }}" -o go-template="{{ '{{' }} .data.token | base64decode {{ '}}' }}" - register: sa_token_out - changed_when: true +- name: Get token of the secret + kubernetes.core.k8s_info: + kind: Secret + namespace: "{{ external_secrets_ns }}" + name: "{{ secret }}" + api_version: v1 + register: token_data +# FIXME: we could bail out nicely if token_data is empty - name: Set sa_token fact ansible.builtin.set_fact: - sa_token: "{{ sa_token_out.stdout }}" + sa_token: "{{ token_data.resources[0].data.token | b64decode }}" - name: Configure hub kubernetes backend kubernetes.core.k8s_exec: From aee7ad7f09b949decc41b31980e247390447e9f8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 29 Jun 2022 15:15:09 +0200 Subject: [PATCH 0364/1263] Fix up PKI creation and add not about it not being currently used --- .../vault_utils/tasks/vault_pki_init.yaml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml index 79b40d5e..ca5fe697 100644 --- a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml @@ -1,15 +1,20 @@ +# NOTE: This task is currently not used --- - include_tasks: pre_check.yaml -# FIXME: this should be done a lot more elegantly -- name: Get cluster domain - ansible.builtin.shell: | - set -o pipefail - oc get ingresses.config/cluster -o jsonpath='{.spec.domain}' | cut -d. -f3- - register: ingress_domain_out - changed_when: false +- name: Fetch Ingress object + kubernetes.core.k8s_info: + kind: Ingress + api_version: config.openshift.io/v1 + register: ingress_domain_raw +# We split the domain and skip the first two parts +# apps.bandini-dc.blueprints.rhecoeng.com -> blueprints.rhecoeng.com - name: Set ingress domain + ansible.builtin.set_fact: + ingress_domain_out: "{{ ingress_raw.resources[0].spec.domain.split('.')[2:] | join('.') }}" + +- name: Set ingress domain cleaned up ansible.builtin.set_fact: ingress_domain: "{{ ingress_domain_out.stdout }}" cert_role: "{{ ingress_domain_out.stdout | replace('.', '_') }}" From 4fd8ecf6901d4cb00370e31a1ae874998a7ab0d6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 29 Jun 2022 15:44:45 +0200 Subject: [PATCH 0365/1263] Fix delete to use native ansible Stop using oc commands and just move to kubernetes.core actions --- ansible/roles/vault_utils/defaults/main.yml | 1 + .../roles/vault_utils/tasks/vault_delete.yaml | 35 ++++++++----------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index e838a388..a3189bd6 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -6,6 +6,7 @@ kubeconfig_backup: "{{ lookup('env', 'HOME') }}/.kube/config" vault_ns: "vault" vault_pod: "vault-0" vault_hub: "hub" +vault_pvc: "data-vault-0" vault_hub_kubernetes_host: https://$KUBERNETES_PORT_443_TCP_ADDR:443 # Needs extra escaping due to how it gets injected via shell in the vault vault_hub_capabilities: '[\\\"read\\\"]' diff --git a/ansible/roles/vault_utils/tasks/vault_delete.yaml b/ansible/roles/vault_utils/tasks/vault_delete.yaml index 6c132b23..ac98a7af 100644 --- a/ansible/roles/vault_utils/tasks/vault_delete.yaml +++ b/ansible/roles/vault_utils/tasks/vault_delete.yaml @@ -1,27 +1,20 @@ --- - include_tasks: pre_check.yaml -# FIXME to use proper k8 ansible collection +# Note: We do not wait on purpose here as the pod will be recreated +# anyways and that would race. We are fine with having it gone once - name: Delete vault pod - ansible.builtin.shell: | - oc -n vault delete pod vault-0 - register: delete_pod - async: 7200 - poll: 0 - changed_when: true + kubernetes.core.k8s: + state: absent + api_version: v1 + kind: Pod + namespace: "{{ vault_ns }}" + name: "{{ vault_pod }}" - name: Delete vault pvc - ansible.builtin.shell: | - oc -n vault delete pvc data-vault-0 - register: delete_pvc - async: 7200 - poll: 0 - changed_when: true - -- name: Wait for tasks - ansible.builtin.wait_for: - timeout: 10 - -- name: Print out result from deletion - ansible.builtin.debug: - msg: "{{ delete_pod }} - {{ delete_pvc }}" + kubernetes.core.k8s: + state: absent + api_version: v1 + kind: PersistentVolumeClaim + namespace: "{{ vault_ns }}" + name: "{{ vault_pvc }}" From 68c07ae68d5271023cd4543fe1d147f27c620346 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 29 Jun 2022 18:40:28 +0200 Subject: [PATCH 0366/1263] Move local roles in front of the path --- ansible/ansible.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index 51a6c098..c8bec627 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -1,4 +1,4 @@ [defaults] display_skipped_hosts=False localhost_warning=False -roles_path=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:./roles +roles_path=./roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles From b13735290c6163589893d5d3acc48ac5e953170e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 30 Jun 2022 15:26:23 +0200 Subject: [PATCH 0367/1263] Improve validate-origin Currently we only run git-ls against the remote repo. While this is helpful it has two drawbacks: 1) It does not check that the local branch exists remotely This is a common error where an operator did the local changes in a local branch but forgot to push it remotely. The only way to catch this error is to logon to the main argo and see the error about remote SHA not being able to be resolved. 2) It can be very verbose due to having many remote branches and clutter the output a bit. Let's tweak this by actually checking that the local branch exists remotely and bailing out if it does not Tested as follows: - Local branch not pushed remotely make validate-origin Checking repo https://github.com/hybrid-cloud-patterns/common - branch ux-improvements ux-improvements not found in https://github.com/hybrid-cloud-patterns/common make: *** [Makefile:43: validate-origin] Error 1 - Local branch that also exists remotely: make validate-origin make -f common/Makefile validate-origin make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Checking repo https://github.com/mbaldessari/multicloud-gitops.git - branch no-values-url https://github.com/mbaldessari/multicloud-gitops.git - no-values-url exist make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 23c76744..6133cb8c 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,10 @@ kubeconform: ## run helm kubeconform @for t in $(CHARTS); do helm template $(TEST_OPTS) $(PATTERN_OPTS) $$t | kubeconform -strict $(KUBECONFORM_SKIP) -verbose -schema-location $(API_URL); if [ $$? != 0 ]; then exit 1; fi; done validate-origin: ## verify the git origin is available - git ls-remote $(TARGET_REPO) + @echo Checking repo $(TARGET_REPO) - branch $(TARGET_BRANCH) + @git ls-remote --exit-code --heads $(TARGET_REPO) $(TARGET_BRANCH) >/dev/null && \ + echo "$(TARGET_REPO) - $(TARGET_BRANCH) exists" || \ + (echo "$(TARGET_BRANCH) not found in $(TARGET_REPO)"; exit 1) deploy: validate-origin ## deploys the pattern helm install $(NAME) common/install/ $(HELM_OPTS) From 565e23a1559fda89766b921cb76198ac127f9bdc Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 1 Jul 2022 09:00:08 +0200 Subject: [PATCH 0368/1263] Update argo crd This adds a bunch of new parameters like: ignoreMissingValueFiles passCredentials skipCrds forceCommonAnnotations forceCommonLabels Note that if helm install fails with: "Error: INSTALLATION FAILED: unable to build kubernetes objects from release manifest: error validating \"\": error validating data: ValidationError(Application.spec.source.helm): unknown field \"ignoreMissingValueFiles\" in io.argoproj.v1alpha1.Application.spec.source.helm It's likely that the cluster has an old crd and it needs to be replaced with this one (delete the old one) --- install/crds/applications.argoproj.io.yaml | 152 ++++++++++++++++++++- 1 file changed, 151 insertions(+), 1 deletion(-) diff --git a/install/crds/applications.argoproj.io.yaml b/install/crds/applications.argoproj.io.yaml index 094bf055..c9866d38 100644 --- a/install/crds/applications.argoproj.io.yaml +++ b/install/crds/applications.argoproj.io.yaml @@ -1,3 +1,6 @@ +# oc get crd/applications.argoproj.io -o yaml > applications.argoproj.io.yaml +# Remove annotation, timestamps and other cluster-specific cruft +# Also remove any status leftovers at the end apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -237,6 +240,11 @@ spec: type: string type: object type: array + ignoreMissingValueFiles: + description: IgnoreMissingValueFiles prevents helm template + from failing when valueFiles do not exist locally by + not appending them to helm template --values + type: boolean parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest @@ -257,10 +265,18 @@ spec: type: string type: object type: array + passCredentials: + description: PassCredentials pass credentials to all domains + (Helm's --pass-credentials) + type: boolean releaseName: description: ReleaseName is the Helm release name to use. If omitted it will use the application name type: string + skipCrds: + description: SkipCrds skips custom resource definition + installation step (Helm's --skip-crds) + type: boolean valueFiles: description: ValuesFiles is a list of Helm value files to use when generating a template @@ -317,6 +333,15 @@ spec: description: CommonLabels is a list of additional labels to add to rendered manifests type: object + forceCommonAnnotations: + description: ForceCommonAnnotations specifies whether + to force applying common annotations to resources for + Kustomize apps + type: boolean + forceCommonLabels: + description: ForceCommonLabels specifies whether to force + applying common labels to resources for Kustomize apps + type: boolean images: description: Images is a list of Kustomize image override specifications @@ -446,18 +471,29 @@ spec: properties: group: type: string + jqPathExpressions: + items: + type: string + type: array jsonPointers: items: type: string type: array kind: type: string + managedFieldsManagers: + description: ManagedFieldsManagers is a list of trusted managers. + Fields mutated by those managers will take precedence over + the desired state defined in the SCM and won't be displayed + in diffs + items: + type: string + type: array name: type: string namespace: type: string required: - - jsonPointers - kind type: object type: array @@ -578,6 +614,11 @@ spec: type: string type: object type: array + ignoreMissingValueFiles: + description: IgnoreMissingValueFiles prevents helm template + from failing when valueFiles do not exist locally by not + appending them to helm template --values + type: boolean parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon manifest generation @@ -597,10 +638,18 @@ spec: type: string type: object type: array + passCredentials: + description: PassCredentials pass credentials to all domains + (Helm's --pass-credentials) + type: boolean releaseName: description: ReleaseName is the Helm release name to use. If omitted it will use the application name type: string + skipCrds: + description: SkipCrds skips custom resource definition installation + step (Helm's --skip-crds) + type: boolean valueFiles: description: ValuesFiles is a list of Helm value files to use when generating a template @@ -656,6 +705,14 @@ spec: description: CommonLabels is a list of additional labels to add to rendered manifests type: object + forceCommonAnnotations: + description: ForceCommonAnnotations specifies whether to force + applying common annotations to resources for Kustomize apps + type: boolean + forceCommonLabels: + description: ForceCommonLabels specifies whether to force + applying common labels to resources for Kustomize apps + type: boolean images: description: Images is a list of Kustomize image override specifications @@ -934,6 +991,11 @@ spec: type: string type: object type: array + ignoreMissingValueFiles: + description: IgnoreMissingValueFiles prevents helm template + from failing when valueFiles do not exist locally + by not appending them to helm template --values + type: boolean parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -955,10 +1017,18 @@ spec: type: string type: object type: array + passCredentials: + description: PassCredentials pass credentials to all + domains (Helm's --pass-credentials) + type: boolean releaseName: description: ReleaseName is the Helm release name to use. If omitted it will use the application name type: string + skipCrds: + description: SkipCrds skips custom resource definition + installation step (Helm's --skip-crds) + type: boolean valueFiles: description: ValuesFiles is a list of Helm value files to use when generating a template @@ -1015,6 +1085,16 @@ spec: description: CommonLabels is a list of additional labels to add to rendered manifests type: object + forceCommonAnnotations: + description: ForceCommonAnnotations specifies whether + to force applying common annotations to resources + for Kustomize apps + type: boolean + forceCommonLabels: + description: ForceCommonLabels specifies whether to + force applying common labels to resources for Kustomize + apps + type: boolean images: description: Images is a list of Kustomize image override specifications @@ -1303,6 +1383,12 @@ spec: type: string type: object type: array + ignoreMissingValueFiles: + description: IgnoreMissingValueFiles prevents + helm template from failing when valueFiles do + not exist locally by not appending them to helm + template --values + type: boolean parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command @@ -1326,11 +1412,19 @@ spec: type: string type: object type: array + passCredentials: + description: PassCredentials pass credentials + to all domains (Helm's --pass-credentials) + type: boolean releaseName: description: ReleaseName is the Helm release name to use. If omitted it will use the application name type: string + skipCrds: + description: SkipCrds skips custom resource definition + installation step (Helm's --skip-crds) + type: boolean valueFiles: description: ValuesFiles is a list of Helm value files to use when generating a template @@ -1388,6 +1482,16 @@ spec: description: CommonLabels is a list of additional labels to add to rendered manifests type: object + forceCommonAnnotations: + description: ForceCommonAnnotations specifies + whether to force applying common annotations + to resources for Kustomize apps + type: boolean + forceCommonLabels: + description: ForceCommonLabels specifies whether + to force applying common labels to resources + for Kustomize apps + type: boolean images: description: Images is a list of Kustomize image override specifications @@ -1654,6 +1758,11 @@ spec: type: string type: object type: array + ignoreMissingValueFiles: + description: IgnoreMissingValueFiles prevents helm + template from failing when valueFiles do not exist + locally by not appending them to helm template --values + type: boolean parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -1676,10 +1785,18 @@ spec: type: string type: object type: array + passCredentials: + description: PassCredentials pass credentials to all + domains (Helm's --pass-credentials) + type: boolean releaseName: description: ReleaseName is the Helm release name to use. If omitted it will use the application name type: string + skipCrds: + description: SkipCrds skips custom resource definition + installation step (Helm's --skip-crds) + type: boolean valueFiles: description: ValuesFiles is a list of Helm value files to use when generating a template @@ -1736,6 +1853,16 @@ spec: description: CommonLabels is a list of additional labels to add to rendered manifests type: object + forceCommonAnnotations: + description: ForceCommonAnnotations specifies whether + to force applying common annotations to resources + for Kustomize apps + type: boolean + forceCommonLabels: + description: ForceCommonLabels specifies whether to + force applying common labels to resources for Kustomize + apps + type: boolean images: description: Images is a list of Kustomize image override specifications @@ -1991,6 +2118,11 @@ spec: type: string type: object type: array + ignoreMissingValueFiles: + description: IgnoreMissingValueFiles prevents helm + template from failing when valueFiles do not exist + locally by not appending them to helm template --values + type: boolean parameters: description: Parameters is a list of Helm parameters which are passed to the helm template command upon @@ -2013,10 +2145,18 @@ spec: type: string type: object type: array + passCredentials: + description: PassCredentials pass credentials to all + domains (Helm's --pass-credentials) + type: boolean releaseName: description: ReleaseName is the Helm release name to use. If omitted it will use the application name type: string + skipCrds: + description: SkipCrds skips custom resource definition + installation step (Helm's --skip-crds) + type: boolean valueFiles: description: ValuesFiles is a list of Helm value files to use when generating a template @@ -2073,6 +2213,16 @@ spec: description: CommonLabels is a list of additional labels to add to rendered manifests type: object + forceCommonAnnotations: + description: ForceCommonAnnotations specifies whether + to force applying common annotations to resources + for Kustomize apps + type: boolean + forceCommonLabels: + description: ForceCommonLabels specifies whether to + force applying common labels to resources for Kustomize + apps + type: boolean images: description: Images is a list of Kustomize image override specifications From f02ab97960aae78aa53a55149e180062a3ff92d4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 1 Jul 2022 16:10:39 +0200 Subject: [PATCH 0369/1263] In push_secrets wait more if the vault is unsealed If a user chooses to unseal the vault from inside the cluster (i.e. without make vault-init) make load-secrets will probably fail because the cronjob to unseal the vault will not have run by the time we try to inject the secrets into the vault. Since ansible has no mechanism to retry/until over an include_task, we just add a vault_status exec call that loops a few times until sealed turns false. Tested this with unsealing done from within the cluster via a cronjob and via make vault-init. --- .../roles/vault_utils/tasks/push_secrets.yaml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index aa0806e6..8146157d 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -2,10 +2,19 @@ - include_tasks: pre_check.yaml - include_tasks: vault_status.yaml -- name: Fail if the vault is sealed - ansible.builtin.fail: - msg: "The vault is still sealed, cannot continue" - failed_when: vault_status['sealed'] | bool +# Unfortunately we cannot loop vault_status and just check if the vault is unsealed +# https://github.com/ansible/proposals/issues/136 +# So here we keep running the 'vault status' command until sealed is set to false +- name: If the vault is still sealed we need to retry + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault status -format=json + register: vault_status_json + until: "'stdout' in vault_status_json and (not (vault_status_json.stdout | from_json)['sealed'] | bool)" + retries: 20 + delay: 45 + failed_when: "'stdout_lines' not in vault_status_json" - name: Check for existence of "{{ values_secret }}" ansible.builtin.stat: From 91ae3ea162ad6d9a972981a2eb1534befb223129 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 1 Jul 2022 11:11:04 +0200 Subject: [PATCH 0370/1263] Remove spurious double empty-line --- ansible/roles/vault_utils/tasks/vault_secrets_init.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 336746c3..4bc1edb7 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -46,7 +46,6 @@ ansible.builtin.set_fact: secrets: "{{ serviceaccount.resources[0].secrets }}" - - name: Filter secrets of "{{ external_secrets_ns }}/{{ external_secrets_sa }}" ansible.builtin.set_fact: secret: "{{ item.name }}" From 0dd48baee31df3ee2dd0e8252bd8d58f3a4986a1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 1 Jul 2022 11:11:19 +0200 Subject: [PATCH 0371/1263] WIP Add job to unseal the vault from the cluster This commit adds support for a separate unsealJob CronJob template which is enabled when when a user sets clusterGroup.unsealVaultInsideCluster to true and the template is applied to the hub. This makes it so that the cluster is unsealed directly via cronjob and the vault unseal keys are stored inside a secret in OCP. Note that this is provided for convenience only and it is not recommended to store vault's unseal keys and root login token inside a kubernetes cluster. Two different design choices were explored and tried: A) Via some global switch + an additional unseal template (which is what this review is all about) If "clusterGroup.unsealVaultInsideCluster" is true we render an additional template in common/clustergroup/templates/imperative/unsealjob.yaml which then kicks off the playbook that unseals the vault and stores the unseal keys inside some secret in ocp (imperative/vaultkeys by default) B) Via a defined job (disabled by default) like the following inside values-hub.yaml: - name: vault-unseal playbook: common/ansible/playbooks/vault/vault.yaml tags: "vault_init,vault_unseal,vault_secrets_init" extravars: - '{"file_unseal": false}' disabled: true In both cases, we need to make sure that 'make vault-init' does not get run whenever the "unseal the vault inside the cluster" is enabled. Otherwise we get two things doing the unsealing in parallel and the second one (which will almost always be the cronjob) will fail forever. While choice (B) offered some advantages (less duplication, more explicit), it made the "detect if unsealVaultInsideCluster inside the Makefile" rather hard and quite error prone. Mainly for this reason I ended up with with (A). Tested with clusterGroup.unsealVaultInsideCluster both set to true and to false and got a correctly configured and set vault in both. --- .../templates/imperative/unsealjob.yaml | 89 +++++++++++++++++++ clustergroup/values.yaml | 7 ++ tests/clustergroup-normal.expected.yaml | 2 + 3 files changed, 98 insertions(+) create mode 100644 clustergroup/templates/imperative/unsealjob.yaml diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml new file mode 100644 index 00000000..018fc7e5 --- /dev/null +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -0,0 +1,89 @@ +{{/* Only define this if the values.unsealVaultInsideCluster is set to tre and we're on the cluster */}} +{{- if and $.Values.clusterGroup.unsealVaultInsideCluster $.Values.clusterGroup.isHubCluster }} +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: unsealvault-cronjob + namespace: {{ $.Values.clusterGroup.imperative.namespace}} +spec: + schedule: {{ $.Values.clusterGroup.imperative.unsealVaultInsideClusterSchedule | quote }} + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: {{ $.Values.clusterGroup.imperative.activeDeadlineSeconds }} + template: + metadata: + name: unsealvault-job + spec: + serviceAccountName: {{ $.Values.clusterGroup.imperative.serviceAccountName }} + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: {{ $.Values.clusterGroup.imperative.image }} + imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- {{ $.Values.global.repoURL }} /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" + - name: unseal-playbook + image: {{ $.Values.clusterGroup.imperative.image }} + imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - {{ .timeout | default "600" | quote }} + - ansible-playbook + {{- if $.Values.clusterGroup.imperative.verbosity }} + - {{ $.Values.clusterGroup.imperative.verbosity }} + {{- end }} + - -e + - "@/values/values.yaml" + - -e + - '{"file_unseal": false}' + - -t + - 'vault_init,vault_unseal,vault_secrets_init' + - "common/ansible/playbooks/vault/vault.yaml" + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: unsealvault-job-done + image: {{ $.Values.clusterGroup.imperative.image }} + imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} + command: + - 'sh' + - '-c' + - echo + - 'Unseal job' + - '\n' + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }} + restartPolicy: Never +{{ end }} diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 1151505c..8e22a837 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -10,6 +10,9 @@ global: clusterGroup: name: example isHubCluster: true + # Note: setting this to true stores the vault unseal keys inside a cluster secret and + # is fundamentally insecure + unsealVaultInsideCluster: false imperative: jobs: [] @@ -25,6 +28,10 @@ clusterGroup: activeDeadlineSeconds: 3600 # By default we run this every 10minutes schedule: "*/10 * * * *" + # Schedule used to trigger the vault unsealing (if explicitely enabled) + # Set to run every 9 minutes in order for load-secrets to succeed within + # a reasonable amount of time (it waits up to 15 mins) + unsealVaultInsideClusterSchedule: "*/9 * * * *" # Increase ansible verbosity with '-v' or '-vv..' verbosity: "" serviceAccountCreate: true diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 0dd6195a..2647da52 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -90,6 +90,7 @@ data: schedule: '*/10 * * * *' serviceAccountCreate: true serviceAccountName: imperative-sa + unsealVaultInsideClusterSchedule: '*/9 * * * *' valuesConfigMap: helm-values-configmap verbosity: "" isHubCluster: true @@ -125,6 +126,7 @@ data: pipelines: csv: redhat-openshift-pipelines.v1.5.2 name: openshift-pipelines-operator-rh + unsealVaultInsideCluster: false global: git: account: hybrid-cloud-patterns From a4997a659367700dbf61d2f643546837d11a0926 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 2 Jul 2022 12:10:25 +0200 Subject: [PATCH 0372/1263] Add ignoreMissingValueFiles: true on helm charts This will allow us to always add values files that do not exist, like per-cluster overrides and so on. Tested on a multicloud-gitops deployment successfully --- acm/templates/policies/application-policies.yaml | 1 + clustergroup/templates/applications.yaml | 1 + install/templates/argocd/application.yaml | 1 + tests/acm-normal.expected.yaml | 1 + tests/clustergroup-normal.expected.yaml | 2 ++ tests/install-naked.expected.yaml | 1 + tests/install-normal.expected.yaml | 1 + 7 files changed, 8 insertions(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index b1c4c842..0186c46c 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -39,6 +39,7 @@ spec: targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} path: {{ default "common/clustergroup" .path }} helm: + ignoreMissingValueFiles: true valueFiles: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ .name }}.yaml" diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index aefe68d8..1c12cb14 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -22,6 +22,7 @@ spec: plugin: {{ .plugin | toPrettyJson }} {{- else if not .kustomize }} helm: + ignoreMissingValueFiles: true valueFiles: - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Values.clusterGroup.name }}.yaml" diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index a462ddde..d8333a7b 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -15,6 +15,7 @@ spec: targetRevision: {{ .Values.main.git.revision }} path: common/clustergroup helm: + ignoreMissingValueFiles: true valueFiles: - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-global.yaml" - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-{{ .Values.main.clusterGroupName }}.yaml" diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 79f16634..28cf5fba 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -119,6 +119,7 @@ spec: targetRevision: main path: common/clustergroup helm: + ignoreMissingValueFiles: true valueFiles: - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - "https://github.com/pattern-clone/mypattern/raw/main/values-edge.yaml" diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 0dd6195a..215606d2 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -378,6 +378,7 @@ spec: targetRevision: path: common/acm helm: + ignoreMissingValueFiles: true valueFiles: - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" @@ -426,6 +427,7 @@ spec: targetRevision: path: charts/datacenter/pipelines helm: + ignoreMissingValueFiles: true valueFiles: - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" diff --git a/tests/install-naked.expected.yaml b/tests/install-naked.expected.yaml index 4ed9b445..e080ec30 100644 --- a/tests/install-naked.expected.yaml +++ b/tests/install-naked.expected.yaml @@ -23,6 +23,7 @@ spec: targetRevision: main path: common/clustergroup helm: + ignoreMissingValueFiles: true valueFiles: - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - "https://github.com/pattern-clone/mypattern/raw/main/values-default.yaml" diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yaml index 42c65416..09a4dfbe 100644 --- a/tests/install-normal.expected.yaml +++ b/tests/install-normal.expected.yaml @@ -23,6 +23,7 @@ spec: targetRevision: main path: common/clustergroup helm: + ignoreMissingValueFiles: true valueFiles: - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" From a1f7d09d9c0cb25a360cdb85257f55dab8fdbfc8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 4 Jul 2022 12:49:00 +0200 Subject: [PATCH 0373/1263] Add some templates to reduce code duplication We start with two templates: - git-init container which is identical across job and unsealjob - volumemounts --- .../templates/imperative/_helpers.tpl | 25 ++++++++++++++++++ clustergroup/templates/imperative/job.yaml | 26 +++---------------- .../templates/imperative/unsealjob.yaml | 26 +++---------------- 3 files changed, 31 insertions(+), 46 deletions(-) create mode 100644 clustergroup/templates/imperative/_helpers.tpl diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl new file mode 100644 index 00000000..93878243 --- /dev/null +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -0,0 +1,25 @@ +{{/* git-init InitContainer */}} +{{- define "imperative.initcontainers.gitinit" }} +- name: git-init + image: {{ $.Values.clusterGroup.imperative.image }} + imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- {{ $.Values.global.repoURL }} /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" +{{- end }} + +{{/* volume-mounts for all containers */}} +{{- define "imperative.volumemounts" }} +- name: git + mountPath: "/git" +- name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml +{{- end }} diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index 86881c13..3697b60c 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -21,19 +21,7 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - - name: git-init - image: {{ $.Values.clusterGroup.imperative.image }} - imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} - env: - - name: HOME - value: /git/home - command: - - 'sh' - - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- {{ $.Values.global.repoURL }} /git/repo;chmod 0770 /git/{repo,home}" - volumeMounts: - - name: git - mountPath: "/git" + {{- include "imperative.initcontainers.gitinit" . | indent 12 -}} {{- range $.Values.clusterGroup.imperative.jobs }} {{- if ne (.disabled | default "false" | toString | lower ) "true" }} - name: {{ .name }} @@ -64,11 +52,7 @@ spec: {{- end }} - {{ .playbook }} volumeMounts: - - name: git - mountPath: "/git" - - name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml + {{- include "imperative.volumemounts" . | indent 16 -}} {{- end }} {{- end }} containers: @@ -82,11 +66,7 @@ spec: - {{ range $.Values.clusterGroup.imperative.jobs }}{{ .name }}{{ end }} - '\n' volumeMounts: - - name: git - mountPath: "/git" - - name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml + {{- include "imperative.volumemounts" . | indent 14 }} volumes: - name: git emptyDir: {} diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index 018fc7e5..a896a6ea 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -21,19 +21,7 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - - name: git-init - image: {{ $.Values.clusterGroup.imperative.image }} - imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} - env: - - name: HOME - value: /git/home - command: - - 'sh' - - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- {{ $.Values.global.repoURL }} /git/repo;chmod 0770 /git/{repo,home}" - volumeMounts: - - name: git - mountPath: "/git" + {{- include "imperative.initcontainers.gitinit" . | indent 12 -}} - name: unseal-playbook image: {{ $.Values.clusterGroup.imperative.image }} imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} @@ -58,11 +46,7 @@ spec: - 'vault_init,vault_unseal,vault_secrets_init' - "common/ansible/playbooks/vault/vault.yaml" volumeMounts: - - name: git - mountPath: "/git" - - name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml + {{- include "imperative.volumemounts" . | indent 16 -}} containers: - name: unsealvault-job-done image: {{ $.Values.clusterGroup.imperative.image }} @@ -74,11 +58,7 @@ spec: - 'Unseal job' - '\n' volumeMounts: - - name: git - mountPath: "/git" - - name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml + {{- include "imperative.volumemounts" . | indent 14 -}} volumes: - name: git emptyDir: {} From b1cd3097275386c61da335cf8bbffb078a3f9e71 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 4 Jul 2022 13:15:48 +0200 Subject: [PATCH 0374/1263] Add done container as a named template --- clustergroup/templates/imperative/_helpers.tpl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl index 93878243..387fb82c 100644 --- a/clustergroup/templates/imperative/_helpers.tpl +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -15,6 +15,19 @@ mountPath: "/git" {{- end }} +{{/* Final done container */}} +{{- define "imperative.containers.done" }} +- name: done + image: {{ $.Values.clusterGroup.imperative.image }} + imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} + command: + - 'sh' + - '-c' + - echo + - 'done' + - '\n' +{{- end }} + {{/* volume-mounts for all containers */}} {{- define "imperative.volumemounts" }} - name: git From 6d1e48f29e6eda76ddcf379d3267dd00fdfaf486 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 4 Jul 2022 21:07:54 +0200 Subject: [PATCH 0375/1263] Switch do a common 'done' container And make it a named template. Also fixup some spurious -}} --- clustergroup/templates/imperative/_helpers.tpl | 12 ++++++------ clustergroup/templates/imperative/job.yaml | 16 +++------------- clustergroup/templates/imperative/unsealjob.yaml | 16 +++------------- 3 files changed, 12 insertions(+), 32 deletions(-) diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl index 387fb82c..8a946b3c 100644 --- a/clustergroup/templates/imperative/_helpers.tpl +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -17,15 +17,15 @@ {{/* Final done container */}} {{- define "imperative.containers.done" }} -- name: done +- name: "done" image: {{ $.Values.clusterGroup.imperative.image }} imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} command: - - 'sh' - - '-c' - - echo - - 'done' - - '\n' + - 'sh' + - '-c' + - 'echo' + - 'done' + - '\n' {{- end }} {{/* volume-mounts for all containers */}} diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index 3697b60c..bdbfa596 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -21,7 +21,7 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - {{- include "imperative.initcontainers.gitinit" . | indent 12 -}} + {{- include "imperative.initcontainers.gitinit" . | indent 12 }} {{- range $.Values.clusterGroup.imperative.jobs }} {{- if ne (.disabled | default "false" | toString | lower ) "true" }} - name: {{ .name }} @@ -52,21 +52,11 @@ spec: {{- end }} - {{ .playbook }} volumeMounts: - {{- include "imperative.volumemounts" . | indent 16 -}} + {{- include "imperative.volumemounts" . | indent 16 }} {{- end }} {{- end }} containers: - - name: {{ $.Values.clusterGroup.imperative.jobName }}-done - image: {{ $.Values.clusterGroup.imperative.image }} - imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} - command: - - 'sh' - - '-c' - - echo - - {{ range $.Values.clusterGroup.imperative.jobs }}{{ .name }}{{ end }} - - '\n' - volumeMounts: - {{- include "imperative.volumemounts" . | indent 14 }} + {{- include "imperative.containers.done" . | indent 12 }} volumes: - name: git emptyDir: {} diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index a896a6ea..56d77770 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -21,7 +21,7 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - {{- include "imperative.initcontainers.gitinit" . | indent 12 -}} + {{- include "imperative.initcontainers.gitinit" . | indent 12 }} - name: unseal-playbook image: {{ $.Values.clusterGroup.imperative.image }} imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} @@ -46,19 +46,9 @@ spec: - 'vault_init,vault_unseal,vault_secrets_init' - "common/ansible/playbooks/vault/vault.yaml" volumeMounts: - {{- include "imperative.volumemounts" . | indent 16 -}} + {{- include "imperative.volumemounts" . | indent 16 }} containers: - - name: unsealvault-job-done - image: {{ $.Values.clusterGroup.imperative.image }} - imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} - command: - - 'sh' - - '-c' - - echo - - 'Unseal job' - - '\n' - volumeMounts: - {{- include "imperative.volumemounts" . | indent 14 -}} + {{- include "imperative.containers.done" . | indent 12 }} volumes: - name: git emptyDir: {} From 4a5f9435baa09af9efe713c0e2b49053ecf37d4d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 4 Jul 2022 21:12:57 +0200 Subject: [PATCH 0376/1263] Explicitely enable clusterGroup.unsealVaultInsideCluster to test unseal templates --- Makefile | 2 +- tests/clustergroup-normal.expected.yaml | 114 +++++++++++++++++++----- 2 files changed, 94 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 6133cb8c..2a3b1b39 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spe # --set values always take precedence over the contents of -f HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) -TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" +TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" --set clusterGroup.unsealVaultInsideCluster=true PATTERN_OPTS=-f common/examples/values-example.yaml diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 2647da52..1a67af06 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -126,7 +126,7 @@ data: pipelines: csv: redhat-openshift-pipelines.v1.5.2 name: openshift-pipelines-operator-rh - unsealVaultInsideCluster: false + unsealVaultInsideCluster: true global: git: account: hybrid-cloud-patterns @@ -280,7 +280,7 @@ spec: serviceAccountName: imperative-sa initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions - # reason for that is ansible refuses to create temporary folders in there + # reason for that is ansible refuses to create temporary folders in there - name: git-init image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest imagePullPolicy: Always @@ -310,28 +310,100 @@ spec: - -e - "@/values/values.yaml" - ansible/test.yml + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: "done" + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + command: + - 'sh' + - '-c' + - 'echo' + - 'done' + - '\n' + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap + restartPolicy: Never +--- +# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: unsealvault-cronjob + namespace: imperative +spec: + schedule: "*/9 * * * *" + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: 3600 + template: + metadata: + name: unsealvault-job + spec: + serviceAccountName: imperative-sa + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" volumeMounts: - name: git mountPath: "/git" - - name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml - containers: - - name: imperative-job-done - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - command: - - 'sh' - - '-c' - - echo - - test - - '\n' - volumeMounts: - - name: git - mountPath: "/git" - - name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml + - name: unseal-playbook + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - "600" + - ansible-playbook + - -e + - "@/values/values.yaml" + - -e + - '{"file_unseal": false}' + - -t + - 'vault_init,vault_unseal,vault_secrets_init' + - "common/ansible/playbooks/vault/vault.yaml" + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: "done" + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + command: + - 'sh' + - '-c' + - 'echo' + - 'done' + - '\n' volumes: - name: git emptyDir: {} From b1740bd0c9c0a01593fcffb2705809c674123cdc Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Jul 2022 10:55:36 +0200 Subject: [PATCH 0377/1263] Rename unsealVaultInsideCluster to insecureUnsealVaultInsideCluster While it's a bit of a mouthful its a little more obvious what it is doing and its implications. --- Makefile | 2 +- clustergroup/templates/imperative/unsealjob.yaml | 6 +++--- clustergroup/values.yaml | 6 +++--- tests/clustergroup-normal.expected.yaml | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 2a3b1b39..a406afe5 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spe # --set values always take precedence over the contents of -f HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) -TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" --set clusterGroup.unsealVaultInsideCluster=true +TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" --set clusterGroup.insecureUnsealVaultInsideCluster=true PATTERN_OPTS=-f common/examples/values-example.yaml diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index 56d77770..0fae9071 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -1,5 +1,5 @@ -{{/* Only define this if the values.unsealVaultInsideCluster is set to tre and we're on the cluster */}} -{{- if and $.Values.clusterGroup.unsealVaultInsideCluster $.Values.clusterGroup.isHubCluster }} +{{/* Only define this if the values.insecureUnsealVaultInsideCluster is set to tre and we're on the cluster */}} +{{- if and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster }} --- apiVersion: batch/v1 kind: CronJob @@ -7,7 +7,7 @@ metadata: name: unsealvault-cronjob namespace: {{ $.Values.clusterGroup.imperative.namespace}} spec: - schedule: {{ $.Values.clusterGroup.imperative.unsealVaultInsideClusterSchedule | quote }} + schedule: {{ $.Values.clusterGroup.imperative.insecureUnsealVaultInsideClusterSchedule | quote }} # if previous Job is still running, skip execution of a new Job concurrencyPolicy: Forbid jobTemplate: diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 8e22a837..6bd739d0 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -12,7 +12,7 @@ clusterGroup: isHubCluster: true # Note: setting this to true stores the vault unseal keys inside a cluster secret and # is fundamentally insecure - unsealVaultInsideCluster: false + insecureUnsealVaultInsideCluster: false imperative: jobs: [] @@ -31,7 +31,7 @@ clusterGroup: # Schedule used to trigger the vault unsealing (if explicitely enabled) # Set to run every 9 minutes in order for load-secrets to succeed within # a reasonable amount of time (it waits up to 15 mins) - unsealVaultInsideClusterSchedule: "*/9 * * * *" + insecureUnsealVaultInsideClusterSchedule: "*/9 * * * *" # Increase ansible verbosity with '-v' or '-vv..' verbosity: "" serviceAccountCreate: true @@ -71,7 +71,7 @@ clusterGroup: # # projects: # - datacenter -# +# # applications: # - name: acm # namespace: default diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 1a67af06..e4160b47 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -80,6 +80,7 @@ data: cronJobName: imperative-cronjob image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest imagePullPolicy: Always + insecureUnsealVaultInsideClusterSchedule: '*/9 * * * *' jobName: imperative-job jobs: - name: test @@ -90,9 +91,9 @@ data: schedule: '*/10 * * * *' serviceAccountCreate: true serviceAccountName: imperative-sa - unsealVaultInsideClusterSchedule: '*/9 * * * *' valuesConfigMap: helm-values-configmap verbosity: "" + insecureUnsealVaultInsideCluster: true isHubCluster: true managedClusterGroups: - clusterSelector: @@ -126,7 +127,6 @@ data: pipelines: csv: redhat-openshift-pipelines.v1.5.2 name: openshift-pipelines-operator-rh - unsealVaultInsideCluster: true global: git: account: hybrid-cloud-patterns From 3d468e9c77c013e34e1f2475dbd7addf7f2e7775 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Jul 2022 13:31:35 +0200 Subject: [PATCH 0378/1263] This step is not really needed when running make vault-init + load-secrets as everything is sequential It is needed when the vault is unsealed/configured inside the cluster and load-secrets gets run *while* the cronjob configures the vault. I.e. it might be half configured and return errors below: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit failed: [localhost] (item=imageregistry) => {"ansible_loop_var": "item", "changed": true, "item": {"key": "imageregistry", "value": "vault kv put 'secret/hub/imageregistry' username='foo'"}, "rc": 2, "return_code": 2, "stderr": "Error making API request.\n\nURL: GET http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/hub/imageregistry\nCode: 403. Errors:\n\n* permission denied\n", "stderr_lines": ["Error making API request.", "", "URL: GET http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/hub/imageregistry", "Code: 403. Errors:", "", "* permission denied"], "stdout": "", "stdout_lines": []} ··failed: [localhost] (item=git) => {"ansible_loop_var": "item", "changed": true, "item": {"key": "git", "value": "vault kv put 'secret/hub/git' 'foo=bar'"}, "rc": 2, "return_code": 2, "stderr": "Error making API request.\n\nURL: GET http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/hub/git\nCode: 403. Errors:\n\n* preflight capability check returned 403, please ensure client's policies grant access to path \"secret/hub/git/\"\n", "stderr_lines": ["Error making API request.", "", "URL: GET http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/hub/git", "Code: 403. Errors:", "", "* preflight capability check returned 403, please ensure client's policies grant access to path \"secret/hub/git/\""], "stdout": "", "stdout_lines": []} --- .../roles/vault_utils/tasks/push_secrets.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 8146157d..9984dd89 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -130,6 +130,23 @@ msg: "{{ vault_cmds }}" when: debug | default(False) | bool +# This step is not really needed when running make vault-init + load-secrets as +# everything is sequential +# It is needed when the vault is unsealed/configured inside the cluster and load-secrets +# gets run *while* the cronjob configures the vault. I.e. it might be half configured and return +# errors +- name: Make sure that the vault auth policy exists + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: + sh -c "vault list auth/{{ vault_hub }}/role | grep '{{ vault_hub }}-role'" + register: vault_role_cmd + until: vault_role_cmd.rc == 0 + retries: 20 + delay: 45 + changed_when: false + - name: Add the actual secrets to the vault kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" From e81efab8755d9f462c091edc8ec7f1085150ce78 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Jul 2022 15:00:16 +0200 Subject: [PATCH 0379/1263] Add finalizers to all applications As suggested by @beekhof via https://github.com/hybrid-cloud-patterns/industrial-edge/pull/123/ we add foreground finalizers for all of our argo applications. While we're at it we replace acm's application policy finalizer from "argoproj.io/finalizer" to the same foreground one we use in the other applications: "resources-finalizer.argocd.argoproj.io/foreground" This should improve our uninstall story in general. --- acm/templates/policies/application-policies.yaml | 2 +- clustergroup/templates/applications.yaml | 2 ++ install/templates/argocd/application.yaml | 2 ++ tests/acm-normal.expected.yaml | 2 +- tests/clustergroup-normal.expected.yaml | 4 ++++ tests/install-naked.expected.yaml | 2 ++ tests/install-normal.expected.yaml | 2 ++ 7 files changed, 14 insertions(+), 2 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 0186c46c..a2392dca 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -31,7 +31,7 @@ spec: name: {{ $.Values.global.pattern }}-{{ .name }} namespace: openshift-gitops finalizers: - - argoproj.io/finalizer + - resources-finalizer.argocd.argoproj.io/foreground spec: project: default source: diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 1c12cb14..398f96c4 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -5,6 +5,8 @@ kind: Application metadata: name: {{ .name }} namespace: {{ $namespace }} + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground spec: destination: name: in-cluster diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index d8333a7b..19b683b4 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -5,6 +5,8 @@ kind: Application metadata: name: {{ .Release.Name }}-{{ .Values.main.clusterGroupName }} namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground spec: destination: name: in-cluster diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 28cf5fba..76136ee5 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -111,7 +111,7 @@ spec: name: mypattern-edge namespace: openshift-gitops finalizers: - - argoproj.io/finalizer + - resources-finalizer.argocd.argoproj.io/foreground spec: project: default source: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 215606d2..c8839408 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -368,6 +368,8 @@ kind: Application metadata: name: acm namespace: mypattern-example + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground spec: destination: name: in-cluster @@ -417,6 +419,8 @@ kind: Application metadata: name: pipelines namespace: mypattern-example + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground spec: destination: name: in-cluster diff --git a/tests/install-naked.expected.yaml b/tests/install-naked.expected.yaml index e080ec30..7694a8d2 100644 --- a/tests/install-naked.expected.yaml +++ b/tests/install-naked.expected.yaml @@ -13,6 +13,8 @@ kind: Application metadata: name: install-default namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground spec: destination: name: in-cluster diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yaml index 09a4dfbe..e2401612 100644 --- a/tests/install-normal.expected.yaml +++ b/tests/install-normal.expected.yaml @@ -13,6 +13,8 @@ kind: Application metadata: name: install-example namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground spec: destination: name: in-cluster From 4180f5fc16ad2e4e6ee719a9f3e71af27838036e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Jul 2022 21:16:00 +0200 Subject: [PATCH 0380/1263] Move to import_tasks for vault_init/main.yml The problem with the current approach, which is working just fine, is the following: - When from a pattern role like 'bootstrap' we import_role value_utils and pass the needed tags, those are completely ignored. Let's switch to import_tasks, which is more efficient as it is more static and it allows us to make things work correctly. Tested with a full MCG deploy. Also verified that 'make load-secrets' works separately and only loads secret (i.e. tags keep on working). Also checked that './common/scripts/vault-utils.sh vault_delete' works correctly Also tested by running 'ansible-playbook ansible/site.yml' in MCG and observed that now the vault tasks are correctly run. --- ansible/roles/vault_utils/tasks/main.yml | 35 +++++++----------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/main.yml b/ansible/roles/vault_utils/tasks/main.yml index a8ab61b9..1da3e440 100644 --- a/ansible/roles/vault_utils/tasks/main.yml +++ b/ansible/roles/vault_utils/tasks/main.yml @@ -1,35 +1,20 @@ --- - name: Delete vault - include_tasks: - file: vault_delete.yaml - apply: - tags: [never, vault_delete] - tags: [never, vault_delete] + import_tasks: vault_delete.yaml + tags: vault_delete - name: Run vault init tasks - include_tasks: - file: vault_init.yaml - apply: - tags: [never, vault_init] - tags: [never, vault_init] + import_tasks: vault_init.yaml + tags: vault_init - name: Unseal vault - include_tasks: - file: vault_unseal.yaml - apply: - tags: [never, vault_unseal] - tags: [never, vault_unseal] + import_tasks: vault_unseal.yaml + tags: vault_unseal - name: Vault secrets init - include_tasks: - file: vault_secrets_init.yaml - apply: - tags: [never, vault_secrets_init] - tags: [never, vault_secrets_init] + import_tasks: vault_secrets_init.yaml + tags: vault_secrets_init - name: Load secrets - include_tasks: - file: push_secrets.yaml - apply: - tags: [never, push_secrets] - tags: [never, push_secrets] + import_tasks: push_secrets.yaml + tags: push_secrets From 9ac995caa5d281ec23711a4b643829bc1c214cbb Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 5 Jul 2022 09:30:51 -0500 Subject: [PATCH 0381/1263] Rename old install machinery New install based on operator Move CRD to correct location Add ignoreMissingValueFiles: true on helm charts This will allow us to always add values files that do not exist, like per-cluster overrides and so on. Tested on a multicloud-gitops deployment successfully Provide upgrade target Update tests Add Pattern to skiplist for now --- Makefile | 10 +- install/Chart.yaml | 2 +- ...ops.hybrid-cloud-patterns.io_patterns.yaml | 157 ++++++++++++++++++ install/templates/pattern.yaml | 10 ++ install/templates/subscription.yaml | 13 ++ install/values.yaml | 20 --- {install => legacy-install}/.helmignore | 0 legacy-install/Chart.yaml | 6 + .../crds/applications.argoproj.io.yaml | 0 .../templates/argocd/application.yaml | 0 .../templates/argocd/namespace.yaml | 0 .../templates/argocd/subscription.yaml | 0 legacy-install/values.yaml | 26 +++ tests/install-naked.expected.yaml | 67 ++------ tests/install-normal.expected.yaml | 67 ++------ tests/legacy-install-naked.expected.yaml | 66 ++++++++ tests/legacy-install-normal.expected.yaml | 66 ++++++++ 17 files changed, 381 insertions(+), 129 deletions(-) create mode 100644 install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml create mode 100644 install/templates/pattern.yaml create mode 100644 install/templates/subscription.yaml rename {install => legacy-install}/.helmignore (100%) create mode 100644 legacy-install/Chart.yaml rename {install => legacy-install}/crds/applications.argoproj.io.yaml (100%) rename {install => legacy-install}/templates/argocd/application.yaml (100%) rename {install => legacy-install}/templates/argocd/namespace.yaml (100%) rename {install => legacy-install}/templates/argocd/subscription.yaml (100%) create mode 100644 legacy-install/values.yaml create mode 100644 tests/legacy-install-naked.expected.yaml create mode 100644 tests/legacy-install-normal.expected.yaml diff --git a/Makefile b/Makefile index a406afe5..810f2d31 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ helmlint: ## run helm lint @for t in $(CHARTS); do helm lint $(TEST_OPTS) $(PATTERN_OPTS) $$t; if [ $$? != 0 ]; then exit 1; fi; done API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.10/ -KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition' +KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition','Pattern' # We need to skip 'CustomResourceDefinition' as openapi2jsonschema seems to be unable to generate them ATM kubeconform: ## run helm kubeconform @for t in $(CHARTS); do helm template $(TEST_OPTS) $(PATTERN_OPTS) $$t | kubeconform -strict $(KUBECONFORM_SKIP) -verbose -schema-location $(API_URL); if [ $$? != 0 ]; then exit 1; fi; done @@ -47,9 +47,15 @@ validate-origin: ## verify the git origin is available deploy: validate-origin ## deploys the pattern helm install $(NAME) common/install/ $(HELM_OPTS) -upgrade: validate-origin ## runs helm upgrade +upgrade: validate-origin helm upgrade $(NAME) common/install/ $(HELM_OPTS) +legacy-install: validate-origin ## runs helm upgrade + helm install $(NAME) common/legacy-install/ $(HELM_OPTS) + +legacy-upgrade: validate-origin ## runs helm upgrade + helm upgrade $(NAME) common/legacy-install/ $(HELM_OPTS) + uninstall: ## runs helm uninstall helm uninstall $(NAME) diff --git a/install/Chart.yaml b/install/Chart.yaml index d1c80ab3..74adcf8f 100644 --- a/install/Chart.yaml +++ b/install/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -description: A Helm chart to build and deploy a Cloud Pattern +description: A Helm chart to build and deploy a Cloud Pattern via the patterns operator keywords: - pattern name: pattern-install diff --git a/install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml b/install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml new file mode 100644 index 00000000..1acd4ad2 --- /dev/null +++ b/install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml @@ -0,0 +1,157 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: patterns.gitops.hybrid-cloud-patterns.io +spec: + group: gitops.hybrid-cloud-patterns.io + names: + kind: Pattern + listKind: PatternList + plural: patterns + shortNames: + - patt + singular: pattern + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.lastStep + name: Step + priority: 1 + type: string + - jsonPath: .status.lastError + name: Error + priority: 2 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Pattern is the Schema for the patterns 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: PatternSpec defines the desired state of Pattern + properties: + clusterGroupName: + type: string + extraParameters: + description: '.Name is dot separated per the helm --set syntax, such + as: global.something.field' + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + extraValueFiles: + description: URLs to additional Helm parameter files + items: + type: string + type: array + gitOpsSpec: + properties: + manualApproval: + description: 'Require manual confirmation before installing and + upgrading operators. Default: False' + type: boolean + manualSync: + description: 'Require manual intervention before Argo will sync + new content. Default: False' + type: boolean + operatorCSV: + description: Specific version of openshift-gitops to deploy. Requires + UseCSV=True + type: string + operatorChannel: + description: 'Channel to deploy openshift-gitops from. Default: + stable' + type: string + operatorSource: + description: 'Source to deploy openshift-gitops from. Default: + redhat-operators' + type: string + useCSV: + description: 'Dangerous. Force a specific version to be installed. + Default: False' + type: boolean + type: object + gitSpec: + properties: + hostname: + description: Optional. FQDN of the git server if automatic parsing + from TargetRepo is broken + type: string + originRepo: + description: Unused + type: string + targetRepo: + description: Git repo containing the pattern to deploy. Must use + https/http + type: string + targetRevision: + description: 'Branch, tag, or commit to deploy. Does not support + short-sha''s. Default: main' + type: string + valuesDirectoryURL: + description: Optional. Alternate URL to obtain Helm values files + from instead of this pattern. + type: string + required: + - targetRepo + type: object + required: + - clusterGroupName + - gitSpec + type: object + status: + description: PatternStatus defines the observed state of Pattern + properties: + clusterDomain: + type: string + clusterID: + type: string + clusterName: + type: string + clusterPlatform: + type: string + lastError: + description: Last error encountered by the pattern + type: string + lastStep: + description: Last action related to the pattern + type: string + version: + description: Number of updates to the pattern + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/install/templates/pattern.yaml b/install/templates/pattern.yaml new file mode 100644 index 00000000..2f54860f --- /dev/null +++ b/install/templates/pattern.yaml @@ -0,0 +1,10 @@ +apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 +kind: Pattern +metadata: + name: {{ .Release.Name }} + namespace: openshift-operators +spec: + clusterGroupName: {{ .Values.main.clusterGroupName }} + gitSpec: + targetRepo: {{ .Values.main.git.repoURL }} + targetRevision: {{ .Values.main.git.revision }} diff --git a/install/templates/subscription.yaml b/install/templates/subscription.yaml new file mode 100644 index 00000000..381e185f --- /dev/null +++ b/install/templates/subscription.yaml @@ -0,0 +1,13 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: patterns-operator + namespace: openshift-operators + labels: + operators.coreos.com/patterns-operator.openshift-operators: "" +spec: + channel: fast + installPlanApproval: Automatic + name: patterns-operator + source: community-operators + sourceNamespace: openshift-marketplace diff --git a/install/values.yaml b/install/values.yaml index b0507225..0a80b80f 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -2,25 +2,5 @@ main: git: repoURL: https://github.com/pattern-clone/mypattern revision: main - #valuesDirectoryURL: https://github.com/hybrid-cloud-patterns/industrial-edge/raw/main/ - - options: - syncPolicy: Automatic - installPlanApproval: Automatic - useCSV: False - - gitops: - channel: stable - source: redhat-operators - csv: v1.3.0 clusterGroupName: default - -global: - imageregistry: - type: quay - - git: - hostname: github.com - # Account is the user or organization under which the pattern repo lives - account: hybrid-cloud-patterns diff --git a/install/.helmignore b/legacy-install/.helmignore similarity index 100% rename from install/.helmignore rename to legacy-install/.helmignore diff --git a/legacy-install/Chart.yaml b/legacy-install/Chart.yaml new file mode 100644 index 00000000..d1c80ab3 --- /dev/null +++ b/legacy-install/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +description: A Helm chart to build and deploy a Cloud Pattern +keywords: +- pattern +name: pattern-install +version: 0.0.1 diff --git a/install/crds/applications.argoproj.io.yaml b/legacy-install/crds/applications.argoproj.io.yaml similarity index 100% rename from install/crds/applications.argoproj.io.yaml rename to legacy-install/crds/applications.argoproj.io.yaml diff --git a/install/templates/argocd/application.yaml b/legacy-install/templates/argocd/application.yaml similarity index 100% rename from install/templates/argocd/application.yaml rename to legacy-install/templates/argocd/application.yaml diff --git a/install/templates/argocd/namespace.yaml b/legacy-install/templates/argocd/namespace.yaml similarity index 100% rename from install/templates/argocd/namespace.yaml rename to legacy-install/templates/argocd/namespace.yaml diff --git a/install/templates/argocd/subscription.yaml b/legacy-install/templates/argocd/subscription.yaml similarity index 100% rename from install/templates/argocd/subscription.yaml rename to legacy-install/templates/argocd/subscription.yaml diff --git a/legacy-install/values.yaml b/legacy-install/values.yaml new file mode 100644 index 00000000..b0507225 --- /dev/null +++ b/legacy-install/values.yaml @@ -0,0 +1,26 @@ +main: + git: + repoURL: https://github.com/pattern-clone/mypattern + revision: main + #valuesDirectoryURL: https://github.com/hybrid-cloud-patterns/industrial-edge/raw/main/ + + options: + syncPolicy: Automatic + installPlanApproval: Automatic + useCSV: False + + gitops: + channel: stable + source: redhat-operators + csv: v1.3.0 + + clusterGroupName: default + +global: + imageregistry: + type: quay + + git: + hostname: github.com + # Account is the user or organization under which the pattern repo lives + account: hybrid-cloud-patterns diff --git a/tests/install-naked.expected.yaml b/tests/install-naked.expected.yaml index 7694a8d2..d6e9a3b0 100644 --- a/tests/install-naked.expected.yaml +++ b/tests/install-naked.expected.yaml @@ -1,66 +1,27 @@ --- -# Source: pattern-install/templates/argocd/namespace.yaml -# Pre-create so we can create our argo app for keeping subscriptions in sync -# Do it here so that we don't try to sync it in the future -apiVersion: v1 -kind: Namespace +# Source: pattern-install/templates/pattern.yaml +apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 +kind: Pattern metadata: - name: openshift-gitops ---- -# Source: pattern-install/templates/argocd/application.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: install-default - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground + name: install + namespace: openshift-operators spec: - destination: - name: in-cluster - namespace: install-default - project: default - source: - repoURL: https://github.com/pattern-clone/mypattern + clusterGroupName: default + gitSpec: + targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - - "https://github.com/pattern-clone/mypattern/raw/main/values-default.yaml" - # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.valuesDirectoryURL - value: https://github.com/pattern-clone/mypattern/raw/main - - name: global.pattern - value: install - - name: global.hubClusterDomain - value: - syncPolicy: - automated: {} --- -# Source: pattern-install/templates/argocd/subscription.yaml +# Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: - name: openshift-gitops-operator + name: patterns-operator namespace: openshift-operators labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" + operators.coreos.com/patterns-operator.openshift-operators: "" spec: - channel: stable + channel: fast installPlanApproval: Automatic - name: openshift-gitops-operator - source: redhat-operators + name: patterns-operator + source: community-operators sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: install-default,openshift-gitops diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yaml index e2401612..d3b046d2 100644 --- a/tests/install-normal.expected.yaml +++ b/tests/install-normal.expected.yaml @@ -1,66 +1,27 @@ --- -# Source: pattern-install/templates/argocd/namespace.yaml -# Pre-create so we can create our argo app for keeping subscriptions in sync -# Do it here so that we don't try to sync it in the future -apiVersion: v1 -kind: Namespace +# Source: pattern-install/templates/pattern.yaml +apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 +kind: Pattern metadata: - name: openshift-gitops ---- -# Source: pattern-install/templates/argocd/application.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: install-example - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground + name: install + namespace: openshift-operators spec: - destination: - name: in-cluster - namespace: install-example - project: default - source: - repoURL: https://github.com/pattern-clone/mypattern + clusterGroupName: example + gitSpec: + targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" - # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.valuesDirectoryURL - value: https://github.com/pattern-clone/mypattern/raw/main - - name: global.pattern - value: install - - name: global.hubClusterDomain - value: hub.example.com - syncPolicy: - automated: {} --- -# Source: pattern-install/templates/argocd/subscription.yaml +# Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: - name: openshift-gitops-operator + name: patterns-operator namespace: openshift-operators labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" + operators.coreos.com/patterns-operator.openshift-operators: "" spec: - channel: stable + channel: fast installPlanApproval: Automatic - name: openshift-gitops-operator - source: redhat-operators + name: patterns-operator + source: community-operators sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: install-example,openshift-gitops diff --git a/tests/legacy-install-naked.expected.yaml b/tests/legacy-install-naked.expected.yaml new file mode 100644 index 00000000..ca964878 --- /dev/null +++ b/tests/legacy-install-naked.expected.yaml @@ -0,0 +1,66 @@ +--- +# Source: pattern-install/templates/argocd/namespace.yaml +# Pre-create so we can create our argo app for keeping subscriptions in sync +# Do it here so that we don't try to sync it in the future +apiVersion: v1 +kind: Namespace +metadata: + name: openshift-gitops +--- +# Source: pattern-install/templates/argocd/application.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: legacy-install-default + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: legacy-install-default + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" + - "https://github.com/pattern-clone/mypattern/raw/main/values-default.yaml" + # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.valuesDirectoryURL + value: https://github.com/pattern-clone/mypattern/raw/main + - name: global.pattern + value: legacy-install + - name: global.hubClusterDomain + value: + syncPolicy: + automated: {} +--- +# Source: pattern-install/templates/argocd/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: "" +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: legacy-install-default,openshift-gitops diff --git a/tests/legacy-install-normal.expected.yaml b/tests/legacy-install-normal.expected.yaml new file mode 100644 index 00000000..4c629315 --- /dev/null +++ b/tests/legacy-install-normal.expected.yaml @@ -0,0 +1,66 @@ +--- +# Source: pattern-install/templates/argocd/namespace.yaml +# Pre-create so we can create our argo app for keeping subscriptions in sync +# Do it here so that we don't try to sync it in the future +apiVersion: v1 +kind: Namespace +metadata: + name: openshift-gitops +--- +# Source: pattern-install/templates/argocd/application.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: legacy-install-example + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: legacy-install-example + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" + - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" + # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.valuesDirectoryURL + value: https://github.com/pattern-clone/mypattern/raw/main + - name: global.pattern + value: legacy-install + - name: global.hubClusterDomain + value: hub.example.com + syncPolicy: + automated: {} +--- +# Source: pattern-install/templates/argocd/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: "" +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: legacy-install-example,openshift-gitops From e30348ba5f9c5873af1514013c060869d62049df Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 6 Jul 2022 09:03:46 +0200 Subject: [PATCH 0382/1263] Remove Pattern from kubeconform skiplist We now added the Pattern API to ocp-schemas, so we can check for it --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 810f2d31..9a10e511 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ helmlint: ## run helm lint @for t in $(CHARTS); do helm lint $(TEST_OPTS) $(PATTERN_OPTS) $$t; if [ $$? != 0 ]; then exit 1; fi; done API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.10/ -KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition','Pattern' +KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition' # We need to skip 'CustomResourceDefinition' as openapi2jsonschema seems to be unable to generate them ATM kubeconform: ## run helm kubeconform @for t in $(CHARTS); do helm template $(TEST_OPTS) $(PATTERN_OPTS) $$t | kubeconform -strict $(KUBECONFORM_SKIP) -verbose -schema-location $(API_URL); if [ $$? != 0 ]; then exit 1; fi; done From 4b0094f410696d0c0ac5128da068d2dd67b9fd32 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 6 Jul 2022 15:23:53 +0200 Subject: [PATCH 0383/1263] Let's switch the legacy-install back to install Via 9ac995caa5d281ec23711a4b643829bc1c214cbb we switched the default deploy target to be operator-based. Let's make that the 'operator-deploy/operator-install' target instead for a little while. That way we can test and get some more soaking-time for this new installation method. We switch 'make deploy' to go back to the old basic 'helm install' command. The plan is still eventually to switch to the new operator-based install way, this just buys us some time and eases testing without breaking anyone's workflow. Tested with both 'make install' (current old-helm-based approach) and 'make operator-deploy' (new operator-based approach) --- Makefile | 8 +-- {legacy-install => install}/.helmignore | 0 install/Chart.yaml | 2 +- .../crds/applications.argoproj.io.yaml | 0 .../templates/argocd/application.yaml | 0 .../templates/argocd/namespace.yaml | 0 .../templates/argocd/subscription.yaml | 0 install/values.yaml | 20 ++++++ legacy-install/Chart.yaml | 6 -- legacy-install/values.yaml | 26 ------- operator-install/Chart.yaml | 6 ++ ...ops.hybrid-cloud-patterns.io_patterns.yaml | 0 .../templates/pattern.yaml | 0 .../templates/subscription.yaml | 0 operator-install/values.yaml | 6 ++ tests/install-naked.expected.yaml | 67 +++++++++++++++---- tests/install-normal.expected.yaml | 67 +++++++++++++++---- tests/operator-install-naked.expected.yaml | 27 ++++++++ tests/operator-install-normal.expected.yaml | 27 ++++++++ 19 files changed, 197 insertions(+), 65 deletions(-) rename {legacy-install => install}/.helmignore (100%) rename {legacy-install => install}/crds/applications.argoproj.io.yaml (100%) rename {legacy-install => install}/templates/argocd/application.yaml (100%) rename {legacy-install => install}/templates/argocd/namespace.yaml (100%) rename {legacy-install => install}/templates/argocd/subscription.yaml (100%) delete mode 100644 legacy-install/Chart.yaml delete mode 100644 legacy-install/values.yaml create mode 100644 operator-install/Chart.yaml rename {install => operator-install}/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml (100%) rename {install => operator-install}/templates/pattern.yaml (100%) rename {install => operator-install}/templates/subscription.yaml (100%) create mode 100644 operator-install/values.yaml create mode 100644 tests/operator-install-naked.expected.yaml create mode 100644 tests/operator-install-normal.expected.yaml diff --git a/Makefile b/Makefile index 9a10e511..9785356e 100644 --- a/Makefile +++ b/Makefile @@ -50,11 +50,11 @@ deploy: validate-origin ## deploys the pattern upgrade: validate-origin helm upgrade $(NAME) common/install/ $(HELM_OPTS) -legacy-install: validate-origin ## runs helm upgrade - helm install $(NAME) common/legacy-install/ $(HELM_OPTS) +operator-deploy: validate-origin ## runs helm install + helm install $(NAME) common/operator-install/ $(HELM_OPTS) -legacy-upgrade: validate-origin ## runs helm upgrade - helm upgrade $(NAME) common/legacy-install/ $(HELM_OPTS) +operator-upgrade: validate-origin ## runs helm upgrade + helm upgrade $(NAME) common/operator-install/ $(HELM_OPTS) uninstall: ## runs helm uninstall helm uninstall $(NAME) diff --git a/legacy-install/.helmignore b/install/.helmignore similarity index 100% rename from legacy-install/.helmignore rename to install/.helmignore diff --git a/install/Chart.yaml b/install/Chart.yaml index 74adcf8f..d1c80ab3 100644 --- a/install/Chart.yaml +++ b/install/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -description: A Helm chart to build and deploy a Cloud Pattern via the patterns operator +description: A Helm chart to build and deploy a Cloud Pattern keywords: - pattern name: pattern-install diff --git a/legacy-install/crds/applications.argoproj.io.yaml b/install/crds/applications.argoproj.io.yaml similarity index 100% rename from legacy-install/crds/applications.argoproj.io.yaml rename to install/crds/applications.argoproj.io.yaml diff --git a/legacy-install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml similarity index 100% rename from legacy-install/templates/argocd/application.yaml rename to install/templates/argocd/application.yaml diff --git a/legacy-install/templates/argocd/namespace.yaml b/install/templates/argocd/namespace.yaml similarity index 100% rename from legacy-install/templates/argocd/namespace.yaml rename to install/templates/argocd/namespace.yaml diff --git a/legacy-install/templates/argocd/subscription.yaml b/install/templates/argocd/subscription.yaml similarity index 100% rename from legacy-install/templates/argocd/subscription.yaml rename to install/templates/argocd/subscription.yaml diff --git a/install/values.yaml b/install/values.yaml index 0a80b80f..b0507225 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -2,5 +2,25 @@ main: git: repoURL: https://github.com/pattern-clone/mypattern revision: main + #valuesDirectoryURL: https://github.com/hybrid-cloud-patterns/industrial-edge/raw/main/ + + options: + syncPolicy: Automatic + installPlanApproval: Automatic + useCSV: False + + gitops: + channel: stable + source: redhat-operators + csv: v1.3.0 clusterGroupName: default + +global: + imageregistry: + type: quay + + git: + hostname: github.com + # Account is the user or organization under which the pattern repo lives + account: hybrid-cloud-patterns diff --git a/legacy-install/Chart.yaml b/legacy-install/Chart.yaml deleted file mode 100644 index d1c80ab3..00000000 --- a/legacy-install/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -description: A Helm chart to build and deploy a Cloud Pattern -keywords: -- pattern -name: pattern-install -version: 0.0.1 diff --git a/legacy-install/values.yaml b/legacy-install/values.yaml deleted file mode 100644 index b0507225..00000000 --- a/legacy-install/values.yaml +++ /dev/null @@ -1,26 +0,0 @@ -main: - git: - repoURL: https://github.com/pattern-clone/mypattern - revision: main - #valuesDirectoryURL: https://github.com/hybrid-cloud-patterns/industrial-edge/raw/main/ - - options: - syncPolicy: Automatic - installPlanApproval: Automatic - useCSV: False - - gitops: - channel: stable - source: redhat-operators - csv: v1.3.0 - - clusterGroupName: default - -global: - imageregistry: - type: quay - - git: - hostname: github.com - # Account is the user or organization under which the pattern repo lives - account: hybrid-cloud-patterns diff --git a/operator-install/Chart.yaml b/operator-install/Chart.yaml new file mode 100644 index 00000000..74adcf8f --- /dev/null +++ b/operator-install/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +description: A Helm chart to build and deploy a Cloud Pattern via the patterns operator +keywords: +- pattern +name: pattern-install +version: 0.0.1 diff --git a/install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml similarity index 100% rename from install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml rename to operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml diff --git a/install/templates/pattern.yaml b/operator-install/templates/pattern.yaml similarity index 100% rename from install/templates/pattern.yaml rename to operator-install/templates/pattern.yaml diff --git a/install/templates/subscription.yaml b/operator-install/templates/subscription.yaml similarity index 100% rename from install/templates/subscription.yaml rename to operator-install/templates/subscription.yaml diff --git a/operator-install/values.yaml b/operator-install/values.yaml new file mode 100644 index 00000000..0a80b80f --- /dev/null +++ b/operator-install/values.yaml @@ -0,0 +1,6 @@ +main: + git: + repoURL: https://github.com/pattern-clone/mypattern + revision: main + + clusterGroupName: default diff --git a/tests/install-naked.expected.yaml b/tests/install-naked.expected.yaml index d6e9a3b0..7694a8d2 100644 --- a/tests/install-naked.expected.yaml +++ b/tests/install-naked.expected.yaml @@ -1,27 +1,66 @@ --- -# Source: pattern-install/templates/pattern.yaml -apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 -kind: Pattern +# Source: pattern-install/templates/argocd/namespace.yaml +# Pre-create so we can create our argo app for keeping subscriptions in sync +# Do it here so that we don't try to sync it in the future +apiVersion: v1 +kind: Namespace metadata: - name: install - namespace: openshift-operators + name: openshift-gitops +--- +# Source: pattern-install/templates/argocd/application.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: install-default + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground spec: - clusterGroupName: default - gitSpec: - targetRepo: https://github.com/pattern-clone/mypattern + destination: + name: in-cluster + namespace: install-default + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern targetRevision: main + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" + - "https://github.com/pattern-clone/mypattern/raw/main/values-default.yaml" + # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.valuesDirectoryURL + value: https://github.com/pattern-clone/mypattern/raw/main + - name: global.pattern + value: install + - name: global.hubClusterDomain + value: + syncPolicy: + automated: {} --- -# Source: pattern-install/templates/subscription.yaml +# Source: pattern-install/templates/argocd/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: - name: patterns-operator + name: openshift-gitops-operator namespace: openshift-operators labels: - operators.coreos.com/patterns-operator.openshift-operators: "" + operators.coreos.com/openshift-gitops-operator.openshift-operators: "" spec: - channel: fast + channel: stable installPlanApproval: Automatic - name: patterns-operator - source: community-operators + name: openshift-gitops-operator + source: redhat-operators sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: install-default,openshift-gitops diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yaml index d3b046d2..e2401612 100644 --- a/tests/install-normal.expected.yaml +++ b/tests/install-normal.expected.yaml @@ -1,27 +1,66 @@ --- -# Source: pattern-install/templates/pattern.yaml -apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 -kind: Pattern +# Source: pattern-install/templates/argocd/namespace.yaml +# Pre-create so we can create our argo app for keeping subscriptions in sync +# Do it here so that we don't try to sync it in the future +apiVersion: v1 +kind: Namespace metadata: - name: install - namespace: openshift-operators + name: openshift-gitops +--- +# Source: pattern-install/templates/argocd/application.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: install-example + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground spec: - clusterGroupName: example - gitSpec: - targetRepo: https://github.com/pattern-clone/mypattern + destination: + name: in-cluster + namespace: install-example + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern targetRevision: main + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" + - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" + # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.valuesDirectoryURL + value: https://github.com/pattern-clone/mypattern/raw/main + - name: global.pattern + value: install + - name: global.hubClusterDomain + value: hub.example.com + syncPolicy: + automated: {} --- -# Source: pattern-install/templates/subscription.yaml +# Source: pattern-install/templates/argocd/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: - name: patterns-operator + name: openshift-gitops-operator namespace: openshift-operators labels: - operators.coreos.com/patterns-operator.openshift-operators: "" + operators.coreos.com/openshift-gitops-operator.openshift-operators: "" spec: - channel: fast + channel: stable installPlanApproval: Automatic - name: patterns-operator - source: community-operators + name: openshift-gitops-operator + source: redhat-operators sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: install-example,openshift-gitops diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml new file mode 100644 index 00000000..32775213 --- /dev/null +++ b/tests/operator-install-naked.expected.yaml @@ -0,0 +1,27 @@ +--- +# Source: pattern-install/templates/pattern.yaml +apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 +kind: Pattern +metadata: + name: operator-install + namespace: openshift-operators +spec: + clusterGroupName: default + gitSpec: + targetRepo: https://github.com/pattern-clone/mypattern + targetRevision: main +--- +# Source: pattern-install/templates/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: patterns-operator + namespace: openshift-operators + labels: + operators.coreos.com/patterns-operator.openshift-operators: "" +spec: + channel: fast + installPlanApproval: Automatic + name: patterns-operator + source: community-operators + sourceNamespace: openshift-marketplace diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml new file mode 100644 index 00000000..86d34dd5 --- /dev/null +++ b/tests/operator-install-normal.expected.yaml @@ -0,0 +1,27 @@ +--- +# Source: pattern-install/templates/pattern.yaml +apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 +kind: Pattern +metadata: + name: operator-install + namespace: openshift-operators +spec: + clusterGroupName: example + gitSpec: + targetRepo: https://github.com/pattern-clone/mypattern + targetRevision: main +--- +# Source: pattern-install/templates/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: patterns-operator + namespace: openshift-operators + labels: + operators.coreos.com/patterns-operator.openshift-operators: "" +spec: + channel: fast + installPlanApproval: Automatic + name: patterns-operator + source: community-operators + sourceNamespace: openshift-marketplace From 1f55830f1dc43ae072eee77fca9f9dcdf4d1829a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Jul 2022 18:54:04 +0200 Subject: [PATCH 0384/1263] Unify install/deploy and upgrade targets Currently we have two make targets: 1. deploy -> runs 'helm install' 2. upgrade -> runs 'helm upgrade' You are supposed to use 'make install/deploy' the very first time you install the pattern. And 'make upgrade' when you "upgrade" the pattern, which in helm-speak, means you have to run it when you change git branch or any parameter that gets passed to the very first 'helm install' call. Since helm offers an 'upgrade --install' option, which installs when the initial helm chart does not exist, whereas calls does a helm upgrade when the chart already exists [1], we can unify those two targets and make them one and the same. Tested with both an initial install and with an upgrade. While we're at it we do the same with the new (albeit temporary) operator-{deploy,upgrade} targets. [1] From helm's help: $ helm upgrade --help |grep -- "-i, --install" -i, --install if a release by this name doesn't already exist, run an install --- Makefile | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 9785356e..4161f6f9 100644 --- a/Makefile +++ b/Makefile @@ -44,17 +44,11 @@ validate-origin: ## verify the git origin is available echo "$(TARGET_REPO) - $(TARGET_BRANCH) exists" || \ (echo "$(TARGET_BRANCH) not found in $(TARGET_REPO)"; exit 1) -deploy: validate-origin ## deploys the pattern - helm install $(NAME) common/install/ $(HELM_OPTS) +deploy upgrade: validate-origin ## deploys the pattern + helm upgrade --install $(NAME) common/install/ $(HELM_OPTS) -upgrade: validate-origin - helm upgrade $(NAME) common/install/ $(HELM_OPTS) - -operator-deploy: validate-origin ## runs helm install - helm install $(NAME) common/operator-install/ $(HELM_OPTS) - -operator-upgrade: validate-origin ## runs helm upgrade - helm upgrade $(NAME) common/operator-install/ $(HELM_OPTS) +operator-deploy operator-upgrade: validate-origin ## runs helm install + helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) uninstall: ## runs helm uninstall helm uninstall $(NAME) From 1f4f7d6793dba5d77df2fab3be792fc38bb38b77 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 6 Jul 2022 21:11:58 +0200 Subject: [PATCH 0385/1263] Cleanup configManagementPlugins section in argocd.yaml The current yaml is unreadable for any practical means. Let's use the '|' yaml operator to join the lines nicely. This is also done upstream [1]. This paves the way to add some code that will skip non existing values files. Tested by me and Lester and we got a correctly deployed manuela-tst (which is a user of the plugin systems) [1] https://argo-cd.readthedocs.io/en/stable/user-guide/config-management-plugins/ --- clustergroup/templates/argocd.yaml | 42 +++++++++++++++---------- tests/clustergroup-naked.expected.yaml | 42 +++++++++++++++---------- tests/clustergroup-normal.expected.yaml | 42 +++++++++++++++---------- 3 files changed, 78 insertions(+), 48 deletions(-) diff --git a/clustergroup/templates/argocd.yaml b/clustergroup/templates/argocd.yaml index 33484a1c..7d278d63 100644 --- a/clustergroup/templates/argocd.yaml +++ b/clustergroup/templates/argocd.yaml @@ -15,22 +15,32 @@ spec: # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - configManagementPlugins: "- name: kustomize-version\n generate:\n command: [sh, - -c]\n args: [\"kustomize version 1>&2 && exit 1\"]\n- name: kustomize-with-helm\n - \ generate:\n command: [\"kustomize\"]\n args: [\"build\", \"--enable-helm\"]\n- name: - helm-with-kustomize\n init:\n command: [\"/bin/sh\", \"-c\"]\n args: [\"helm - dependency build\"]\n generate:\n command: [/bin/bash, -c]\n args: [\"helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f {{ .Values.global.valuesDirectoryURL }}/values-global.yaml - -f {{ .Values.global.valuesDirectoryURL }}/values-{{ .Values.clusterGroup.name }}.yaml - --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL - --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION - --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern={{ .Values.global.pattern }} - --set global.hubClusterDomain={{ .Values.global.hubClusterDomain }} - --set global.localClusterDomain={{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }} - --set global.valuesDirectoryURL={{ .Values.global.valuesDirectoryURL }} - --post-renderer ./kustomize\"] - \ \n" + configManagementPlugins: | + - name: kustomize-version + generate: + command: ["sh", "-c"] + args: ["kustomize version 1>&2 && exit 1"] + - name: kustomize-with-helm + generate: + command: ["kustomize"] + args: ["build", "--enable-helm"] + - name: helm-with-kustomize + init: + command: ["/bin/sh", "-c"] + args: ["helm dependency build"] + generate: + command: ["/bin/bash", "-c"] + args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f {{ .Values.global.valuesDirectoryURL }}/values-global.yaml + -f {{ .Values.global.valuesDirectoryURL }}/values-{{ .Values.clusterGroup.name }}.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern={{ .Values.global.pattern }} + --set global.hubClusterDomain={{ .Values.global.hubClusterDomain }} + --set global.localClusterDomain={{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }} + --set global.valuesDirectoryURL={{ .Values.global.valuesDirectoryURL }} + --post-renderer ./kustomize"] applicationSet: resources: limits: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 42e91fed..47892c09 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -73,22 +73,32 @@ spec: # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - configManagementPlugins: "- name: kustomize-version\n generate:\n command: [sh, - -c]\n args: [\"kustomize version 1>&2 && exit 1\"]\n- name: kustomize-with-helm\n - \ generate:\n command: [\"kustomize\"]\n args: [\"build\", \"--enable-helm\"]\n- name: - helm-with-kustomize\n init:\n command: [\"/bin/sh\", \"-c\"]\n args: [\"helm - dependency build\"]\n generate:\n command: [/bin/bash, -c]\n args: [\"helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f https://github.com/pattern-clone/common/raw/main/values-global.yaml - -f https://github.com/pattern-clone/common/raw/main/values-example.yaml - --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL - --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION - --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern=common - --set global.hubClusterDomain= - --set global.localClusterDomain= - --set global.valuesDirectoryURL=https://github.com/pattern-clone/common/raw/main - --post-renderer ./kustomize\"] - \ \n" + configManagementPlugins: | + - name: kustomize-version + generate: + command: ["sh", "-c"] + args: ["kustomize version 1>&2 && exit 1"] + - name: kustomize-with-helm + generate: + command: ["kustomize"] + args: ["build", "--enable-helm"] + - name: helm-with-kustomize + init: + command: ["/bin/sh", "-c"] + args: ["helm dependency build"] + generate: + command: ["/bin/bash", "-c"] + args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f https://github.com/pattern-clone/common/raw/main/values-global.yaml + -f https://github.com/pattern-clone/common/raw/main/values-example.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern=common + --set global.hubClusterDomain= + --set global.localClusterDomain= + --set global.valuesDirectoryURL=https://github.com/pattern-clone/common/raw/main + --post-renderer ./kustomize"] applicationSet: resources: limits: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index ffd41cc1..04e83514 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -546,22 +546,32 @@ spec: # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - configManagementPlugins: "- name: kustomize-version\n generate:\n command: [sh, - -c]\n args: [\"kustomize version 1>&2 && exit 1\"]\n- name: kustomize-with-helm\n - \ generate:\n command: [\"kustomize\"]\n args: [\"build\", \"--enable-helm\"]\n- name: - helm-with-kustomize\n init:\n command: [\"/bin/sh\", \"-c\"]\n args: [\"helm - dependency build\"]\n generate:\n command: [/bin/bash, -c]\n args: [\"helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml - -f https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml - --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL - --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION - --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern=mypattern - --set global.hubClusterDomain=hub.example.com - --set global.localClusterDomain=region.example.com - --set global.valuesDirectoryURL=https://github.com/pattern-clone/mypattern/raw/main - --post-renderer ./kustomize\"] - \ \n" + configManagementPlugins: | + - name: kustomize-version + generate: + command: ["sh", "-c"] + args: ["kustomize version 1>&2 && exit 1"] + - name: kustomize-with-helm + generate: + command: ["kustomize"] + args: ["build", "--enable-helm"] + - name: helm-with-kustomize + init: + command: ["/bin/sh", "-c"] + args: ["helm dependency build"] + generate: + command: ["/bin/bash", "-c"] + args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml + -f https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern=mypattern + --set global.hubClusterDomain=hub.example.com + --set global.localClusterDomain=region.example.com + --set global.valuesDirectoryURL=https://github.com/pattern-clone/mypattern/raw/main + --post-renderer ./kustomize"] applicationSet: resources: limits: From b9d3a5fe0c7de08e193d49b5dab98cc09b30073c Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 7 Jul 2022 10:54:45 -0500 Subject: [PATCH 0386/1263] Add explicit legacy-deploy legacy-upgrade targets --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4161f6f9..d5f376ab 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,11 @@ validate-origin: ## verify the git origin is available echo "$(TARGET_REPO) - $(TARGET_BRANCH) exists" || \ (echo "$(TARGET_BRANCH) not found in $(TARGET_REPO)"; exit 1) -deploy upgrade: validate-origin ## deploys the pattern +# Default targets are "deploy" and "upgrade"; they can "move" to whichever install mechanism should be default. +# legacy-deploy and legacy-upgrade should be present so that patterns don't need to depend on "deploy" and "upgrade" +# pointing to one place or another, and don't need to change when they do (provide they use either legacy- or operator- +# targets) +deploy upgrade legacy-deploy legacy-upgrade: validate-origin ## deploys the pattern helm upgrade --install $(NAME) common/install/ $(HELM_OPTS) operator-deploy operator-upgrade: validate-origin ## runs helm install From a294c609c15dac500c3936a50975759ff2db7146 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 7 Jul 2022 12:16:40 +0200 Subject: [PATCH 0387/1263] Drop valuesDirectoryURL entirely valuesDirectoryURL was only needed when Argo did not support referencing the values files inside the pattern's git repo root. This stopped being the case around Argo 2.3 [1]. I.e. Now any value file that starts with '/' is assumed to reference the git repo's root. The main reason we do this, is because argo tends to cache values files over http and ignore refresh requests (needs a hard-refresh), so the expectation of doing gitops and tweaking values files which are then fetched over http, breaks down somewhat. [2] We have three possible options to fix this: 1. We ignore the issue [2] entirely. We keep things as is and we revert the operator change [3]. I.e. we always fetch the values files from URL (this keeps things as is and we unbreak IE when deployed via the operator), but we are more susceptible to the gitops values caching issue. We could potentially reevaluate this once [4] gets implemented. 2. We completely drop valuesDirectoryURL and we always reference local files. External charts need to override all the things. This is quite simple, but has a bit of an unknown as we do not know how painful this could be with certain external charts, as those get extracted in a separate folder and there is no access to the git-managed values-files and also because overrides won't allow for the use of variables in helm [5] 3. We add a bunch of ifs everywhere and use local files when: A) we're not an external chart and B) when valuesDirectoryURL is not defined For the time being we choose option 2. and completely drop any mention of valuesDirectoryURL. If this becomes a serious issue with external charts, we can re-evaluate our option at that time. [1] https://github.com/argoproj/argo-cd/blob/bb77664b6f0299bb332843bcd2524820c7ba1558/reposerver/repository/repository.go#L674 [2] https://issues.redhat.com/browse/GITOPS-2069 [3] https://github.com/hybrid-cloud-patterns/patterns-operator/commit/ba3c35a15dd5f239c7a7d337747b01d494278429 [4] https://github.com/argoproj/argo-cd/pull/8322/files [5] https://github.com/helm/helm/issues/2492 --- Makefile | 2 +- .../policies/application-policies.yaml | 6 ++---- acm/values.yaml | 1 - clustergroup/templates/applications.yaml | 6 ++---- clustergroup/templates/argocd.yaml | 5 ++--- clustergroup/values.yaml | 2 -- install/templates/argocd/application.yaml | 8 ++------ install/values.yaml | 1 - tests/acm-normal.expected.yaml | 6 ++---- tests/clustergroup-naked.expected.yaml | 5 ++--- tests/clustergroup-normal.expected.yaml | 18 ++++++------------ tests/install-naked.expected.yaml | 6 ++---- tests/install-normal.expected.yaml | 6 ++---- 13 files changed, 23 insertions(+), 49 deletions(-) diff --git a/Makefile b/Makefile index d5f376ab..2b4b41c8 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spe # --set values always take precedence over the contents of -f HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) -TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.valuesDirectoryURL="https://github.com/pattern-clone/mypattern/raw/main" --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" --set clusterGroup.insecureUnsealVaultInsideCluster=true +TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" --set clusterGroup.insecureUnsealVaultInsideCluster=true PATTERN_OPTS=-f common/examples/values-example.yaml diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index a2392dca..f05895e8 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -41,8 +41,8 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" - - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ .name }}.yaml" + - "/values-global.yaml" + - "/values-{{ .name }}.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -52,8 +52,6 @@ spec: value: $ARGOCD_APP_NAMESPACE - name: global.pattern value: {{ $.Values.global.pattern }} - - name: global.valuesDirectoryURL - value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain diff --git a/acm/values.yaml b/acm/values.yaml index d9c94583..2e12f8b4 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -1,6 +1,5 @@ global: pattern: none - valuesDirectoryURL: none repoURL: none targetRevision: main diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 398f96c4..817055ad 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -26,8 +26,8 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-global.yaml" - - "{{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }}/values-{{ $.Values.clusterGroup.name }}.yaml" + - "/values-global.yaml" + - "/values-{{ $.Values.clusterGroup.name }}.yaml" # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL @@ -38,8 +38,6 @@ spec: value: $ARGOCD_APP_NAMESPACE - name: global.pattern value: {{ $.Values.global.pattern }} - - name: global.valuesDirectoryURL - value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain diff --git a/clustergroup/templates/argocd.yaml b/clustergroup/templates/argocd.yaml index 7d278d63..12a5312a 100644 --- a/clustergroup/templates/argocd.yaml +++ b/clustergroup/templates/argocd.yaml @@ -31,15 +31,14 @@ spec: generate: command: ["/bin/bash", "-c"] args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f {{ .Values.global.valuesDirectoryURL }}/values-global.yaml - -f {{ .Values.global.valuesDirectoryURL }}/values-{{ .Values.clusterGroup.name }}.yaml + -f $(git rev-parse --show-toplevel)/values-global.yaml + -f $(git rev-parse --show-toplevel)/values-{{ .Values.clusterGroup.name }}.yaml --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern={{ .Values.global.pattern }} --set global.hubClusterDomain={{ .Values.global.hubClusterDomain }} --set global.localClusterDomain={{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }} - --set global.valuesDirectoryURL={{ .Values.global.valuesDirectoryURL }} --post-renderer ./kustomize"] applicationSet: resources: diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 6bd739d0..8069ab74 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -1,6 +1,5 @@ global: pattern: common - valuesDirectoryURL: https://github.com/pattern-clone/common/raw/main options: useCSV: True syncPolicy: Automatic @@ -46,7 +45,6 @@ clusterGroup: # - name: factory # # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git # # Location of values-global.yaml, values-{name}.yaml, values-{app}.yaml -# # valuesDirectoryURL: https://github.com/dagger-refuse-cool/edge-gitops/raw/main/ # targetRevision: main # path: applications/factory # helmOverrides: diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 19b683b4..324e1f32 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -1,5 +1,3 @@ -{{- $valuesDirectoryURL := cat .Values.main.git.repoURL "/raw/" .Values.main.git.revision -}} -{{- $valuesDirectoryURLFixed := $valuesDirectoryURL | replace " " "" | replace ".git" "" }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -19,8 +17,8 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-global.yaml" - - "{{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }}/values-{{ .Values.main.clusterGroupName }}.yaml" + - "/values-global.yaml" + - "/values-{{ .Values.main.clusterGroupName }}.yaml" # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 parameters: - name: global.repoURL @@ -29,8 +27,6 @@ spec: value: $ARGOCD_APP_SOURCE_TARGET_REVISION - name: global.namespace value: $ARGOCD_APP_NAMESPACE - - name: global.valuesDirectoryURL - value: {{ coalesce .Values.main.git.valuesDirectoryURL $valuesDirectoryURLFixed }} - name: global.pattern value: {{ .Release.Name }} - name: global.hubClusterDomain diff --git a/install/values.yaml b/install/values.yaml index b0507225..370e6b15 100644 --- a/install/values.yaml +++ b/install/values.yaml @@ -2,7 +2,6 @@ main: git: repoURL: https://github.com/pattern-clone/mypattern revision: main - #valuesDirectoryURL: https://github.com/hybrid-cloud-patterns/industrial-edge/raw/main/ options: syncPolicy: Automatic diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 76136ee5..29af29a8 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -121,8 +121,8 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - - "https://github.com/pattern-clone/mypattern/raw/main/values-edge.yaml" + - "/values-global.yaml" + - "/values-edge.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -132,8 +132,6 @@ spec: value: $ARGOCD_APP_NAMESPACE - name: global.pattern value: mypattern - - name: global.valuesDirectoryURL - value: https://github.com/pattern-clone/mypattern/raw/main - name: global.hubClusterDomain value: hub.example.com - name: global.localClusterDomain diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 47892c09..fc7f2fb7 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -89,15 +89,14 @@ spec: generate: command: ["/bin/bash", "-c"] args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f https://github.com/pattern-clone/common/raw/main/values-global.yaml - -f https://github.com/pattern-clone/common/raw/main/values-example.yaml + -f $(git rev-parse --show-toplevel)/values-global.yaml + -f $(git rev-parse --show-toplevel)/values-example.yaml --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern=common --set global.hubClusterDomain= --set global.localClusterDomain= - --set global.valuesDirectoryURL=https://github.com/pattern-clone/common/raw/main --post-renderer ./kustomize"] applicationSet: resources: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 04e83514..6fcb5816 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -142,7 +142,6 @@ data: useCSV: false pattern: mypattern repoURL: https://github.com/pattern-clone/mypattern - valuesDirectoryURL: https://github.com/pattern-clone/mypattern/raw/main main: clusterGroupName: example git: @@ -456,8 +455,8 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" + - "/values-global.yaml" + - "/values-example.yaml" # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL @@ -468,8 +467,6 @@ spec: value: $ARGOCD_APP_NAMESPACE - name: global.pattern value: mypattern - - name: global.valuesDirectoryURL - value: https://github.com/pattern-clone/mypattern/raw/main - name: global.hubClusterDomain value: hub.example.com - name: global.localClusterDomain @@ -507,8 +504,8 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" + - "/values-global.yaml" + - "/values-example.yaml" # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL @@ -519,8 +516,6 @@ spec: value: $ARGOCD_APP_NAMESPACE - name: global.pattern value: mypattern - - name: global.valuesDirectoryURL - value: https://github.com/pattern-clone/mypattern/raw/main - name: global.hubClusterDomain value: hub.example.com - name: global.localClusterDomain @@ -562,15 +557,14 @@ spec: generate: command: ["/bin/bash", "-c"] args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml - -f https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml + -f $(git rev-parse --show-toplevel)/values-global.yaml + -f $(git rev-parse --show-toplevel)/values-example.yaml --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern=mypattern --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com - --set global.valuesDirectoryURL=https://github.com/pattern-clone/mypattern/raw/main --post-renderer ./kustomize"] applicationSet: resources: diff --git a/tests/install-naked.expected.yaml b/tests/install-naked.expected.yaml index 7694a8d2..7272f0eb 100644 --- a/tests/install-naked.expected.yaml +++ b/tests/install-naked.expected.yaml @@ -27,8 +27,8 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - - "https://github.com/pattern-clone/mypattern/raw/main/values-default.yaml" + - "/values-global.yaml" + - "/values-default.yaml" # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 parameters: - name: global.repoURL @@ -37,8 +37,6 @@ spec: value: $ARGOCD_APP_SOURCE_TARGET_REVISION - name: global.namespace value: $ARGOCD_APP_NAMESPACE - - name: global.valuesDirectoryURL - value: https://github.com/pattern-clone/mypattern/raw/main - name: global.pattern value: install - name: global.hubClusterDomain diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yaml index e2401612..d115d8ae 100644 --- a/tests/install-normal.expected.yaml +++ b/tests/install-normal.expected.yaml @@ -27,8 +27,8 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" + - "/values-global.yaml" + - "/values-example.yaml" # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 parameters: - name: global.repoURL @@ -37,8 +37,6 @@ spec: value: $ARGOCD_APP_SOURCE_TARGET_REVISION - name: global.namespace value: $ARGOCD_APP_NAMESPACE - - name: global.valuesDirectoryURL - value: https://github.com/pattern-clone/mypattern/raw/main - name: global.pattern value: install - name: global.hubClusterDomain From 6df4f49b56f81949960506d9a4bc86fb9d4a4aeb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 8 Jul 2022 15:29:22 +0200 Subject: [PATCH 0388/1263] Remove spurious older tests These we forgot to remove with the last rename when we moved legacy-install to install, and install to operator-install. --- tests/legacy-install-naked.expected.yaml | 66 ----------------------- tests/legacy-install-normal.expected.yaml | 66 ----------------------- 2 files changed, 132 deletions(-) delete mode 100644 tests/legacy-install-naked.expected.yaml delete mode 100644 tests/legacy-install-normal.expected.yaml diff --git a/tests/legacy-install-naked.expected.yaml b/tests/legacy-install-naked.expected.yaml deleted file mode 100644 index ca964878..00000000 --- a/tests/legacy-install-naked.expected.yaml +++ /dev/null @@ -1,66 +0,0 @@ ---- -# Source: pattern-install/templates/argocd/namespace.yaml -# Pre-create so we can create our argo app for keeping subscriptions in sync -# Do it here so that we don't try to sync it in the future -apiVersion: v1 -kind: Namespace -metadata: - name: openshift-gitops ---- -# Source: pattern-install/templates/argocd/application.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: legacy-install-default - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: in-cluster - namespace: legacy-install-default - project: default - source: - repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - - "https://github.com/pattern-clone/mypattern/raw/main/values-default.yaml" - # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.valuesDirectoryURL - value: https://github.com/pattern-clone/mypattern/raw/main - - name: global.pattern - value: legacy-install - - name: global.hubClusterDomain - value: - syncPolicy: - automated: {} ---- -# Source: pattern-install/templates/argocd/subscription.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: openshift-gitops-operator - namespace: openshift-operators - labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" -spec: - channel: stable - installPlanApproval: Automatic - name: openshift-gitops-operator - source: redhat-operators - sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: legacy-install-default,openshift-gitops diff --git a/tests/legacy-install-normal.expected.yaml b/tests/legacy-install-normal.expected.yaml deleted file mode 100644 index 4c629315..00000000 --- a/tests/legacy-install-normal.expected.yaml +++ /dev/null @@ -1,66 +0,0 @@ ---- -# Source: pattern-install/templates/argocd/namespace.yaml -# Pre-create so we can create our argo app for keeping subscriptions in sync -# Do it here so that we don't try to sync it in the future -apiVersion: v1 -kind: Namespace -metadata: - name: openshift-gitops ---- -# Source: pattern-install/templates/argocd/application.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: legacy-install-example - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: in-cluster - namespace: legacy-install-example - project: default - source: - repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "https://github.com/pattern-clone/mypattern/raw/main/values-global.yaml" - - "https://github.com/pattern-clone/mypattern/raw/main/values-example.yaml" - # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.valuesDirectoryURL - value: https://github.com/pattern-clone/mypattern/raw/main - - name: global.pattern - value: legacy-install - - name: global.hubClusterDomain - value: hub.example.com - syncPolicy: - automated: {} ---- -# Source: pattern-install/templates/argocd/subscription.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: openshift-gitops-operator - namespace: openshift-operators - labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" -spec: - channel: stable - installPlanApproval: Automatic - name: openshift-gitops-operator - source: redhat-operators - sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: legacy-install-example,openshift-gitops From b175f751080d0bd2d272401e13d1dc6b042cf0b5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 7 Jul 2022 17:56:43 +0200 Subject: [PATCH 0389/1263] Move operator installation in patterns-operator-system namespace The UI installs it by default in the patterns-operator-system namespace. Let's have UI and make operator-install in sync on this. We also add an OperatorGroup otherwise the installation fails with (as that is what the UI does when it installs the operator in the 'patterns-operator-system' namespace thanks to the 'operatorframework.io/suggested-namespace: patterns-operator-system' annotation): no operator group found that is managing this namespace Tested and got MCG correctly deployed after a 'make operator-deploy' --- operator-install/templates/namespace.yaml | 4 ++++ operator-install/templates/operator_group.yaml | 7 +++++++ operator-install/templates/pattern.yaml | 2 +- operator-install/templates/subscription.yaml | 2 +- operator-install/values.yaml | 3 +++ tests/operator-install-naked.expected.yaml | 18 ++++++++++++++++-- tests/operator-install-normal.expected.yaml | 18 ++++++++++++++++-- 7 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 operator-install/templates/namespace.yaml create mode 100644 operator-install/templates/operator_group.yaml diff --git a/operator-install/templates/namespace.yaml b/operator-install/templates/namespace.yaml new file mode 100644 index 00000000..332f5356 --- /dev/null +++ b/operator-install/templates/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .Values.vpoperator.namespace }} diff --git a/operator-install/templates/operator_group.yaml b/operator-install/templates/operator_group.yaml new file mode 100644 index 00000000..131befaa --- /dev/null +++ b/operator-install/templates/operator_group.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: patterns-operator + namespace: {{ .Values.vpoperator.namespace }} +spec: diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index 2f54860f..2e8e4493 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -2,7 +2,7 @@ apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern metadata: name: {{ .Release.Name }} - namespace: openshift-operators + namespace: {{ .Values.vpoperator.namespace }} spec: clusterGroupName: {{ .Values.main.clusterGroupName }} gitSpec: diff --git a/operator-install/templates/subscription.yaml b/operator-install/templates/subscription.yaml index 381e185f..b1d22b74 100644 --- a/operator-install/templates/subscription.yaml +++ b/operator-install/templates/subscription.yaml @@ -2,7 +2,7 @@ apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: patterns-operator - namespace: openshift-operators + namespace: {{ .Values.vpoperator.namespace }} labels: operators.coreos.com/patterns-operator.openshift-operators: "" spec: diff --git a/operator-install/values.yaml b/operator-install/values.yaml index 0a80b80f..b0284e7d 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -1,3 +1,6 @@ +vpoperator: + namespace: patterns-operator-system + main: git: repoURL: https://github.com/pattern-clone/mypattern diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index 32775213..6f5cbbc4 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -1,10 +1,24 @@ --- +# Source: pattern-install/templates/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: patterns-operator-system +--- +# Source: pattern-install/templates/operator_group.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: patterns-operator + namespace: patterns-operator-system +spec: +--- # Source: pattern-install/templates/pattern.yaml apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern metadata: name: operator-install - namespace: openshift-operators + namespace: patterns-operator-system spec: clusterGroupName: default gitSpec: @@ -16,7 +30,7 @@ apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: patterns-operator - namespace: openshift-operators + namespace: patterns-operator-system labels: operators.coreos.com/patterns-operator.openshift-operators: "" spec: diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index 86d34dd5..dd6e8db3 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -1,10 +1,24 @@ --- +# Source: pattern-install/templates/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: patterns-operator-system +--- +# Source: pattern-install/templates/operator_group.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: patterns-operator + namespace: patterns-operator-system +spec: +--- # Source: pattern-install/templates/pattern.yaml apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern metadata: name: operator-install - namespace: openshift-operators + namespace: patterns-operator-system spec: clusterGroupName: example gitSpec: @@ -16,7 +30,7 @@ apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: patterns-operator - namespace: openshift-operators + namespace: patterns-operator-system labels: operators.coreos.com/patterns-operator.openshift-operators: "" spec: From 4fdaa6f1c08013895525c7da68e608c2863d23a4 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 11 Jul 2022 11:33:33 -0500 Subject: [PATCH 0390/1263] Update tls route config for centralized cert management --- clustergroup/templates/argocd.yaml | 3 +++ tests/clustergroup-naked.expected.yaml | 3 +++ tests/clustergroup-normal.expected.yaml | 3 +++ 3 files changed, 9 insertions(+) diff --git a/clustergroup/templates/argocd.yaml b/clustergroup/templates/argocd.yaml index 12a5312a..0126c0a5 100644 --- a/clustergroup/templates/argocd.yaml +++ b/clustergroup/templates/argocd.yaml @@ -100,6 +100,9 @@ spec: memory: 128Mi route: enabled: true + tls: + insecureEdgeTerminationPolicy: Redirect + termination: reencrypt service: type: "" tls: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index fc7f2fb7..993e6bb5 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -158,6 +158,9 @@ spec: memory: 128Mi route: enabled: true + tls: + insecureEdgeTerminationPolicy: Redirect + termination: reencrypt service: type: "" tls: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 6fcb5816..e93d80a1 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -626,6 +626,9 @@ spec: memory: 128Mi route: enabled: true + tls: + insecureEdgeTerminationPolicy: Redirect + termination: reencrypt service: type: "" tls: From c85718aa004484299406e7628b1494c7094b9df5 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 8 Jul 2022 16:57:33 -0600 Subject: [PATCH 0391/1263] Adding support for .spec.config.env to subscription.yaml template. While testing deploying Industrial Edge on a FIPS mode the AMQ Streams operator was not deploying due to the FIPS mode. There was a need to pass an environment variable to the operator so the way we do that is by defining the ENV in the manifest. This PR partly implements the .spec.config.env found in the 4.10 Subscription Spec. It only implements the .spec.config.env[].name and .spec.config.env[].value and does not implement the .spec.config.env[].valueFrom. A subscription in our values files can add environment variables in the subscription definition which will get passed to the subscription operator: name: amq-streams namespaces: - manuela-tst-all channel: amq-streams-2.x csv: amqstreams.v2.0.1-0 config: env: - name: foo value: bar - name: "var" value: "value" The VP Framework will generate the following: --- apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: amq-streams namespace: manuela-tst-all spec: name: amq-streams source: redhat-operators sourceNamespace: openshift-marketplace channel: amq-streams-2.x installPlanApproval: Automatic config: env: - name: "foo" value: "bar" - name: "var" value: "value" --- This can now be applied to the OpenShift cluster environment to create the subscription. --- clustergroup/templates/subscriptions.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/clustergroup/templates/subscriptions.yaml b/clustergroup/templates/subscriptions.yaml index 2a0b2586..e08b727f 100644 --- a/clustergroup/templates/subscriptions.yaml +++ b/clustergroup/templates/subscriptions.yaml @@ -15,6 +15,16 @@ spec: sourceNamespace: {{ default "openshift-marketplace" $subs.sourceNamespace }} channel: {{ default "stable" $subs.channel }} installPlanApproval: {{ coalesce $installPlanValue $.Values.global.options.installPlanApproval }} + {{- if $subs.config }} + {{- if $subs.config.env }} + config: + env: + {{- range $subs.config.env }} + - name: {{ .name }} + value: {{ .value }} + {{- end }} + {{- end }} + {{- end }} {{- if $.Values.global.options.useCSV }} startingCSV: {{ $subs.csv }} {{- end }} @@ -32,6 +42,16 @@ spec: sourceNamespace: {{ default "openshift-marketplace" $subs.sourceNamespace }} channel: {{ default "stable" $subs.channel }} installPlanApproval: {{ coalesce $installPlanValue $.Values.global.options.installPlanApproval }} + {{- if $subs.config }} + {{- if $subs.config.env }} + config: + env: + {{- range $subs.config.env }} + - name: {{ .name }} + value: {{ .value }} + {{- end }} + {{- end }} + {{- end }} {{- if $.Values.global.options.useCSV }} startingCSV: {{ $subs.csv }} {{- end }} From 130c9299256e65472eb05ccf5c515be34188b4a6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 12 Jul 2022 09:25:12 +0200 Subject: [PATCH 0392/1263] Revert "Move operator installation in patterns-operator-system namespace" This reverts commit b175f751080d0bd2d272401e13d1dc6b042cf0b5. It seems OLM's general direction is to want the initial namespace of an operator to be 'openshift-operators' This will leave us with a discrepancy between installing with the UI and installing the operator via a rebuild and 'make deploy' but so be it. --- operator-install/templates/namespace.yaml | 4 ---- operator-install/templates/operator_group.yaml | 7 ------- operator-install/templates/pattern.yaml | 2 +- operator-install/templates/subscription.yaml | 2 +- operator-install/values.yaml | 3 --- tests/operator-install-naked.expected.yaml | 18 ++---------------- tests/operator-install-normal.expected.yaml | 18 ++---------------- 7 files changed, 6 insertions(+), 48 deletions(-) delete mode 100644 operator-install/templates/namespace.yaml delete mode 100644 operator-install/templates/operator_group.yaml diff --git a/operator-install/templates/namespace.yaml b/operator-install/templates/namespace.yaml deleted file mode 100644 index 332f5356..00000000 --- a/operator-install/templates/namespace.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: {{ .Values.vpoperator.namespace }} diff --git a/operator-install/templates/operator_group.yaml b/operator-install/templates/operator_group.yaml deleted file mode 100644 index 131befaa..00000000 --- a/operator-install/templates/operator_group.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup -metadata: - name: patterns-operator - namespace: {{ .Values.vpoperator.namespace }} -spec: diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index 2e8e4493..2f54860f 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -2,7 +2,7 @@ apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern metadata: name: {{ .Release.Name }} - namespace: {{ .Values.vpoperator.namespace }} + namespace: openshift-operators spec: clusterGroupName: {{ .Values.main.clusterGroupName }} gitSpec: diff --git a/operator-install/templates/subscription.yaml b/operator-install/templates/subscription.yaml index b1d22b74..381e185f 100644 --- a/operator-install/templates/subscription.yaml +++ b/operator-install/templates/subscription.yaml @@ -2,7 +2,7 @@ apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: patterns-operator - namespace: {{ .Values.vpoperator.namespace }} + namespace: openshift-operators labels: operators.coreos.com/patterns-operator.openshift-operators: "" spec: diff --git a/operator-install/values.yaml b/operator-install/values.yaml index b0284e7d..0a80b80f 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -1,6 +1,3 @@ -vpoperator: - namespace: patterns-operator-system - main: git: repoURL: https://github.com/pattern-clone/mypattern diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index 6f5cbbc4..32775213 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -1,24 +1,10 @@ --- -# Source: pattern-install/templates/namespace.yaml -apiVersion: v1 -kind: Namespace -metadata: - name: patterns-operator-system ---- -# Source: pattern-install/templates/operator_group.yaml -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup -metadata: - name: patterns-operator - namespace: patterns-operator-system -spec: ---- # Source: pattern-install/templates/pattern.yaml apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern metadata: name: operator-install - namespace: patterns-operator-system + namespace: openshift-operators spec: clusterGroupName: default gitSpec: @@ -30,7 +16,7 @@ apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: patterns-operator - namespace: patterns-operator-system + namespace: openshift-operators labels: operators.coreos.com/patterns-operator.openshift-operators: "" spec: diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index dd6e8db3..86d34dd5 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -1,24 +1,10 @@ --- -# Source: pattern-install/templates/namespace.yaml -apiVersion: v1 -kind: Namespace -metadata: - name: patterns-operator-system ---- -# Source: pattern-install/templates/operator_group.yaml -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup -metadata: - name: patterns-operator - namespace: patterns-operator-system -spec: ---- # Source: pattern-install/templates/pattern.yaml apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern metadata: name: operator-install - namespace: patterns-operator-system + namespace: openshift-operators spec: clusterGroupName: example gitSpec: @@ -30,7 +16,7 @@ apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: patterns-operator - namespace: patterns-operator-system + namespace: openshift-operators labels: operators.coreos.com/patterns-operator.openshift-operators: "" spec: From f0eadb4df79b2df78425dc2eea2062a835287744 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 18 Jul 2022 09:02:30 +0200 Subject: [PATCH 0393/1263] WIP add validate-prereq target And add it as a dependency to the legacy target --- Makefile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2b4b41c8..2a5177e4 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spe HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" --set clusterGroup.insecureUnsealVaultInsideCluster=true PATTERN_OPTS=-f common/examples/values-example.yaml +EXECUTABLES=git helm oc ansible .PHONY: help @@ -38,6 +39,15 @@ KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition' kubeconform: ## run helm kubeconform @for t in $(CHARTS); do helm template $(TEST_OPTS) $(PATTERN_OPTS) $$t | kubeconform -strict $(KUBECONFORM_SKIP) -verbose -schema-location $(API_URL); if [ $$? != 0 ]; then exit 1; fi; done +validate-prereq: ## verify pre-requisites + @for t in $(EXECUTABLES); do if ! which $$t > /dev/null 2>&1; then echo "No $$t in PATH"; exit 1; fi; done + @echo "Prerequisites checked '$(EXECUTABLES)': OK" + @ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1 + @echo "Python kubernetes module: OK" + @echo -n "Check for kubernetes.core collection: " + @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi + @echo "OK" + validate-origin: ## verify the git origin is available @echo Checking repo $(TARGET_REPO) - branch $(TARGET_BRANCH) @git ls-remote --exit-code --heads $(TARGET_REPO) $(TARGET_BRANCH) >/dev/null && \ @@ -48,7 +58,7 @@ validate-origin: ## verify the git origin is available # legacy-deploy and legacy-upgrade should be present so that patterns don't need to depend on "deploy" and "upgrade" # pointing to one place or another, and don't need to change when they do (provide they use either legacy- or operator- # targets) -deploy upgrade legacy-deploy legacy-upgrade: validate-origin ## deploys the pattern +deploy upgrade legacy-deploy legacy-upgrade: validate-prereq validate-origin ## deploys the pattern helm upgrade --install $(NAME) common/install/ $(HELM_OPTS) operator-deploy operator-upgrade: validate-origin ## runs helm install From 8b9211b7f46526644b628eae8a5829a8759986fe Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 22 Jul 2022 15:32:39 -0500 Subject: [PATCH 0394/1263] Extend values-secret parsing to include files section --- .../roles/vault_utils/tasks/push_secrets.yaml | 66 +++++++++++++++---- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 9984dd89..04aae51e 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -31,17 +31,18 @@ msg: "Was not able to parse any secrets from file {{ values_secret }}. 'secrets:' top-level key is missing" failed_when: "'secrets' not in all_values.keys()" -- name: Set secrets fact +- name: Set secrets and file_secrets facts ansible.builtin.set_fact: - secrets: "{{ all_values['secrets'] }}" + secrets: "{{ all_values['secrets'] | default({}) }}" + file_secrets: "{{ all_values['files'] | default({}) }}" - name: Verify we have any secrets at all ansible.builtin.fail: msg: "Was not able to parse any secrets from file {{ values_secret }}: {{ all_values }}" failed_when: - secrets is not defined or secrets | length == 0 + secrets | combine(file_secrets) | length == 0 -- name: Check the value-secret.yaml file for errors +- name: Check the value-secret.yaml file for errors in the secrets section ansible.builtin.fail: msg: > "{{ item }}" is not properly formatted. Each key under 'secrets:' @@ -54,18 +55,33 @@ loop_control: label: "{{ item.key }}" -- name: Check the value-secret.yaml file for errors +- name: Validate file references in the files section of value-secret.yaml + ansible.builtin.stat: + path: '{{ file_stat.value }}' + register: file_values + loop_control: + loop_var: file_stat + loop: + "{{ file_secrets | dict2items }}" + +- name: debug file_stat + ansible.builtin.debug: + var: file_stat + when: debug | default(False) | bool + +- name: debug file_valaues + ansible.builtin.debug: + var: file_values + when: debug | default(False) | bool + +- name: Fail if referenced file does not exist ansible.builtin.fail: msg: > - "{{ item }}" is not properly formatted. Each key under 'secrets:' - needs to point to a dictionary of key, value pairs. See values-secret.yaml.template. + "file {{ item.file_stat.key }} {{ item.file_stat.value }}" must exist and be a file when: > - item.key | length == 0 or - item.value is not mapping + not item.stat.exists loop: - "{{ secrets | dict2items }}" - loop_control: - label: "{{ item.key }}" + "{{ file_values.results }}" # Detect here if we have only the following two keys under a password # s3.accessKey: @@ -114,7 +130,7 @@ # and https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/filter/core.py#L124 # C. The \\A and \\Z match the beginning and end of a string (in case of multiline strings # do not want to match every line) -- name: Set vault commands fact +- name: Set secrets vault commands fact ansible.builtin.set_fact: vault_cmds: "{{ vault_cmds | default({}) | combine({ item.key: vault_cmd}) }}" vars: @@ -125,11 +141,26 @@ loop_control: label: "{{ item.key }}" +- name: Set files vault commands fact + ansible.builtin.set_fact: + secret_files_vault_cmds: "{{ secret_files_vault_cmds | default({}) | combine({ item.key: vault_cmd}) }}" + vars: + vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'vault kv put {{ vault_path }}/{{ item.key }} file=-'" + loop: + "{{ file_secrets | dict2items }}" + loop_control: + label: "{{ item.key }}" + - name: Debug vault commands ansible.builtin.debug: msg: "{{ vault_cmds }}" when: debug | default(False) | bool +- name: Debug files vault commands + ansible.builtin.debug: + msg: "{{ secret_files_vault_cmds }}" + when: debug | default(False) | bool + # This step is not really needed when running make vault-init + load-secrets as # everything is sequential # It is needed when the vault is unsealed/configured inside the cluster and load-secrets @@ -158,3 +189,12 @@ loop_control: label: "{{ item.key }}" when: not debug | default(False) | bool + +- name: Add the file secrets to the vault + ansible.builtin.shell: + "{{ item.value }}" + loop: + "{{ secret_files_vault_cmds | dict2items }}" + loop_control: + label: "{{ item.key }}" + when: not debug | default(False) | bool From 90e2dce26b1a4a21e4f715b930b42ea517ac9b6a Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 22 Jul 2022 15:48:05 -0500 Subject: [PATCH 0395/1263] Add config file for ansible-lint --- .ansible-lint | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .ansible-lint diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 00000000..57d051e0 --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,6 @@ +Vim filetype=yaml +--- +offline: false + +warn_list: + - command-instead-of-shell From d682e2bebdc9ec57bdff649c129defba1602f199 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 22 Jul 2022 15:50:25 -0500 Subject: [PATCH 0396/1263] Comment to allow parsing of config --- .ansible-lint | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ansible-lint b/.ansible-lint index 57d051e0..3a80915e 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -1,4 +1,4 @@ -Vim filetype=yaml +# Vim filetype=yaml --- offline: false From 1258d5a525267a21522b8ae6194f62494be4e84f Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Sat, 23 Jul 2022 08:08:00 -0500 Subject: [PATCH 0397/1263] Fix issues pointed out in review --- .ansible-lint | 3 --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 5 +++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.ansible-lint b/.ansible-lint index 3a80915e..138ae765 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -1,6 +1,3 @@ # Vim filetype=yaml --- offline: false - -warn_list: - - command-instead-of-shell diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 04aae51e..7d0d05d9 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -69,7 +69,7 @@ var: file_stat when: debug | default(False) | bool -- name: debug file_valaues +- name: debug file_values ansible.builtin.debug: var: file_values when: debug | default(False) | bool @@ -190,8 +190,9 @@ label: "{{ item.key }}" when: not debug | default(False) | bool + # This has to be shell because of the use of stdin and pipes - name: Add the file secrets to the vault - ansible.builtin.shell: + ansible.builtin.shell: # noqa: command-instead-of-shell "{{ item.value }}" loop: "{{ secret_files_vault_cmds | dict2items }}" From 06039bc8996899070ae4b8f4be87c9591f0f0781 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Sat, 23 Jul 2022 08:12:45 -0500 Subject: [PATCH 0398/1263] Change file to content to remove ambiguity --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 7d0d05d9..8d8abe5d 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -145,7 +145,7 @@ ansible.builtin.set_fact: secret_files_vault_cmds: "{{ secret_files_vault_cmds | default({}) | combine({ item.key: vault_cmd}) }}" vars: - vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'vault kv put {{ vault_path }}/{{ item.key }} file=-'" + vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'vault kv put {{ vault_path }}/{{ item.key }} content=-'" loop: "{{ file_secrets | dict2items }}" loop_control: From 16cb9ef1addeb379be47b8a041a538db9623142b Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 26 Jul 2022 09:44:52 -0500 Subject: [PATCH 0399/1263] Add support for extraValueFiles section for applications --- acm/templates/policies/application-policies.yaml | 3 +++ clustergroup/templates/applications.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index f05895e8..aa33f5ab 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -43,6 +43,9 @@ spec: valueFiles: - "/values-global.yaml" - "/values-{{ .name }}.yaml" + {{- range $valueFile := .extraValueFiles }} + - {{ $valueFile | quote }} + {{- end }} parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 817055ad..a747df13 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -28,6 +28,9 @@ spec: valueFiles: - "/values-global.yaml" - "/values-{{ $.Values.clusterGroup.name }}.yaml" + {{- range $valueFile := .extraValueFiles }} + - {{ $valueFile | quote }} + {{- end }} # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL From 7c2bcbd646a5bb442e4dafc8b5f6a4306ca62adc Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 26 Jul 2022 11:51:52 -0500 Subject: [PATCH 0400/1263] Also fix edge cases with secrets loading --- .../roles/vault_utils/tasks/push_secrets.yaml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 8d8abe5d..0bb248cf 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -27,9 +27,10 @@ all_values: "{{ lookup('file', values_secret) | from_yaml | default({}, true) }}" - name: Check for secrets keys - ansible.builtin.fail: - msg: "Was not able to parse any secrets from file {{ values_secret }}. 'secrets:' top-level key is missing" - failed_when: "'secrets' not in all_values.keys()" + ansible.builtin.assert: + that: + - "('secrets' in all_values.keys()) or ('files' in all_values.keys())" + fail_msg: "Was not able to parse any secrets from file {{ values_secret }}. Either 'secrets:' or 'files:' top-level keys (or both) must exist" - name: Set secrets and file_secrets facts ansible.builtin.set_fact: @@ -130,9 +131,15 @@ # and https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/filter/core.py#L124 # C. The \\A and \\Z match the beginning and end of a string (in case of multiline strings # do not want to match every line) + +- name: Set defaults for vault_cmds and secret_files_vault_cmds to prevent undefined variable errors + ansible.builtin.set_fact: + vault_cmds: "{{ vault_cmds | default({}) }}" + secret_files_vault_cmds: "{{ secret_files_vault_cmds | default({}) }}" + - name: Set secrets vault commands fact ansible.builtin.set_fact: - vault_cmds: "{{ vault_cmds | default({}) | combine({ item.key: vault_cmd}) }}" + vault_cmds: "{{ vault_cmds | combine({ item.key: vault_cmd}) }}" vars: vault_cmd: "vault kv put '{{ vault_path }}/{{ item.key }}' {{ item.value.keys() | zip(item.value.values() | map('regex_replace', '(?ms)\\A(.*)\\Z', \"'\\1'\")) | map('join', '=') | list | join(' ') }}" @@ -143,7 +150,7 @@ - name: Set files vault commands fact ansible.builtin.set_fact: - secret_files_vault_cmds: "{{ secret_files_vault_cmds | default({}) | combine({ item.key: vault_cmd}) }}" + secret_files_vault_cmds: "{{ secret_files_vault_cmds | combine({ item.key: vault_cmd}) }}" vars: vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'vault kv put {{ vault_path }}/{{ item.key }} content=-'" loop: @@ -159,7 +166,7 @@ - name: Debug files vault commands ansible.builtin.debug: msg: "{{ secret_files_vault_cmds }}" - when: debug | default(False) | bool + #when: debug | default(False) | bool # This step is not really needed when running make vault-init + load-secrets as # everything is sequential From 524d806bf268766960a82ab30f13a095ebda64e9 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 26 Jul 2022 11:56:00 -0500 Subject: [PATCH 0401/1263] Remove stray comment marker --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 0bb248cf..d2ec376a 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -166,7 +166,7 @@ - name: Debug files vault commands ansible.builtin.debug: msg: "{{ secret_files_vault_cmds }}" - #when: debug | default(False) | bool + when: debug | default(False) | bool # This step is not really needed when running make vault-init + load-secrets as # everything is sequential From 4688016c4efa11cbaccb9cfc2c501da819ad678e Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 26 Jul 2022 15:13:27 -0500 Subject: [PATCH 0402/1263] Add fileParameters --- acm/templates/policies/application-policies.yaml | 7 +++++++ clustergroup/templates/applications.yaml | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index aa33f5ab..da4505a1 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -63,6 +63,13 @@ spec: - name: {{ .name }} value: {{ .value | quote }} {{- end }} + {{- if .fileParameters }} + fileParameters: + {{- range .fileParameters }} + - name: {{ .name }} + path: {{ .path }} + {{- end }} + {{- end }} destination: server: https://kubernetes.default.svc namespace: {{ $.Values.global.pattern }}-{{ .name }} diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index a747df13..7e17afc3 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -52,6 +52,13 @@ spec: forceString: true {{- end }} {{- end }} + {{- if .fileParameters }} + fileParameters: + {{- range .fileParameters }} + - name: {{ .name }} + path: {{ .path }} + {{- end }} + {{- end }} {{- end }} {{- if .ignoreDifferences }} ignoreDifferences: {{ .ignoreDifferences | toPrettyJson }} From 8454544bdfee073ca3a7018a5f353e5b64e56003 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 28 Jul 2022 13:49:27 -0500 Subject: [PATCH 0403/1263] Add applicationsets --- clustergroup/templates/applicationsets.yaml | 73 +++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 clustergroup/templates/applicationsets.yaml diff --git a/clustergroup/templates/applicationsets.yaml b/clustergroup/templates/applicationsets.yaml new file mode 100644 index 00000000..83524f2e --- /dev/null +++ b/clustergroup/templates/applicationsets.yaml @@ -0,0 +1,73 @@ +{{/* Only define this if there are any applicationSets defined */}} +{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} +{{- range .Values.clusterGroup.applicationsets }} +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: {{ .name }} + namespace: {{ $namespace }} + labels: + app: {{ .name }} +spec: + generators: {{ .generators | toPrettyJson }} + template: + metadata: + name: {{ coalesce .namespace $namespace }} + spec: + project: {{ .project }} + {{- if .syncPolicy }} + syncPolicy: {{ .syncPolicy | toPrettyJson }} + {{- else }} + syncPolicy: + automated: {} + {{- end }} + {{- if .ignoreDifferences }} + ignoreDifferences: {{ .ignoreDifferences | toPrettyJson }} + {{- end }} + source: + repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} + targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} + {{- if .chart }} + chart: {{ .chart }} + {{- end }} + {{- if .path }} + path: {{ .path }} + {{- end }} + {{- if .plugin }} + plugin: {{ .plugin }} + {{- end }} + {{- if not .kustomize }} + helm: + ignoreMissingValueFiles: true + valueFiles: | + - "/values-global.yaml" + - "/values-{{ $.Values.clusterGroup.name }}.yaml" + {{- range .extraValueFiles }} + - "{{ . }}" + {{- end }} + #parameters: + # - name: global.hubClusterDomain + # value: {{ $.Values.global.hubClusterDomain }} + # - name: global.localClusterDomain + # value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} + {{- end }} + #parameters: + # - name: global.repoURL + # value: $ARGOCD_APP_SOURCE_REPO_URL + # - name: global.targetRevision + # value: $ARGOCD_APP_SOURCE_TARGET_REVISION + # - name: global.namespace + # value: $ARGOCD_APP_NAMESPACE + # - name: global.pattern + # value: {{ $.Values.global.pattern }} + # - name: global.valuesDirectoryURL + # value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} + # - name: global.hubClusterDomain + # value: {{ $.Values.global.hubClusterDomain }} + # - name: global.localClusterDomain + # value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} + destination: + server: {{ coalesce .destinationURL "https://kubernetes.default.svc" }} + namespace: {{ coalesce .namespace $namespace }} +{{- end }} +--- From 0388fe22fa13a3955bde10c9db315cd2e9cc466d Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 28 Jul 2022 13:55:55 -0500 Subject: [PATCH 0404/1263] Add overrides --- clustergroup/templates/applicationsets.yaml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/clustergroup/templates/applicationsets.yaml b/clustergroup/templates/applicationsets.yaml index 83524f2e..ac272913 100644 --- a/clustergroup/templates/applicationsets.yaml +++ b/clustergroup/templates/applicationsets.yaml @@ -43,13 +43,20 @@ spec: - "/values-global.yaml" - "/values-{{ $.Values.clusterGroup.name }}.yaml" {{- range .extraValueFiles }} - - "{{ . }}" + - {{ . | quote }} + {{- end }} + parameters: + - name: global.hubClusterDomain + value: {{ $.Values.global.hubClusterDomain }} + - name: global.localClusterDomain + value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} + {{- range .overrides }} + - name: {{ .name }} + value: {{ .value | quote }} + {{- if .forceString }} + forceString: true + {{- end }} {{- end }} - #parameters: - # - name: global.hubClusterDomain - # value: {{ $.Values.global.hubClusterDomain }} - # - name: global.localClusterDomain - # value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} {{- end }} #parameters: # - name: global.repoURL From 95b7839abe32c0d116e60a4a41203ee033c5cfe8 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 28 Jul 2022 16:34:29 -0500 Subject: [PATCH 0405/1263] Argo gets angry if it things valuesfiles is a string --- clustergroup/templates/applicationsets.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/applicationsets.yaml b/clustergroup/templates/applicationsets.yaml index ac272913..860eb20d 100644 --- a/clustergroup/templates/applicationsets.yaml +++ b/clustergroup/templates/applicationsets.yaml @@ -26,7 +26,7 @@ spec: {{- end }} source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} - targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} + targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} {{- if .chart }} chart: {{ .chart }} {{- end }} @@ -39,7 +39,7 @@ spec: {{- if not .kustomize }} helm: ignoreMissingValueFiles: true - valueFiles: | + valueFiles: - "/values-global.yaml" - "/values-{{ $.Values.clusterGroup.name }}.yaml" {{- range .extraValueFiles }} From c19b4ff2205c6774960f639dfe3800ab23dc04c2 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 3 Aug 2022 16:48:23 -0500 Subject: [PATCH 0406/1263] Add applicationset functionality to clustergroup --- clustergroup/templates/applications.yaml | 78 ++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 7e17afc3..334047bc 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -1,5 +1,82 @@ {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} {{- range .Values.clusterGroup.applications }} +{{- if or (.generators) (.generatorFile) (.useGeneratorValues) (.destinationURL) (.destinationNamespace) }} +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: {{ .name }} + namespace: {{ $namespace }} + labels: + app: {{ .name }} +spec: + {{- if .generators }} + generators: {{ .generators | toPrettyJson }} + {{- else }} + generators: + - git: + repoURL: {{ $.Values.global.repoURL }} + revision: {{ $.Values.global.targetRevision }} + {{- if .generatorFile }} + files: + - path: {{ .generatorFile | quote }} + {{- end }} + {{- end }} + template: + metadata: + name: {{ coalesce .namespace $namespace }} + spec: + project: {{ .project }} + {{- if .syncPolicy }} + syncPolicy: {{ .syncPolicy | toPrettyJson }} + {{- else }} + syncPolicy: + automated: {} + {{- end }} + {{- if .ignoreDifferences }} + ignoreDifferences: {{ .ignoreDifferences | toPrettyJson }} + {{- end }} + source: + repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} + targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} + {{- if .chart }} + chart: {{ .chart }} + {{- end }} + {{- if .path }} + path: {{ .path }} + {{- end }} + {{- if .plugin }} + plugin: {{ .plugin }} + {{- end }} + {{- if not .kustomize }} + helm: + ignoreMissingValueFiles: true + valueFiles: + - "values.yaml" + {{- range .extraValueFiles }} + - {{ . | quote }} + {{- end }} + {{- if .useGeneratorValues }} + values: |- + {{ `{{ values }}` }} + {{- end }} + parameters: + - name: global.hubClusterDomain + value: {{ $.Values.global.hubClusterDomain }} + - name: global.localClusterDomain + value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} + {{- range .overrides }} + - name: {{ .name }} + value: {{ .value | quote }} + {{- if .forceString }} + forceString: true + {{- end }} + {{- end }} + {{- end }} + destination: + server: {{ coalesce .destinationURL "https://kubernetes.default.svc" }} + namespace: {{ coalesce .destinationNamespace .namespace $namespace }} +{{- else }} +--- apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -70,3 +147,4 @@ spec: {{- end }} --- {{- end }} +{{- end }} From b0eefddccd82452a035109f3680265231757b63d Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 3 Aug 2022 16:57:27 -0500 Subject: [PATCH 0407/1263] Remove now redundany applicationset separate template file --- clustergroup/templates/applicationsets.yaml | 80 --------------------- 1 file changed, 80 deletions(-) delete mode 100644 clustergroup/templates/applicationsets.yaml diff --git a/clustergroup/templates/applicationsets.yaml b/clustergroup/templates/applicationsets.yaml deleted file mode 100644 index 860eb20d..00000000 --- a/clustergroup/templates/applicationsets.yaml +++ /dev/null @@ -1,80 +0,0 @@ -{{/* Only define this if there are any applicationSets defined */}} -{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} -{{- range .Values.clusterGroup.applicationsets }} -apiVersion: argoproj.io/v1alpha1 -kind: ApplicationSet -metadata: - name: {{ .name }} - namespace: {{ $namespace }} - labels: - app: {{ .name }} -spec: - generators: {{ .generators | toPrettyJson }} - template: - metadata: - name: {{ coalesce .namespace $namespace }} - spec: - project: {{ .project }} - {{- if .syncPolicy }} - syncPolicy: {{ .syncPolicy | toPrettyJson }} - {{- else }} - syncPolicy: - automated: {} - {{- end }} - {{- if .ignoreDifferences }} - ignoreDifferences: {{ .ignoreDifferences | toPrettyJson }} - {{- end }} - source: - repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} - targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} - {{- if .chart }} - chart: {{ .chart }} - {{- end }} - {{- if .path }} - path: {{ .path }} - {{- end }} - {{- if .plugin }} - plugin: {{ .plugin }} - {{- end }} - {{- if not .kustomize }} - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-{{ $.Values.clusterGroup.name }}.yaml" - {{- range .extraValueFiles }} - - {{ . | quote }} - {{- end }} - parameters: - - name: global.hubClusterDomain - value: {{ $.Values.global.hubClusterDomain }} - - name: global.localClusterDomain - value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} - {{- range .overrides }} - - name: {{ .name }} - value: {{ .value | quote }} - {{- if .forceString }} - forceString: true - {{- end }} - {{- end }} - {{- end }} - #parameters: - # - name: global.repoURL - # value: $ARGOCD_APP_SOURCE_REPO_URL - # - name: global.targetRevision - # value: $ARGOCD_APP_SOURCE_TARGET_REVISION - # - name: global.namespace - # value: $ARGOCD_APP_NAMESPACE - # - name: global.pattern - # value: {{ $.Values.global.pattern }} - # - name: global.valuesDirectoryURL - # value: {{ coalesce .valuesDirectoryURL $.Values.global.valuesDirectoryURL }} - # - name: global.hubClusterDomain - # value: {{ $.Values.global.hubClusterDomain }} - # - name: global.localClusterDomain - # value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} - destination: - server: {{ coalesce .destinationURL "https://kubernetes.default.svc" }} - namespace: {{ coalesce .namespace $namespace }} -{{- end }} ---- From 15029f561c5f389eab3be02d413c791a2c1e107c Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 3 Aug 2022 16:59:43 -0500 Subject: [PATCH 0408/1263] Remove spurious YAML --- --- clustergroup/templates/applications.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 334047bc..da0325c1 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -76,7 +76,6 @@ spec: server: {{ coalesce .destinationURL "https://kubernetes.default.svc" }} namespace: {{ coalesce .destinationNamespace .namespace $namespace }} {{- else }} ---- apiVersion: argoproj.io/v1alpha1 kind: Application metadata: From fb32cd77a5ccaffa585a7421035e72dfcb51e78c Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 4 Aug 2022 09:01:07 -0500 Subject: [PATCH 0409/1263] destinationURL -> destinationServer + whitespace fix --- clustergroup/templates/applications.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index da0325c1..c3325e64 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -1,6 +1,6 @@ {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} {{- range .Values.clusterGroup.applications }} -{{- if or (.generators) (.generatorFile) (.useGeneratorValues) (.destinationURL) (.destinationNamespace) }} +{{- if or (.generators) (.generatorFile) (.useGeneratorValues) (.destinationServer) (.destinationNamespace) }} apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: @@ -37,7 +37,7 @@ spec: {{- end }} source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} - targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} + targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} {{- if .chart }} chart: {{ .chart }} {{- end }} @@ -73,7 +73,7 @@ spec: {{- end }} {{- end }} destination: - server: {{ coalesce .destinationURL "https://kubernetes.default.svc" }} + server: {{ coalesce .destinationServer "https://kubernetes.default.svc" }} namespace: {{ coalesce .destinationNamespace .namespace $namespace }} {{- else }} apiVersion: argoproj.io/v1alpha1 From 1d07a05863ba293d1509ddeada24cd8f79f25804 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 4 Aug 2022 09:09:50 -0500 Subject: [PATCH 0410/1263] Add global pattern variables --- clustergroup/templates/applications.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index c3325e64..cea11787 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -64,6 +64,14 @@ spec: value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} + - name: global.repoURL + value: {{ $.Values.global.repoURL }} + - name: global.targetRevision + value: {{ $.Values.global.targetRevision }} + - name: global.namespace + value: {{ $.Values.global.namespace }} + - name: global.pattern + value: {{ $.Values.global.pattern }} {{- range .overrides }} - name: {{ .name }} value: {{ .value | quote }} From fd2e54f8fb86a00ed9d2c627e69bf516c6584663 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 11 Aug 2022 12:20:41 +0200 Subject: [PATCH 0411/1263] Support k8s 1.24 In kubernetes 1.24 (OCP 4.11) the token for service account does not get created anymore [1]. In order to support all of our supported OCP versions with the least effort, we just create a secret and associate it to the golang-external-secret service account. While we're at it we simplify the ansible a code a bit and simply always rely on having the golang-external-secrets secret created by default. Tested on OCP 4.8, 4.9, 4.10 and 4.11. In all tests I saw the config-demo-secret being created inside the cluster and 'make install' succeeded correctly. Closes: https://issues.redhat.com/browse/MBP-321 [1] https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.24.md#deprecation= --- ansible/roles/vault_utils/defaults/main.yml | 1 + .../vault_utils/tasks/vault_secrets_init.yaml | 24 ++----------------- ...ternal-secrets-hub-clusterrolebinding.yaml | 9 +++++++ ...lang-external-secrets-hub-secretstore.yaml | 3 ++- ...olang-external-secrets-naked.expected.yaml | 13 +++++++++- ...lang-external-secrets-normal.expected.yaml | 13 +++++++++- 6 files changed, 38 insertions(+), 25 deletions(-) diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index a3189bd6..04fccf7b 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -17,6 +17,7 @@ vault_pki_max_lease_ttl: "8760h" output_file: "common/pattern-vault.init" external_secrets_ns: golang-external-secrets external_secrets_sa: golang-external-secrets +external_secrets_secret: golang-external-secrets # Setting this to false makes the role store the vault unseal keys and root login # token inside a secret in the cluster. # *Note* that this is fundamentally unsafe diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 4bc1edb7..2c89f285 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -33,34 +33,14 @@ command: "vault auth enable -path={{ vault_hub }} kubernetes" when: kubernetes_enabled.rc != 0 -- name: Fetch "{{ external_secrets_ns }}/{{ external_secrets_sa }} secret name" - kubernetes.core.k8s_info: - kind: ServiceAccount - namespace: "{{ external_secrets_ns }}" - name: "{{ external_secrets_sa }}" - api_version: v1 - register: serviceaccount - -# FIXME: we could bail out nicely if secrets is empty -- name: Get "{{ external_secret_sa }} secrets" - ansible.builtin.set_fact: - secrets: "{{ serviceaccount.resources[0].secrets }}" - -- name: Filter secrets of "{{ external_secrets_ns }}/{{ external_secrets_sa }}" - ansible.builtin.set_fact: - secret: "{{ item.name }}" - when: '"golang-external-secrets-token-" in item.name' - loop: "{{ secrets }}" - -- name: Get token of the secret +- name: Get token kubernetes.core.k8s_info: kind: Secret namespace: "{{ external_secrets_ns }}" - name: "{{ secret }}" + name: "{{ external_secrets_secret }}" api_version: v1 register: token_data -# FIXME: we could bail out nicely if token_data is empty - name: Set sa_token fact ansible.builtin.set_fact: sa_token: "{{ token_data.resources[0].data.token | b64decode }}" diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml index 17fd4835..a8ab9e78 100644 --- a/golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml +++ b/golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml @@ -1,4 +1,13 @@ --- +apiVersion: v1 +kind: Secret +metadata: + name: golang-external-secrets + namespace: golang-external-secrets + annotations: + kubernetes.io/service-account.name: golang-external-secrets +type: kubernetes.io/service-account-token +--- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml index 30483a16..6a95745d 100644 --- a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +++ b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml @@ -19,6 +19,7 @@ spec: kubernetes: mountPath: {{ .Values.mountPath }} role: {{ .Values.mountRole }} - serviceAccountRef: + secretRef: name: golang-external-secrets namespace: golang-external-secrets + key: "token" diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 4070d8ae..caad1538 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -52,6 +52,16 @@ metadata: app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml +apiVersion: v1 +kind: Secret +metadata: + name: golang-external-secrets + namespace: golang-external-secrets + annotations: + kubernetes.io/service-account.name: golang-external-secrets +type: kubernetes.io/service-account-token +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -5718,9 +5728,10 @@ spec: kubernetes: mountPath: hub role: hub-role - serviceAccountRef: + secretRef: name: golang-external-secrets namespace: golang-external-secrets + key: "token" --- # Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml apiVersion: admissionregistration.k8s.io/v1 diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 4070d8ae..caad1538 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -52,6 +52,16 @@ metadata: app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml +apiVersion: v1 +kind: Secret +metadata: + name: golang-external-secrets + namespace: golang-external-secrets + annotations: + kubernetes.io/service-account.name: golang-external-secrets +type: kubernetes.io/service-account-token +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -5718,9 +5728,10 @@ spec: kubernetes: mountPath: hub role: hub-role - serviceAccountRef: + secretRef: name: golang-external-secrets namespace: golang-external-secrets + key: "token" --- # Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml apiVersion: admissionregistration.k8s.io/v1 From f093a99106f177402521789cb765c6e43ae6043a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 Aug 2022 09:28:08 +0200 Subject: [PATCH 0412/1263] Split long lines for easier readability --- Makefile | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 2a5177e4..73d7191a 100644 --- a/Makefile +++ b/Makefile @@ -8,12 +8,16 @@ TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spec.domain}) # --set values always take precedence over the contents of -f -HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) -TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" --set clusterGroup.insecureUnsealVaultInsideCluster=true +HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) \ + --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) +TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" \ + --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" \ + --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com \ + --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" \ + --set clusterGroup.insecureUnsealVaultInsideCluster=true PATTERN_OPTS=-f common/examples/values-example.yaml EXECUTABLES=git helm oc ansible - .PHONY: help help: ## This help message @printf "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)\n" From 72306f861afc6501ba657dbcdfa6bb4cd2b325d2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 Aug 2022 09:40:51 +0200 Subject: [PATCH 0413/1263] Add a DISABLE_LINTERS variable to super-linter target This way in the patterns we can just rely on the common target and disable additional linters via the DISABLE_LINTERS variable. --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 73d7191a..117cfab4 100644 --- a/Makefile +++ b/Makefile @@ -88,6 +88,7 @@ super-linter: ## Runs super linter locally -e VALIDATE_JSCPD=false \ -e VALIDATE_KUBERNETES_KUBEVAL=false \ -e VALIDATE_YAML=false \ + $(DISABLE_LINTERS) \ -v $(PWD):/tmp/lint:rw,z docker.io/github/super-linter:slim-v4 ansible-lint: ## run ansible lint on ansible/ folder From 3ab539360ab91cfd40a09f80cff282e7d6749de2 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 24 Aug 2022 09:27:00 -0500 Subject: [PATCH 0414/1263] Change conditionals on imperative framework to allow unseal or job or both --- clustergroup/templates/imperative/clusterrole.yaml | 5 +++-- clustergroup/templates/imperative/configmap.yaml | 5 +++-- clustergroup/templates/imperative/job.yaml | 4 ++-- clustergroup/templates/imperative/namespace.yaml | 5 +++-- clustergroup/templates/imperative/rbac.yaml | 5 +++-- clustergroup/templates/imperative/role.yaml | 5 +++-- clustergroup/templates/imperative/serviceaccount.yaml | 5 +++-- 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/clustergroup/templates/imperative/clusterrole.yaml b/clustergroup/templates/imperative/clusterrole.yaml index ca2015a8..17e33d8d 100644 --- a/clustergroup/templates/imperative/clusterrole.yaml +++ b/clustergroup/templates/imperative/clusterrole.yaml @@ -1,5 +1,6 @@ -{{/* Only define this if there are any imperativejobs defined */}} -{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +{{/* Define this if needed (jobs defined or insecure unseal configured) */}} +{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) + (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/clustergroup/templates/imperative/configmap.yaml b/clustergroup/templates/imperative/configmap.yaml index b9f91682..5cde2d37 100644 --- a/clustergroup/templates/imperative/configmap.yaml +++ b/clustergroup/templates/imperative/configmap.yaml @@ -1,5 +1,6 @@ -{{/* Only define this if there are any imperativejobs defined */}} -{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +{{/* Define this if needed (jobs defined or insecure unseal configured) */}} +{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) + (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} {{- $valuesyaml := toYaml $.Values -}} apiVersion: v1 kind: ConfigMap diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index bdbfa596..b237e11f 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -1,5 +1,5 @@ -{{/* Only define this if there are any imperativejobs defined */}} -{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +{{/* Define this if needed (jobs defined */}} +{{- if (gt (len $.Values.clusterGroup.imperative.jobs) 0) -}} --- apiVersion: batch/v1 kind: CronJob diff --git a/clustergroup/templates/imperative/namespace.yaml b/clustergroup/templates/imperative/namespace.yaml index 5f7778d4..827bbee5 100644 --- a/clustergroup/templates/imperative/namespace.yaml +++ b/clustergroup/templates/imperative/namespace.yaml @@ -1,5 +1,6 @@ -{{/* Only define this if there are any imperativejobs defined */}} -{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +{{/* Define this if needed (jobs defined or insecure unseal configured) */}} +{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) + (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} apiVersion: v1 kind: Namespace metadata: diff --git a/clustergroup/templates/imperative/rbac.yaml b/clustergroup/templates/imperative/rbac.yaml index 4b83b124..f62b23ac 100644 --- a/clustergroup/templates/imperative/rbac.yaml +++ b/clustergroup/templates/imperative/rbac.yaml @@ -1,5 +1,6 @@ -{{/* Only define this if there are any imperativejobs defined */}} -{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +{{/* Define this if needed (jobs defined or insecure unseal configured) */}} +{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) + (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/clustergroup/templates/imperative/role.yaml b/clustergroup/templates/imperative/role.yaml index 3c495c8f..f4909c76 100644 --- a/clustergroup/templates/imperative/role.yaml +++ b/clustergroup/templates/imperative/role.yaml @@ -1,5 +1,6 @@ -{{/* Only define this if there are any imperativejobs defined */}} -{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +{{/* Define this if needed (jobs defined or insecure unseal configured) */}} +{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) + (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/clustergroup/templates/imperative/serviceaccount.yaml b/clustergroup/templates/imperative/serviceaccount.yaml index af1ccba5..bb500deb 100644 --- a/clustergroup/templates/imperative/serviceaccount.yaml +++ b/clustergroup/templates/imperative/serviceaccount.yaml @@ -1,5 +1,6 @@ -{{/* Only define this if there are any imperativejobs defined */}} -{{- if gt (len $.Values.clusterGroup.imperative.jobs) 0 -}} +{{/* Define this if needed (jobs defined or insecure unseal configured) */}} +{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) + (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} {{- if $.Values.clusterGroup.imperative.serviceAccountCreate -}} apiVersion: v1 kind: ServiceAccount From bdb82ab5209a347ecc8a712efb3f9f4ec9fbf657 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 26 Aug 2022 09:26:48 +0200 Subject: [PATCH 0415/1263] Upgrade external secrets to 0.5.9 Tested on MCG --- .gitignore | 1 + golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.5.7.tgz | Bin 39371 -> 0 bytes .../charts/external-secrets-0.5.9.tgz | Bin 0 -> 40987 bytes ...olang-external-secrets-naked.expected.yaml | 236 +++++++++++++----- ...lang-external-secrets-normal.expected.yaml | 236 +++++++++++++----- 6 files changed, 350 insertions(+), 125 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.5.7.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.5.9.tgz diff --git a/.gitignore b/.gitignore index 34ec269e..fd2282bc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ values-secret.yaml pattern-vault.init pattern-vault.init.bak super-linter.log +golang-external-secrets/Chart.lock diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index d988604c..8fbec047 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.5.7" + version: "0.5.9" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.5.7.tgz b/golang-external-secrets/charts/external-secrets-0.5.7.tgz deleted file mode 100644 index 47356aa69dfdda93d754ad0fe0796c969373a372..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39371 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

QzOHocYPA`p?4@$@ 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

QzOHocYPA`p?4@$@ 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 a=u2Pv(wDzh`F{Zb0RR7MQHBiw9tHsG>&of? diff --git a/golang-external-secrets/charts/external-secrets-0.5.9.tgz b/golang-external-secrets/charts/external-secrets-0.5.9.tgz new file mode 100644 index 0000000000000000000000000000000000000000..3d5b93f969793a05cbfc12ed95d1547568084e9a GIT binary patch literal 40987 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 a3Pw*e84!s_2mlTa1_RN#9>Xaj!~HKK%^wf| literal 0 HcmV?d00001 diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index caad1538..7d9fa628 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: clusterexternalsecrets.external-secrets.io spec: @@ -83,7 +83,20 @@ spec: singular: clusterexternalsecret scope: Cluster versions: - - name: v1beta1 + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 schema: openAPIV3Schema: description: ClusterExternalSecret is the Schema for the clusterexternalsecrets API. @@ -117,6 +130,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -142,8 +159,6 @@ spec: dataFrom: description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order items: - maxProperties: 1 - minProperties: 1 properties: extract: description: Used to extract multiple key/value pairs from one secret @@ -152,6 +167,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -174,6 +193,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string name: description: Finds secrets based on the name. properties: @@ -190,6 +213,25 @@ spec: description: Find secrets based on tags. type: object type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array type: object type: array refreshInterval: @@ -334,6 +376,7 @@ spec: 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 refreshTime: description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. type: string @@ -400,7 +443,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: clustersecretstores.external-secrets.io spec: @@ -1424,6 +1467,12 @@ spec: - jsonPath: .metadata.creationTimestamp name: AGE type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string name: v1beta1 schema: openAPIV3Schema: @@ -1806,7 +1855,23 @@ spec: properties: auth: description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object secretRef: properties: secretApiKeySecretRef: @@ -1823,8 +1888,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object serviceUrl: description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance @@ -1915,13 +1978,13 @@ spec: description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2287,13 +2350,13 @@ spec: description: The provider for the CA bundle to use to validate Vault server certificate. properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2559,7 +2622,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: externalsecrets.external-secrets.io spec: @@ -2801,6 +2864,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string name: v1beta1 schema: openAPIV3Schema: @@ -2829,6 +2895,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -2854,8 +2924,6 @@ spec: dataFrom: description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order items: - maxProperties: 1 - minProperties: 1 properties: extract: description: Used to extract multiple key/value pairs from one secret @@ -2864,6 +2932,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -2886,6 +2958,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string name: description: Finds secrets based on the name. properties: @@ -2902,6 +2978,25 @@ spec: description: Find secrets based on tags. type: object type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array type: object type: array refreshInterval: @@ -3067,7 +3162,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: secretstores.external-secrets.io spec: @@ -4094,6 +4189,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string name: v1beta1 schema: openAPIV3Schema: @@ -4476,7 +4574,23 @@ spec: properties: auth: description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object secretRef: properties: secretApiKeySecretRef: @@ -4493,8 +4607,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object serviceUrl: description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance @@ -4585,13 +4697,13 @@ spec: description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -4957,13 +5069,13 @@ spec: description: The provider for the CA bundle to use to validate Vault server certificate. properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -5230,10 +5342,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5288,10 +5400,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5379,10 +5491,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -5405,10 +5517,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -5432,10 +5544,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5452,10 +5564,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5488,10 +5600,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5527,10 +5639,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5548,10 +5660,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook spec: @@ -5572,10 +5684,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5592,7 +5704,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.5.7" + image: "ghcr.io/external-secrets/external-secrets:v0.5.9" imagePullPolicy: IfNotPresent args: - certcontroller @@ -5619,10 +5731,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5639,7 +5751,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.5.7" + image: "ghcr.io/external-secrets/external-secrets:v0.5.9" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -5655,10 +5767,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5676,7 +5788,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.5.7" + image: "ghcr.io/external-secrets/external-secrets:v0.5.9" imagePullPolicy: IfNotPresent args: - webhook @@ -5698,13 +5810,13 @@ spec: initialDelaySeconds: 20 periodSeconds: 5 volumeMounts: - - name: certs - mountPath: /tmp/certs - readOnly: true + - name: certs + mountPath: /tmp/certs + readOnly: true volumes: - - name: certs - secret: - secretName: golang-external-secrets-webhook + - name: certs + secret: + secretName: golang-external-secrets-webhook --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index caad1538..7d9fa628 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: clusterexternalsecrets.external-secrets.io spec: @@ -83,7 +83,20 @@ spec: singular: clusterexternalsecret scope: Cluster versions: - - name: v1beta1 + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 schema: openAPIV3Schema: description: ClusterExternalSecret is the Schema for the clusterexternalsecrets API. @@ -117,6 +130,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -142,8 +159,6 @@ spec: dataFrom: description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order items: - maxProperties: 1 - minProperties: 1 properties: extract: description: Used to extract multiple key/value pairs from one secret @@ -152,6 +167,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -174,6 +193,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string name: description: Finds secrets based on the name. properties: @@ -190,6 +213,25 @@ spec: description: Find secrets based on tags. type: object type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array type: object type: array refreshInterval: @@ -334,6 +376,7 @@ spec: 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 refreshTime: description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. type: string @@ -400,7 +443,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: clustersecretstores.external-secrets.io spec: @@ -1424,6 +1467,12 @@ spec: - jsonPath: .metadata.creationTimestamp name: AGE type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string name: v1beta1 schema: openAPIV3Schema: @@ -1806,7 +1855,23 @@ spec: properties: auth: description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object secretRef: properties: secretApiKeySecretRef: @@ -1823,8 +1888,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object serviceUrl: description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance @@ -1915,13 +1978,13 @@ spec: description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2287,13 +2350,13 @@ spec: description: The provider for the CA bundle to use to validate Vault server certificate. properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2559,7 +2622,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: externalsecrets.external-secrets.io spec: @@ -2801,6 +2864,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string name: v1beta1 schema: openAPIV3Schema: @@ -2829,6 +2895,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -2854,8 +2924,6 @@ spec: dataFrom: description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order items: - maxProperties: 1 - minProperties: 1 properties: extract: description: Used to extract multiple key/value pairs from one secret @@ -2864,6 +2932,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -2886,6 +2958,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string name: description: Finds secrets based on the name. properties: @@ -2902,6 +2978,25 @@ spec: description: Find secrets based on tags. type: object type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array type: object type: array refreshInterval: @@ -3067,7 +3162,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: secretstores.external-secrets.io spec: @@ -4094,6 +4189,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string name: v1beta1 schema: openAPIV3Schema: @@ -4476,7 +4574,23 @@ spec: properties: auth: description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object secretRef: properties: secretApiKeySecretRef: @@ -4493,8 +4607,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object serviceUrl: description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance @@ -4585,13 +4697,13 @@ spec: description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -4957,13 +5069,13 @@ spec: description: The provider for the CA bundle to use to validate Vault server certificate. properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -5230,10 +5342,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5288,10 +5400,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5379,10 +5491,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -5405,10 +5517,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -5432,10 +5544,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5452,10 +5564,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5488,10 +5600,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5527,10 +5639,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5548,10 +5660,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook spec: @@ -5572,10 +5684,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5592,7 +5704,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.5.7" + image: "ghcr.io/external-secrets/external-secrets:v0.5.9" imagePullPolicy: IfNotPresent args: - certcontroller @@ -5619,10 +5731,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5639,7 +5751,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.5.7" + image: "ghcr.io/external-secrets/external-secrets:v0.5.9" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -5655,10 +5767,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5676,7 +5788,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.5.7" + image: "ghcr.io/external-secrets/external-secrets:v0.5.9" imagePullPolicy: IfNotPresent args: - webhook @@ -5698,13 +5810,13 @@ spec: initialDelaySeconds: 20 periodSeconds: 5 volumeMounts: - - name: certs - mountPath: /tmp/certs - readOnly: true + - name: certs + mountPath: /tmp/certs + readOnly: true volumes: - - name: certs - secret: - secretName: golang-external-secrets-webhook + - name: certs + secret: + secretName: golang-external-secrets-webhook --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 From 4a88dab8fc1fa559b75adf2b88881467b19d83e2 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 26 Aug 2022 16:26:56 -0500 Subject: [PATCH 0420/1263] Update noqa and remove trailing space --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 96812e15..b3a1f17c 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -149,11 +149,11 @@ label: "{{ item.key }}" - name: Set files vault commands fact - # noqa: yaml + # noqa yaml ansible.builtin.set_fact: secret_files_vault_cmds: "{{ secret_files_vault_cmds | combine({ item.key: vault_cmd}) }}" vars: - vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'cat - > /tmp/vault_content'; oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'base64 --wrap=0 /tmp/vault_content | vault kv put {{ vault_path }}/{{ item.key }} b64content=- content=@/tmp/vault_content; rm /tmp/vault_content'" + vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'cat - > /tmp/vault_content'; oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'base64 --wrap=0 /tmp/vault_content | vault kv put {{ vault_path }}/{{ item.key }} b64content=- content=@/tmp/vault_content; rm /tmp/vault_content'" loop: "{{ file_secrets | dict2items }}" loop_control: From 6db9638862a284485424089a154c296980341fc4 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 26 Aug 2022 16:27:44 -0500 Subject: [PATCH 0421/1263] Put noqa on the right line --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index b3a1f17c..8fbd506b 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -149,11 +149,10 @@ label: "{{ item.key }}" - name: Set files vault commands fact - # noqa yaml ansible.builtin.set_fact: secret_files_vault_cmds: "{{ secret_files_vault_cmds | combine({ item.key: vault_cmd}) }}" vars: - vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'cat - > /tmp/vault_content'; oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'base64 --wrap=0 /tmp/vault_content | vault kv put {{ vault_path }}/{{ item.key }} b64content=- content=@/tmp/vault_content; rm /tmp/vault_content'" + vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'cat - > /tmp/vault_content'; oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'base64 --wrap=0 /tmp/vault_content | vault kv put {{ vault_path }}/{{ item.key }} b64content=- content=@/tmp/vault_content; rm /tmp/vault_content'" # noqa yaml loop: "{{ file_secrets | dict2items }}" loop_control: From 5985662ceb4a5241d665b919f0e98811472c6f7c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 27 Aug 2022 15:11:37 +0200 Subject: [PATCH 0422/1263] Remove whitespace at the end of the line --- examples/values-example.yaml | 4 ++-- reference-output.yaml | 4 ++-- values-global.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 7d3168c9..c6ea7186 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -17,7 +17,7 @@ clusterGroup: namespace: open-cluster-management channel: release-2.4 csv: advanced-cluster-management.v2.4.1 - + odh: name: opendatahub-operator source: community-operators @@ -30,7 +30,7 @@ clusterGroup: projects: - datacenter - + applications: acm: name: acm diff --git a/reference-output.yaml b/reference-output.yaml index aa0e27e7..cd59ac24 100644 --- a/reference-output.yaml +++ b/reference-output.yaml @@ -31,11 +31,11 @@ kind: Secret apiVersion: v1 metadata: name: s3-secret -type: Opaque +type: Opaque data: # Pre-create as part of the initial 'helm install' chart # Create a file with the following: - # s3.accessKey: KEY + # s3.accessKey: KEY # s3.secretKey: secret key #application.properties: base64 encrypted value of the above file # This should live in the values-secret.yaml file diff --git a/values-global.yaml b/values-global.yaml index dcda0607..8a890f3d 100644 --- a/values-global.yaml +++ b/values-global.yaml @@ -6,7 +6,7 @@ global: git: hostname: github.com - # Account is the user or organization under which the pattern repos lives + # Account is the user or organization under which the pattern repos lives account: hybrid-cloud-patterns email: someone@somewhere.com dev_revision: main From fdc6e49648d44c661bd9578bd0b35b7b284be65c Mon Sep 17 00:00:00 2001 From: ruromero Date: Mon, 29 Aug 2022 10:48:08 +0200 Subject: [PATCH 0423/1263] Fix lint warnings and avoid using json_path Signed-off-by: ruromero --- ansible/roles/vault_utils/tasks/vault_status.yaml | 11 +++++++---- ansible/roles/vault_utils/tasks/vault_unseal.yaml | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_status.yaml b/ansible/roles/vault_utils/tasks/vault_status.yaml index 54ae96a7..374a0bf0 100644 --- a/ansible/roles/vault_utils/tasks/vault_status.yaml +++ b/ansible/roles/vault_utils/tasks/vault_status.yaml @@ -48,9 +48,12 @@ register: vault_pods_list - name: "Get pods" - set_fact: - vault_pods: "{{ vault_pods_list | json_query(\"resources[].metadata.name\") }}" + ansible.builtin.set_fact: + vault_pods: "{{ vault_pods + [item.metadata.name] }}" + loop: "{{ vault_pods_list.resources }}" + vars: + vault_pods: [] - name: "Followers" - set_fact: - followers: "{{ vault_pods | difference(vault_pod) }}" \ No newline at end of file + ansible.builtin.set_fact: + followers: "{{ vault_pods | difference(vault_pod) }}" diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index 6454bc10..5de1840a 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -96,7 +96,7 @@ namespace: "{{ vault_ns }}" pod: "{{ item.0 }}" command: vault operator unseal "{{ item.1 }}" - loop: "{{ followers|product(unseal_keys)|list }}" + loop: "{{ followers | product(unseal_keys) | list }}" loop_control: extended: true label: "Unsealing {{ item.0 }} with key {{ ansible_loop.index }}" From 46a6210c4c91e633edc2b3f5d5bb953914a87744 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 29 Aug 2022 13:12:13 +0200 Subject: [PATCH 0424/1263] Use Apache 2.0 License --- LICENSE | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 18 +++++ 2 files changed, 220 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README.md b/README.md new file mode 100644 index 00000000..9c6fa0cb --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# Validated Patterns common/ repository + +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +## Start Here + +This repository is never used as standalone. It is usually imported in each pattern as a subtree. +In order to import the common/ the very first time you can use +`https://github.com/hybrid-cloud-patterns/multicloud-gitops/blob/main/common/scripts/make_common_subtree.sh` + +In order to update your common subtree inside your pattern repository you can either use +`https://github.com/hybrid-cloud-patterns/utilities/blob/main/scripts/update-common-everywhere.sh` or +do it manually by doing the following: + +```sh +git remote add -f upstream-common https://github.com/hybrid-cloud-patterns/common.git +git merge -s subtree -Xtheirs -Xsubtree=common upstream-common/ha-vault +``` From 15eb0e687410a53f0fc0b086d2113323de6fc7cc Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 29 Aug 2022 14:52:18 +0200 Subject: [PATCH 0425/1263] Silence Get Pods loop a bit Otherwise we get the printout of all the metadata returned by the pod list call. Tested on an MCG cluster. --- ansible/roles/vault_utils/tasks/vault_status.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/vault_status.yaml b/ansible/roles/vault_utils/tasks/vault_status.yaml index 374a0bf0..319ac393 100644 --- a/ansible/roles/vault_utils/tasks/vault_status.yaml +++ b/ansible/roles/vault_utils/tasks/vault_status.yaml @@ -51,6 +51,9 @@ ansible.builtin.set_fact: vault_pods: "{{ vault_pods + [item.metadata.name] }}" loop: "{{ vault_pods_list.resources }}" + loop_control: + extended: true + label: "{{ ansible_loop.index }}" vars: vault_pods: [] From 912c14e7443ae83e6d4be264da4c8e9bf915458b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 29 Aug 2022 15:10:04 +0200 Subject: [PATCH 0426/1263] Print the name of the pod when getting pods This will make things a bit simpler to debug as the index is not very interesting. Also remove the extended loop as it just uses up memory needlessly --- ansible/roles/vault_utils/tasks/vault_status.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_status.yaml b/ansible/roles/vault_utils/tasks/vault_status.yaml index 319ac393..9dc3e426 100644 --- a/ansible/roles/vault_utils/tasks/vault_status.yaml +++ b/ansible/roles/vault_utils/tasks/vault_status.yaml @@ -52,8 +52,7 @@ vault_pods: "{{ vault_pods + [item.metadata.name] }}" loop: "{{ vault_pods_list.resources }}" loop_control: - extended: true - label: "{{ ansible_loop.index }}" + label: "{{ item.metadata.name }}" vars: vault_pods: [] From 8f03fa648d300adec315e0fb3cdb4afc4d734014 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 1 Sep 2022 08:51:08 -0500 Subject: [PATCH 0427/1263] Call expanduser on file to match stat behavior --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 8fbd506b..3569d24a 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -152,7 +152,7 @@ ansible.builtin.set_fact: secret_files_vault_cmds: "{{ secret_files_vault_cmds | combine({ item.key: vault_cmd}) }}" vars: - vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'cat - > /tmp/vault_content'; oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'base64 --wrap=0 /tmp/vault_content | vault kv put {{ vault_path }}/{{ item.key }} b64content=- content=@/tmp/vault_content; rm /tmp/vault_content'" # noqa yaml + vault_cmd: "cat '{{ item.value | expanduser }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'cat - > /tmp/vault_content'; oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'base64 --wrap=0 /tmp/vault_content | vault kv put {{ vault_path }}/{{ item.key }} b64content=- content=@/tmp/vault_content; rm /tmp/vault_content'" # noqa yaml loop: "{{ file_secrets | dict2items }}" loop_control: From 663e4397592ee40db882fa82e28fe29fc14a1a96 Mon Sep 17 00:00:00 2001 From: ruromero Date: Thu, 1 Sep 2022 16:01:53 +0200 Subject: [PATCH 0428/1263] Set retries for joining raft cluster Signed-off-by: ruromero --- ansible/roles/vault_utils/tasks/vault_unseal.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index 5de1840a..10e63629 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -83,6 +83,10 @@ namespace: "{{ vault_ns }}" pod: "{{ item }}" command: vault operator raft join http://{{ vault_pod }}.{{ vault_ns }}-internal:8200 + register: join_raft_cluster_out + until: join_raft_cluster_out is not failed + retries: 10 + delay: 15 loop: "{{ followers }}" loop_control: extended: true From 174725ac3e37a03466fb2fa3b6ccb8420ae18b17 Mon Sep 17 00:00:00 2001 From: ruromero Date: Thu, 1 Sep 2022 21:59:34 +0200 Subject: [PATCH 0429/1263] Remove CSV after uninstalling Signed-off-by: ruromero --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 117cfab4..3d2881c8 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set glo --set clusterGroup.insecureUnsealVaultInsideCluster=true PATTERN_OPTS=-f common/examples/values-example.yaml EXECUTABLES=git helm oc ansible +CSV=$(shell oc get subscriptions -n openshift-operators openshift-gitops-operator -ojsonpath={.status.currentCSV}) .PHONY: help help: ## This help message @@ -70,6 +71,7 @@ operator-deploy operator-upgrade: validate-origin ## runs helm install uninstall: ## runs helm uninstall helm uninstall $(NAME) + @oc delete csv -n openshift-operators $(CSV) vault-init: ## inits, unseals and configured the vault common/scripts/vault-utils.sh vault_init common/pattern-vault.init From ce7b037c9be8264a4a487bf43cec3a548fc21ba3 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 1 Sep 2022 15:42:58 -0500 Subject: [PATCH 0430/1263] Standardize sync-policy handling and deprecate variable that does not vary --- clustergroup/templates/applications.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index cea11787..350f4446 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -147,11 +147,13 @@ spec: {{- if .ignoreDifferences }} ignoreDifferences: {{ .ignoreDifferences | toPrettyJson }} {{- end }} -{{- if eq $.Values.global.options.syncPolicy "Automatic" }} + {{- if .syncPolicy }} + syncPolicy: {{ .syncPolicy | toPrettyJson }} + {{- else }} syncPolicy: automated: {} # selfHeal: true -{{- end }} + {{- end }} --- {{- end }} {{- end }} From 684cff79acc1fadb79caa8a05d5dfdfc73cd589a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 31 Aug 2022 16:21:49 +1000 Subject: [PATCH 0431/1263] Draft of a cluster pool chart based on https://github.com/one-touch-provisioning/otp-gitops-clusters --- charts/clusterpools/aws/.gitignore | 4 + charts/clusterpools/aws/.helmignore | 1 + charts/clusterpools/aws/Chart.yaml | 5 + .../aws/templates/_install-config.aws.tpl | 37 +++++++ .../aws/templates/clusterpool.yaml | 104 ++++++++++++++++++ charts/clusterpools/aws/values.yaml | 22 ++++ 6 files changed, 173 insertions(+) create mode 100644 charts/clusterpools/aws/.gitignore create mode 100644 charts/clusterpools/aws/.helmignore create mode 100644 charts/clusterpools/aws/Chart.yaml create mode 100644 charts/clusterpools/aws/templates/_install-config.aws.tpl create mode 100644 charts/clusterpools/aws/templates/clusterpool.yaml create mode 100755 charts/clusterpools/aws/values.yaml diff --git a/charts/clusterpools/aws/.gitignore b/charts/clusterpools/aws/.gitignore new file mode 100644 index 00000000..684bd48c --- /dev/null +++ b/charts/clusterpools/aws/.gitignore @@ -0,0 +1,4 @@ +id_* +id_*.pub +pullsecret +create.sh \ No newline at end of file diff --git a/charts/clusterpools/aws/.helmignore b/charts/clusterpools/aws/.helmignore new file mode 100644 index 00000000..90ffe6b9 --- /dev/null +++ b/charts/clusterpools/aws/.helmignore @@ -0,0 +1 @@ +templates/install-config.aws.yaml \ No newline at end of file diff --git a/charts/clusterpools/aws/Chart.yaml b/charts/clusterpools/aws/Chart.yaml new file mode 100644 index 00000000..85dfd6df --- /dev/null +++ b/charts/clusterpools/aws/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +name: aws-clusterpools +version: "1.3" +kubeVersion: ">= 1.9.0" +description: RHACM ClusterPools for AWS hosted OCP using ExternalSecrets Operator diff --git a/charts/clusterpools/aws/templates/_install-config.aws.tpl b/charts/clusterpools/aws/templates/_install-config.aws.tpl new file mode 100644 index 00000000..005495f8 --- /dev/null +++ b/charts/clusterpools/aws/templates/_install-config.aws.tpl @@ -0,0 +1,37 @@ +{{- define "cluster.install-config" -}} +apiVersion: v1 +metadata: + name: '{{ .name }}' +baseDomain: {{ .provider.baseDomain }} +controlPlane: + architecture: amd64 + hyperthreading: Enabled + name: master + replicas: 3 + platform: + aws: + type: {{ .masters.machineType }} +compute: +- hyperthreading: Enabled + architecture: amd64 + name: 'worker' + replicas: {{ .workers.count }} + platform: + aws: + type: {{ .workers.machineType }} +networking: + clusterNetwork: + - cidr: 10.128.0.0/14 + hostPrefix: 23 + machineNetwork: + - cidr: 10.0.0.0/16 + networkType: OpenShiftSDN + serviceNetwork: + - 172.30.0.0/16 +platform: + aws: + region: {{ .provider.region }} +pullSecret: "" # skip, hive will inject based on it's secrets +sshKey: |- + {{ .provider.sshPublickey }} +{{- end -}} \ No newline at end of file diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml new file mode 100644 index 00000000..3c0e504f --- /dev/null +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -0,0 +1,104 @@ +{{- range .Values.clusterPools }} +{{ if .count }} +apiVersion: hive.openshift.io/v1 +kind: ClusterPool +metadata: + name: '{{ .name }}' + annotations: + argocd.argoproj.io/sync-wave: "320" + helm.sh/hook-weight: "320" + labels: + cloud: 'AWS' + region: '{{ .provider.region }}' + vendor: OpenShift +spec: + size: {{ .count }} + runningCount: 0 + baseDomain: {{ .provider.baseDomain }} + installConfigSecretTemplateRef: + name: {{ .name }}-install-config + imageSetRef: + name: img{{ .version }}-x86-64-appsub + pullSecretRef: + name: {{ .name }}-pull-secret + skipMachinePools: true # Disable MachinePool as using custom install-config + platform: + aws: + credentialsSecretRef: + name: {{ .name }}-aws-creds + region: {{ .provider.region }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ .name }}-install-config + annotations: + argocd.argoproj.io/sync-wave: "310" + helm.sh/hook-weight: "310" + namespace: rhacm-clusterpools +data: + # Base64 encoding of install-config yaml + install-config.yaml: {{ include "cluster.install-config" . | b64enc }} +type: Opaque +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ .name }}-pull-secret + annotations: + argocd.argoproj.io/sync-wave: "310" + helm.sh/hook-weight: "310" + namespace: rhacm-clusterpools +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: adc32372-cafe-0488-a9ab-6a971a6ca2d8 + refreshInterval: 24h0m0s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: {{ .name }}-pull-secret + creationPolicy: Owner + template: + type: kubernetes.io/dockerconfigjson + data: + .dockerconfigjson: |- + {{ "{{ .openshiftPullSecret | toString }}" }} +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ .name }}-aws-creds + annotations: + argocd.argoproj.io/sync-wave: "310" + helm.sh/hook-weight: "310" + namespace: rhacm-clusterpools +spec: + data: + - secretKey: awsAccessKey + remoteRef: + key: 0d1d17a7-9d6b-a2b4-6b20-04d87f770fce + - secretKey: awsSecretKey + remoteRef: + key: 57afa209-35b7-da90-5b92-9621cc27030c + refreshInterval: 24h0m0s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: {{ .name }}-aws-creds + creationPolicy: Owner + template: + type: Opaque + data: + aws_access_key_id: |- + {{ "{{ .awsAccessKey | toString }}" }} + aws_secret_access_key: |- + {{ "{{ .awsSecretKey | toString }}" }} +--- +{{- end }} +{{- end }} + + diff --git a/charts/clusterpools/aws/values.yaml b/charts/clusterpools/aws/values.yaml new file mode 100755 index 00000000..520f2a6d --- /dev/null +++ b/charts/clusterpools/aws/values.yaml @@ -0,0 +1,22 @@ +clusterPools: + poolA: + name: foo + count: 1 + version: 4.10.18 + + provider: + region: us-east-2 + baseDomain: + sshPublickey: + + masters: + machineType: m5.xlarge + + workers: + count: 3 + machineType: m5.xlarge + + +secretStore: + name: vault-backend + kind: ClusterSecretStore From ee67ff58c13cb4df406c0aa931fcea3c19500c7c Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 31 Aug 2022 19:46:39 +1000 Subject: [PATCH 0432/1263] Clean up and simplify external secrets --- .../aws/templates/clusterpool.yaml | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index 3c0e504f..80dc8c1d 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -25,7 +25,7 @@ spec: platform: aws: credentialsSecretRef: - name: {{ .name }}-aws-creds + name: {{ .name }}-creds region: {{ .provider.region }} --- apiVersion: v1 @@ -50,10 +50,10 @@ metadata: helm.sh/hook-weight: "310" namespace: rhacm-clusterpools spec: - data: - - secretKey: openshiftPullSecret - remoteRef: - key: adc32372-cafe-0488-a9ab-6a971a6ca2d8 + dataFrom: + - extract: + # Expects an entry called: .dockerconfigjson + key: {{ default 'secret/data/hub/openshiftPullSecret' .pullSecretKeyPath }} refreshInterval: 24h0m0s secretStoreRef: name: {{ $.Values.secretStore.name }} @@ -63,26 +63,24 @@ spec: creationPolicy: Owner template: type: kubernetes.io/dockerconfigjson - data: - .dockerconfigjson: |- - {{ "{{ .openshiftPullSecret | toString }}" }} --- apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: - name: {{ .name }}-aws-creds + name: {{ .name }}-creds annotations: argocd.argoproj.io/sync-wave: "310" helm.sh/hook-weight: "310" namespace: rhacm-clusterpools spec: - data: - - secretKey: awsAccessKey - remoteRef: - key: 0d1d17a7-9d6b-a2b4-6b20-04d87f770fce - - secretKey: awsSecretKey - remoteRef: - key: 57afa209-35b7-da90-5b92-9621cc27030c + dataFrom: + - extract: + # Expects an entry called: aws_access_key_id + key: {{ default 'secret/data/hub/aws/access_key_id' .awsAccessKeyIdPath }} + dataFrom: + - extract: + # Expects an entry called: aws_secret_access_key + key: {{ default 'secret/data/hub/aws/secret_access_key' .awsSecretKeyPath }} refreshInterval: 24h0m0s secretStoreRef: name: {{ $.Values.secretStore.name }} @@ -92,11 +90,6 @@ spec: creationPolicy: Owner template: type: Opaque - data: - aws_access_key_id: |- - {{ "{{ .awsAccessKey | toString }}" }} - aws_secret_access_key: |- - {{ "{{ .awsSecretKey | toString }}" }} --- {{- end }} {{- end }} From bd66e386a8bac130ed8a341e68bab38a1ff92557 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 31 Aug 2022 19:47:13 +1000 Subject: [PATCH 0433/1263] Disable the cluster pool by default --- charts/clusterpools/aws/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/clusterpools/aws/values.yaml b/charts/clusterpools/aws/values.yaml index 520f2a6d..c4ed9ad3 100755 --- a/charts/clusterpools/aws/values.yaml +++ b/charts/clusterpools/aws/values.yaml @@ -1,7 +1,7 @@ clusterPools: poolA: name: foo - count: 1 + count: 0 version: 4.10.18 provider: From 56a7d43cdf2fd6f26efa5940c6b7d9723ee7b305 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 31 Aug 2022 22:03:17 +1000 Subject: [PATCH 0434/1263] Assume multiple entries in the aws vault key --- charts/clusterpools/aws/templates/clusterpool.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index 80dc8c1d..d10e515f 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -75,12 +75,8 @@ metadata: spec: dataFrom: - extract: - # Expects an entry called: aws_access_key_id - key: {{ default 'secret/data/hub/aws/access_key_id' .awsAccessKeyIdPath }} - dataFrom: - - extract: - # Expects an entry called: aws_secret_access_key - key: {{ default 'secret/data/hub/aws/secret_access_key' .awsSecretKeyPath }} + # Expects entries called: aws_access_key_id and aws_secret_access_key + key: {{ default 'secret/data/hub/aws' .awsKeyPath }} refreshInterval: 24h0m0s secretStoreRef: name: {{ $.Values.secretStore.name }} From c2e1844ee8381d045a155ef9e07f1cab818aa8b6 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 31 Aug 2022 22:07:35 +1000 Subject: [PATCH 0435/1263] Defaults need double quotes --- charts/clusterpools/aws/templates/clusterpool.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index d10e515f..b0a9bf4f 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -53,7 +53,7 @@ spec: dataFrom: - extract: # Expects an entry called: .dockerconfigjson - key: {{ default 'secret/data/hub/openshiftPullSecret' .pullSecretKeyPath }} + key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} refreshInterval: 24h0m0s secretStoreRef: name: {{ $.Values.secretStore.name }} @@ -76,7 +76,7 @@ spec: dataFrom: - extract: # Expects entries called: aws_access_key_id and aws_secret_access_key - key: {{ default 'secret/data/hub/aws' .awsKeyPath }} + key: {{ default "secret/data/hub/aws" .awsKeyPath }} refreshInterval: 24h0m0s secretStoreRef: name: {{ $.Values.secretStore.name }} From 2763de384c87c60394be05fb4ffbebe3eb2b8492 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 31 Aug 2022 22:10:15 +1000 Subject: [PATCH 0436/1263] Use the current namespace --- charts/clusterpools/aws/templates/clusterpool.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index b0a9bf4f..1b3aa78f 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -35,7 +35,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "310" helm.sh/hook-weight: "310" - namespace: rhacm-clusterpools data: # Base64 encoding of install-config yaml install-config.yaml: {{ include "cluster.install-config" . | b64enc }} @@ -48,7 +47,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "310" helm.sh/hook-weight: "310" - namespace: rhacm-clusterpools spec: dataFrom: - extract: @@ -71,7 +69,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "310" helm.sh/hook-weight: "310" - namespace: rhacm-clusterpools spec: dataFrom: - extract: From dc9f5003d57fab0e38f39a46e50bba0c310e2e8c Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 31 Aug 2022 22:17:07 +1000 Subject: [PATCH 0437/1263] vault keys cannot start with a . --- charts/clusterpools/aws/templates/clusterpool.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index 1b3aa78f..481a9bb2 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -48,9 +48,9 @@ metadata: argocd.argoproj.io/sync-wave: "310" helm.sh/hook-weight: "310" spec: - dataFrom: - - extract: - # Expects an entry called: .dockerconfigjson + data: + - secretKey: openshiftPullSecret + remoteRef: key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} refreshInterval: 24h0m0s secretStoreRef: @@ -61,6 +61,9 @@ spec: creationPolicy: Owner template: type: kubernetes.io/dockerconfigjson + data: + .dockerconfigjson: |- + {{ "{{ .openshiftPullSecret | toString }}" }} --- apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret From 6366fa91a71dd127b646c7bc926035943d0ddd2d Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 31 Aug 2022 22:32:33 +1000 Subject: [PATCH 0438/1263] Consistently name the platform secret --- charts/clusterpools/aws/templates/clusterpool.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index 481a9bb2..eacf6641 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -82,7 +82,7 @@ spec: name: {{ $.Values.secretStore.name }} kind: {{ $.Values.secretStore.kind }} target: - name: {{ .name }}-aws-creds + name: {{ .name }}-creds creationPolicy: Owner template: type: Opaque From b2debc8e48db45c6f16a4e183a29676acf1dc9bc Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 08:05:12 +1000 Subject: [PATCH 0439/1263] Create cluster claims for managed clusters if a pool is defined --- acm/templates/policies/application-policies.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index da4505a1..8e7daa4c 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -110,6 +110,14 @@ spec: clusterConditions: - status: 'True' type: ManagedClusterConditionAvailable + {{- if .clusterSelector }} clusterSelector: {{ .clusterSelector | toPrettyJson }} + {{- else }} + clusterSelector: + matchLabels: + {{- range .labels }} + {{ .name }}: {{ .value }} + {{- end }} + {{- end }} --- {{- end }} From 320a3b2d37856633858d4ad303902a071ed5de5d Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 09:45:42 +1000 Subject: [PATCH 0440/1263] Obain the specific key from the vault secret --- charts/clusterpools/aws/templates/clusterpool.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index eacf6641..267a2c4c 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -63,7 +63,7 @@ spec: type: kubernetes.io/dockerconfigjson data: .dockerconfigjson: |- - {{ "{{ .openshiftPullSecret | toString }}" }} + {{ "{{ .openshiftPullSecret.dockerconfigjson | toString }}" }} --- apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret From 1d2dd35eae0f654503f05e8dcc53089bc2a3a6bf Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 09:52:14 +1000 Subject: [PATCH 0441/1263] Try to obtain the correct pull secret --- charts/clusterpools/aws/templates/clusterpool.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index 267a2c4c..40188e90 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -51,7 +51,8 @@ spec: data: - secretKey: openshiftPullSecret remoteRef: - key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} + key: {{ default "secret/data/hub/openshift" .pullSecretKeyPath }} + property: imagePullSecret refreshInterval: 24h0m0s secretStoreRef: name: {{ $.Values.secretStore.name }} @@ -63,7 +64,7 @@ spec: type: kubernetes.io/dockerconfigjson data: .dockerconfigjson: |- - {{ "{{ .openshiftPullSecret.dockerconfigjson | toString }}" }} + {{ "{{ .openshiftPullSecret | toString }}" }} --- apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret From bfe62e3afacc0dd69a3e7daaf8a1468dcc59ed75 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 11:46:52 +1000 Subject: [PATCH 0442/1263] Create infrastructure credientials --- .../aws/templates/clusterpool.yaml | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index 40188e90..9a4cf221 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -88,6 +88,63 @@ spec: template: type: Opaque --- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ .name }}-infra-creds + labels: + cluster.open-cluster-management.io/credentials: "" + cluster.open-cluster-management.io/type: aws + annotations: + argocd.argoproj.io/sync-wave: "310" + helm.sh/hook-weight: "310" +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: {{ default "secret/data/hub/openshift" .pullSecretKeyPath }} + property: imagePullSecret + - secretKey: awsKeyId + remoteRef: + key: {{ default "secret/data/hub/aws" .awsKeyPath }} + property: aws_access_key_id + - secretKey: awsAccessKey + remoteRef: + key: {{ default "secret/data/hub/aws" .awsKeyPath }} + property: aws_secret_access_key + - secretKey: sshPublicKey + remoteRef: + key: {{ default "secret/data/hub/ssh" .sshKeyPath }} + property: publickey + - secretKey: sshPrivateKey + remoteRef: + key: {{ default "secret/data/hub/ssh" .sshKeyPath }} + property: privatekey + refreshInterval: 24h0m0s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: {{ .name }}-a-infra-creds + creationPolicy: Owner + template: + type: Opaque + data: + baseDomain: "{{ .provider.baseDomain }}" + pullSecret: |- + {{ "{{ .openshiftPullSecret | toString }}" }} + ssh-privatekey: |- + {{ "{{ .awsKeyId | toString }}" }} + ssh-publickey: |- + {{ "{{ .awsAccessKey | toString }}" }} + ssh-privatekey: |- + {{ "{{ .sshPrivateKey | toString }}" }} + ssh-publickey: |- + {{ "{{ .sshPublicKey | toString }}" }} + httpProxy: "" + httpsProxy: "" + noProxy: "" + additionalTrustBundle: "" {{- end }} {{- end }} From 698a09504dd8bdbb7a9b693e761f6b75e90d76f2 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 12:48:07 +1000 Subject: [PATCH 0443/1263] Put pools and clusters into a set --- charts/clusterpools/aws/templates/clusterpool.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index 9a4cf221..dd4bd391 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -1,5 +1,15 @@ {{- range .Values.clusterPools }} {{ if .count }} +apiVersion: cluster.open-cluster-management.io/v1beta1 +kind: ManagedClusterSet +metadata: + annotations: + cluster.open-cluster-management.io/submariner-broker-ns: {{ .name }}-broker + name: {{ .name }} +spec: + clusterSelector: + selectorType: LegacyClusterSetLabel +--- apiVersion: hive.openshift.io/v1 kind: ClusterPool metadata: @@ -11,6 +21,7 @@ metadata: cloud: 'AWS' region: '{{ .provider.region }}' vendor: OpenShift + cluster.open-cluster-management.io/clusterset: {{ .name }} spec: size: {{ .count }} runningCount: 0 From 202cf043787c76a8597eb4a1f3f5aac1859c0a35 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 12:48:45 +1000 Subject: [PATCH 0444/1263] Correctly apply acm labels for the infra secret --- charts/clusterpools/aws/templates/clusterpool.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index dd4bd391..5623ed58 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -103,9 +103,6 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: {{ .name }}-infra-creds - labels: - cluster.open-cluster-management.io/credentials: "" - cluster.open-cluster-management.io/type: aws annotations: argocd.argoproj.io/sync-wave: "310" helm.sh/hook-weight: "310" @@ -140,6 +137,10 @@ spec: creationPolicy: Owner template: type: Opaque + metadata: + labels: + cluster.open-cluster-management.io/credentials: "" + cluster.open-cluster-management.io/type: aws data: baseDomain: "{{ .provider.baseDomain }}" pullSecret: |- From 4a7bdb00a775f8b2725cc4878c0822a4a12cf146 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 12:49:31 +1000 Subject: [PATCH 0445/1263] Avoid the term 'master' --- charts/clusterpools/aws/templates/_install-config.aws.tpl | 8 ++++---- charts/clusterpools/aws/values.yaml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/charts/clusterpools/aws/templates/_install-config.aws.tpl b/charts/clusterpools/aws/templates/_install-config.aws.tpl index 005495f8..424bd126 100644 --- a/charts/clusterpools/aws/templates/_install-config.aws.tpl +++ b/charts/clusterpools/aws/templates/_install-config.aws.tpl @@ -6,16 +6,16 @@ baseDomain: {{ .provider.baseDomain }} controlPlane: architecture: amd64 hyperthreading: Enabled - name: master - replicas: 3 + name: controlPlane + replicas: {{ default 3 .controlPlane.count }} platform: aws: - type: {{ .masters.machineType }} + type: {{ .controlPlane.machineType }} compute: - hyperthreading: Enabled architecture: amd64 name: 'worker' - replicas: {{ .workers.count }} + replicas: {{ default 3 .workers.count }} platform: aws: type: {{ .workers.machineType }} diff --git a/charts/clusterpools/aws/values.yaml b/charts/clusterpools/aws/values.yaml index c4ed9ad3..d1ef75cf 100755 --- a/charts/clusterpools/aws/values.yaml +++ b/charts/clusterpools/aws/values.yaml @@ -6,10 +6,10 @@ clusterPools: provider: region: us-east-2 - baseDomain: + baseDomain: example.com sshPublickey: - masters: + controlPlane: machineType: m5.xlarge workers: From a8dd10146083ac33069a7ab85695dba27cec0f3c Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 12:53:34 +1000 Subject: [PATCH 0446/1263] Drop stray characters --- charts/clusterpools/aws/templates/clusterpool.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index 5623ed58..c965a9a2 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -133,7 +133,7 @@ spec: name: {{ $.Values.secretStore.name }} kind: {{ $.Values.secretStore.kind }} target: - name: {{ .name }}-a-infra-creds + name: {{ .name }}-infra-creds creationPolicy: Owner template: type: Opaque From 10aaecee6ec59e76704c56494a89776dee0b9e27 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 13:12:47 +1000 Subject: [PATCH 0447/1263] Additional cleanups --- .../aws/templates/_install-config.aws.tpl | 7 +++---- .../clusterpools/aws/templates/clusterpool.yaml | 2 +- charts/clusterpools/aws/values.yaml | 15 +++++++-------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/charts/clusterpools/aws/templates/_install-config.aws.tpl b/charts/clusterpools/aws/templates/_install-config.aws.tpl index 424bd126..18d6d4ea 100644 --- a/charts/clusterpools/aws/templates/_install-config.aws.tpl +++ b/charts/clusterpools/aws/templates/_install-config.aws.tpl @@ -7,7 +7,7 @@ controlPlane: architecture: amd64 hyperthreading: Enabled name: controlPlane - replicas: {{ default 3 .controlPlane.count }} + replicas: {{ .controlPlane.count }} platform: aws: type: {{ .controlPlane.machineType }} @@ -15,7 +15,7 @@ compute: - hyperthreading: Enabled architecture: amd64 name: 'worker' - replicas: {{ default 3 .workers.count }} + replicas: {{ .workers.count }} platform: aws: type: {{ .workers.machineType }} @@ -32,6 +32,5 @@ platform: aws: region: {{ .provider.region }} pullSecret: "" # skip, hive will inject based on it's secrets -sshKey: |- - {{ .provider.sshPublickey }} +sshKey: "" # skip, hive will inject based on it's secrets {{- end -}} \ No newline at end of file diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index c965a9a2..a5312a54 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -29,7 +29,7 @@ spec: installConfigSecretTemplateRef: name: {{ .name }}-install-config imageSetRef: - name: img{{ .version }}-x86-64-appsub + name: img{{ .openshiftVersion }}-x86-64-appsub pullSecretRef: name: {{ .name }}-pull-secret skipMachinePools: true # Disable MachinePool as using custom install-config diff --git a/charts/clusterpools/aws/values.yaml b/charts/clusterpools/aws/values.yaml index d1ef75cf..405fb883 100755 --- a/charts/clusterpools/aws/values.yaml +++ b/charts/clusterpools/aws/values.yaml @@ -1,21 +1,20 @@ clusterPools: - poolA: - name: foo - count: 0 - version: 4.10.18 + default: + name: spoke + openshiftVersion: 4.10.18 + count: 1 provider: region: us-east-2 baseDomain: example.com - sshPublickey: controlPlane: machineType: m5.xlarge - - workers: count: 3 + + workers: machineType: m5.xlarge - + count: 3 secretStore: name: vault-backend From 1344df6db2a116c8baebd85c3070d95a6d870a5e Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 13:21:44 +1000 Subject: [PATCH 0448/1263] It is only important to create the pool last --- .../clusterpools/aws/templates/clusterpool.yaml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/charts/clusterpools/aws/templates/clusterpool.yaml index a5312a54..d26e32e6 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/charts/clusterpools/aws/templates/clusterpool.yaml @@ -15,8 +15,7 @@ kind: ClusterPool metadata: name: '{{ .name }}' annotations: - argocd.argoproj.io/sync-wave: "320" - helm.sh/hook-weight: "320" + argocd.argoproj.io/sync-wave: "10" labels: cloud: 'AWS' region: '{{ .provider.region }}' @@ -43,9 +42,6 @@ apiVersion: v1 kind: Secret metadata: name: {{ .name }}-install-config - annotations: - argocd.argoproj.io/sync-wave: "310" - helm.sh/hook-weight: "310" data: # Base64 encoding of install-config yaml install-config.yaml: {{ include "cluster.install-config" . | b64enc }} @@ -55,9 +51,6 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: {{ .name }}-pull-secret - annotations: - argocd.argoproj.io/sync-wave: "310" - helm.sh/hook-weight: "310" spec: data: - secretKey: openshiftPullSecret @@ -81,9 +74,6 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: {{ .name }}-creds - annotations: - argocd.argoproj.io/sync-wave: "310" - helm.sh/hook-weight: "310" spec: dataFrom: - extract: @@ -103,9 +93,6 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: {{ .name }}-infra-creds - annotations: - argocd.argoproj.io/sync-wave: "310" - helm.sh/hook-weight: "310" spec: data: - secretKey: openshiftPullSecret From 8e332950dc489c3c3f3ed06b94c80b178a113773 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 14:55:33 +1000 Subject: [PATCH 0449/1263] Move the cluster provisioning into the acm chart --- .../templates/_install-config.aws.tpl | 22 +++++++++++--- .../aws => acm}/templates/clusterpool.yaml | 30 +++++++++++++++---- .../policies/application-policies.yaml | 2 ++ acm/values.yaml | 18 +++++++++++ charts/clusterpools/aws/.gitignore | 4 --- charts/clusterpools/aws/.helmignore | 1 - charts/clusterpools/aws/Chart.yaml | 5 ---- charts/clusterpools/aws/values.yaml | 21 ------------- 8 files changed, 63 insertions(+), 40 deletions(-) rename {charts/clusterpools/aws => acm}/templates/_install-config.aws.tpl (61%) rename {charts/clusterpools/aws => acm}/templates/clusterpool.yaml (86%) delete mode 100644 charts/clusterpools/aws/.gitignore delete mode 100644 charts/clusterpools/aws/.helmignore delete mode 100644 charts/clusterpools/aws/Chart.yaml delete mode 100755 charts/clusterpools/aws/values.yaml diff --git a/charts/clusterpools/aws/templates/_install-config.aws.tpl b/acm/templates/_install-config.aws.tpl similarity index 61% rename from charts/clusterpools/aws/templates/_install-config.aws.tpl rename to acm/templates/_install-config.aws.tpl index 18d6d4ea..8c5f54a4 100644 --- a/charts/clusterpools/aws/templates/_install-config.aws.tpl +++ b/acm/templates/_install-config.aws.tpl @@ -7,18 +7,32 @@ controlPlane: architecture: amd64 hyperthreading: Enabled name: controlPlane - replicas: {{ .controlPlane.count }} + {{- if .controlPlane }} + replicas: {{ default 3 .controlPlane.count }} platform: aws: - type: {{ .controlPlane.machineType }} + type: {{ default "m5.xlarge" .controlPlane.machineType }} + {{- else }} + replicas: 3 + platform: + aws: + type: "m5.xlarge" + {{- end }} compute: - hyperthreading: Enabled architecture: amd64 name: 'worker' - replicas: {{ .workers.count }} + {{- if .workers }} + replicas: {{ default 3 .workers.count }} + platform: + aws: + type: {{ default "m5.xlarge" .workers.machineType }} + {{- else }} + replicas: 3 platform: aws: - type: {{ .workers.machineType }} + type: "m5.xlarge" + {{- end }} networking: clusterNetwork: - cidr: 10.128.0.0/14 diff --git a/charts/clusterpools/aws/templates/clusterpool.yaml b/acm/templates/clusterpool.yaml similarity index 86% rename from charts/clusterpools/aws/templates/clusterpool.yaml rename to acm/templates/clusterpool.yaml index d26e32e6..1c30f368 100644 --- a/charts/clusterpools/aws/templates/clusterpool.yaml +++ b/acm/templates/clusterpool.yaml @@ -1,5 +1,5 @@ -{{- range .Values.clusterPools }} -{{ if .count }} +{{- range .Values.clusterGroup.managedClusterGroups }} +{{- $group := . }} apiVersion: cluster.open-cluster-management.io/v1beta1 kind: ManagedClusterSet metadata: @@ -10,10 +10,12 @@ spec: clusterSelector: selectorType: LegacyClusterSetLabel --- +{{- range .clusterPool }} +{{- $pool := . }} apiVersion: hive.openshift.io/v1 kind: ClusterPool metadata: - name: '{{ .name }}' + name: "{{ .name }}" annotations: argocd.argoproj.io/sync-wave: "10" labels: @@ -22,8 +24,8 @@ metadata: vendor: OpenShift cluster.open-cluster-management.io/clusterset: {{ .name }} spec: - size: {{ .count }} - runningCount: 0 + size: {{ len .clusters }} + runningCount: {{ len .clusters }} baseDomain: {{ .provider.baseDomain }} installConfigSecretTemplateRef: name: {{ .name }}-install-config @@ -38,6 +40,24 @@ spec: name: {{ .name }}-creds region: {{ .provider.region }} --- +{{- range .clusters }} +apiVersion: hive.openshift.io/v1 +kind: ClusterClaim +metadata: + name: '{{ . }}' + annotations: + argocd.argoproj.io/sync-wave: "20" + cluster.open-cluster-management.io/createmanagedcluster: "true" + labels: + clusterClaimName: {{ . }} + {{- range $group.labels }} + {{ .name }}: {{ .value }} + {{- end }} + patternGroup: {{ $group.name }} +spec: + clusterPoolName: {{ $pool.name }} +--- +{{- end }} apiVersion: v1 kind: Secret metadata: diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 8e7daa4c..55fa473d 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -1,5 +1,6 @@ # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io {{- range .Values.clusterGroup.managedClusterGroups }} +{{- $group := . }} apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: @@ -118,6 +119,7 @@ spec: {{- range .labels }} {{ .name }}: {{ .value }} {{- end }} + patternGroup: {{ $group.name }} {{- end }} --- {{- end }} diff --git a/acm/values.yaml b/acm/values.yaml index 2e12f8b4..7cbe0eeb 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -6,3 +6,21 @@ global: clusterGroup: managedClusterGroups: +# testRegion: +# name: region-one +# clusterPool: +# testPool: +# name: spoke +# openshiftVersion: 4.10.18 +# provider: +# region: ap-southeast-2 +# baseDomain: blueprints.rhecoeng.com +# clusters: +# - spoke1 +# labels: +# - name: clusterGroup +# value: region-one + +secretStore: + name: vault-backend + kind: ClusterSecretStore \ No newline at end of file diff --git a/charts/clusterpools/aws/.gitignore b/charts/clusterpools/aws/.gitignore deleted file mode 100644 index 684bd48c..00000000 --- a/charts/clusterpools/aws/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -id_* -id_*.pub -pullsecret -create.sh \ No newline at end of file diff --git a/charts/clusterpools/aws/.helmignore b/charts/clusterpools/aws/.helmignore deleted file mode 100644 index 90ffe6b9..00000000 --- a/charts/clusterpools/aws/.helmignore +++ /dev/null @@ -1 +0,0 @@ -templates/install-config.aws.yaml \ No newline at end of file diff --git a/charts/clusterpools/aws/Chart.yaml b/charts/clusterpools/aws/Chart.yaml deleted file mode 100644 index 85dfd6df..00000000 --- a/charts/clusterpools/aws/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: aws-clusterpools -version: "1.3" -kubeVersion: ">= 1.9.0" -description: RHACM ClusterPools for AWS hosted OCP using ExternalSecrets Operator diff --git a/charts/clusterpools/aws/values.yaml b/charts/clusterpools/aws/values.yaml deleted file mode 100755 index 405fb883..00000000 --- a/charts/clusterpools/aws/values.yaml +++ /dev/null @@ -1,21 +0,0 @@ -clusterPools: - default: - name: spoke - openshiftVersion: 4.10.18 - count: 1 - - provider: - region: us-east-2 - baseDomain: example.com - - controlPlane: - machineType: m5.xlarge - count: 3 - - workers: - machineType: m5.xlarge - count: 3 - -secretStore: - name: vault-backend - kind: ClusterSecretStore From ce5e5b2dd2899074bfca9a6349f3c99273bb2e76 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 20:16:09 +1000 Subject: [PATCH 0450/1263] Switch to file based secrets where appropriate --- acm/templates/clusterpool.yaml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/acm/templates/clusterpool.yaml b/acm/templates/clusterpool.yaml index 1c30f368..3c791be4 100644 --- a/acm/templates/clusterpool.yaml +++ b/acm/templates/clusterpool.yaml @@ -109,6 +109,7 @@ spec: template: type: Opaque --- +# For use when manually creating clusters with ACM apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: @@ -117,8 +118,8 @@ spec: data: - secretKey: openshiftPullSecret remoteRef: - key: {{ default "secret/data/hub/openshift" .pullSecretKeyPath }} - property: imagePullSecret + key: {{ default "secret/data/hub/imagePullSecret" .pullSecretKeyPath }} + property: content - secretKey: awsKeyId remoteRef: key: {{ default "secret/data/hub/aws" .awsKeyPath }} @@ -129,12 +130,12 @@ spec: property: aws_secret_access_key - secretKey: sshPublicKey remoteRef: - key: {{ default "secret/data/hub/ssh" .sshKeyPath }} - property: publickey + key: {{ default "secret/data/hub/publickey" .sshPublicKeyPath }} + property: content - secretKey: sshPrivateKey remoteRef: - key: {{ default "secret/data/hub/ssh" .sshKeyPath }} - property: privatekey + key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} + property: content refreshInterval: 24h0m0s secretStoreRef: name: {{ $.Values.secretStore.name }} From b3ca8f09ce850d70ada98fa502b1fa3769141e07 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 1 Sep 2022 21:32:18 +1000 Subject: [PATCH 0451/1263] File based pullSecret. Fix aws keys in infra secret --- acm/templates/clusterpool.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/acm/templates/clusterpool.yaml b/acm/templates/clusterpool.yaml index 3c791be4..b4c7b5ec 100644 --- a/acm/templates/clusterpool.yaml +++ b/acm/templates/clusterpool.yaml @@ -75,8 +75,8 @@ spec: data: - secretKey: openshiftPullSecret remoteRef: - key: {{ default "secret/data/hub/openshift" .pullSecretKeyPath }} - property: imagePullSecret + key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} + property: content refreshInterval: 24h0m0s secretStoreRef: name: {{ $.Values.secretStore.name }} @@ -118,7 +118,7 @@ spec: data: - secretKey: openshiftPullSecret remoteRef: - key: {{ default "secret/data/hub/imagePullSecret" .pullSecretKeyPath }} + key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} property: content - secretKey: awsKeyId remoteRef: @@ -153,9 +153,9 @@ spec: baseDomain: "{{ .provider.baseDomain }}" pullSecret: |- {{ "{{ .openshiftPullSecret | toString }}" }} - ssh-privatekey: |- + aws_access_key_id: |- {{ "{{ .awsKeyId | toString }}" }} - ssh-publickey: |- + aws_secret_access_key: |- {{ "{{ .awsAccessKey | toString }}" }} ssh-privatekey: |- {{ "{{ .sshPrivateKey | toString }}" }} From 91d89a43eb3388d0b8fdf6302d477253b16c86ca Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 2 Sep 2022 07:29:26 +1000 Subject: [PATCH 0452/1263] Split out provisioning secrets into a new template for legibility --- .../{ => provision}/_install-config.aws.tpl | 0 acm/templates/provision/clusterpool.yaml | 64 +++++++++++++++++++ .../secrets.yaml} | 58 ----------------- 3 files changed, 64 insertions(+), 58 deletions(-) rename acm/templates/{ => provision}/_install-config.aws.tpl (100%) create mode 100644 acm/templates/provision/clusterpool.yaml rename acm/templates/{clusterpool.yaml => provision/secrets.yaml} (68%) diff --git a/acm/templates/_install-config.aws.tpl b/acm/templates/provision/_install-config.aws.tpl similarity index 100% rename from acm/templates/_install-config.aws.tpl rename to acm/templates/provision/_install-config.aws.tpl diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml new file mode 100644 index 00000000..edf4396d --- /dev/null +++ b/acm/templates/provision/clusterpool.yaml @@ -0,0 +1,64 @@ +{{- range .Values.clusterGroup.managedClusterGroups }} +{{- $group := . }} +apiVersion: cluster.open-cluster-management.io/v1beta1 +kind: ManagedClusterSet +metadata: + annotations: + cluster.open-cluster-management.io/submariner-broker-ns: {{ .name }}-broker + name: {{ .name }} +spec: + clusterSelector: + selectorType: LegacyClusterSetLabel +--- +{{- range .clusterPool }} +{{- $pool := . }} +apiVersion: hive.openshift.io/v1 +kind: ClusterPool +metadata: + name: "{{ .name }}" + annotations: + argocd.argoproj.io/sync-wave: "10" + labels: + cloud: 'AWS' + region: '{{ .provider.region }}' + vendor: OpenShift + cluster.open-cluster-management.io/clusterset: {{ .name }} +spec: + size: {{ len .clusters }} + runningCount: {{ len .clusters }} + baseDomain: {{ .provider.baseDomain }} + installConfigSecretTemplateRef: + name: {{ .name }}-install-config + imageSetRef: + name: img{{ .openshiftVersion }}-x86-64-appsub + pullSecretRef: + name: {{ .name }}-pull-secret + skipMachinePools: true # Disable MachinePool as using custom install-config + platform: + aws: + credentialsSecretRef: + name: {{ .name }}-creds + region: {{ .provider.region }} +--- +{{- range .clusters }} +apiVersion: hive.openshift.io/v1 +kind: ClusterClaim +metadata: + name: '{{ . }}' + annotations: + argocd.argoproj.io/sync-wave: "20" + cluster.open-cluster-management.io/createmanagedcluster: "true" + labels: + clusterClaimName: {{ . }} + {{- range $group.labels }} + {{ .name }}: {{ .value }} + {{- end }} + patternGroup: {{ $group.name }} +spec: + clusterPoolName: {{ $pool.name }} +--- +{{- end }} +{{- end }} +{{- end }} + + diff --git a/acm/templates/clusterpool.yaml b/acm/templates/provision/secrets.yaml similarity index 68% rename from acm/templates/clusterpool.yaml rename to acm/templates/provision/secrets.yaml index b4c7b5ec..26b2fe31 100644 --- a/acm/templates/clusterpool.yaml +++ b/acm/templates/provision/secrets.yaml @@ -1,63 +1,5 @@ {{- range .Values.clusterGroup.managedClusterGroups }} -{{- $group := . }} -apiVersion: cluster.open-cluster-management.io/v1beta1 -kind: ManagedClusterSet -metadata: - annotations: - cluster.open-cluster-management.io/submariner-broker-ns: {{ .name }}-broker - name: {{ .name }} -spec: - clusterSelector: - selectorType: LegacyClusterSetLabel ---- {{- range .clusterPool }} -{{- $pool := . }} -apiVersion: hive.openshift.io/v1 -kind: ClusterPool -metadata: - name: "{{ .name }}" - annotations: - argocd.argoproj.io/sync-wave: "10" - labels: - cloud: 'AWS' - region: '{{ .provider.region }}' - vendor: OpenShift - cluster.open-cluster-management.io/clusterset: {{ .name }} -spec: - size: {{ len .clusters }} - runningCount: {{ len .clusters }} - baseDomain: {{ .provider.baseDomain }} - installConfigSecretTemplateRef: - name: {{ .name }}-install-config - imageSetRef: - name: img{{ .openshiftVersion }}-x86-64-appsub - pullSecretRef: - name: {{ .name }}-pull-secret - skipMachinePools: true # Disable MachinePool as using custom install-config - platform: - aws: - credentialsSecretRef: - name: {{ .name }}-creds - region: {{ .provider.region }} ---- -{{- range .clusters }} -apiVersion: hive.openshift.io/v1 -kind: ClusterClaim -metadata: - name: '{{ . }}' - annotations: - argocd.argoproj.io/sync-wave: "20" - cluster.open-cluster-management.io/createmanagedcluster: "true" - labels: - clusterClaimName: {{ . }} - {{- range $group.labels }} - {{ .name }}: {{ .value }} - {{- end }} - patternGroup: {{ $group.name }} -spec: - clusterPoolName: {{ $pool.name }} ---- -{{- end }} apiVersion: v1 kind: Secret metadata: From 07a01f7d78f8750d66f7e71769fec9c1cd07625f Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 2 Sep 2022 07:36:48 +1000 Subject: [PATCH 0453/1263] Allow the pool to have spare members --- acm/templates/provision/clusterpool.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index edf4396d..99f8753b 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -24,7 +24,11 @@ metadata: vendor: OpenShift cluster.open-cluster-management.io/clusterset: {{ .name }} spec: + {{- if .size }} + size: {{ .size }} + {{- else }} size: {{ len .clusters }} + {{- end }} runningCount: {{ len .clusters }} baseDomain: {{ .provider.baseDomain }} installConfigSecretTemplateRef: From 47903f9fc1b4bc5b40e0571bf9688615a48713a7 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 2 Sep 2022 08:58:56 +1000 Subject: [PATCH 0454/1263] Disambiguate cluster pools and names by group --- acm/templates/provision/clusterpool.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index 99f8753b..16d43e69 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -15,7 +15,7 @@ spec: apiVersion: hive.openshift.io/v1 kind: ClusterPool metadata: - name: "{{ .name }}" + name: "{{ .name }}-{{ $group.name }}" annotations: argocd.argoproj.io/sync-wave: "10" labels: @@ -48,12 +48,12 @@ spec: apiVersion: hive.openshift.io/v1 kind: ClusterClaim metadata: - name: '{{ . }}' + name: '{{ . }}-{{ $group.name }}' annotations: argocd.argoproj.io/sync-wave: "20" cluster.open-cluster-management.io/createmanagedcluster: "true" labels: - clusterClaimName: {{ . }} + clusterClaimName: {{ . }}-{{ $group.name }} {{- range $group.labels }} {{ .name }}: {{ .value }} {{- end }} From 8deb4c1afc0a3771dfe3fff3798d726c165e3933 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 2 Sep 2022 12:58:40 +1000 Subject: [PATCH 0455/1263] Add support for provisioning azure clusters --- acm/.helmignore | 1 + ...all-config.aws.tpl => _install-config.tpl} | 37 +++--- acm/templates/provision/clusterpool.yaml | 34 ++++-- acm/templates/provision/secrets.yaml | 105 ++++++++++++++++-- acm/values.yaml | 2 +- 5 files changed, 143 insertions(+), 36 deletions(-) create mode 100644 acm/.helmignore rename acm/templates/provision/{_install-config.aws.tpl => _install-config.tpl} (57%) diff --git a/acm/.helmignore b/acm/.helmignore new file mode 100644 index 00000000..b25c15b8 --- /dev/null +++ b/acm/.helmignore @@ -0,0 +1 @@ +*~ diff --git a/acm/templates/provision/_install-config.aws.tpl b/acm/templates/provision/_install-config.tpl similarity index 57% rename from acm/templates/provision/_install-config.aws.tpl rename to acm/templates/provision/_install-config.tpl index 8c5f54a4..39aa03eb 100644 --- a/acm/templates/provision/_install-config.aws.tpl +++ b/acm/templates/provision/_install-config.tpl @@ -1,22 +1,35 @@ {{- define "cluster.install-config" -}} + +{{- $type := "None" }} +{{- $cloud := "None" }} +{{- $region := "None" }} + +{{- if .platform.aws }} +{{- $cloud = "aws" }} +{{- $region = .platform.aws.region }} +{{- $type = "m5.xlarge" }} +{{- else if .platform.azure }} +{{- $cloud = "azure" }} +{{- $region = .platform.azure.region }} +{{- $type = "Standard_D8s_v3" }} +{{- end }} + apiVersion: v1 metadata: name: '{{ .name }}' -baseDomain: {{ .provider.baseDomain }} +baseDomain: {{ .baseDomain }} controlPlane: architecture: amd64 hyperthreading: Enabled name: controlPlane {{- if .controlPlane }} replicas: {{ default 3 .controlPlane.count }} - platform: - aws: - type: {{ default "m5.xlarge" .controlPlane.machineType }} + platform: {{- .controlPlane.platform | toPrettyJson }} {{- else }} replicas: 3 platform: - aws: - type: "m5.xlarge" + {{ $cloud }}: + type: {{ $type }} {{- end }} compute: - hyperthreading: Enabled @@ -24,14 +37,12 @@ compute: name: 'worker' {{- if .workers }} replicas: {{ default 3 .workers.count }} - platform: - aws: - type: {{ default "m5.xlarge" .workers.machineType }} + platform: {{- .workers.platform | toPrettyJson }} {{- else }} replicas: 3 platform: - aws: - type: "m5.xlarge" + {{ $cloud }}: + type: {{ $type }} {{- end }} networking: clusterNetwork: @@ -42,9 +53,7 @@ networking: networkType: OpenShiftSDN serviceNetwork: - 172.30.0.0/16 -platform: - aws: - region: {{ .provider.region }} +platform: {{ .platform | toPrettyJson }} pullSecret: "" # skip, hive will inject based on it's secrets sshKey: "" # skip, hive will inject based on it's secrets {{- end -}} \ No newline at end of file diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index 16d43e69..33caca44 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -10,17 +10,31 @@ spec: clusterSelector: selectorType: LegacyClusterSetLabel --- -{{- range .clusterPool }} +{{- range .clusterPools }} + {{- $pool := . }} +{{- $poolName := cat .name $group.name | replace " " "-" }} + +{{- $cloud := "None" }} +{{- $region := "None" }} + +{{- if .platform.aws }} +{{- $cloud = "aws" }} +{{- $region = .platform.aws.region }} +{{- else if .platform.azure }} +{{- $cloud = "azure" }} +{{- $region = .platform.azure.region }} +{{- end }} + apiVersion: hive.openshift.io/v1 kind: ClusterPool metadata: - name: "{{ .name }}-{{ $group.name }}" + name: "{{ $poolName }}" annotations: argocd.argoproj.io/sync-wave: "10" labels: - cloud: 'AWS' - region: '{{ .provider.region }}' + cloud: {{ $cloud }} + region: '{{ $region }}' vendor: OpenShift cluster.open-cluster-management.io/clusterset: {{ .name }} spec: @@ -30,19 +44,19 @@ spec: size: {{ len .clusters }} {{- end }} runningCount: {{ len .clusters }} - baseDomain: {{ .provider.baseDomain }} + baseDomain: {{ .baseDomain }} installConfigSecretTemplateRef: - name: {{ .name }}-install-config + name: {{ $poolName }}-install-config imageSetRef: name: img{{ .openshiftVersion }}-x86-64-appsub pullSecretRef: - name: {{ .name }}-pull-secret + name: {{ $poolName }}-pull-secret skipMachinePools: true # Disable MachinePool as using custom install-config platform: - aws: + {{ $cloud }}: credentialsSecretRef: - name: {{ .name }}-creds - region: {{ .provider.region }} + name: {{ $poolName }}-creds + region: {{ $region }} --- {{- range .clusters }} apiVersion: hive.openshift.io/v1 diff --git a/acm/templates/provision/secrets.yaml b/acm/templates/provision/secrets.yaml index 26b2fe31..717959a0 100644 --- a/acm/templates/provision/secrets.yaml +++ b/acm/templates/provision/secrets.yaml @@ -1,9 +1,11 @@ {{- range .Values.clusterGroup.managedClusterGroups }} -{{- range .clusterPool }} +{{- $group := . }} +{{- range .clusterPools }} +{{- $poolName := cat .name $group.name | replace " " "-" }} apiVersion: v1 kind: Secret metadata: - name: {{ .name }}-install-config + name: {{ $poolName }}-install-config data: # Base64 encoding of install-config yaml install-config.yaml: {{ include "cluster.install-config" . | b64enc }} @@ -12,7 +14,7 @@ type: Opaque apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: - name: {{ .name }}-pull-secret + name: {{ $poolName }}-pull-secret spec: data: - secretKey: openshiftPullSecret @@ -24,7 +26,7 @@ spec: name: {{ $.Values.secretStore.name }} kind: {{ $.Values.secretStore.kind }} target: - name: {{ .name }}-pull-secret + name: {{ $poolName }}-pull-secret creationPolicy: Owner template: type: kubernetes.io/dockerconfigjson @@ -32,10 +34,11 @@ spec: .dockerconfigjson: |- {{ "{{ .openshiftPullSecret | toString }}" }} --- +{{- if .platform.aws }} apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: - name: {{ .name }}-creds + name: {{ $poolName }}-creds spec: dataFrom: - extract: @@ -46,7 +49,7 @@ spec: name: {{ $.Values.secretStore.name }} kind: {{ $.Values.secretStore.kind }} target: - name: {{ .name }}-creds + name: {{ $poolName }}-creds creationPolicy: Owner template: type: Opaque @@ -55,7 +58,7 @@ spec: apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: - name: {{ .name }}-infra-creds + name: {{ $poolName }}-infra-creds spec: data: - secretKey: openshiftPullSecret @@ -83,7 +86,7 @@ spec: name: {{ $.Values.secretStore.name }} kind: {{ $.Values.secretStore.kind }} target: - name: {{ .name }}-infra-creds + name: {{ $poolName }}-infra-creds creationPolicy: Owner template: type: Opaque @@ -92,7 +95,87 @@ spec: cluster.open-cluster-management.io/credentials: "" cluster.open-cluster-management.io/type: aws data: - baseDomain: "{{ .provider.baseDomain }}" + baseDomain: "{{ .baseDomain }}" + pullSecret: |- + {{ "{{ .openshiftPullSecret | toString }}" }} + aws_access_key_id: |- + {{ "{{ .awsKeyId | toString }}" }} + aws_secret_access_key: |- + {{ "{{ .awsAccessKey | toString }}" }} + ssh-privatekey: |- + {{ "{{ .sshPrivateKey | toString }}" }} + ssh-publickey: |- + {{ "{{ .sshPublicKey | toString }}" }} + httpProxy: "" + httpsProxy: "" + noProxy: "" + additionalTrustBundle: "" +{{- else if .platform.azure }} +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ $poolName }}-creds +spec: + data: + - secretKey: azureOsServicePrincipal + remoteRef: + key: {{ default "secret/data/hub/azureOsServicePrincipal" .azureKeyPath }} + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: {{ $poolName }}-creds + creationPolicy: Owner + template: + type: Opaque + data: + osServicePrincipal.json: |- + {{ "{{ .azureOsServicePrincipal | toString }}" }} +--- +# For use when manually creating clusters with ACM +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ $poolName }}-infra-creds +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} + property: content + - secretKey: awsKeyId + remoteRef: + key: {{ default "secret/data/hub/aws" .awsKeyPath }} + property: aws_access_key_id + - secretKey: awsAccessKey + remoteRef: + key: {{ default "secret/data/hub/aws" .awsKeyPath }} + property: aws_secret_access_key + - secretKey: sshPublicKey + remoteRef: + key: {{ default "secret/data/hub/publickey" .sshPublicKeyPath }} + property: content + - secretKey: sshPrivateKey + remoteRef: + key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: {{ $poolName }}-infra-creds + creationPolicy: Owner + template: + type: Opaque + metadata: + labels: + cluster.open-cluster-management.io/credentials: "" + cluster.open-cluster-management.io/type: aws + data: + baseDomain: "{{ .baseDomain }}" pullSecret: |- {{ "{{ .openshiftPullSecret | toString }}" }} aws_access_key_id: |- @@ -107,7 +190,7 @@ spec: httpsProxy: "" noProxy: "" additionalTrustBundle: "" -{{- end }} {{- end }} - +{{- end }} +{{- end }} \ No newline at end of file diff --git a/acm/values.yaml b/acm/values.yaml index 7cbe0eeb..e423d531 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -8,7 +8,7 @@ clusterGroup: managedClusterGroups: # testRegion: # name: region-one -# clusterPool: +# clusterPools: # testPool: # name: spoke # openshiftVersion: 4.10.18 From f2a54ccd526701abe0da82a73a22b30098ec1910 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 2 Sep 2022 13:03:52 +1000 Subject: [PATCH 0456/1263] Update tests - we define a ManagedClusterSet for every cluster 'group' --- tests/acm-normal.expected.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 29af29a8..1cca7131 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -1,4 +1,15 @@ --- +# Source: acm/templates/provision/clusterpool.yaml +apiVersion: cluster.open-cluster-management.io/v1beta1 +kind: ManagedClusterSet +metadata: + annotations: + cluster.open-cluster-management.io/submariner-broker-ns: edge-broker + name: edge +spec: + clusterSelector: + selectorType: LegacyClusterSetLabel +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub From 815257d1b937a5fcc35755cf09a9ef6544c5eff2 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 3 Sep 2022 12:09:15 +1000 Subject: [PATCH 0457/1263] Separate out the platform specific secrets --- acm/templates/provision/secrets-aws.yaml | 83 +++++++++++ acm/templates/provision/secrets-azure.yaml | 74 ++++++++++ acm/templates/provision/secrets.yaml | 159 --------------------- 3 files changed, 157 insertions(+), 159 deletions(-) create mode 100644 acm/templates/provision/secrets-aws.yaml create mode 100644 acm/templates/provision/secrets-azure.yaml diff --git a/acm/templates/provision/secrets-aws.yaml b/acm/templates/provision/secrets-aws.yaml new file mode 100644 index 00000000..ec43a408 --- /dev/null +++ b/acm/templates/provision/secrets-aws.yaml @@ -0,0 +1,83 @@ +{{- range .Values.clusterGroup.managedClusterGroups }} +{{- $group := . }} +{{- range .clusterPools }} +{{- $poolName := cat .name $group.name | replace " " "-" }} +{{- if .platform.aws }} +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ $poolName }}-creds +spec: + dataFrom: + - extract: + # Expects entries called: aws_access_key_id and aws_secret_access_key + key: {{ default "secret/data/hub/aws" .awsKeyPath }} + refreshInterval: 24h0m0s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: {{ $poolName }}-creds + creationPolicy: Owner + template: + type: Opaque +--- +# For use when manually creating clusters with ACM +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ $poolName }}-infra-creds +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} + property: content + - secretKey: awsKeyId + remoteRef: + key: {{ default "secret/data/hub/aws" .awsKeyPath }} + property: aws_access_key_id + - secretKey: awsAccessKey + remoteRef: + key: {{ default "secret/data/hub/aws" .awsKeyPath }} + property: aws_secret_access_key + - secretKey: sshPublicKey + remoteRef: + key: {{ default "secret/data/hub/publickey" .sshPublicKeyPath }} + property: content + - secretKey: sshPrivateKey + remoteRef: + key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: {{ $poolName }}-infra-creds + creationPolicy: Owner + template: + type: Opaque + metadata: + labels: + cluster.open-cluster-management.io/credentials: "" + cluster.open-cluster-management.io/type: aws + data: + baseDomain: "{{ .baseDomain }}" + pullSecret: |- + {{ "{{ .openshiftPullSecret | toString }}" }} + aws_access_key_id: |- + {{ "{{ .awsKeyId | toString }}" }} + aws_secret_access_key: |- + {{ "{{ .awsAccessKey | toString }}" }} + ssh-privatekey: |- + {{ "{{ .sshPrivateKey | toString }}" }} + ssh-publickey: |- + {{ "{{ .sshPublicKey | toString }}" }} + httpProxy: "" + httpsProxy: "" + noProxy: "" + additionalTrustBundle: "" +--- +{{- end }} +{{- end }} \ No newline at end of file diff --git a/acm/templates/provision/secrets-azure.yaml b/acm/templates/provision/secrets-azure.yaml new file mode 100644 index 00000000..c6cfa7b5 --- /dev/null +++ b/acm/templates/provision/secrets-azure.yaml @@ -0,0 +1,74 @@ +{{- range .Values.clusterGroup.managedClusterGroups }} +{{- range .clusterPools }} +{{- $poolName := cat .name $group.name | replace " " "-" }} +{{- if .platform.azure }} +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ $poolName }}-creds +spec: + data: + - secretKey: azureOsServicePrincipal + remoteRef: + key: {{ default "secret/data/hub/azureOsServicePrincipal" .azureKeyPath }} + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: {{ $poolName }}-creds + creationPolicy: Owner + template: + type: Opaque + data: + osServicePrincipal.json: |- + {{ "{{ .azureOsServicePrincipal | toString }}" }} +--- +# For use when manually creating clusters with ACM +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ $poolName }}-infra-creds +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} + property: content + - secretKey: sshPublicKey + remoteRef: + key: {{ default "secret/data/hub/publickey" .sshPublicKeyPath }} + property: content + - secretKey: sshPrivateKey + remoteRef: + key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: {{ $poolName }}-infra-creds + creationPolicy: Owner + template: + type: Opaque + metadata: + labels: + cluster.open-cluster-management.io/credentials: "" + cluster.open-cluster-management.io/type: aws + data: + baseDomain: "{{ .baseDomain }}" + pullSecret: |- + {{ "{{ .openshiftPullSecret | toString }}" }} + ssh-privatekey: |- + {{ "{{ .sshPrivateKey | toString }}" }} + ssh-publickey: |- + {{ "{{ .sshPublicKey | toString }}" }} + httpProxy: "" + httpsProxy: "" + noProxy: "" + additionalTrustBundle: "" +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/acm/templates/provision/secrets.yaml b/acm/templates/provision/secrets.yaml index 717959a0..4bc6eb2d 100644 --- a/acm/templates/provision/secrets.yaml +++ b/acm/templates/provision/secrets.yaml @@ -33,164 +33,5 @@ spec: data: .dockerconfigjson: |- {{ "{{ .openshiftPullSecret | toString }}" }} ---- -{{- if .platform.aws }} -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: {{ $poolName }}-creds -spec: - dataFrom: - - extract: - # Expects entries called: aws_access_key_id and aws_secret_access_key - key: {{ default "secret/data/hub/aws" .awsKeyPath }} - refreshInterval: 24h0m0s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} - target: - name: {{ $poolName }}-creds - creationPolicy: Owner - template: - type: Opaque ---- -# For use when manually creating clusters with ACM -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: {{ $poolName }}-infra-creds -spec: - data: - - secretKey: openshiftPullSecret - remoteRef: - key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} - property: content - - secretKey: awsKeyId - remoteRef: - key: {{ default "secret/data/hub/aws" .awsKeyPath }} - property: aws_access_key_id - - secretKey: awsAccessKey - remoteRef: - key: {{ default "secret/data/hub/aws" .awsKeyPath }} - property: aws_secret_access_key - - secretKey: sshPublicKey - remoteRef: - key: {{ default "secret/data/hub/publickey" .sshPublicKeyPath }} - property: content - - secretKey: sshPrivateKey - remoteRef: - key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} - property: content - refreshInterval: 24h0m0s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} - target: - name: {{ $poolName }}-infra-creds - creationPolicy: Owner - template: - type: Opaque - metadata: - labels: - cluster.open-cluster-management.io/credentials: "" - cluster.open-cluster-management.io/type: aws - data: - baseDomain: "{{ .baseDomain }}" - pullSecret: |- - {{ "{{ .openshiftPullSecret | toString }}" }} - aws_access_key_id: |- - {{ "{{ .awsKeyId | toString }}" }} - aws_secret_access_key: |- - {{ "{{ .awsAccessKey | toString }}" }} - ssh-privatekey: |- - {{ "{{ .sshPrivateKey | toString }}" }} - ssh-publickey: |- - {{ "{{ .sshPublicKey | toString }}" }} - httpProxy: "" - httpsProxy: "" - noProxy: "" - additionalTrustBundle: "" -{{- else if .platform.azure }} -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: {{ $poolName }}-creds -spec: - data: - - secretKey: azureOsServicePrincipal - remoteRef: - key: {{ default "secret/data/hub/azureOsServicePrincipal" .azureKeyPath }} - property: content - refreshInterval: 24h0m0s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} - target: - name: {{ $poolName }}-creds - creationPolicy: Owner - template: - type: Opaque - data: - osServicePrincipal.json: |- - {{ "{{ .azureOsServicePrincipal | toString }}" }} ---- -# For use when manually creating clusters with ACM -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: {{ $poolName }}-infra-creds -spec: - data: - - secretKey: openshiftPullSecret - remoteRef: - key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} - property: content - - secretKey: awsKeyId - remoteRef: - key: {{ default "secret/data/hub/aws" .awsKeyPath }} - property: aws_access_key_id - - secretKey: awsAccessKey - remoteRef: - key: {{ default "secret/data/hub/aws" .awsKeyPath }} - property: aws_secret_access_key - - secretKey: sshPublicKey - remoteRef: - key: {{ default "secret/data/hub/publickey" .sshPublicKeyPath }} - property: content - - secretKey: sshPrivateKey - remoteRef: - key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} - property: content - refreshInterval: 24h0m0s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} - target: - name: {{ $poolName }}-infra-creds - creationPolicy: Owner - template: - type: Opaque - metadata: - labels: - cluster.open-cluster-management.io/credentials: "" - cluster.open-cluster-management.io/type: aws - data: - baseDomain: "{{ .baseDomain }}" - pullSecret: |- - {{ "{{ .openshiftPullSecret | toString }}" }} - aws_access_key_id: |- - {{ "{{ .awsKeyId | toString }}" }} - aws_secret_access_key: |- - {{ "{{ .awsAccessKey | toString }}" }} - ssh-privatekey: |- - {{ "{{ .sshPrivateKey | toString }}" }} - ssh-publickey: |- - {{ "{{ .sshPublicKey | toString }}" }} - httpProxy: "" - httpsProxy: "" - noProxy: "" - additionalTrustBundle: "" -{{- end }} - {{- end }} {{- end }} \ No newline at end of file From ed91ffc4a09902034673b9180d7cba733f436bfe Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 3 Sep 2022 12:19:41 +1000 Subject: [PATCH 0458/1263] Include the private ssh key --- acm/templates/provision/clusterpool.yaml | 2 ++ acm/templates/provision/secrets.yaml | 25 +++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index 33caca44..1d69fc01 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -51,6 +51,8 @@ spec: name: img{{ .openshiftVersion }}-x86-64-appsub pullSecretRef: name: {{ $poolName }}-pull-secret + sshPrivateKeySecretRef: + name: {{ $poolName }}-ssh-private-key skipMachinePools: true # Disable MachinePool as using custom install-config platform: {{ $cloud }}: diff --git a/acm/templates/provision/secrets.yaml b/acm/templates/provision/secrets.yaml index 4bc6eb2d..48b5950a 100644 --- a/acm/templates/provision/secrets.yaml +++ b/acm/templates/provision/secrets.yaml @@ -22,7 +22,7 @@ spec: key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} property: content refreshInterval: 24h0m0s - secretStoreRef: + secretStoreRef: name: {{ $.Values.secretStore.name }} kind: {{ $.Values.secretStore.kind }} target: @@ -33,5 +33,28 @@ spec: data: .dockerconfigjson: |- {{ "{{ .openshiftPullSecret | toString }}" }} +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ .Values.cluster }}-ssh-private-key +spec: + data: + - secretKey: sshPrivateKey + remoteRef: + key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: {{ .Values.cluster }}-ssh-private-key + creationPolicy: Owner + template: + type: Opaque + data: + ssh-privatekey: |- + {{ "{{ .sshPrivateKey | toString }}" }} {{- end }} {{- end }} \ No newline at end of file From 9e8f43212f3b31cbd9a29f1a2dc2464940bbd9b6 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 3 Sep 2022 12:20:45 +1000 Subject: [PATCH 0459/1263] Rename manifest for clarity --- acm/templates/provision/{secrets.yaml => secrets-common.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename acm/templates/provision/{secrets.yaml => secrets-common.yaml} (100%) diff --git a/acm/templates/provision/secrets.yaml b/acm/templates/provision/secrets-common.yaml similarity index 100% rename from acm/templates/provision/secrets.yaml rename to acm/templates/provision/secrets-common.yaml From e6e50207c36b40950d91149a8ef2603a81993531 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 3 Sep 2022 12:22:06 +1000 Subject: [PATCH 0460/1263] Add the missing azure credientials --- acm/templates/provision/secrets-azure.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/acm/templates/provision/secrets-azure.yaml b/acm/templates/provision/secrets-azure.yaml index c6cfa7b5..6805eee5 100644 --- a/acm/templates/provision/secrets-azure.yaml +++ b/acm/templates/provision/secrets-azure.yaml @@ -44,6 +44,10 @@ spec: remoteRef: key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} property: content + - secretKey: azureOsServicePrincipal + remoteRef: + key: {{ default "secret/data/hub/azureOsServicePrincipal" .azureKeyPath }} + property: content refreshInterval: 24h0m0s secretStoreRef: name: {{ $.Values.secretStore.name }} @@ -58,7 +62,11 @@ spec: cluster.open-cluster-management.io/credentials: "" cluster.open-cluster-management.io/type: aws data: + cloudName: AzurePublicCloud + osServicePrincipal.json: |- + {{ "{{ .azureOsServicePrincipal | toString }}" }} baseDomain: "{{ .baseDomain }}" + baseDomainResourceGroupName: "{{ .platform.azure.baseDomainResourceGroupName | toString }}" pullSecret: |- {{ "{{ .openshiftPullSecret | toString }}" }} ssh-privatekey: |- From d39f2dafea8fa48b43d8d983c2d76a2d3a129289 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 3 Sep 2022 12:31:28 +1000 Subject: [PATCH 0461/1263] ACM provisioning test cases and fixes --- acm/templates/provision/secrets-aws.yaml | 1 + acm/templates/provision/secrets-azure.yaml | 1 + acm/templates/provision/secrets-common.yaml | 4 +-- acm/test.yaml | 35 +++++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 acm/test.yaml diff --git a/acm/templates/provision/secrets-aws.yaml b/acm/templates/provision/secrets-aws.yaml index ec43a408..99022df6 100644 --- a/acm/templates/provision/secrets-aws.yaml +++ b/acm/templates/provision/secrets-aws.yaml @@ -80,4 +80,5 @@ spec: additionalTrustBundle: "" --- {{- end }} +{{- end }} {{- end }} \ No newline at end of file diff --git a/acm/templates/provision/secrets-azure.yaml b/acm/templates/provision/secrets-azure.yaml index 6805eee5..66b470c1 100644 --- a/acm/templates/provision/secrets-azure.yaml +++ b/acm/templates/provision/secrets-azure.yaml @@ -1,4 +1,5 @@ {{- range .Values.clusterGroup.managedClusterGroups }} +{{- $group := . }} {{- range .clusterPools }} {{- $poolName := cat .name $group.name | replace " " "-" }} {{- if .platform.azure }} diff --git a/acm/templates/provision/secrets-common.yaml b/acm/templates/provision/secrets-common.yaml index 48b5950a..62641dde 100644 --- a/acm/templates/provision/secrets-common.yaml +++ b/acm/templates/provision/secrets-common.yaml @@ -37,7 +37,7 @@ spec: apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: - name: {{ .Values.cluster }}-ssh-private-key + name: {{ $poolName }}-ssh-private-key spec: data: - secretKey: sshPrivateKey @@ -49,7 +49,7 @@ spec: name: {{ $.Values.secretStore.name }} kind: {{ $.Values.secretStore.kind }} target: - name: {{ .Values.cluster }}-ssh-private-key + name: {{ $poolName }}-ssh-private-key creationPolicy: Owner template: type: Opaque diff --git a/acm/test.yaml b/acm/test.yaml new file mode 100644 index 00000000..225f4bf8 --- /dev/null +++ b/acm/test.yaml @@ -0,0 +1,35 @@ +clusterGroup: + managedClusterGroups: + exampleRegion: + name: region-one + + # Before enabling cluster provisioning, ensure AWS/Azure credentials and OCP + # pull secrets are defined in Vault. See values-secret.yaml.template + # + clusterPools: + exampleAWSPool: + name: aws-ap + openshiftVersion: 4.10.18 + baseDomain: blueprints.rhecoeng.com + platform: + aws: + region: ap-southeast-2 + clusters: + - One + exampleAzurePool: + name: azure-us + openshiftVersion: 4.10.18 + baseDomain: blueprints.rhecoeng.com + platform: + azure: + baseDomainResourceGroupName: dojo-dns-zones + region: eastus + clusters: + - Two + - Three + labels: + - name: clusterGroup + value: region-one + helmOverrides: + - name: clusterGroup.isHubCluster + value: false From 1ac9a655757fa581c08568b3f6c1062335798c55 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 5 Sep 2022 12:35:55 +1000 Subject: [PATCH 0462/1263] Avoid using LegacyClusterSetLabel for determining cluster sets --- acm/templates/provision/clusterpool.yaml | 13 +++++++++++-- tests/acm-normal.expected.yaml | 7 +++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index 1d69fc01..db1fd5d8 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -7,8 +7,17 @@ metadata: cluster.open-cluster-management.io/submariner-broker-ns: {{ .name }}-broker name: {{ .name }} spec: - clusterSelector: - selectorType: LegacyClusterSetLabel + {{- if .clusterPoolSelector }} + clusterSelector: {{ .clusterPoolSelector | toPrettyJson }} + {{- else }} + clusterSelector: + labelSelector: + matchLabels: + {{- range .labels }} + {{ .name }}: {{ .value }} + {{- end }} + patternGroup: {{ $group.name }} + {{- end }} --- {{- range .clusterPools }} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 1cca7131..00808c3a 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -7,8 +7,11 @@ metadata: cluster.open-cluster-management.io/submariner-broker-ns: edge-broker name: edge spec: - clusterSelector: - selectorType: LegacyClusterSetLabel + selectorType: LabelSelector + clusterSelector: + labelSelector: + matchLabels: + patternGroup: edge --- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 From e564ccdcc7bde7456b98132cf5262e98dba90542 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 5 Sep 2022 09:42:07 +0200 Subject: [PATCH 0463/1263] Fix tests --- tests/acm-normal.expected.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 00808c3a..aa683e35 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -7,7 +7,6 @@ metadata: cluster.open-cluster-management.io/submariner-broker-ns: edge-broker name: edge spec: - selectorType: LabelSelector clusterSelector: labelSelector: matchLabels: From 91a0e5fc5479417e147a4c98bcb82754a47c12c8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 5 Sep 2022 09:51:55 +0200 Subject: [PATCH 0464/1263] Revert "Avoid using LegacyClusterSetLabel for determining cluster sets" This reverts commit 1ac9a655757fa581c08568b3f6c1062335798c55. --- acm/templates/provision/clusterpool.yaml | 13 ++----------- tests/acm-normal.expected.yaml | 6 ++---- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index db1fd5d8..1d69fc01 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -7,17 +7,8 @@ metadata: cluster.open-cluster-management.io/submariner-broker-ns: {{ .name }}-broker name: {{ .name }} spec: - {{- if .clusterPoolSelector }} - clusterSelector: {{ .clusterPoolSelector | toPrettyJson }} - {{- else }} - clusterSelector: - labelSelector: - matchLabels: - {{- range .labels }} - {{ .name }}: {{ .value }} - {{- end }} - patternGroup: {{ $group.name }} - {{- end }} + clusterSelector: + selectorType: LegacyClusterSetLabel --- {{- range .clusterPools }} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index aa683e35..1cca7131 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -7,10 +7,8 @@ metadata: cluster.open-cluster-management.io/submariner-broker-ns: edge-broker name: edge spec: - clusterSelector: - labelSelector: - matchLabels: - patternGroup: edge + clusterSelector: + selectorType: LegacyClusterSetLabel --- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 From b999a15c008427b1b3622d7e1f9ecea3de9076a5 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 5 Sep 2022 19:57:44 +1000 Subject: [PATCH 0465/1263] Integrate the ability to specify IIBs as part of the pattern --- clustergroup/templates/catalog-sources.yaml | 10 ++ clustergroup/test.yaml | 104 ++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 clustergroup/templates/catalog-sources.yaml create mode 100644 clustergroup/test.yaml diff --git a/clustergroup/templates/catalog-sources.yaml b/clustergroup/templates/catalog-sources.yaml new file mode 100644 index 00000000..35208b37 --- /dev/null +++ b/clustergroup/templates/catalog-sources.yaml @@ -0,0 +1,10 @@ +{{- range .Values.clusterGroup.indexImages }} +apiVersion: operators.coreos.com/v1alpha1 +kind: CatalogSource +metadata: + name: {{ .name }} + namespace: openshift-marketplace +spec: + sourceType: grpc + image: {{ .image }}:{{ .version }} +{{- end -}} diff --git a/clustergroup/test.yaml b/clustergroup/test.yaml new file mode 100644 index 00000000..0ab52838 --- /dev/null +++ b/clustergroup/test.yaml @@ -0,0 +1,104 @@ +clusterGroup: + name: hub + isHubCluster: true + # Note: setting this to true stores the vault unseal keys inside a cluster secret and + # is fundamentally insecure + insecureUnsealVaultInsideCluster: false + + namespaces: + - open-cluster-management + - vault + - golang-external-secrets + - config-demo + + indexImages: + - name: snr + image: quay.io/mshitrit/self-node-remediation-manager-index + version: 0.0.104 + + subscriptions: + acm: + name: advanced-cluster-management + namespace: open-cluster-management + channel: release-2.5 + csv: advanced-cluster-management.v2.5.0 + + projects: + - hub + - config-demo + + applications: + acm: + name: acm + namespace: open-cluster-management + project: hub + path: common/acm + ignoreDifferences: + - group: internal.open-cluster-management.io + kind: ManagedClusterInfo + jsonPointers: + - /spec/loggingCA + + vault: + name: vault + namespace: vault + project: hub + chart: vault + repoURL: https://helm.releases.hashicorp.com + targetRevision: v0.21.0 + overrides: + - name: global.openshift + value: "true" + - name: injector.enabled + value: "false" + - name: ui.enabled + value: "true" + - name: ui.serviceType + value: LoadBalancer + - name: server.route.enabled + value: "true" + - name: server.route.host + value: null + - name: server.route.tls.termination + value: edge + - name: server.image.repository + value: "registry.connect.redhat.com/hashicorp/vault" + - name: server.image.tag + value: "1.11.2-ubi" + + golang-external-secrets: + name: golang-external-secrets + namespace: golang-external-secrets + project: hub + path: common/golang-external-secrets + + config-demo: + name: config-demo + namespace: config-demo + project: config-demo + path: charts/all/config-demo + + imperative: + # NOTE: We *must* use lists and not hashes. As hashes lose ordering once parsed by helm + # The default schedule is every 10 minutes: imperative.schedule + # Total timeout of all jobs is 1h: imperative.activeDeadlineSeconds + # imagePullPolicy is set to always: imperative.imagePullPolicy + # For additional overrides that apply to the jobs, please refer to + # https://hybrid-cloud-patterns.io/imperative-actions/#additional-job-customizations + jobs: + - name: regional-ca + # ansible playbook to be run + playbook: ansible/playbooks/on-hub-get-regional-ca.yml + # per playbook timeout in seconds + timeout: 234 + # verbosity: "-v" + + managedClusterGroups: + region-one: + name: region-one + labels: + - name: clusterGroup + value: region-one + helmOverrides: + - name: clusterGroup.isHubCluster + value: false From 0d14aedd39097764cdf01fac832497f669e0ef25 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 6 Sep 2022 08:06:44 +0200 Subject: [PATCH 0466/1263] Add hashicorp-vault chart in common/ This allows us to simplify any values-* file that has to include vault and not rely on external chart repos. Note: We add a hashicorp-vault/update-helm-dependency.sh script which needs to be run after 'helm dependency update hashicorp-vault' is called. The reason for this is explained in detail in the README.md file, but the TLDR is that helm has a bug with subcharts and it won't remove null values in there and so "host: null" breaks the vault route. Once either the helm bug is fixed or we switch to OCP 4.11 as a minimum version and to a vault-helm chart that has subdomains support (unmerged at this point in time), we can drop this one liner patch. We explored the approach of patching in vault.server.route.host as a top-level variable. This was investigated here: https://github.com/hybrid-cloud-patterns/multicloud-gitops/pull/104 It was discarded because it really special-cases vault, which is undesirable. It also will need changes everywhere once the bugs are fixed and also it needs changes in the operator as well. --- hashicorp-vault/Chart.yaml | 10 + hashicorp-vault/README.md | 12 + hashicorp-vault/charts/vault-0.21.0.tgz | Bin 0 -> 41273 bytes hashicorp-vault/patch-server-route.diff | 13 + hashicorp-vault/update-helm-dependency.sh | 26 ++ hashicorp-vault/values.yaml | 19 + tests/hashicorp-vault-naked.expected.yaml | 384 +++++++++++++++++++++ tests/hashicorp-vault-normal.expected.yaml | 384 +++++++++++++++++++++ 8 files changed, 848 insertions(+) create mode 100644 hashicorp-vault/Chart.yaml create mode 100644 hashicorp-vault/README.md create mode 100644 hashicorp-vault/charts/vault-0.21.0.tgz create mode 100644 hashicorp-vault/patch-server-route.diff create mode 100755 hashicorp-vault/update-helm-dependency.sh create mode 100644 hashicorp-vault/values.yaml create mode 100644 tests/hashicorp-vault-naked.expected.yaml create mode 100644 tests/hashicorp-vault-normal.expected.yaml diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml new file mode 100644 index 00000000..c9e6f210 --- /dev/null +++ b/hashicorp-vault/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +description: A Helm chart to configure Hashicorp's vault +keywords: +- pattern +name: hashicorp-vault +version: 0.0.1 +dependencies: + - name: vault + version: "0.21.0" + repository: "https://helm.releases.hashicorp.com" diff --git a/hashicorp-vault/README.md b/hashicorp-vault/README.md new file mode 100644 index 00000000..153b516a --- /dev/null +++ b/hashicorp-vault/README.md @@ -0,0 +1,12 @@ +# VP hashicorp-vault + +**IMPORTANT**: Due to the fact that 'null' values do not work in helm charts ([GH#9136](https://github.com/helm/helm/issues/9136)), +we need to patch the chart to skip setting the host. + +Make sure to run "./update-helm-dependency.sh" + +We can drop this local patch when any one the two conditions is true: + +- [1] is fixed in helm and we can require the version that for installs +- [PR#779](https://github.com/hashicorp/vault-helm/pull/779) is merged in vault-helm *and* our minimum supported OCP version + is OCP 4.11 (route subdomain is broken in OCP < 4.11 due to missing [commit](https://github.com/openshift/router/commit/6f730c7cae966f0ed8def50c81d1bf10fe9eb77b) diff --git a/hashicorp-vault/charts/vault-0.21.0.tgz b/hashicorp-vault/charts/vault-0.21.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..b6802ed5dd248b726aeecb8e0eeb33357442c32d GIT binary patch literal 41273 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%0AA86=MT`q!W&_LgC`&kHx~o~Su&77Vueeg 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 F0|4xH8qNR! literal 0 HcmV?d00001 diff --git a/hashicorp-vault/patch-server-route.diff b/hashicorp-vault/patch-server-route.diff new file mode 100644 index 00000000..808988b6 --- /dev/null +++ b/hashicorp-vault/patch-server-route.diff @@ -0,0 +1,13 @@ +diff -up vault/values.yaml.orig vault/values.yaml +--- vault/values.yaml.orig 2022-09-05 20:42:02.468428184 +0200 ++++ vault/values.yaml 2022-09-05 20:42:05.218435871 +0200 +@@ -406,7 +406,8 @@ server: + + labels: {} + annotations: {} +- host: chart-example.local ++ #host: chart-example.local ++ host: null + # tls will be passed directly to the route's TLS config, which + # can be used to configure other termination methods that terminate + # TLS at the router diff --git a/hashicorp-vault/update-helm-dependency.sh b/hashicorp-vault/update-helm-dependency.sh new file mode 100755 index 00000000..afd6d522 --- /dev/null +++ b/hashicorp-vault/update-helm-dependency.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -eu + +# Get the version of the dependency and then unquote it +TMPVER=$(sed -e '1,/^version:/ d' "Chart.yaml" | grep "version:" | awk '{ print $2 }') +VERSION=$(eval echo "${TMPVER}") + +# Chart format is vault-0.21.0.tgz +NAME="vault" +TAR="${NAME}-${VERSION}.tgz" +CHARTDIR="charts" + +if [ ! -f "${CHARTDIR}/${TAR}" ]; then + echo "Charts $TAR not found" + exit 1 +fi + +pushd "${CHARTDIR}" +rm -rf "${NAME}" +tar xfz "${TAR}" +pushd "${NAME}" +patch -p1 < ../../patch-server-route.diff +popd +tar cvfz "${TAR}" "${NAME}" +rm -rf "${NAME}" +popd diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml new file mode 100644 index 00000000..66d078fd --- /dev/null +++ b/hashicorp-vault/values.yaml @@ -0,0 +1,19 @@ +--- +global: + openshift: true + +vault: + injector: + enabled: false + ui: + enabled: true + serviceType: "LoadBalancer" + server: + route: + host: null + enabled: true + tls: + termination: "edge" + image: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.11.2-ubi" diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml new file mode 100644 index 00000000..36c748e1 --- /dev/null +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -0,0 +1,384 @@ +--- +# Source: hashicorp-vault/charts/vault/templates/server-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +--- +# Source: hashicorp-vault/charts/vault/templates/server-config-configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hashicorp-vault-config + namespace: default + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +data: + extraconfig-from-values.hcl: |- + disable_mlock = true + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + } + storage "file" { + path = "/vault/data" + } + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} +--- +# Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: hashicorp-vault-server-binding + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: hashicorp-vault + namespace: default +--- +# Source: hashicorp-vault/charts/vault/templates/server-headless-service.yaml +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault-internal + namespace: default + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm + annotations: + +spec: + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: "http" + port: 8200 + targetPort: 8200 + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server +--- +# Source: hashicorp-vault/charts/vault/templates/server-service.yaml +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm + annotations: + +spec: + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: true + ports: + - name: http + port: 8200 + targetPort: 8200 + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server +--- +# Source: hashicorp-vault/charts/vault/templates/ui-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault-ui + namespace: default + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault-ui + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + publishNotReadyAddresses: true + ports: + - name: http + port: 8200 + targetPort: 8200 + type: LoadBalancer + externalTrafficPolicy: Cluster +--- +# Source: hashicorp-vault/charts/vault/templates/server-statefulset.yaml +# StatefulSet to run the actual vault server cluster. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: hashicorp-vault + namespace: default + labels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + serviceName: hashicorp-vault-internal + podManagementPolicy: Parallel + replicas: 1 + updateStrategy: + type: OnDelete + selector: + matchLabels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + template: + metadata: + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + spec: + + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: "hashicorp-vault" + component: server + topologyKey: kubernetes.io/hostname + + + + + terminationGracePeriodSeconds: 10 + serviceAccountName: hashicorp-vault + + volumes: + + - name: config + configMap: + name: hashicorp-vault-config + + - name: home + emptyDir: {} + containers: + - name: vault + + image: registry.connect.redhat.com/hashicorp/vault:1.11.2-ubi + imagePullPolicy: IfNotPresent + command: + - "/bin/sh" + - "-ec" + args: + - | + cp /vault/config/extraconfig-from-values.hcl /tmp/storageconfig.hcl; + [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /tmp/storageconfig.hcl; + [ -n "${API_ADDR}" ] && sed -Ei "s|API_ADDR|${API_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${TRANSIT_ADDR}" ] && sed -Ei "s|TRANSIT_ADDR|${TRANSIT_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${RAFT_ADDR}" ] && sed -Ei "s|RAFT_ADDR|${RAFT_ADDR?}|g" /tmp/storageconfig.hcl; + /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl + + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: VAULT_K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: VAULT_ADDR + value: "http://127.0.0.1:8200" + - name: VAULT_API_ADDR + value: "http://$(POD_IP):8200" + - name: SKIP_CHOWN + value: "true" + - name: SKIP_SETCAP + value: "true" + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_CLUSTER_ADDR + value: "https://$(HOSTNAME).hashicorp-vault-internal:8201" + - name: HOME + value: "/home/vault" + + + + volumeMounts: + + + + - name: data + mountPath: /vault/data + + + + - name: config + mountPath: /vault/config + + - name: home + mountPath: /home/vault + ports: + - containerPort: 8200 + name: http + - containerPort: 8201 + name: https-internal + - containerPort: 8202 + name: http-rep + readinessProbe: + # Check status; unsealed vault servers return 0 + # The exit code reflects the seal status: + # 0 - unsealed + # 1 - error + # 2 - sealed + exec: + command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + lifecycle: + # Vault container doesn't receive SIGTERM from Kubernetes + # and after the grace period ends, Kube sends SIGKILL. This + # causes issues with graceful shutdowns such as deregistering itself + # from Consul (zombie services). + preStop: + exec: + command: [ + "/bin/sh", "-c", + # Adding a sleep here to give the pod eviction a + # chance to propagate, so requests will not be made + # to this pod while it's terminating + "sleep 5 && kill -SIGTERM $(pidof vault)", + ] + + + volumeClaimTemplates: + - metadata: + name: data + + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +# Source: hashicorp-vault/charts/vault/templates/server-route.yaml +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + host: + to: + kind: Service + name: hashicorp-vault + weight: 100 + port: + targetPort: 8200 + tls: + termination: edge +--- +# Source: hashicorp-vault/charts/vault/templates/tests/server-test.yaml +apiVersion: v1 +kind: Pod +metadata: + name: "hashicorp-vault-server-test" + namespace: default + annotations: + "helm.sh/hook": test +spec: + + containers: + - name: hashicorp-vault-server-test + image: registry.connect.redhat.com/hashicorp/vault:1.11.2-ubi + imagePullPolicy: IfNotPresent + env: + - name: VAULT_ADDR + value: http://hashicorp-vault.default.svc:8200 + + command: + - /bin/sh + - -c + - | + echo "Checking for sealed info in 'vault status' output" + ATTEMPTS=10 + n=0 + until [ "$n" -ge $ATTEMPTS ] + do + echo "Attempt" $n... + vault status -format yaml | grep -E '^sealed: (true|false)' && break + n=$((n+1)) + sleep 5 + done + if [ $n -ge $ATTEMPTS ]; then + echo "timed out looking for sealed info in 'vault status' output" + exit 1 + fi + + exit 0 + volumeMounts: + volumes: + restartPolicy: Never diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml new file mode 100644 index 00000000..36c748e1 --- /dev/null +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -0,0 +1,384 @@ +--- +# Source: hashicorp-vault/charts/vault/templates/server-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +--- +# Source: hashicorp-vault/charts/vault/templates/server-config-configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hashicorp-vault-config + namespace: default + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +data: + extraconfig-from-values.hcl: |- + disable_mlock = true + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + } + storage "file" { + path = "/vault/data" + } + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} +--- +# Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: hashicorp-vault-server-binding + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: hashicorp-vault + namespace: default +--- +# Source: hashicorp-vault/charts/vault/templates/server-headless-service.yaml +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault-internal + namespace: default + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm + annotations: + +spec: + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: "http" + port: 8200 + targetPort: 8200 + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server +--- +# Source: hashicorp-vault/charts/vault/templates/server-service.yaml +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm + annotations: + +spec: + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: true + ports: + - name: http + port: 8200 + targetPort: 8200 + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server +--- +# Source: hashicorp-vault/charts/vault/templates/ui-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault-ui + namespace: default + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault-ui + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + publishNotReadyAddresses: true + ports: + - name: http + port: 8200 + targetPort: 8200 + type: LoadBalancer + externalTrafficPolicy: Cluster +--- +# Source: hashicorp-vault/charts/vault/templates/server-statefulset.yaml +# StatefulSet to run the actual vault server cluster. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: hashicorp-vault + namespace: default + labels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + serviceName: hashicorp-vault-internal + podManagementPolicy: Parallel + replicas: 1 + updateStrategy: + type: OnDelete + selector: + matchLabels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + template: + metadata: + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + spec: + + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: "hashicorp-vault" + component: server + topologyKey: kubernetes.io/hostname + + + + + terminationGracePeriodSeconds: 10 + serviceAccountName: hashicorp-vault + + volumes: + + - name: config + configMap: + name: hashicorp-vault-config + + - name: home + emptyDir: {} + containers: + - name: vault + + image: registry.connect.redhat.com/hashicorp/vault:1.11.2-ubi + imagePullPolicy: IfNotPresent + command: + - "/bin/sh" + - "-ec" + args: + - | + cp /vault/config/extraconfig-from-values.hcl /tmp/storageconfig.hcl; + [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /tmp/storageconfig.hcl; + [ -n "${API_ADDR}" ] && sed -Ei "s|API_ADDR|${API_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${TRANSIT_ADDR}" ] && sed -Ei "s|TRANSIT_ADDR|${TRANSIT_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${RAFT_ADDR}" ] && sed -Ei "s|RAFT_ADDR|${RAFT_ADDR?}|g" /tmp/storageconfig.hcl; + /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl + + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: VAULT_K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: VAULT_ADDR + value: "http://127.0.0.1:8200" + - name: VAULT_API_ADDR + value: "http://$(POD_IP):8200" + - name: SKIP_CHOWN + value: "true" + - name: SKIP_SETCAP + value: "true" + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_CLUSTER_ADDR + value: "https://$(HOSTNAME).hashicorp-vault-internal:8201" + - name: HOME + value: "/home/vault" + + + + volumeMounts: + + + + - name: data + mountPath: /vault/data + + + + - name: config + mountPath: /vault/config + + - name: home + mountPath: /home/vault + ports: + - containerPort: 8200 + name: http + - containerPort: 8201 + name: https-internal + - containerPort: 8202 + name: http-rep + readinessProbe: + # Check status; unsealed vault servers return 0 + # The exit code reflects the seal status: + # 0 - unsealed + # 1 - error + # 2 - sealed + exec: + command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + lifecycle: + # Vault container doesn't receive SIGTERM from Kubernetes + # and after the grace period ends, Kube sends SIGKILL. This + # causes issues with graceful shutdowns such as deregistering itself + # from Consul (zombie services). + preStop: + exec: + command: [ + "/bin/sh", "-c", + # Adding a sleep here to give the pod eviction a + # chance to propagate, so requests will not be made + # to this pod while it's terminating + "sleep 5 && kill -SIGTERM $(pidof vault)", + ] + + + volumeClaimTemplates: + - metadata: + name: data + + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +# Source: hashicorp-vault/charts/vault/templates/server-route.yaml +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.21.0 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + host: + to: + kind: Service + name: hashicorp-vault + weight: 100 + port: + targetPort: 8200 + tls: + termination: edge +--- +# Source: hashicorp-vault/charts/vault/templates/tests/server-test.yaml +apiVersion: v1 +kind: Pod +metadata: + name: "hashicorp-vault-server-test" + namespace: default + annotations: + "helm.sh/hook": test +spec: + + containers: + - name: hashicorp-vault-server-test + image: registry.connect.redhat.com/hashicorp/vault:1.11.2-ubi + imagePullPolicy: IfNotPresent + env: + - name: VAULT_ADDR + value: http://hashicorp-vault.default.svc:8200 + + command: + - /bin/sh + - -c + - | + echo "Checking for sealed info in 'vault status' output" + ATTEMPTS=10 + n=0 + until [ "$n" -ge $ATTEMPTS ] + do + echo "Attempt" $n... + vault status -format yaml | grep -E '^sealed: (true|false)' && break + n=$((n+1)) + sleep 5 + done + if [ $n -ge $ATTEMPTS ]; then + echo "timed out looking for sealed info in 'vault status' output" + exit 1 + fi + + exit 0 + volumeMounts: + volumes: + restartPolicy: Never From bbfe82103349f4547218f04074d31c92b78a42a6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 6 Sep 2022 13:55:53 +0200 Subject: [PATCH 0467/1263] Only create managedclustersets if there are clusterpools Otherwise we end up with the following error whenever we have managedClusters that do not have clusterpools defined in them: cluster.open-cluster-management.io/v1beta1/ManagedClusterSet open-cluster-management region-one SyncFailed the server could not find the requested resource --- acm/templates/provision/clusterpool.yaml | 10 +++++----- tests/acm-normal.expected.yaml | 11 ----------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index 1d69fc01..84af10be 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -1,5 +1,6 @@ {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} +{{- if .clusterPools }}{{- /* We only create ManagedClusterSets if there are clusterPools defined */}} apiVersion: cluster.open-cluster-management.io/v1beta1 kind: ManagedClusterSet metadata: @@ -77,8 +78,7 @@ metadata: spec: clusterPoolName: {{ $pool.name }} --- -{{- end }} -{{- end }} -{{- end }} - - +{{- end }}{{- /* range .range clusters */}} +{{- end }}{{- /* range .clusterPools */}} +{{- end }}{{- /* if .clusterPools) */}} +{{- end }}{{- /* range .Values.clusterGroup.managedClusterGroups */}} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 1cca7131..29af29a8 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -1,15 +1,4 @@ --- -# Source: acm/templates/provision/clusterpool.yaml -apiVersion: cluster.open-cluster-management.io/v1beta1 -kind: ManagedClusterSet -metadata: - annotations: - cluster.open-cluster-management.io/submariner-broker-ns: edge-broker - name: edge -spec: - clusterSelector: - selectorType: LegacyClusterSetLabel ---- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub From 7f2c3ed3d6d15532b37676624a660ba8b207a6a8 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 6 Sep 2022 14:34:35 -0500 Subject: [PATCH 0468/1263] Add operatorchannel parameter --- operator-install/templates/pattern.yaml | 2 ++ operator-install/values.yaml | 3 +++ tests/operator-install-naked.expected.yaml | 2 ++ tests/operator-install-normal.expected.yaml | 2 ++ 4 files changed, 9 insertions(+) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index 2f54860f..2ac7b52a 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -8,3 +8,5 @@ spec: gitSpec: targetRepo: {{ .Values.main.git.repoURL }} targetRevision: {{ .Values.main.git.revision }} + gitOpsSpec: + operatorChannel: {{ default "stable" .Values.main.gitops.channel }} diff --git a/operator-install/values.yaml b/operator-install/values.yaml index 0a80b80f..b6e67044 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -3,4 +3,7 @@ main: repoURL: https://github.com/pattern-clone/mypattern revision: main + gitops: + channel: "stable" + clusterGroupName: default diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index 32775213..6b4cddc7 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -10,6 +10,8 @@ spec: gitSpec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main + gitOpsSpec: + operatorChannel: stable --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index 86d34dd5..dc07be36 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -10,6 +10,8 @@ spec: gitSpec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main + gitOpsSpec: + operatorChannel: stable --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 From 5c9ca0937f4812f8fb72f7612427151345f70539 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 15 Aug 2022 08:57:43 -0500 Subject: [PATCH 0469/1263] Add mechanism to use pattern variables as other overrides --- clustergroup/templates/applications.yaml | 48 ++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 350f4446..bb0148b7 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -72,6 +72,30 @@ spec: value: {{ $.Values.global.namespace }} - name: global.pattern value: {{ $.Values.global.pattern }} + {{- range .hubClusterDomainFields }} + - name: {{ . }} + value: {{ $.Values.global.hubClusterDomain }} + {{- end }} + {{- range .localClusterDomainFields }} + - name: {{ . }} + value: {{ $.Values.global.localClusterDomain }} + {{- end }} + {{- range .repoURLFields }} + - name: {{ . }} + value: {{ $.Values.global.repoURL }} + {{- end }} + {{- range .targetRevisionFields }} + - name: {{ . }} + value: {{ $.Values.global.targetRevision }} + {{- end }} + {{- range .namespaceFields }} + - name: {{ . }} + value: {{ $.Values.global.namespace }} + {{- end }} + {{- range .patternNameFields }} + - name: {{ . }} + value: {{ $.Values.global.pattern }} + {{- end }} {{- range .overrides }} - name: {{ .name }} value: {{ .value | quote }} @@ -129,6 +153,30 @@ spec: value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} + {{- range .hubClusterDomainFields }} + - name: {{ . }} + value: {{ $.Values.global.hubClusterDomain }} + {{- end }} + {{- range .localClusterDomainFields }} + - name: {{ . }} + value: {{ $.Values.global.localClusterDomain }} + {{- end }} + {{- range .repoURLFields }} + - name: {{ . }} + value: $ARGOCD_APP_SOURCE_REPO_URL + {{- end }} + {{- range .targetRevisionFields }} + - name: {{ . }} + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + {{- end }} + {{- range .namespaceFields }} + - name: {{ . }} + value: $ARGOCD_APP_NAMESPACE + {{- end }} + {{- range .patternNameFields }} + - name: {{ . }} + value: {{ $.Values.global.pattern }} + {{- end }} {{- range .overrides }} - name: {{ .name }} value: {{ .value | quote }} From 1f7e2c404715ecaeecc098aff6aaa6c38e3e1263 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 15 Aug 2022 09:08:24 -0500 Subject: [PATCH 0470/1263] Denote new fields as 'extra' --- clustergroup/templates/applications.yaml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index bb0148b7..262c859a 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -72,27 +72,27 @@ spec: value: {{ $.Values.global.namespace }} - name: global.pattern value: {{ $.Values.global.pattern }} - {{- range .hubClusterDomainFields }} + {{- range .extraHubClusterDomainFields }} - name: {{ . }} value: {{ $.Values.global.hubClusterDomain }} {{- end }} - {{- range .localClusterDomainFields }} + {{- range .extraLocalClusterDomainFields }} - name: {{ . }} value: {{ $.Values.global.localClusterDomain }} {{- end }} - {{- range .repoURLFields }} + {{- range .extraRepoURLFields }} - name: {{ . }} value: {{ $.Values.global.repoURL }} {{- end }} - {{- range .targetRevisionFields }} + {{- range .extraTargetRevisionFields }} - name: {{ . }} value: {{ $.Values.global.targetRevision }} {{- end }} - {{- range .namespaceFields }} + {{- range .extraNamespaceFields }} - name: {{ . }} value: {{ $.Values.global.namespace }} {{- end }} - {{- range .patternNameFields }} + {{- range .extraPatternNameFields }} - name: {{ . }} value: {{ $.Values.global.pattern }} {{- end }} @@ -153,27 +153,27 @@ spec: value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} - {{- range .hubClusterDomainFields }} + {{- range .extraHubClusterDomainFields }} - name: {{ . }} value: {{ $.Values.global.hubClusterDomain }} {{- end }} - {{- range .localClusterDomainFields }} + {{- range .extraLocalClusterDomainFields }} - name: {{ . }} value: {{ $.Values.global.localClusterDomain }} {{- end }} - {{- range .repoURLFields }} + {{- range .extraRepoURLFields }} - name: {{ . }} value: $ARGOCD_APP_SOURCE_REPO_URL {{- end }} - {{- range .targetRevisionFields }} + {{- range .extraTargetRevisionFields }} - name: {{ . }} value: $ARGOCD_APP_SOURCE_TARGET_REVISION {{- end }} - {{- range .namespaceFields }} + {{- range .extraNamespaceFields }} - name: {{ . }} value: $ARGOCD_APP_NAMESPACE {{- end }} - {{- range .patternNameFields }} + {{- range .extraPatternNameFields }} - name: {{ . }} value: {{ $.Values.global.pattern }} {{- end }} From db1140cccad7fb7cd4ac1a638ef8fc1b0384d265 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 7 Sep 2022 11:56:51 +0200 Subject: [PATCH 0471/1263] Move CSV definition into the uninstall target Let's not call oc too many times for every invocation. Moving the CSV variable definition inside the uninstall avoids that. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3d2881c8..98850ab4 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,6 @@ TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set glo --set clusterGroup.insecureUnsealVaultInsideCluster=true PATTERN_OPTS=-f common/examples/values-example.yaml EXECUTABLES=git helm oc ansible -CSV=$(shell oc get subscriptions -n openshift-operators openshift-gitops-operator -ojsonpath={.status.currentCSV}) .PHONY: help help: ## This help message @@ -70,6 +69,7 @@ operator-deploy operator-upgrade: validate-origin ## runs helm install helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) uninstall: ## runs helm uninstall + $(eval CSV := $(shell oc get subscriptions -n openshift-operators openshift-gitops-operator -ojsonpath={.status.currentCSV})) helm uninstall $(NAME) @oc delete csv -n openshift-operators $(CSV) From 1fbc87a36eb18272b1e724b01e2032584780d9f6 Mon Sep 17 00:00:00 2001 From: ruromero Date: Fri, 2 Sep 2022 13:03:38 +0200 Subject: [PATCH 0472/1263] Allow configuring clusters with ExternalSecrets Signed-off-by: ruromero --- .../templates/cluster-external-secrets.yaml | 26 ++++++++++++ clustergroup/values.yaml | 9 +++++ examples/values-example.yaml | 4 ++ examples/values-secret.yaml | 20 +++++++++- tests/clustergroup-normal.expected.yaml | 40 +++++++++++++++++++ 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 clustergroup/templates/cluster-external-secrets.yaml diff --git a/clustergroup/templates/cluster-external-secrets.yaml b/clustergroup/templates/cluster-external-secrets.yaml new file mode 100644 index 00000000..ff71f8c6 --- /dev/null +++ b/clustergroup/templates/cluster-external-secrets.yaml @@ -0,0 +1,26 @@ +{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} +{{ if .Values.clusterGroup.isHubCluster }} +{{- range .Values.clusterGroup.externalClusters }} +--- +apiVersion: "external-secrets.io/v1beta1" +kind: ExternalSecret +metadata: + name: {{ . | kebabcase }}-secret + namespace: {{ $namespace }} +spec: + refreshInterval: 15s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: {{ . | kebabcase }}-secret + template: + type: Opaque + metadata: + labels: + argocd.argoproj.io/secret-type: cluster + dataFrom: + - extract: + key: {{ $.Values.secretsBase.key }}/cluster_{{ . }} +{{- end }} +{{ end }} \ No newline at end of file diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 8069ab74..7c92aae7 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -41,6 +41,15 @@ clusterGroup: roleName: imperative-role roleYaml: "" +secretStore: + name: vault-backend + kind: ClusterSecretStore + +# Depends on the value of 'vault_hub' ansible variable used +# during the installation +secretsBase: + key: secret/data/hub + # managedClusterGroups: # - name: factory # # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git diff --git a/examples/values-example.yaml b/examples/values-example.yaml index c6ea7186..8e8269da 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -67,3 +67,7 @@ clusterGroup: values: - OpenShift + # Create an ExternalSecret with a label that ArgoCD + # will detect and register as a new Cluster + externalClusters: + - example # Will read the key: cluster_example diff --git a/examples/values-secret.yaml b/examples/values-secret.yaml index 1794f60a..baa7269c 100644 --- a/examples/values-secret.yaml +++ b/examples/values-secret.yaml @@ -17,5 +17,21 @@ secrets: aws: s3Secret: test-secret - - + # The cluster_xxxx pattern is used for creating externalSecrets that + # will be used by ArgoCD to detect other clusters. + # + # Create a service account with enough permissions and extract the token + # + # CLUSTER_TOKEN=$(oc describe secret -n default argocd-external-token | grep 'token:' | awk '{print$2}') + # CLUSTER_CA=$(oc extract -n openshift-config cm/kube-root-ca.crt --to=- --keys=ca.crt | base64 | awk '{print}' ORS='') + cluster_example: + name: example + server: https://api.example.openshiftapps.com:6443 + config: | + { + \"bearerToken\": \"\", + \"tlsClientConfig\": { + \"insecure\": false, + \"caData\": \"\" + } + } \ No newline at end of file diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index e93d80a1..f2abf486 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -73,6 +73,8 @@ data: namespace: application-ci path: charts/datacenter/pipelines project: datacenter + externalClusters: + - example imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role @@ -147,15 +149,31 @@ data: git: repoURL: https://github.com/pattern-clone/mypattern revision: main + secretStore: + kind: ClusterSecretStore + name: vault-backend secrets: aws: s3Secret: test-secret + cluster_example: + config: |- + { + \"bearerToken\": \"\", + \"tlsClientConfig\": { + \"insecure\": false, + \"caData\": \"\" + } + } + name: example + server: https://api.example.openshiftapps.com:6443 git: token: test-git-token username: test-user imageregistry: account: test-account token: test-quay-token + secretsBase: + key: secret/data/hub --- # Source: pattern-clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -649,6 +667,28 @@ spec: location: ApplicationMenu text: 'Example ArgoCD' --- +# Source: pattern-clustergroup/templates/cluster-external-secrets.yaml +apiVersion: "external-secrets.io/v1beta1" +kind: ExternalSecret +metadata: + name: example-secret + namespace: mypattern-example +spec: + refreshInterval: 15s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: example-secret + template: + type: Opaque + metadata: + labels: + argocd.argoproj.io/secret-type: cluster + dataFrom: + - extract: + key: secret/data/hub/cluster_example +--- # Source: pattern-clustergroup/templates/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup From bc2716058a90fea6095eadbfa4f8000dfa06754f Mon Sep 17 00:00:00 2001 From: ruromero Date: Tue, 6 Sep 2022 14:56:46 +0200 Subject: [PATCH 0473/1263] Allow defining the application destination to be a different cluster Signed-off-by: ruromero --- clustergroup/templates/applications.yaml | 2 +- examples/values-example.yaml | 5 +++ tests/clustergroup-normal.expected.yaml | 45 ++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 262c859a..3ffd6a0f 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -117,7 +117,7 @@ metadata: - resources-finalizer.argocd.argoproj.io/foreground spec: destination: - name: in-cluster + name: {{ coalesce .clusterName "in-cluster" }} namespace: {{ default $namespace .namespace }} project: {{ .project }} source: diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 8e8269da..7b3237fc 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -47,6 +47,11 @@ clusterGroup: namespace: application-ci project: datacenter path: charts/datacenter/pipelines + external: + name: external-app + namespace: demo + project: datacenter + clusterName: example managedClusterGroups: - name: edge diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index f2abf486..f171e511 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -68,6 +68,11 @@ data: namespace: open-cluster-management path: common/acm project: datacenter + external: + clusterName: example + name: external-app + namespace: demo + project: datacenter pipe: name: pipelines namespace: application-ci @@ -505,6 +510,46 @@ spec: # Source: pattern-clustergroup/templates/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application +metadata: + name: external-app + namespace: mypattern-example + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: example + namespace: demo + project: datacenter + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: + path: + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-example.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.hubClusterDomain + value: hub.example.com + - name: global.localClusterDomain + value: region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application metadata: name: pipelines namespace: mypattern-example From 06d2f4aa045f5aedd4c4a5a46b6709c7fe9fb5c5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 11 Sep 2022 21:04:19 +0200 Subject: [PATCH 0474/1263] Add vault consolelink and fix helmlint Make sure vault.server.host: null is allowed by the schema and add a consoleLink for it Tested on a pattern POC --- hashicorp-vault/charts/vault-0.21.0.tgz | Bin 41273 -> 41268 bytes hashicorp-vault/patch-server-route.diff | 15 +++++++++++++++ hashicorp-vault/templates/vault-app.yaml | 12 ++++++++++++ hashicorp-vault/values.yaml | 1 + tests/hashicorp-vault-naked.expected.yaml | 14 ++++++++++++++ tests/hashicorp-vault-normal.expected.yaml | 14 ++++++++++++++ 6 files changed, 56 insertions(+) create mode 100644 hashicorp-vault/templates/vault-app.yaml diff --git a/hashicorp-vault/charts/vault-0.21.0.tgz b/hashicorp-vault/charts/vault-0.21.0.tgz index b6802ed5dd248b726aeecb8e0eeb33357442c32d..c00d5787f929aa4691650c8b61d3867f9cfa9367 100644 GIT binary patch literal 41268 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 Ao&W#< literal 41273 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%0AA86=MT`q!W&_LgC`&kHx~o~Su&77Vueeg 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 F0|4xH8qNR! diff --git a/hashicorp-vault/patch-server-route.diff b/hashicorp-vault/patch-server-route.diff index 808988b6..edc22c57 100644 --- a/hashicorp-vault/patch-server-route.diff +++ b/hashicorp-vault/patch-server-route.diff @@ -11,3 +11,18 @@ diff -up vault/values.yaml.orig vault/values.yaml # tls will be passed directly to the route's TLS config, which # can be used to configure other termination methods that terminate # TLS at the router +diff -up vault/values.schema.json.orig vault/values.schema.json +--- vault/values.schema.json.orig 2022-09-11 21:00:34.834334961 +0200 ++++ vault/values.schema.json 2022-09-11 21:00:57.190368032 +0200 +@@ -838,7 +838,10 @@ + "type": "boolean" + }, + "host": { +- "type": "string" ++ "type": [ ++ "null", ++ "string" ++ ] + }, + "labels": { + "type": "object" diff --git a/hashicorp-vault/templates/vault-app.yaml b/hashicorp-vault/templates/vault-app.yaml new file mode 100644 index 00000000..bbe16e14 --- /dev/null +++ b/hashicorp-vault/templates/vault-app.yaml @@ -0,0 +1,12 @@ +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: vault-link + namespace: vault +spec: + applicationMenu: + section: HashiCorp Vault + imageURL: data:image/png;base64,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 + href: 'https://vault-vault.{{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }}' + location: ApplicationMenu + text: 'Vault' diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index 66d078fd..a5ddab21 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -1,6 +1,7 @@ --- global: openshift: true + localClusterDomain: apps.foo.cluster.com vault: injector: diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index 36c748e1..5e19edf2 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -319,6 +319,20 @@ spec: requests: storage: 10Gi --- +# Source: hashicorp-vault/templates/vault-app.yaml +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: vault-link + namespace: vault +spec: + applicationMenu: + section: HashiCorp Vault + imageURL: data:image/png;base64,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 + href: 'https://vault-vault.apps.foo.cluster.com' + location: ApplicationMenu + text: 'Vault' +--- # Source: hashicorp-vault/charts/vault/templates/server-route.yaml kind: Route apiVersion: route.openshift.io/v1 diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index 36c748e1..a4cdc9d3 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -319,6 +319,20 @@ spec: requests: storage: 10Gi --- +# Source: hashicorp-vault/templates/vault-app.yaml +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: vault-link + namespace: vault +spec: + applicationMenu: + section: HashiCorp Vault + imageURL: data:image/png;base64,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 + href: 'https://vault-vault.region.example.com' + location: ApplicationMenu + text: 'Vault' +--- # Source: hashicorp-vault/charts/vault/templates/server-route.yaml kind: Route apiVersion: route.openshift.io/v1 From dca765baa264be526eccf4dfc2aa7561dfd315ce Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 13 Sep 2022 08:57:09 +0200 Subject: [PATCH 0475/1263] Unset KUBECONFIG and specify a non existing kubeconfig file when running tests This should help avoiding surprises when KUBECONFIG is set and helm produces different results. --- scripts/test.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/test.sh b/scripts/test.sh index f426ef01..5790b5de 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,4 +1,10 @@ #!/bin/bash + +# helm template (even with --dry-run) can interact with the cluster +# This won't protect us if a user has ~/.kube +# Also call helm template with a non existing --kubeconfig while we're at it +unset KUBECONFIG + target=$1 name=$(echo $1 | sed -e s@/@-@g -e s@charts-@@) TEST_VARIANT="$2" @@ -11,7 +17,7 @@ OUTPUT=${TESTDIR}/.${name}-${TEST_VARIANT}.expected.yaml #OUTPUT=${TESTDIR}/.${name}.expected.yaml echo "Testing $1 chart (${TEST_VARIANT})" >&2 -helm template $target --name-template $name ${CHART_OPTS} > ${OUTPUT} +helm template --kubeconfig /tmp/doesnotexistever $target --name-template $name ${CHART_OPTS} > ${OUTPUT} rc=$? if [ $rc -ne 0 ]; then echo "FAIL on helm template $target --name-template $name ${CHART_OPTS}" From 04b07226c53fd3a060ea8ba2a841739db8ef1102 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 13 Sep 2022 15:21:10 +0200 Subject: [PATCH 0476/1263] Disable ansible linting in the super-linter This does it in common/ so we can simplify pattern's makefiles We have ansible-lint that covers things linting-wise. --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 98850ab4..3f371774 100644 --- a/Makefile +++ b/Makefile @@ -90,6 +90,7 @@ super-linter: ## Runs super linter locally -e VALIDATE_JSCPD=false \ -e VALIDATE_KUBERNETES_KUBEVAL=false \ -e VALIDATE_YAML=false \ + -e VALIDATE_ANSIBLE=false \ $(DISABLE_LINTERS) \ -v $(PWD):/tmp/lint:rw,z docker.io/github/super-linter:slim-v4 From f492111baa16febc0c084a5da48edc9f8bf24316 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 13 Sep 2022 15:37:43 +0200 Subject: [PATCH 0477/1263] Update vault helm chart to 0.22.0 This also brings newer vault 1.11.3 version Tested on MCG main --- hashicorp-vault/Chart.yaml | 2 +- hashicorp-vault/charts/vault-0.21.0.tgz | Bin 41268 -> 0 bytes hashicorp-vault/charts/vault-0.22.0.tgz | Bin 0 -> 51863 bytes hashicorp-vault/values.yaml | 2 +- tests/hashicorp-vault-naked.expected.yaml | 31 ++++++++++++++------- tests/hashicorp-vault-normal.expected.yaml | 31 ++++++++++++++------- 6 files changed, 44 insertions(+), 22 deletions(-) delete mode 100644 hashicorp-vault/charts/vault-0.21.0.tgz create mode 100644 hashicorp-vault/charts/vault-0.22.0.tgz diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index c9e6f210..51ae676f 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -6,5 +6,5 @@ name: hashicorp-vault version: 0.0.1 dependencies: - name: vault - version: "0.21.0" + version: "0.22.0" repository: "https://helm.releases.hashicorp.com" diff --git a/hashicorp-vault/charts/vault-0.21.0.tgz b/hashicorp-vault/charts/vault-0.21.0.tgz deleted file mode 100644 index c00d5787f929aa4691650c8b61d3867f9cfa9367..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41268 zcmV()K;OR~iwFP!000001MFOTbK5wQpTG4fP;%X7=hoE2iIa?~_U=56C-IFFyDVp> zuC}(a5D7_KQzVZdZEGIi{rUwEl;uZelbN}@RqbXZ5NI?SjbA^|^p$S1dicd>{fyxA zVt*h1JASc03jgaqUyPqU9le16KN~~8@!n`W`a?AcxWKOXHrh5g@yro9)V@l)9Ur!Ss9`$COA2Q!T0j2%hLZG560ucUN12=uF|rmw$u4MjZ>YeH`*@K!=ftHcc?*% zG^n>MR%W7>bzR!YaCm)vJs|xmtK#n_t_Nw+OJgMCdMwg<*~|uUu^KLsU1usaEQ$|b zXF9$dl=-4}Y1Y?8mDouyQbZd5Yfao#X}yN8CXcH%t&ZC(Q(O49(z#xkmC3QYZt7yC zrB9mAD{bqliR-2^y~}21yvUyX+n-Tv&j+Ju^rV+-lm^dfi>8VVG4b%?AJr)WbzD8J z=jBWLxQ?++BiO8~G%;20%B^ZN*c%TGPJyF#M z&Se?stX%2=x*b&xht+~tHH1S^T$|*qD&}cs-hr+Fg`|itO*NE*uv;~t$t&Z23wdz( zbhRp~mj2Atk0QUF-lN!+ApyOKx`W=M_O2xR2K+1>0GUgMqTTi06@@&H4{!6 zwDKT-`u{_yO%y}kK(Hl5nSMlSTY<&`a)6}9qLW2FPZ!`bGBDzi>>ez;ESfA)8Gw(; z6*!xUI!)Be(aVuP38ZHwu<0fMhzVe4Hvm7-%FJjyqI42xcnIN;tw2W;A_t0CFEt#< zJOhl%!$V%964!*yF&`#Gnb=e{Oap*u2+5fIQRW4^9c}0IW4D9pTIEGz;Mn6Dfkm)9 zRT^6av9d@(^uX6p8gVN5x-m&`=3kkzGRPS;p8;cl=7{KSqc-6M)(Udg$phF!kc(%P zcMXJIuyxc6#4*Nyak8azU`oRM^~ldlKuR`+_BBkl#bfVNFPH`%j|SrxgAunN`ZESi zK(Ox$P5+afivDF$>rm_RYcqfV~7>3pxh7~R}3`oNO z{xu#v9gP13KuHuU^WRNc84`WV@WWyWMZ(dkY|_Qj+~ugS^Z|;4Wra&}EWG03qF+1e z8yF9`g9LySM}Kfca44N(gmn-+hjqIk2t(6-3t9lc+n}AJF^-;#zBZ7ni0u$a#s<#X z(9x!WLk>YBKS|VbJtU%W*_@=S6ab>XKN_w2uJIhiYb-YJ?YricS-HOBy_auO-`aM5 z{tTOiT@i;jWvX76tRfO4t7~wL;0UW2Jm_N8c0~PfVC1aiSz&F;&`O(==5f{}fYSZH z+am9ia`wSU_IZG#HQ~lho`i_FeYB%btRM7&r3j{1=RfE!L5ARo*cNk>)~ES7;L7?N z@PPrD1+>JJ;ReR+iJb!~nd&$Pn1I<^ba-2Ul~HH9UUm=Gw|pT3! z{y+o22Kvz7Y9_K`p}<3$;h|W&NkT{gpQ|YeLlf$p8SqR45rh911N0_l)+}~On*oP) zmG3H3RYip$UMx;<5U_-g@8*LK!Yw8?7~o!D_xiX6iLR3Vu59#xXZwbFgdJ{8iEe&) zVl8-@v98D-RpzR=G!mcG@HJ;;w9AH_&!o7{$pT})Q*afV8k+)ZacySHqPTR2GnjbJ znjr3?ELFM>2x$6E?iIZ6VP|HR`YMH!imobXi$$=2vCf}7J64h=azwv3vo}c8d6M81 zX5g}+d&(vk`(7`>qjB@&*=lm_0=8X-U-Bi0TaeA)P?P@#+K?t!1m%uNOH zf*I-jL$ttzD00N?_$TCEt9T8Z>Jn)qCZ%2|~s}?!HsXNQC z|M60U?4-$x1|DBk=y-{%L551&Qi$vF1@xGKSx-c?;NoS_RLq4YHoa}|)aHxtSz?DF zqW(Japp^j!QN_q=BWel7LpVyt1^h9Pk{twM3nVjv&`ig*lNq|RMNJ5XFDym&t_rOil$FvZ<18|bg4HPV-EzQcxYcenUgk*>(VK0=UPaZS|I zkrN~a)3yXyoEr9Aoh+^U&1nFP-eL)7TUml$qQ5M(M0w+usQs<S7k&mrZ8xW~26xEMEyG08Nn|gdWGaZ)c}$;(tf# z#~)j#uulvRkWya_)XzUhdR)d#qh#ds zCd=r4CJe*c5p*@-k{kv2yVhUx$HDDC>}_J0Q5vE_PZqgQ)5J>}8w^y)3m0+uY2Y{5 zIkcFDrhq8)Rt{AK!kQ4<&Kk!0>n2Y!!xsz~`aFLYfj$FEi8?!a>%WI`oAD5r|2jPI z(UHb5dXbxCtGwxXc=|}rcEBmWRby*O_&HI|H#Rwk^TR@K@(gc|bnX+YUQk&IkvmQs zv5YIu2_nHuV@ihre&tP>phmNtT&v5!kc@y9aez4_&=CGE;SWKYQ#!mG2rdh~7S6r5 z7K(-F1)8^uC{^NJ0ABo&2LOi)Y(3^zwJqp)3LViah{T$ju6#gnoF+?uz?;#z$_?U? zuH+ucEnLICW@&o&>$j^(S_USSI;B{3r5!FOCk;Il%DG*LhKNyekm2;9DK?0e9O?L(W* zq3y`|FPjp@F%PBSQqkx{;_cQM>@?`d!W`LHXPmhWE~82LNcYEz^Pj38@*OCsoaaRBNwz0zeTXmNrQOVrM|GAqiJ@9jT-oBGzcY_Msxl9mi} zzaKG*nhe^7qx>qZiX5@0@?Pn zJ>Z;tT>SVDN%VI#c!FR$5IN2bIY`g8PsouC5?S_`mJ(1f7nvz!WKE#Ku^vC>e-!RsLn-1lNnCRKI(-2a{hyf?qk?>3RuMRb60LXYN`9Vm%VcEaogU#7UW4I+MUM0_;VJH|5@&N|e$bvnOOxx}IRZ5pb?7_~9yDED`l<_EhCLv;l0)2~Gxj#^V z!=V_vf}32FWwySHiLE&%UW{nPt##bOrdw*Py)HPG0$SU+aduK5)H5<=lD8N~7t3Yo z5tYE>tPwucA0Z~3HyPiUHE27;K?0-0m2D6+ygSA-a`f%=OL&OG-|^^9y~VMwTM^&h zhv=-ZpDe%uV%Lkq zk!A&F4tJckh|1u^k`r*0{57T0ABos7l)=kOF!QM3IyXgQK|khIu~Jt>2K+!65zI@+ zut;9{hOdba?^AAjCb+QDH}BijqAy%R_^!=@i{~gHbLbj4a_vzi_1=nqn#hGH=tz6+ z+J>K;%UfjVeY~eYFz{gY&9tc&1|8Oa30pfWXrE#I!bF{i#Cko(awh7Flz`EG)Tcmk zqsmJ58U`WrQLFtW@fZDxvn7D^z%Y5K4kuZO>SxUB}{&Iw+ z2Y{{+bDrke`mZc*t~{kQ7prercn~I8u*wZ%RU*dqTnRssD8ynHtRVH%fGV0CTqG0> z4o5`NfyBmxmx_#dEv2UZ(FwETFyUe&Pj!bh zmqnKFiQXo!3!iP0F)nxF(hfL*Pqs2eshP5^8ll{Z+v%ZxKVTJw800g43Ae!n{c1hjmR>$)&pZx z)_Teux9^~mVUg;@FMX>g=X(F-zip-xXyO4yK@*jO*4^CEwa#p$W5R889s0hTQa)$# zDakvqBHs>nc}+4DiasWDmp_QOiN7=oVYn1) z+bQJv1ad&;B6LGJbSA$C4pgTymS=R!m_W#F=IM=-$@l0Py*c1Jsnq(HsxZ5ag~u)s zlNXrSuS~HZ3=biwLb5BA;tq5=#DH_irT1c&`OIs&^KiuFRTXNRN_f{+&0?$=6qMl4 z3BQvS8O}-!01D*bxuv@IpMWtJmQgeDQ&AO#uYqJQ{k9?EPV|t~d)1o9WoZu2R z&q$qxY^kW@4R$0OLD!aUuoF9OJGN~^j{!q~o8JV9+$$eiuy-p!Z@OZ1Zzo2Ehq>4Y zJv%&=C7knm3C|WwG^5z(uGcv}r@H$!{R7Ir&BSaoBs=#X0e(t%zG9*>>&1rRg5_>{ z%f$W@%Quy@uw5(>ikEJ&9E0Qf>FpiAc19e20f>dQqOx==cfaz$MK4HSu7qjI%Il+? z-hk4iv$vd4YU(Arjti_7lfA>tsEZN6Ed?Y_^F&_i!?XcqUyMKPp`ytRyL$y?E0v9v zgQMG&p$S2jh#0RMXx;Ot3K-fqYV-%odWBonS=^oPJrS}T=V^WD-`wclpW(er=lz;# z=j~BWm0;&Vn?!7pP6F-R@NzNZrxpLP28Pb!Dy34h%{RA{Bqgt_6b=GjP<0?xZptZ3 zo~!}R(inX4wZE(DuBfBF!lNjq5Px~#6$$IktTo!yuuUIOT=Kixb$(huW3rx$WI_J+5e*|puZU~hgK=dOu@lYG}H zmSU|(ALI>UZOsoRNpxk-ssclGA6~@6X6}#kjd@os_LicdiPEkpnn1EI7#si@YeEm?SC3 zEZyuX95i~B&U(2al{N+NUZ9@Ozk3%AuN~p1wZIhDEYS@kh3?^#ZNSayaomMgjG{E1 zV{!|_04hueq1Ffi@gA?nL%X)arO{cv{C7$%%+|%_pIaY7+#HRYJL5Mh>~3hZ$Q!`F zMj#cn7fbNfF{ei_IQX|c7U>mvJ$xWG`%cuJ%=XUTLAcjN^c)Q-d2hZt28;6W4`ZM& zClfG#Xt7eXAriphuM8}qUihtg=rVAT0*iM^_VK9 z*X+Awu!3RLtdKcNH3#UPHI$SHUh`z`B+~&s@edQAr_UWx(|MU#oQ+8$kdKtfOP4@Y z;|D~=RwtO1_5sYOyllAn-bqJO{oBtOETq-Zpoj}a+j)3rii4G`f3KGH|Bq_f^U*)m zY(vd@VeUsI&YNqc;>!dP*Z9w_IG>(SK-?yY416^#Gf#OoM>QY@aK zQj{QCc#U(Nb@yRB>LR*(t-)r-l(bk}k-K;-D>JQ&612ylD$36(_6>G+tBGKz&U%m8 z>m>>X%oCc3b>MGr1Qk*wY@9I;YOO?1(;wO^Tu_@?QDAUdQf(_bEiUT9IUuI4r+5PV zpMZ(f5W<%-NWo&6!KV_=t`uxO9cCM12iGr(3?^BPOqjHYv9 zk@R^$%9WAv1rA6pYT#t)>=7k@uTM{3pB#NSKYD$9`p*5SjqbCv^V9!4I=q;u)mlQf z?(>7QvycVV45dmadj>=Jqx@EYFugZi>MSQb)xBbnGqe$O->B?M17 zmg6Di_jJi;fA2a-`6U`eJ>?%EY(bXb!bgkyiz(-UI9IgQr_ta2Ue^P%LVBcIIHCcD zeF;R^Vds7&cznnT;cT=X3}ezHa^9~Vf_Mn5v!8Hq2g(v^xl*jL^L5s?rU)zJw@M1INMAspSUnIrv`9we2ye*gU1s#(Ri0!i~BlpS^be}6yNpwUp)m=HK z>l#~WXYUGK*6UeQ@yC0Rj&t<>nmno}j`3(0YJNS&?^AkEYY*Io|8&NGI(sw=@9EFR zd-_l)@uVZ}Kqn-$Z%s519z;d5f56@kn$ZXhcL-uTBCQ2&c;VoC^ue$VkL?gMowe4Q z^na`Co0{v0VpEISKxrq9+EI7cJKsMw2H^yCo0}VjF5>p)COU{0`BYDU?9vX^_Bc+T z>Jpy8&ehDnwBagERGG!L8%LA<|Q?cj0&K$DT9f-lSLAKW$_BrU5gh6r!*H&IO zqIw4>XQrN+O!0lRtY3vP{upVQ2cj`j<+z!>l3m)Q)G}w%c_}zB{SArNwGI|`wa@yG zeL1Wc54xNHo;!_Vp^tjTa*l2%8XiRCHvHg+smi`8z>icd%s36NfD6ApiPCrY-5%c- z^7y8v{G54aa@{&-UoMfKLnvv)<%&lruOB8oRu*@T1tovAE;5_lkTn#?aF*YTqogKs zch*rUm!P~;5ez9Nlx<3rD3{xi_-^%?+;Bs9ubGLqoQI+AKpxwXaKAu=$p=)Io2##& zIYyc1=kW{jv;l))zLB)X8=;$~aDh9OOOdLs=B_ZX-*%&lOFO5{W zz#o6ur|ow9l%rk}Ul4oGgD;3K)N z;2Q7x(F3I{fnjlZXWXio$e)G(rM&ZTU&6_Yeof#vWFdbDMPtOYB5P3~{H?H$1Z~nh z$b1fl8lo=LS-2o+vN|y{hKM6!zEWfy_wWglYAh7#IfTowB!%jQDO@c!>k(Bu0Rgb~G*hq*n`Jnn&e(U?Lz?yz`r5qhxAO_c^Q@g3W45s9 z4?7|2bwIjAmI{}nLr+-0f|h?}$PmtUthmQtw68!-@LI+h3qv_2U{UkQxz1HWcnt-N zK%PmRyk1`^Sf}P=V4Hq-1Gky!eV))wOIm>UIPiWq>}w5v9Trk2>MICDEMvU-k#{|e ziBgc8-wJ+178N6T&q_`5d;KV5oy)lmQ~iXGXdIT>O5)9VDfnkJWY|R8sc>23cd ztQtLF787P@h#(jz9SH8ndE%Y{M)|P!{`maj;PAu6ySH!8FV5foczAMgbih0fwSz%0 zOUyZD(dp_kFFL8OIekq>r-Rgtp5Vv}P9 z<^0TOJe;_Ww74#butZss^+OrvHoUn7FxK`H1TDc8;qNGcwEeEI1q}QIGw=&VwTScO zpjWy7%$bXDPyM#pJ`wv5N)2|c&c}AyG}sDX5dtxm8KrxmElA3*=@c^(B5RnkEdo$Q z*i$$f)9`EBY{*ay^>m^LsKzytXhmqlTGl&00cBe<&4h)~gw#*AwA|A-xe+#8*E=5x&y>ZZzz8KfXEB zVZ7v20N)UPXE#Z|OPUL|$FC@NOX-ttMkHkt#HkSpv4!lC9>v6I=~%lT_D^XIvNbN$ ztKYN-?Ymy0kAoDo3>ozAm%^w+mEjslI*o=HBmsvY8?sM@*#Ne8K{zHi5}#D#E;gG} zd7z15cxS#N0<(YL^6@ z&DFp0Ga!;@JgG^FL=$c0Cgx8eXGR1w$MZzz)ej%r;>(OkOWSvChK}gTi@nCrF z*L$i*RU!1599%0+gnS|sG#YY&QBLGE>Y(0MXabpE9E4n(#mtc?_h6zI+Q)Fl`!FY{ zuopSYH%pkc>}2oF;d0YqhegjjBfC}DXT=_2$C`mY^9LZ6{ItX8=!KRBH!&$g-l0-K zfHDBv`@Zr#NJj&Jg|2)h3ClC4E*?h8ye}0myd=pbgZT0agB#)wnvvd}p)VK1s^L=Q zxS($)jZA(5v1Kf|Y~eF%0Ok-qE@Vu%p~5hgv!XzqK*{3$)1Y|jqAU^_=}1PQ1cAWT zMfc?1FOI(}@-w1#X4jxE;>c;Y#(9gQ7VR8w&;(di;qeH-M2l^jxhDouM^eKe#l}ZA zS3Xk~=xLasso0>oF+zD($iWPaVTXztqDd@KIi|>9Z4Ax`LDF8u7~aSpf?1LaD{QiE z)S)qw^v_caVQivEFrcO^6;{uCArt(GWr8-riAyRla=2vXM%sIEbh`UuOHZ$!MEM7? z5H7TE(OBQy-fC@rUNQ!$9%q<@O`YbW(y)2PzjdZH{N#d#aUwf;SuRESQh#-QxzQ4; z=f29TF83owk$k>P-1S^Iocc04judt)6yJvSHnviZ(BU}}Nsc^H=ZK|dQ>y7;#RFx& z(|O&kRD9CoA(nEV>E<2{1m}gKAC^STDU6Kt;g%^AMkKpEvzE{7d^JEQNb&7X`gf*E zO=&lkYz}vg=1o%o|83dD&Tq3>?Q_ZChlyfJQI{X3BorAYr48h)!Yg2>~;%;hzO z=v_(Sji2&4+J&%Il%d_|n@bBXx)vmb=bo;#@Fk|SQ?S!_)ep>hR63YR&!JX3Ipcgm z`gl8md@hB2P5RNjKdKGG;yTC5GAweMU}C^Ho4f7(5n*c@M7&f^t&~eIf_{Hes}OJ; zBM&R^XA_xQ8b`#>tf&wGS^m9lx?k08XFG!opu-ytB%R*8C2}!U8Qp$-aVC>a@Vi+y zI3HXh?=Zd42KAqp(47}zh`^-JP$J}Ipxo2HtM`x7O?;Ta1l-}PB?JwQh z*55UEUNqM?nj5Pd>wM9734^e%xao6NSbc6ld#bNR2IYy zp$LyzzO2D7#VANTQewYK4F{rm(8Jj5RHG%8@l}i_xuv2Z;fH~btW|fkB4z7muJkM# z^cqQcgW%>K@R^vn0kbxW8mZ;}BqKk?YPiA-Z&D|I)tosO_LQtIS6!xLIYNEPs2L=i zt{>Z3toDrB^-;{EN{&jrb~5U>p*;qenImV}Y4Mu_W!B2qGRKpU+q4s`u9vH6{%4G6 zl<|z5(@jU->eQgQ7+uQ$W*pd`H^ec2cn)7I__1D-dW&o@%T;G@rHbp~ z+iZr{37y>X597kD^pkR`2zqFEI;s^vQain`O5*0oi|!WuOURjO#!o6o=~XzTC~kWy z5ig~bBn{#c>DDL!jxD%nN9sJ$90qMTUR!>dP+)O=HAveA$#75$)j=L1;@B>x29uN1 z_jmj)N>yUydq2jo+*R?5eFH_IQhBtik3^|19v3|YgL(ynW<#lXqO3$(T&+w?u_APT z%v_HVjyzL@Ee2sVBc(e2lwb@C7^?;-iUGBD;J)t}#DU_d*+@`lEa;=)RT@8M1gUbk zT)1Ch)FO4C5VBaduExwBb08ENu%=`T9YHRXM^3h$>dd;uFMm;}=%;%TpvOetZ=Ieu9fPa$Ra4 zcCu+@@n;1`f6Mw0W7y7X$up7mj0i?UO-tj;GuJ}2XzUSnx$gQm+4P%0+5fWmd<`F# zg+=j!qG zSmipZ4X`z*Mw8zHRvKWQ@gRZUw0}+=5R&yp@eu0Qf}zy!N+5LpJb8k$6RCCvPdv7v zIrYzDTT#ogi?doXbDzz#WPg4s{PmCG8R`b-TzYGA;g6coR&K6lN^@*lW#~r#uGc>1}Ej7}d=FIAl<}|IXX`1DvQ$YDRW@#Z|nV-)}!ZHkT zvph?5u_$wrOdOy7$fp79tXXd|m=`V9HN0&{T9Ah)nt4GUW-#d#uSmH?m4@(A!`kNE z1pzWXpA!L^0&q;%y=lb42^6VFOv6t(kx+Obem-lgx&LLXT}EEcSmj<;h_9#`5I*_j zxr~7qv!%l%d!NDua9s9m1$#KJeuAMtGd`0+GFad|a|*^lhi#_S z?(H*&Mjp&w>t@g<)(d7#X~o`c|}oUvwev$ z>~U}(6tm@z8np+KNZ6hmv+{Y}6b7v)1HY|`N6r>4n`J4VQky*wMw#cB#ct-Y+CkJg z$Rq-s?bTxTqsb!{8UjCwd7QHp&Un>NWR8r3`{*us+dsP%M*T0bwG{>8Wlq1);p4wK+GYzO^hZWM_9#(oUuq$PQTo7$6ld{oHbQP~}9UnQS^ zdPV3opf?}av7>&Nen}_t6s~T?6+b7>Qz6dGtfDZ}fp7h}F~(fH`wQd?#^4+T*CE;4NpQ!XV zKc6Ns-V-p#q}sh%v2WEs?uT}=frDUB*rk1{AjerA(H^7DAQ32@#~Cpdc2 zbv(hVndtqOTkezE{}#UgGo0_U@%@K^v54pVG{0EO;B0~)UT*TK=)pq!JEMan0Sk4q z6;RR`sdUnG7{*k2rU`UBOoLuY5?+ZxpdJ;O1nPsEEZy2#SCxFoCCDU=Jt%d#&gD$F zXlpf}gq#XYH`!HR^_T^s@6IuuA>Q6Ok24PvR%Kh0(7+TF?2}}eoiF13^Cc{to8fPf z8gT}bPV;)dq`*s2N4;#!_KZ-?cm_Q;>l7T<=ViqFnnE!_x=hcow%kk&6N5N7OQXSA zHwXrkp7m!M;P@%>|1OC4lg_*#sWX^#3cUGL;k~fYIeG{#^hD0jm!KyqrPKJLEWPuT z;w#D3Pnmpr5iOnnd$L>Y?^O z8>idk){g@abWu<_kzv!5!Kqr39DG53H|o!gY=50ja8D3cQ+`{eyiT4fjR?HV>tT0M zGTQz;4vH4Fjmz{{)HeQHY8y|1I6&m+g?)O)Kg2-SPD?2PAE%UFb&iDOBhcpvAthVZj&!2G*f7i#tPz( z!{5~uRF6Usm9;~U6t`G4Fi~MKgJ(Wg#Zm4`#cF4dj!P)RNGv=6lfD0wx95jv&GchB!#VJRw>LKn?|*Bv1^2%WU#;(~wKm}X zw>CF+{^G68a1Di@Kl%OF&Tswxoi~bx-gOjvA+YIww+raed+jH;;eHekJmPNtsa89Q z9=!C{PpS2-e;;_AV91gF4^As5pOqg5PmwN|VBJsQSd z3>V`kffpto>O?!1&^^x8me>A=G$b)aJ`h z_YEwd+w)LiG)!D$={7(cyq@0=uY)9Yw)}LMz1%1J{x4u#!0;x`bkLpE9OK{r&Sq<8 zE%W}jHvlCp-v1mvzx}rQZEc^d7M>69naXB**TAujykCcYH@ptvJ^6zHy{u;KrvRSG zu-~Tkskgo1wQv16@veflKZGZkL;&~3qsCqyjM44D1BYkV#X6EOVj9)bTX?TaS0j3a z2PbFP77x4;Y@nW^#uEG=i?Y9PQ zEbIp$wBq)f*n*5FUPJsIUVF_Ces>ro>c_W$yW+4Ds5XTSe&-{>!@xho-q{eC|Bqh1 zp(hBsi5}@2TKw)yb>7W=+!G@WIgN7-uugRjJc^BR)LN}?D=%;W403wy)&DtZ{Bu&z zjK=ep8nfRS`*+QRKI2+__#JkhfY6IqN>?l3gj;w3p~wue)%?HZYckit4M|k9vb49Cj#Hm4sd=4v|X3 zTNLtp@wq19p%C#kC|@8PCqM?9Ufpc46vaR$mx5otCAoI^xhdwr^+snThb@Bu6%T{@ zGB(GfE7zw{U$$Fv>^+EH9Tk6BNlsQRv4{<`IcyOJP?Ge&s ze+I`fc*{Hl-BQtD<8Lb0>vKK(Ju8#yxHGp~PC9EVL+hAr>$9Hf%9fShX{jHjG}QQ! zhgA0(WdD&VKLxWf& zv?!7lgxS}S6@Cc1#eDCebJffwb20@7JZ9j8vycAPZ^{)cdo9rcW;5jiLp;*Gve~pU zbIXw)+c6`Obi#XYDH&dQOY~Q7*e$V}wPkM|{)2y&5Jb)bRtVkuaYze!-itcHb3Afm-d8M%rH-$?EM<6H7hWUBRO$6{W#XPsZYnmdu ztsEN1p}R+QMYK9;t%g5kcFmKzS{=qcr{{^#u+oe4nD4ssQJx2Jn-s(0^);Y8_!`Gf zJodR9#{9DIRTaG)l|xD`MqDYqDSLm$ek4Y1>23x%h`xA_MxSCF1II20+{g?#VNN)z zF{d^hh?D5~1CJ7hCrsgi=7dTAc}mKRic_py_$= z=tKO|M@3(lqSFzaKI!ZI{-{Rza6IASp%GlqAF<+}h#08`0VlU_S@FiHfLX79etfoE zi1eiEh>gsMX@ExJ4)Dz`Gt*lP{k*MZ*E1GBEz66*PlI$ayW%tqt1N%<#vkljrdn z)6aFat@^S(g0VH{_!@Z`qw)Rbhtesa#||jZMSsM*z-kHH)A+)8S)1!?o`r8rsL7n| zy?v}iQlr27y>3y%-@Sg=@8AP^Q8)*)@mYB%li$0kkD+YaAA^w-JPF1cU2bcf828#8 zd-xSrIqo*rHaFMo(-?=~{rHBNX@zZiJ;Fy3;C?t==CaxBXJMk+bqIVGVT_|w&zHU| z*sSe(^-6=EzCCb{%|HAuTT~wzmvplC=CEMh_|xyYRrT@rPLD434i4Tq#+N_+u3J?f z|NPzF$=T6)(H!Q_zuT>=&-HHaRpCVHx8IF+<)>I3Ch=-FYWv;QtFVto^}8T$0Mdv@ zg9xOnW^&8OND4oK(!F#K?m|E^9lv-VPIimf^Re%z`a+RAnz)?FjF_+ZB=}h+cvm7~ zVso>&okHva{R^4Z{0V4k@L})$@%hEU;fITNZ{MC@oWK3?@Z{p?Ame35_`^j4P;@ej zhD@I3_~`5$ra(|SJUesGQ~!B)_m|xl>uYQEQs?+^8zP&iPgBxgq3E~@n9U{AFx#eN zNJlMq#Dg;*vb+Eb~-#E#y;Lxah=7dyzAJ=VI^Qo3uXe>j1q>jy`K`vPl zr4|1=9j{x|{8Etsu1je>;+U{^l~Xcj%N$bfm@-E;jTBKAPqt4#r;rW~MW^?z&jHo(HdY)t;*~N6rBaW!ZAkTJz?u^-s>6n`d9d zpu6wh-7a@EC3x-OfEAQ7gOkAT;9%0*#7lR9J?HFFo(|Uac42 z+@_?qLspqO$+k&|+_m7Ap<_3^3rI!q9BCbp-F|1qyNwvf%`l(Ec(JQ^!5E7M((98n%;d)g?dptIrig4D;gXwlGt zcWE#Ec6ZoOXOF*s(A#gC!6shQcz@gaC7|j@&!g)dCZ=54n1IxEHxuJvK*LXQm`d<*-aHjOi%szies^>zsBNKs?8TRDgRze%reyjE*^i@FS>Z9 zDFCPFjTcW=<=k4Y+92F@1xLY3k+>7qd0jkqp*7%_+Uzc;Z znOvB2y>ep~ukx`%q~yR=4|<-@cFp83n1 zaNT1b=a`GkLFz1SZNgxWf1tC!Irq>rzdOUligW*XiAer4=GtVFNmnM@@yg`Cc59`s z$nHG;YKpE!jbUENrgbIR>lk+xYB+N+x9WcU6|e&uz7j*ZRH$4q^-sS<^8HC6pTU3v6JO)Mm>*-SEME8iMQlx`sjNaKiIU2g_+)! zbJ!=jEg2?J2rAN_bT)_nTuhd3GzEc1a{{Y}_0tR=0rwKdB`g1z4Z{JJ|2 zvz^Vp*N>TYI0U<$A5k?%q8%$la$%c%4^^i1X(X|1~^&Oel@ zk44WmajwU|n~4|Vce~NUX&l~%-QXrTOxk{z(Dbf%?RPWI8Et>yUxnQe(?0HIcHW7j zLH0W|T<#qoKNhxSW@-$wLChk94IHqoeV1e|>k25oT<58febp(ZVwfk*GBYN6(nOy< zDG>X33?UF*E0ci$DCera}7N^0nRo0RC1i__8)?)TTG8w?$$-? zy~5i@=fb6Y9JuJWylie}*%2_FpFlc@)Lap{CIS(BWJCC8>~GC);gg{6$!tr2scGuXN^H_08_86C9OtoWl| zWvp?@9wY=;;%%Hq_(;aaz5Z!HzvPJ3U&G{qAOAomk3Bnv<_6=n%iOSNTHaLW=I-uXI;)$a3fE=si5ix_rCjsJ-ci9okG`S&cvIg`e#lwfjup9lR-XEo zx6j!)q3E2;Ai>#Vzb8R^Grp%&D)>b@0EjX$2GFl8;^vAg7Kz=g`!cXhJ~VmpB5H4rg=Otj1Nt$4h`|Q%NAFLtS*!~%X`fb`DqFNWR^A=6B>QJaK)!+= zyn;zg3C_n&%P}N{?JMJ(2_sd)}qIX%I#Z^>nNHa#*XU0r!!jEVgw_yLr? z(lH}XceSM*E*^2kC4DSre$`{V3C1sQk|ROM-X)CEkG=QY9J9UsNge_-0D|$(0Ht7I zHi2`vOlVRfZqCtCA3&wBxv?Wnx=Ll?8aLWZLULRK&mtg4Q`A8m-G`V^bYk3rTuV7} znrW$5>WDg+d`-@g%3M?a&1HuxaB8b~K@$TF9(_TTtUtJ%(zYLS4>Pp!lo{@E=<-Rd zI}-+;iX%EAgeWdElkPIXFjT~vfFXN+>fHsS-FsZp3ZBa#8Vr#^>J%ow#4!)`kRM<~ z+e+$cp&D?Oyl-IA74I9G7H`ts2f8$LdTU+%txo+tqsUuR7I_;E21HkW9Y(1*ewu_v z5c3(o793)Q16AjRDbhIKTvFj6@b<8-#m9O}51_EBO6Q=K@PPUTe`_2_}AoO$np zYk?>3?~>VM2-b@a=!36^qn+2=UMHCZ4-A2AQ=0jX-k0=)<6t z*_q?WtsEO?zuYw;%RF1ex<`q|oJ|G=6TGZ*g)X!iEIYwo54rYP&x9%fRFefr&ST8v z(-u~p1o!1j8{BguY>sBAJ{(X52sr9y?F{fo6ys|S`-5T1l}?>zDS}Vs$l`Wdrb1Az zX^mn{l#m}VVBLma(fu5MwcEaQ|EQ|_q|PqZx8_+2{i%DMD%~NEQ$LRU&i8)T@3%of zS~h<#)<%D-U!0|M!ZC8Gp0m?$4acx+2cp zXI_PsqR%=i&$wB0z@t+>Ec-h?rF^^0k?_klo1KxK>m5GXBtNkEtNpmed$e9|@hi-H z{i*97FFyRq*8c|fvLOnV#UR8rN5FedJIDC?-&g^B!><3m4h^;z^}pxwAqgW>+{P^9 zYteAy7y8;Cgdezc@~#(O`E9Cb8O7m$5mIg5y-1p2w0hsF-2o}z_4XOA-T_?I%;ck` ztLy-5O8MUeQp8wT5L$?OK$)9&sscH#KuwVGs4N(}V3e?rS$Nul`LIf23$8Zbr|0*9 zKk78DMs_dh5+4YZppxRog00f;Uqux^Z4pNF8r^IWHaIZCgK0q z+UEL}jsIIa@UI2_pT}or_#d!cqEY>UG~-Q;;Y;F-FB<6^28IG-DCuaAX@`a*%S0Qh z+0C>JH2$zn11io=U(6Vv6cYDkK7Y=McV=X+^GMfcB?GVrv%APjmgXsx>X)e+Ek*$P z)deo#J++kAd_U*9&iL-*m$3UaJOtG{~xreT`%CYOrKG*RJ zecZz-dIM;!bVL6H6Q>*YUswa!@Xz05-ayWT+>|ReD_V?sI!>RUre{6ktT8$cBkCf{ zLg?1HpK`!Frn3dOv;~W+J9!*>R`-xQH_Jo9*s^lvFu>1H>{ogg&eJzM1Djq3U`k(< z;nb5Egp3}$UOjX2^fi`-j*<{C0xG9>olR2z27aq&M?^Dj~Mda!P(owK6h zH5$?Y_HtDo*{ZO7MeVy>>rQYrym^^LjQFKJmlI&KRUR{~9WPY(D4p6veuDmecC%&$ zLo8c3ZoR^qCCv#`lymegr4Efl8PmC*U%4|CiZ(1~;9MFc5!iNDS0}#1W)U;g zOzztz;AJAC(4bN3pv_ur?h}`Q1uZkXG^(K075vqmgHTg~)J7+ai^nM>+DK};L5k^+ z4nn+&RX{$8bD%gH_%|YZ%j+mf&!c_i^EU=&a6VTZJG+Ae%*U6rn{fW!@IL4VNdl|B z3hWC&Yx57;QqVvhxa=HT#8?RNXT5(u zHApW$6+T(~FJodF?I2E*279AUNdhMN|LkP(f2-A6*nj5oap8Xn0|i7N^K;fG)T(E# zf`tL+0a`s|UHSN0**B@9Kl<#4ZJ(oY(`Bzk4x5()AAivO7r+16IRQ@M|JHUkv+sXxYkl$l=kb}*`=7HXp#7F+?F;xRU#i0!(EGXW zcY0xhryB=1A=TK@r&|BT@)LyCy&>{u{Tq>a)$(*tsJZeNBuB%WAg#;4yD+@^#{*h3 z)xQS(FP;A2Q^W4!GySIw{-4$fbfW*~X2$<#eSLFn;r~6C&nL(K(|Cer$iNI8KtCx7 zn5PTq7)m-rC(zt+mU9DL5Sl0d6ykqf_H=3m@C5(Qt*xB=zqYfs(ErZmGb8-}%sbF! zaD;bmI?Z2cCFe+IuSa@Ex(oYGc8}EkuCLJtV3cu(bTv(* zLDY?IMrQ-Gtnb52u>7!}CZ+niy%624?SkvYewwPoqn$O&j8MvBvtAWZ*2kVefx~Je zdlKc!YoQmxNsvB7@tu43MuRc?9_KQegq@)6$N6Viw8LWeP={xy>NUZ=Gt;hwkLQ?K zEKkpR_DxT|vtr-XLhs4F9}Z8>FOE+B<8c4{;`r$7{P3jEb5SPM+5PAHx&xlk!7%GE zf@8Rja+U6!6MmL1qAslRxVgu6|N0zM$6xaM z^Vb)rd*`pq!xqZd=x#@pA{QJyizY9jr$&0|ga8Uq8Zwn!) zqH^W>ru>{2uiu`XpUr4ycyB9@=~VcFXFD3=gGax1q#>`~zI(HGUWtgj;xu*!6+s5Q z)d7x(nX+&u)ilCwk}`%b=Q~lb-tc zYNiD%K0Cz!-+%w^==|>&`)^Oq5C2EOk=1Dtj)#-Kk|G=g;#M5C6J_Zy_1q4R58oV~ zzx(?bv|V2nBUf47yxkHe?)3fHA@DtW2Z!%2V9)#KZ{HQ{mL_+Oce3~9@a%MN--#_% zT0ZM@9R}Tw&d-lt;hK{nPjGQ2w9yhi65QR!H^^_&op}84TYAze0nw z93{aykB{CQO*P|j*b7U}_vY};)O+j&y-It;Ij7qr%vfQM2ZyhS+c`gcb9%gYUYX%h z(<+)#jwuk+A+~yT^uxvB|2R5FjQfs|#H+odqXESwXz*(vEAJe(jt0vRoab(Tbm;tbBt_s);@5rf0_;RsF;dmI54fA?XpKz^!z z2xAvp9_89{gFE+Yc41=!0p-!0zCE}=%=ffN&uHojtu#5C%unlo`OL>{-5iuwT*@ScOIWYt47p0P!RAvK|t2w z8Xy_-!0!74|0?VPzJvZh4zB{yp!p{3168lDw=lyxsUzc&S09j8d-Wc^)qh_B^=*;d z&1T`#{Oq5~J`9)MYbLj=?OR_Ej|_K1KNxEEug#$R#Tto=jh`;y)@XX&bd&$ua}^ij zf9XWr5J&oS_xesUYOqj=B+ z{7p*|LY0vMX(_L96$36WH?8J|20hosZYcIogskjTMq|@raQM-0r^;M_ZU~3h*W@zm zod7|qxg@noQ)N;`7_)7xsi_ozzYFfe;6c7J4E}&;v;t5gSgK!`x_3i>83t%cwS}=& z%S4ZaYSVO6j=SQ|K;#A@I-_*lZU6gWzmrMPmo+SkHg>%pkT5xPMoNUyK$Ta6W7&Uq zxOaYtNAv#l0RQ?$t;CA7vA@q8tp3f4x4{e-@SDJaSoRF@jt*j* zxnIl*rWFSr_i7v~P?b=(EkU*bCq(2verEVmH9kN2_s{+*h@(WKXw6{g`JSKe>RKQ@X}gJL^Ul?~!3joMLn*E`=owMfo_ip=Cw zi2n!4pfRHgKojFXTkF~QpY_c}{Qq1&Pmccqj)Y%L2?9_ebaYlZ17KZ`(g#0AW}mN+ z?o1)@ocXxt*<=CFQFsx~PhD#F@9yz>&!;7K;)m~GZFvK zpaRrn{(n2m|8Ijxu)zOw_lg3Vp`ghyLk zm}wA)_hC1Had$bW1ipLbp__Ra!>y$ev}M>$lD z^l+9RUPF}Be&Y|szhSxQ=s@HYZ=fy*4?pOLf3s(u_y}Qz_hf zVc&Et?fD;fz3r{74Rln!PMFZoaZ2eTiYNUFE+ZKw0P^yB7li*QK85suX4StZ%YR#I zIr_i3wUGbj^7(r5|ISf&$eU>hgGM9Pg4I`^dq}xhG@M=&mRCd~T;Nx{Ix_}6e~^fV zM*dMsEy_j9>-r0ok~Eu#G?S#1{>eof>*bauoHbmluqPd4ChcHdlc4^U(V?;Ue%#Z<{e)P{k2_BcEbUGcRi|#(Hwo!JiP_q8ZA_|50 zUnb+3fe0|!{*%rBj4@#g{6CM+6Xbu+R)U%Zs%0q;o37*H2fN?eWsw2eg`{z`Qv#kD6CjLMB{?}Uz|DXAM98ZuzU=PH&1K9A5 zpQ=R25(79|REPo{C#ct}RTC)+03FwQOD=&gLJ#07RwB%O*ssx% za5YsGk_^VQ&ZwMJ{{>0%kNXtif6~}Dl;Zv=`M-^|?R@;#LjOCTkK@(!qE28V{C?ok zw_aW5m9FQ>fkPdv`*ni*x(xr!#K_9z&y;<&3qoF#p>8?X$_M39g{P_a zqu9)^5Vyw;8%I)w?1m~^}qEE*OzrNfp5~$ zAn~h9K!1!oLP%-kIFhPL4=yjv^o%fQ{j+x!^ z%aUCYPzK#xdh#rUTFJdO(U=j3J-s-Y+g%z(vd7{8@7TXi-Nq5l$h$m#h3bLKb7jJ8 zGihNoK#A4q>}qzZ?u>^>cuSqIodTXRfu>F{vpgOo&3)k{r*V*`qyI>v zz7Ch)%x`G=i7U)$ov7m8F7I18{6d@W*HteqfX2|JGJc|JT}F=>O*O zc~bt*!qc-RYS>Ia7|MU6G7yp0B3gT%s!_FtQP&Lfcoh(D}KMU3J zC#I9d_gn_CzKgBbfM<|dcF0s8DyAmEpk?R-H?+Ez-np8xTE-@o_6E&`JC z00*6`rrk^}c$&25;}2p6+1D7Dtt90|e@}S-1^EBgZ}{yrybosK2RzySm&^aXxwFv! z%;hsJ|L=Al5wEepfy~urtl02OGG^X2;8+Gsk4oEO-oz(xKBgHK_*8Dta5dv(!)|6c zbC@I207c-0M)+mk4Lhn1?ZMstuPK{9S#MSaAGI-6$0D*__3hO?k6xvE%W z%P|*tk7sX6wbDIJp0J=BKfDuo``FdZD_X!{F^95W-6+D@7vGIrtUar}V58QPc*WI~)Ix|61^W^Z0yL{GZL|F@Gm0@$7j#19>l)yxA~$25Tki z-_3=`sgGxJ>T~6CzAg@7p#l2JKLzw(6m^d7 zo@>V?8sk~IzkIqJ@sJBrbB)J;sa#9S_kyh{wfe*k$ZKK>eC3g3bfxe09z~h1X0~K< zPQT@eto$ePVw?KkIaO%p&*D9b|L%AFB+(;fzjg81<3_NSt}+u(JU{Db_yl=o{40Mz z$j9q?mWbq7ID*}pq)O08h9mO_6_D9l%Pl&4ZA*ki&7$8+7-{3xSL@5mW)~`_Kn1kv zR;^zsh_)gQHG4}uPGf7tpPwVM4g6gB?K}8-*ZOkW1vzS!%a6i87WK>Z7lK#rTNq&2 z0~BKoVe5a*{i;}G$J$QHt;Cq4x*38D zOwq!57lGP@$&Z!jo#WURai>*21^9o4)t^hm|7Y_*wRUzkwioz+9-mT*oe2RxTJMt$ z?d%w?zp&V2hX03e7x;f3pU)8gKV1>%FAkf#%uQc!)n_v~ zYgQYHH+_~yF`U>qU8FMW3=o=<6gK*Smb>5e!(QH=we^X;ncqE*GCiYJ|?Ka~`*U|w5jemxQ2iXE|FiN-FUfrlWxxrGT@ zWR27Q3mK1b{4tdmUu-(#MWO4EJ_Gt)1NaB}9se?UZyK-7m9gKy*?QP}r@&y4wAQ2bq_6>xOx&pDq!|Id|3 zV8JPqzQBt>x|itwsFbTs{T#Ua@DwlxuKuMU0%?a<)~W6RS`4Z0mml zE^hLv&8A@V;`3E~its-GN&o^=GG*$2`i#f_>ucK^YZ?5%wzav?|Ig(!gTQXX{6F4j z0K|I%R%e{4AC03ZANlQc=yy34RVGotorE87W1_vljM3(%0r)aVfkQ8&&uWZLI~&Ya zK84O8>b#+~p&6Ma{7G5%d|Uv>|TrV%^3oF_9iK6mhXu0h3A=fU2E+m;+V~ zuP2$@u`6{ulbVtvh=BP_TbBlH5QhTSJdw~^-^Oy=y7cZw-9TMlODujI-oIuhZLN~Q9I!M83cjmcKy67t4xbwty8ek zvYf@h%Q7MZ;3EaNZURoV)@eCj6+nS#lX=2Q+Z6Qz<5@{pEh8k*4J`D^jsVj4rEimQ z3!(MBy}IHzn;Fe`yTx$ScFZETM%%YghJ(zA2$;<2z!rKyxt#e2@CT$%Q4+$t8bP<+ zqE@MYV?^%PiJwmEj)_izib@h$G}u73u?GO83Os-nVc+oTt5;!vHMy8N#QtNSz)a5a~%fVj&N^x{&q@9 z5pbD9&h(HOUCw;^_TUL8hKE})?}xqj$LAM6zBs$U6`bt7Ih^?#Bnv@!Zo(pP0cWRs z`%ky>JNoo&koP`_na?g zY+rohpA^jZkKdo2AHJ(}^FsEle*0rPPKE$72GK>!V_R;Pm%z4#6TF#E#JqsX|@9W*(tMiMKw+Dw8M+b8RnhCGx^;NHI1cWE?)XzUe*444@!^NV<6>Mo5 z6vq!>pWaUsgt@!4LJ}7jL{yqmh;PL=LAI*wcI-6#i83eJCEZ05K-R;5!_*11y;eGA)R zHt>RvVG36cYl(wv)aN9^iOBDYQFk-FJZ}x4M9!4^v4v4taU8|DU)CugE7vLbsI^}5 zBLz?IXC-Z}u!y-L&P*)MYoy(zk=%uY2JAGv9@T#-gXpgPuse){^IMS8Z=-If#HDAu zWQRn>MCf-9g04Rrqio8I>gbgncrZzSm4!~X`ieivu#GyTDjUys$qt!@y&xK<g!jP&++j7@odyHglg%FilmS?KjWdE*d(c^cYvw4Yg;smG>4DWAuQIH5 z!|R|uYIlPy+7u4PDqOP@1xf!ox?i>ftT20a^aDtaZz!dj_oK3aWJW>$6l5L>wG6x) zR9A}=$E74I)Dy5QE*3`o@#y%t=}|amW}vn|M3>7jNnr0B=|ZD{xaTFe!xV#M6TE<1 z&riHg5C=CH;fk`A7p6(jz0M3s>qcV(46yWHQST}Yq{=*5ZrUIc2WM$C$i6)nAuvgS zL)x;a0}uyClV{!@$8P#w(hY)vcN<{nY~;2~giG4Z==z%`CyP-@vh6}wEEag$ z7=)g8zWS6I3$y+|XtPLGXFCUa`Rr_M75RVS=P#|zjkO*4|IHS(Yi+gQXK!n^%W!}G zUQcfK815+S<(ee{HNU{J-Y%nSuY8!_&*@_F1=-Lqv)=_>)~*A&{8?HE|gbU>$4FHGj0QdjLgNvJe7IbGSiMV&}kFM zc!SkM+eNeJI-ALT+i?o+$e~&2K3`Z~S~f$}@B~k5eA8$f|M=ZFo1lFgd3DkOA&W}| zi&+5__OB!OAMd%TEqU&NEHfBdrh~on^TRi%=VvclrnuUFY5r^2Ps6VFvseG7U-ufo zqkSWLSokJezR%I0k$INnicj)0pO@^07kvWq4)fM@`iT3B{b;# zPFYiypCg-l6$k#EHDLecH%m+Xe{C%<+hQbf&|B6@`zDJHc;#nUUv_K%hT=iru_u%Y zJ9w5}7asd#Pu^qYo2jC`4r{f{&+;DJT5LDVYPNMaS~wY2t4|4T)$$J-w~T@`3GSvw z?gY`xF1!&S0aY%aC&3~f>`VG&`M+T}+t`2nOv?YWv6apLv%Ryqi2t9}Oe zU;RMe&c2-MFf7d|&OH{47x&3y0EXdM*ZJ|hF%;L73$quEI zQkl}`+DXYGV5Q>aVd6Q($iqzXk)4)_kB9bSV&aWfGPWZZneL2dAJ@S;zFb&_?s;w{ zbf}tEVHl3-+l~Cr_kI`QD$aH>-TfEb$Ak~l|0bfr@TMQdPeI@&(EqjV^=$s%P58eB z{h!C@nRn`^_zo#M34?(505X<$H4M9Cr2)cu=Tp8?k%biVg(AB15;&2rhdBc|z6x5e zfIxr=WA6hp+#25lB8LI`!3~igZ)p$**WpLt2guU(Ps>dYk+}zi0DIz+(2VAF!+y}L zH4n}%F#T%)L+pcm4g-AHKl3_aoYa~(VY*8H%ge7}_z2(>5MJTytN)7mf8~$2H>>zR z@}J3le^n2P1?+|c5c;}7Qv0@)-Oxar*wZUJ_jvoM}`wW&PVOOk8f%1%&wY64tSzBw%H1<@xvS2`F&La_23R0IeNwy6N z$(^f_ZUnnV**v@+@w4cwUr@8nL()j1r*iG#{cpFb?jL#(3S;h0=+9F|WLj+i44Qa$ zd|l6zOc<^>{f)|}zp*Uo4-gS=>5V_~*0(hADMfp>p(oA1@mpd^&1DNH;3O`^JuOVQYM0 zt?dGCK@uU>0bbJDvFbuXzHCu@h)0eqOmBVM+=_y|Tvyl-nVk48#NO}#;!(O!XzFJkU)Gt>>x&j zo3Odw*1rmhlDi&;>B{74hso6&|Bih|_c`{`6N*`H$&CD5R*WDa06Kv6$|Pd#yJdsh zqnR&@)WVncjB@Wtpalq2W&fdvIsG1C2%bb>Gq z!|xCM7+8e@IrW2qq2y{?&F`A)7ACWQhp{)IfrWfnn6mD4;DCDz%`@7m{LZ#EG+>WS zgyMv-K+angyrIXkd!x)_d5Ore^zU3gt3yN{tZ!YjNT>t zA+2VkGM(DkG*+`_tfrNuYBfKey#49KJ32di&+v)SmwYxPT41~uUp|5;A_R-2X?^#v z446`s4|(m9TYrfL=S$4^j)6TWG+qwGt}caeR)SK!Xl_n|h&2Z~6E932CjbM!{yvww zqdIY?M|8GZm08iP?<_rml+#E_;k4;8b9{Mh*ldLkL~A=FmyG6QfsocKBc%0h*TTfL zXmS!MDM20&16gHCvax0e>WBRVcm?kgy&t;pY$+5VBw!EJeU2mA?e=&B`?Qp`Qg>*(o8A zr8hnvstJxO>oc}dpW$hkF#$lFJRh4!$A))ExNf`ZgMl8)VvSYxW6(prwJpP-w!RT} z3g{tI!)!2ufv<=&q@C4Y8!7^oo|gjcUn|T#b#?XkHZG0M_Ap?P38{!FNlWGfZ|p z9`^f^+WYM`V08||rI1@`?*ybBwc!n3qFd*sOyxZNLb8mORqFAQ4@8*zq%J&^4A5;f z>~;uWij31}K9O7pKdH*lv$K|6nWF@ydtKDabqT(%Ig&#eKM<)4``r>RVFMNX*jzjD0ZJmrhtJ(8EzVGX5Ro0A^ zaoWa~J}Nh^=*c@$ZkMX_kSHS3cFs=%MwU0&Moy0*u!6TAO2;tV@Lg zwJA0Dh`IN}6j(IO+2oPCez(iA*M}?$-mI)8+S0Cv>N(;fMs9dhlPVhvP zJ+L;}1M5f|;KyD3i>fCMJ*?sRX!gkHFB3~kHZ4&%pP%@$pM4Tf6LXG(mggapjX955u&|x;? zqwVk_6IEV{i>NA+L61Tf;*v(XBrU#a9aU#RK*9G=m6kw{?8`^mDa?-VqAnW`Xs-uS z-qqeQ%Kj@d)&wye7 zNO`ec12l*#u*MN=62zui?8b9NT;2eB&tuobF zdtrf(#On5S89uDJn#nNp=6dBbTi+^%TFd)edm_O!m3O!PLbLvtK6;j%#^HU~4Q_(N zr0sWU3%l%hM{MH+E}=4iwNCuiiQoZ6auVlj5PTvxX$HjYz_vFk^K=_8 z6c_!%G7?H(5|?(66y$;>J~3BHw!)7{uoSO;Fge3Un#d9 zo~5cl13l=~$dt)E_7H?%x?K#z_7b>W)HoJ#(;JnU+6`uEEzNTQQ)@8vLMq&->^il< zu2bix#@Lw82ofO7=gZ`t;$j(PROX=940+m6E4X4B_)23k)04Y#h_0+ULnZon@z$$8 zZRo+2-{wNEl?a38JD`8JR9qbBU=Q5}$P-rj7PED7OsI);HRcso zBW>)sd}75J36}Q_a2S`l<+8i%2XTxvdu29egV~t3tgs~kkKqI_1-0m>vC3(nthhzz1!!f(90t0-hMWc<%+Nq75R@s9+{je{uuQI~e-n8uH)p?-nlT=_vS872 z1(TgIR^#1T46k(Dhqr@115FR4*lrp&F!_Nr%F1r2T24}QR>H6*fO|z_D%)n(*)}85 z-_hy*#qrzyAJGGwWxqstIVBJN0AX4NlHt9W;vVDvssIB$mVZHGO(GPtQnlYz<`d|__TR3b~;gHG?|U23P(=G_ZQ38iB| z_<2?3jiASv14fU#8|_6woVdhuulHO|78|X~1gf=@lUYjzt{AsBBtrv}~_{gACdAkZ8R~9Vjkytun4$ z`%Ynj%-v9ECLbn|*IQpc$;#aH&YE$aSHl||5D+Q;D5BzE4)_M^KQLv_M{R13X1y}5 zTmMec@-1zz{mv~G$Q%Rt7%TITFKoiazEU#n77;Be2r`pe`L%ziUOVDS$}?E33dQ-PH>VL-`6p@H%fKyXma%J7k&Ufk!Y&18t zy|-oRQl&o5plc4ZCY&3|g&t;h#D@@Fn>uq-5k8cTV`2-~ItltoD0l~AdK018t8m?{ zJb+zk9fzTh{)dPo&`13M2==A`(;_R8SSuvz8G*-S3o}PpVzeAF+{);PLiYnBW-n zIKalJ^Pt&Un|e0Dk_SOsL;(rbhXiGd^*PLd=LByjoZ5lY*xL|*2>tZQOr~48$gcxc zzagR6^6n!zRbJ@y4O+Abb9PXzri2cedKdK=*1u{pE|9RRt$@yYW>=(VLj zO9NF7ScZd}7)dXJ3N)i=DpFq%VkZ9>k#BUcUNO`Tu3UFUm(DVlJ12 zW5&_xiYGnHS3EOlbHxkNwy7{8FA|Zfg*LOo*;|Hv3V&hPLM@+*5u!C|28e}CCIecl z${t{eYB3{XV}K-n>k2C{0OF+V=GBJgpTkPMt+JOS4-QWVe~tkZ zLdVX!5F_@NmGhGD2H1f_1(+bklm=Wu!HOQuWpU+dgZ)rCX%0n@ssSI1H9ASX9zX3S z2dp2>8$SheE%8tqwWF@ch@!DMOu=>(;(v^kEjHt1M1W*si+uHP#d9LOqY`NuX}K>k zb9dy`W&670g&Jf{5*MtXO3%WeL~1`dghS!(mwDT-VUOU8r;W0qTtq}&VJ#Q|G)+&Y z)#5-5=Gj*9rg-a@#Qzi7k;Gjj30=><1`TbJP(9Fu6zeZT^}`Ak>`H()BD1u7FrUW*gP3WS*QJAUVRTM`=J-*2;`&LzE8|M<#mL&c}paBzFkA zq6}!5n1N<}eus*hp&-`4GNYGDN@M+hsL(w7iU5p+tj>VTITN)>#p9$}0`NkB_*3Om z9}8F~VK40ZF|$u<7X6G{>KX5hCw-S0#zba)BL8dYPpHl&>wA4oHL$M$+oC>dFu=yV zNOXWfH;SlJNUO3L8{3jzUW)i2k_|__?xn}!9)fpGqnn#9!n0;t73IaWFMg+s?7=YyHg`@p$e4O8poAZ`QD|Mrw12Pt{5`|Y>hPXM#%szHq?xPgUx zViBTY%wYc<(Pj9_M`*c ziZ|d@04f*&9t*F!qZ;rc?QWEifJ>4@J7sCx^HVj@e^-J&iv3O8(5o;`Qrt>Ni$Npu zkTZ7>GLHSQgJL6kaZxtOixux5xEDXU!xqW#suz+0uf~f+$HXDAtW%G*VWtMg8R3~-VS@PJ8h%Ne5O{cer|19Q1-1 zsOm~B^}BbhM*Al^Lp%ZC6}Y$?9v2|#;mvI-Gid1v=rGM))%g6|U4R0QxE2s7?n7dL z0c<1?_c2PTE5ewFrtM*|J3pmuBD2C(qT$9#UZlZqZ zP-ive%pz@!2nO1WqXr0dU87DN+0o)&BH|q2g0%41tbIR{DXYnx0E2OjY)W~V8*GKB zwI3z{(|RtHjScZAYe2=@cc_HI<0hCF`4V%d2Q$}TJG-7#pmlh&P3uvuwwWIzQglQs z#y_6{S0VQS7f3(Ul=n1V8G;E3R5EJ-B|D*tmXcs~q*kEm9TC6`q6A+*64M9N?z0Ys zSs(0s#&}Bn z62oh?SylZ8o8Y!ky~dUIIMxvF77ZPWw;K}8r(c_%z~CZ8%fl4eDY&OWMsK>{`M*Fg{bS0ybl>FfR8#qXK;BfqYZ-ALq6>#+Y_>NO<*73VKtZAd^ zz3&rG7ZTLAffV5eSM()AH}oL$y1_M*1YaBkVbu@>p(VoM@E3CLs5>6PEeChW@`@xM zfDh3&q~9!N9ceUh=qq|&|Mc(pPhVgFxD*^oK6kYgSny`%5Jel7*E49F0zG;_1I<3- zPI3G=xJEO2n>%v~eryg1CrMB-kVGoaG5pjKxEtS?If&mVx?*K#q()Qqd{ml66VTBPL9|bV!BnT`;c-KP|P5?jx5D#H~ z5mP`q@J0j_JLqunJT&$BF+;a^&b1+sMg#&9Ii1vPI1rwT<4Qhm<1VNzvCx!s74HwrwX5~dqZZ^^hlsN8dnaT0d^x7FGw`g>07VD1@B}!k1hJxMC_K#p^Q!#wD63z>^#yIj09<>P@R%41 zcYrMCiC~^nYthQpQJ*+^JgRIy)+EfEL zadP*@wfD_$r#~J1L51H}`L|VEc$5F8T)~beUPETx!Ub}nYnY;TLgWP=O?#LIdI36F zTmArcb8EwMXaIOO8gb8S+&Lm$*q-k`ntPr;I zkRixC5?bQ8kPq@|7VfvVL70YpRgss~yRAzDa9v_TstfoOyTyCd)6w4b8+s$`j#E4u zpoc`yDPuqDubZyt@~!vt_t48}bH(8rz%>T?FRTd-YrXQRwDwgD%#sY~P;Iygjy(ER zk2@F*!e$iTtcvnmtD|36$Bf2a>W$H_dNe#xJs$q6ncR*ULKn59yAT7J5B;27mQxEk z?{f8MI?JX3o@VPT=5+MYO=>7bJF{=03Ufu6me?XeNhuNueR(WgLoS4s^g1U2QA0B1 zk?;t32i|EwK1|4tNxb5~>A+x6EohldibnDhJS_ zz`3mFc3Iw>p8|KgiroCFJ;t&|rZpvEOL7M|LJCM%F^49@Du4L#Fhf8E)v76?#bIHL ztOXTla<#mI;gbREkwz!nDTn9UE*Xjs?4%<07a)wnm0cL*RX$jKC2}}HN`FEI6dKSl z2&_QZSi7G{W4gtONP2{7)84@Ehk}0|Adti%#~#TQNy5pwKA5*@c&9_TU;%xc)+=O- zol1Nf#TZgABp2;IfeH^lmMxWP)$bv9&@ifOiBMRva$)pf*5a1K^;6bOj1rc;;X>FV z#Z@R)NC!dEJr{4qM~(k|)|kPEN3x85at_!9d19-hxgx0%a{-4Sc;8phGLdr0GGgKw zH0l;19F<4$(BQ ztalIqyM_k9YJ^6LzX|EJMl!ra@n9t9a5+nk4nzilWfT#DiY@vwKo-q7J}NZ(9xKTV zPKQ=5gjC)=UabmR6=6d|k}!W38SmIxMDGSTgQO{F649pBXMzFXmdnD00!!E=Vs8GW z!yZML2Q{LE{o&1R%JB}o5n40@jz3xv>LY2Uq}x`~*}W)qiNywlBbM=;rQ0{#>RAgR zjA^W-fO|XiagjJE+xrrAsX~`&@V?UIuZ& zS>yGS9Yp{o_776-snjGatcXh$%r+dxrF9dR>blrq*7pYCfbsWaHl$g*9Q}I9_GIKL zDdW$OgPB>E%xo9GVoq0smq4j&MQxn^iY#$QJ~S*+v(A_}+R&hqe7l5NRVF4w$!yHy zU*Qtq^p*(g;aJ_Ef0N!W^Ql}mVm>X4XmK(S+!86Q5ay8R_zyB<23aIPw&b!WgYzOX z?jnrB2?oS&lkXdhcs=a0VcOJy65ar@l|u}KjLU@(e5$fdawxofjVMKVKb@|R4>q8J zg^cRaIpo(Na=KR}>!XK?T9wZMRB@Ovxpa+)A8x+Vuj zx;R9Fmlh5^WI+ay=xg%Zz<@H0_YeY3!o~rrhbwM?D+KQ%)!IjKS1R=Rl+3^|(=*IL zfQI3!hzi)ng!mMjqq|5F<9TgN9F4E~0?zAFe91{=S-<)6n0eMG298SeD2NUvo^@=(6aX5L&a4rtVpR zoFW*=b!dT}9cCtgo@mW(hQTKUJGwgWxz6p1&N(klr@QC#150zlZkW0l-N{Vou&hLPT2S9%aCu^OS~uQ`zU>qL0@>tBl?g%{9}m1E&R z6vT*52rIh2tXQU1Z{)eNv08LfsPU6#FX({;{BnJ3`%PH7Q+qIk9&0_>!3~c1iw%fk zi~KL`+Wqs|FU3o9LhfH3U`ty-c>U}TzY7nh;?avPPkQjyvZ^1ug&>5I$3rER5MO%{ z8|VT5KSY{iOUO|DAo>5Bi2c}-0O*S^bjLYn%a0YCBsp}gFh^RK$mD!P^vJS`|AE^= zLwpq9<Hybp=9P z$%263Q3!fehJES4WAX|HUz4PxE_p`LX2pa69r|qpoxxi$rLTP7ekqs{0^CfBZtY9T-ZMhlhu(^GDZTcDWTU zK3~X({XdV7_76|a4rlNBNuTllpBp3Lnz*3#?1H3Yes*Dk zZd{3~s*;rKn~ra!4MOc32-rYPfRZe;DhzdtJeWC?FEvico`h{OA6rNB|0dS>wBQYU z!H$(?_sRM-_Z&3jrWn%hmL2wtQ`PUI>kZm$@W6#O`IZwzq06|rzgrAkN>iUnqr4m7 zj?jf2#QMXJx8W7=a(k@qJ|yEmsg!AaST=<}WhW*_Hy-Ov)Is6hSEpalA?!0?p+-T_ zCoawuYwHTGAr7KzI+86qlB2$nD(NSbIk|!^#=Q!NPw<%ZDTx#Fn_3%Q=49ldup#A& z7zM>B$z%Fn3e4G?`Zo{bP^s;eF>wwKFtmzX@3c$Ou+O%xK>Lx(fJ!+%23epN1J{I; zaIYRbIW?9XiIg3k@1)rW1%&L4)5tqC@-de4 zh`pbnM{ZOehfxYW?ZEMRNY}t~p#>)n(IJoRK0@T+F^ShhZ8KUaR+K6k&&G*)Uhwkx zDDv3Ga0I62-xZ(1vnMEY_tP%bFk?4yETcDPe9niAW?{x#Zw(2|ZAL?(0* zAeO~3BLTpqaTY_7y$?%NQqdW>vFmHjflMLFJ_5FQ6NvL*$-MzZCgV&nOL9%fObjNB zW@y>UQ1r}~nnB>(Vg4b9+B6JHz9tmdD)4?ddGGxY^hu?dWuHE1_Gv{mK1QF~SPVD0 zXG{0o!!wC8eZ)Yyo*OtPu_Yt&r;oLxI$a0`&2$CTV%$)o^8Y0jyK~w%0GLFkxn9nedzRfxYAA`6Zva=rJCrPft#6A=D*)}>; zG_ziYlyK>&EXEYUBTbh+&gAJOG9h z5wp57gLS-}Tp@O>+4Bzkcp1e9dZMaK?U&^7VQ9n8H( zvK2>`sOzQDUlF?8r->TJ!kWqnflAK7&iP&A6Y!G|ZRrT=7SU(SqsNmPTi-JnxPK$g zUy)coQHwGvc*qPNUeS&slA#71OFGt3@;rn^u*dxY zvXR^q9RVboWdP)mh%@m*G0zB{UmOT$qeyTMdJ!0r9`(d?3e+su#K)VbBNEFI4v@-o zbHVI@r9naL&OTzfqYZp{lA$2yUwUNTSsTE7!tRJOR9jR|#_V{_uS2%ZVr_-APa-q* zljwzrT+XLl-g<*>Xc@@m1{EWyx`~`pIk7;m!dyU#H=}Tu*bk2g_l7vpzk-p_TrXd^ zSRIlDxkHLMGOoeEBG-aI>j)S>Ct1o$7DAW^+psW9H#8@ytdOi%7t;1`g~@34BWLA+`u_&RhrL zS*sSRprEMl5ONqUpP;IJ8-*&o%rlUTTYJGb<{QGNQZ@<}Nv@5@TSA+T+ziN>>&pm! zE)7LqGePRR0lHbGsj%U>dg`Ov;G^1VV)!cT6IMr%UBq9+vQ=X0YxM6T)IBL8F9eW; z-NGAk1(O<25cHVv5GF{5*T^wZtx1M8sTI&*pnu3qKZXFGmEG4-bMwj%V@$y7f_I_& zK!&F;vagZ2X+r+rWhB42+yNC%<3L?}r_o@<%8X9bMxQ}uhp&B3BfP?Ybpl^VfU!Um z2+{XI&imwLzK)t8lmDZ!&!oY30|lmy<~sh*22*D3}61oUuzoO0Y)3@uWfbm^orC zE`6=^>A*3f$LW=!A`~hd3H&zc!`{?`{YEk9BOHV_fM*vhDDAcTy(2xu@A?w_0{7f} zWw+6T(&CQDz!_bkW`(>p2cv=oi!c`SQ|t|xlj@@2EAg$&&c#YCwIv{X8QkqqKyDz1 zm%Z(bo>ScT5n)6V1G?@zs~<n z4F^+NuT5x6)pjyMhlCsBvS`KGO*+Vh1+}J_kdR)6v1TfI!m}ms?Uehk$Vh~aeowNG zDaE)9DT?zj;G9u0m4g#?F>LtOr#%+VU2HId{ze}3ElEzCr#wu7jwqFgK3A)UoJk(0 z!9s|$v=4}6c7*#3?_={jnOl*1NNKCf=wm$*LT4^ZI)t0NY_=m0C*q4p`(Wx#X9KhMXf$9%qBJ<@q#{BY>F4i~L@dJu13Pr>b%S1CGAYDL~CvYm+|b;WfS2(F6JsuV@Mq;cFlb2keaS86L$|aO-!k zW%7HfR?T0jTg?c62J=N)K4h`4lBheR6i<{CL%J-ou27((-1ha(SBkcd=!VXfQ5Q@s z_z(1H)qH_apZe&7#^v^C;Omh{Z)2&LSfe2XTs0>=j$tgO$!YX5;$+2}WNd)zq^)S0 zE)(WxET}|N*R!MTc1?|!@#1u30qf^@q<;+KFzIl9`VQj?U)P$yDUk0=#e#GZF}jR~(2lbv-) zH(w)`rh|Y^a;{uAv8xJFcgawZCC!Bk9B;uU4vh>Bl4BL@k-iy8Ls4y3^MsV(lVTLL z8vtHr4677<23@}`li6VYBWcVfD3LW^yR>DKg-i+$&80Lv6i8LMFUj4-5&3r*c=J_U29@`aUT3p>O&n zj9H*aLE!e5(z=&8p!^Ov2m&&(o{8X_R$yGp;zY$o`)SzCAuz#*d_=+)Qo1a3NDrCq zQpb}pQD7C?iUM*rszm*ph_h{=MMdB{hw+1hA5#kcM+D4hPdFX*8}g{yY)Mi^gPf2> z#lUc!?KQ81Vey=RR%vkP;3?4$g%Im;n^F-G2a%+Ia#Q1vJA2Z!aX|&m-8=V9+YN(c z?1lxqic8OKSTM_>3_YgIis%%hcFb9(gN{_BO7=1{i@!w zV_wN1)!W4N&_i8sO63AO@uY|rQ%THaES_P;Mykz`c9iXi8Bj@0wVi++K}hW^G)|oW zzM`yXBcug4)XHxQ;~u5a4dRCI9}y1;Q|*xx zZ;O5K;6A1O2uBRKc(Jq%2v!pGGo_@6jY_`~v5)O=<(dYvGyvBv>utdR!_(CG=Q0~fe3Tnp?B^(R* z&O1t&!K~D;AZ^L~yTarzW!0h;D*qH5?u-*KxZKw0P6DwJykp@kCKyZ>fR;!f#cZ+h z*p?Cd3`KquRMW|O(dH~Rjgi7X4hDW0OY<0oeu_k_oJiFIC3Vvv2`7N-$Y~tIcoMY3 zpjU~G5@XY{X;{XfXbj_bZaC!Z<|1Qc9uk?WBR0?YQ7;c5f5)_)<4ng=kRZ^1Pu==J zSHrkH?6G1&av0(#_E&;~BgmAlIw>Y-u~iNI;0EM5v|=u=C_f%;K4BLgex?~wQ~HN- zmWIe;d|ke?Xr7f;G>N$|du1pShae~z0-$tJ(#}dE0|dP^c<1mIx|}f)E|IN(cyk>e zbJ$7CC;-qWxRFoP`95V=uDq8J-wiJfumt=|>WnbR5Lwq1*4`y)G#Egy6{62W z%D)6`mPiKO16h68Z}afNTrd1%NjRcwq2yR`+gdy=fb?`o6BJiNmUeL;7LsPgd(e@O zmgON;!H8u89$dVLt_5OBR;?)_MXz!&;N;3$mc6DDdsvqgJ=1__koqO;gE%`9ayWTj zO2!5UR1N|LyxBS-!`|n0{RbuSB)oLJSjITWrJ3?GI!FuG^DbKyJmG)Eer)z;HT)=a z;ea5aprphKawuEM*G40GOoS3^FYp0N&Whgb9_hpsvqcqDM6Ps;ObLViRZQurSs|oS zpbq0y0-M+96H~sI7&kpY%!Yz2p28J+%>u@&3HD1Iv|1!X0fp6S z1vT7FBC~c|Yp^J(O5iF@$(Zd7t~YH&*9~#KEF>kQO1)JnstnH`nxCL2X1>Tb;K)#v zaFR8Xd1Rzua+z=`-!;8AfMtQv@%uqEWL=?UrM3+5fl6_zxhUL58;(Lc*C#_NErhWE zDYIT8Yy=r?;yUFvbH)Yu^ET*qgn&Kl2l#&50aJJNY*cr37jYopd7mN9ctVz&hfx0! zxzS@c84}?JY=sQ7eJa6~HY@r4n<1tImC+l@)l;pANN7AX(s>XZ#w?R)RAS_>lU-Q* zVw{Q+LNaklHH;1TV0j=bmvH*A1z;3Bs3y`GSVpG3!r}l*X=&Qak~9G7)KRJz-(VxP zrBe(C6K#;Y7AbtSf2LzVa=EYqFxb0NyU2=yZFUy$oN1c8MWkuAMIEm{Z!DKPm0a1$+WAJ z(^H2O*g5wfajM!+pleEB>bmJvK<;`Yol&y~BFan%W2EvmBcCnH6Cvw-U{o#;LrH@s z-Lzq+WrUlk-H!4GP~7CkGS*CeY2>-0%svJL?0xh?c_l12L-}WkwXui^pAIccSaCZv+EQfI5P=MUIHi^uzfduqq2agQuBe zeEx^E?TwA?T>gjcwMG7id3??f&(1H7-t7Hw`0p>X`TT!)`}T+9!;AfQhX;oz=SO?T zXSp`be>s~BgN0n2?7gwBLv^`(p%@nK@#BlLiw}qI z&W_%mK!;XyeUpj;ibzo)FnB*)CE6zfWv0q)0+Q#=tiC(kJ9vZnUpR^ronhG04AM6@ zZtx(xwZBqsD*jjFhU*&3?A+ep11{5{y5Vg(!3)E#emoS=coRkVN@SX05HmVL;dSq$AqZ8nwal!$weULl z)m!>z$qkRoD+u4qwZD?O0cIHv8U$s=!UF_Y`#6hE@;_^TZ3JE(XT1F8xBQoXh~`er z0X>}G%T7PO|Ne0Q?a8a7A6^=F(!^B!tDKr&G~~Leg3r6yKZ1d?U-6g^p1lzpD*jU( zk$EnCkdg4}I_Oy*jjR#@B7?OoIKcNJ8fJm>Q=ewJor}dcB!YvDYyH;5t*fS=+vZDy zQn^!7`!vsqgr*dI7D?X8mgmVnd+5P}5oL%M5uKS*Mf}`3hc*ke*1I=m3;XxcUC^o7 zNTFW}E8MiZ(XhjnajBjZkQ157wjm4ZHX3)qsDY^1eK+Ev{8iPGS^eg>?2LaD9Fsau zc~%CXJ^Gk5*yc|ucYnLHI)%Tg7eW3To?Vf|{F$v0je9?q$i5hasTGcQ(e8$nfYfWC zCsu(2Y zsod(gwZj4J)@seu*Kbe$zUx{1Vvgt0{E@;5c?4#Gk^O9qX1Vv8qf?(+y88S-EdML^ z|4HVUApdW)c5?Y2;g5yc+-+i!QLs{oE_=h|aR zfYbU`yo-Dxa8DKjzwm0VQs6(kYk~i=)C1`@jdtBXFUum%Sj8s^$s1DT_@aas*CK_D z&ZldX(BgCqR5%WObHbjCE*aK005g4S}hRsCL0gHT&%eOJH0UBBf1S zv;w`#Qp{w%lZHFjVAlDFnlj4FtYWYHn9~w{3y+k#Z8W$4_1ka7H^#WFW($At8mfE) zIUZ0V$i&LNV07ZzU~Dt8i*HL00|l;B6_P{kU)#c90mcC%0G+B@7VL$z2kRdzqqzT(tAldOlqCnZqR$i4}BV zWveb8mr4^we9338n?yBy?;{0ILC5gA<}vtz)*gZ@d`MN_B5rHhL>H(ztf?3kv0+)# zQP&w=*n>Wo5A%Nlkg#Yq0Od6o5182;6Zk*)t(D>bwp%-k{NHo=JQElL{bFJ+kcha$ zsPri+SR}%E6w6$&(5>)4JnpvuIcEqtwHlX~!19v{oOV6(7^}$&#rUHz2e3DbMBckz zJq~Wr?_h-0O8SU+fYbrKan^YS6jY}XQXg2=`dYKK(_Cu|ufjUgMHs)I9!)=uIR)i$ zT^CcZb!tYNDVpU|1IXgD_$)q)&*HQAEIy0R;J| Ao&W#< diff --git a/hashicorp-vault/charts/vault-0.22.0.tgz b/hashicorp-vault/charts/vault-0.22.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e6111e1f7482f02b422e1341eca5d369e2f3cc98 GIT binary patch literal 51863 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 Ly%<5xTYojN( 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 bYunSyd(&rT3oiTbU%q^$4TourIX}iX0ap0H literal 0 HcmV?d00001 diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index a5ddab21..e16cc0e3 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -17,4 +17,4 @@ vault: termination: "edge" image: repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.11.2-ubi" + tag: "1.11.3-ubi" diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index 5e19edf2..51800147 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -31,6 +31,10 @@ data: tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" + # Enable unauthenticated metrics access (necessary for Prometheus Operator) + #telemetry { + # unauthenticated_metrics_access = "true" + #} } storage "file" { path = "/vault/data" @@ -45,6 +49,12 @@ data: # key_ring = "vault-helm-unseal-kr" # crypto_key = "vault-helm-unseal-key" #} + + # Example configuration for enabling Prometheus metrics in your config. + #telemetry { + # prometheus_retention_time = "30s", + # disable_hostname = true + #} --- # Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -52,7 +62,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -73,10 +83,11 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm + vault-internal: "true" annotations: spec: @@ -102,7 +113,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -131,7 +142,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -173,7 +184,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -206,7 +217,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.11.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.11.3-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -340,7 +351,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -367,7 +378,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.11.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.11.3-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index a4cdc9d3..d2af3d1b 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -31,6 +31,10 @@ data: tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" + # Enable unauthenticated metrics access (necessary for Prometheus Operator) + #telemetry { + # unauthenticated_metrics_access = "true" + #} } storage "file" { path = "/vault/data" @@ -45,6 +49,12 @@ data: # key_ring = "vault-helm-unseal-kr" # crypto_key = "vault-helm-unseal-key" #} + + # Example configuration for enabling Prometheus metrics in your config. + #telemetry { + # prometheus_retention_time = "30s", + # disable_hostname = true + #} --- # Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -52,7 +62,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -73,10 +83,11 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm + vault-internal: "true" annotations: spec: @@ -102,7 +113,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -131,7 +142,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -173,7 +184,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -206,7 +217,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.11.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.11.3-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -340,7 +351,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.21.0 + helm.sh/chart: vault-0.22.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -367,7 +378,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.11.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.11.3-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR From 6452a4e2427e61bc50f30ecf92be650b3b226460 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 13 Sep 2022 16:00:31 +0200 Subject: [PATCH 0478/1263] Switch unsealing in the cluster from 9mins to 5mins Because I am just that impatient and this sounds like a more reasonable trade-off. --- clustergroup/values.yaml | 4 ++-- tests/clustergroup-normal.expected.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 7c92aae7..b6c802e6 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -28,9 +28,9 @@ clusterGroup: # By default we run this every 10minutes schedule: "*/10 * * * *" # Schedule used to trigger the vault unsealing (if explicitely enabled) - # Set to run every 9 minutes in order for load-secrets to succeed within + # Set to run every 5 minutes in order for load-secrets to succeed within # a reasonable amount of time (it waits up to 15 mins) - insecureUnsealVaultInsideClusterSchedule: "*/9 * * * *" + insecureUnsealVaultInsideClusterSchedule: "*/5 * * * *" # Increase ansible verbosity with '-v' or '-vv..' verbosity: "" serviceAccountCreate: true diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index f171e511..4a21855c 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -87,7 +87,7 @@ data: cronJobName: imperative-cronjob image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest imagePullPolicy: Always - insecureUnsealVaultInsideClusterSchedule: '*/9 * * * *' + insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' jobName: imperative-job jobs: - name: test @@ -363,7 +363,7 @@ metadata: name: unsealvault-cronjob namespace: imperative spec: - schedule: "*/9 * * * *" + schedule: "*/5 * * * *" # if previous Job is still running, skip execution of a new Job concurrencyPolicy: Forbid jobTemplate: From 14e39c0c4f5233620dd32c28545f4b15bff77382 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 14 Sep 2022 03:54:27 -0500 Subject: [PATCH 0479/1263] Add pattern-util wrapper script --- scripts/pattern-util.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 scripts/pattern-util.sh diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh new file mode 100755 index 00000000..1715e31a --- /dev/null +++ b/scripts/pattern-util.sh @@ -0,0 +1,20 @@ +#!/bin/sh -x + +if [ -z "$PATTERN_UTILITY_CONTAINER" ]; then + PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/hybridcloudpatterns-utility-ee" +fi + +# Copy Kubeconfig from current environment. The utilities will pick up ~/.kube/config if set so it's not mandatory +# /home/runner is the normal homedir +# $HOME is mounted as itself for any files that are referenced with absolute paths +# $HOME is mounted to /root because the UID in the container is 0 and that's where SSH looks for credentials + +podman run -it \ + --security-opt label=disable \ + -e KUBECONFIG="${KUBECONFIG}" \ + -v ${HOME}:/home/runner \ + -v ${HOME}:${HOME} \ + -v ${HOME}:/root \ + -w $(pwd) \ + "$PATTERN_UTILITY_CONTAINER" \ + $@ From 1155d97da221e58c2f4bac5185e5c1aee137ccbd Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 14 Sep 2022 03:57:01 -0500 Subject: [PATCH 0480/1263] Remove -x --- scripts/pattern-util.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 1715e31a..d2be2e97 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -1,4 +1,4 @@ -#!/bin/sh -x +#!/bin/sh if [ -z "$PATTERN_UTILITY_CONTAINER" ]; then PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/hybridcloudpatterns-utility-ee" From f9270040a8482425d00fa7c169b57b3954b89361 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 14 Sep 2022 18:15:35 +0200 Subject: [PATCH 0481/1263] Implement a per-subscription csv field Currently a user can only get the global.options.useCSV to true (defaults to false) and once it is set to true *all* subscrptions will get their 'startingCSV' fields populated with whatever is set in the 'csv' subscription field. So all subscriptions *must* have a csv field set. With this change we can add a per-subscription useCSV toggle which allows us to do this case by case. A use-case here is to pass something like the following: subscriptions: amqstreams-dev: name: amq-streams namespace: manufacturing-dev channel: amq-streams-1.8.x csv: amqstreams.v1.8.0 installPlanApproval: Manual The above will make sure the startingCSV is amqstreams.v1.8.0 *only* for the amq-streams operator, and it will make the installation manual. So once the manual installation is approved we get v1.8.0 installed. The caveat is that this is really dependent on how each operator implements the allowed versions and their lifecycle. Tested it with the above subscription so and correctly got amq-streams v1.8.0 after approving manually. The other subscriptions without .csv had no "startingCSV" field in the Subscription object. Co-Authored-By: Lester Claudio --- clustergroup/templates/subscriptions.yaml | 4 ++++ tests/clustergroup-normal.expected.yaml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/clustergroup/templates/subscriptions.yaml b/clustergroup/templates/subscriptions.yaml index e08b727f..80905a95 100644 --- a/clustergroup/templates/subscriptions.yaml +++ b/clustergroup/templates/subscriptions.yaml @@ -27,6 +27,8 @@ spec: {{- end }} {{- if $.Values.global.options.useCSV }} startingCSV: {{ $subs.csv }} + {{- else if $subs.csv }} + startingCSV: {{ $subs.csv }} {{- end }} --- {{- end }} @@ -54,6 +56,8 @@ spec: {{- end }} {{- if $.Values.global.options.useCSV }} startingCSV: {{ $subs.csv }} + {{- else if $subs.csv }} + startingCSV: {{ $subs.csv }} {{- end }} --- {{- end }} diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 4a21855c..cf0218fd 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -766,6 +766,7 @@ spec: sourceNamespace: openshift-marketplace channel: release-2.4 installPlanApproval: Automatic + startingCSV: advanced-cluster-management.v2.4.1 --- # Source: pattern-clustergroup/templates/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 @@ -779,3 +780,4 @@ spec: sourceNamespace: openshift-marketplace channel: stable installPlanApproval: Automatic + startingCSV: redhat-openshift-pipelines.v1.5.2 From fbdf9a67359b54a18bcde446541e07d5bfa50323 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 14 Sep 2022 16:33:59 +1000 Subject: [PATCH 0482/1263] Reduce the complexity of secrets needed to drive the argo push model --- .../templates/cluster-external-secrets.yaml | 27 ++++++++++++++++--- examples/values-secret.yaml | 15 ++++------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/clustergroup/templates/cluster-external-secrets.yaml b/clustergroup/templates/cluster-external-secrets.yaml index ff71f8c6..f9286f99 100644 --- a/clustergroup/templates/cluster-external-secrets.yaml +++ b/clustergroup/templates/cluster-external-secrets.yaml @@ -19,8 +19,29 @@ spec: metadata: labels: argocd.argoproj.io/secret-type: cluster - dataFrom: - - extract: + data: + name: {{ . }} + server: {{ "{{ .kubeServer | toString }}" }} + config: | + { + "bearerToken": {{ "{{ .kubeBearer | toString }}" }}, + "tlsClientConfig": { + "insecure": false, + "caData": {{ "{{ .kubeCA | toString }}" }}, + } + } + data: + - secretKey: kubeServer + remoteRef: key: {{ $.Values.secretsBase.key }}/cluster_{{ . }} + property: server + - secretKey: kubeBearer + remoteRef: + key: {{ $.Values.secretsBase.key }}/cluster_{{ . }} + property: bearerToken + - secretKey: kubeCA + remoteRef: + key: {{ $.Values.secretsBase.key }}/cluster_{{ . }}_ca + property: b64content {{- end }} -{{ end }} \ No newline at end of file +{{ end }} diff --git a/examples/values-secret.yaml b/examples/values-secret.yaml index baa7269c..1b6fec08 100644 --- a/examples/values-secret.yaml +++ b/examples/values-secret.yaml @@ -18,20 +18,15 @@ secrets: s3Secret: test-secret # The cluster_xxxx pattern is used for creating externalSecrets that - # will be used by ArgoCD to detect other clusters. + # will be used by ArgoCD to push manifests to other clusters. # # Create a service account with enough permissions and extract the token # # CLUSTER_TOKEN=$(oc describe secret -n default argocd-external-token | grep 'token:' | awk '{print$2}') # CLUSTER_CA=$(oc extract -n openshift-config cm/kube-root-ca.crt --to=- --keys=ca.crt | base64 | awk '{print}' ORS='') cluster_example: - name: example server: https://api.example.openshiftapps.com:6443 - config: | - { - \"bearerToken\": \"\", - \"tlsClientConfig\": { - \"insecure\": false, - \"caData\": \"\" - } - } \ No newline at end of file + bearerToken: + +files: + cluster_example_ca: /path/to/ca.file From 247ee873c3b9d1e951f857c7e196a1ea837ef3f0 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 15 Sep 2022 10:17:41 +1000 Subject: [PATCH 0483/1263] Fix external secret expansion --- clustergroup/templates/cluster-external-secrets.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clustergroup/templates/cluster-external-secrets.yaml b/clustergroup/templates/cluster-external-secrets.yaml index f9286f99..df386c35 100644 --- a/clustergroup/templates/cluster-external-secrets.yaml +++ b/clustergroup/templates/cluster-external-secrets.yaml @@ -21,7 +21,8 @@ spec: argocd.argoproj.io/secret-type: cluster data: name: {{ . }} - server: {{ "{{ .kubeServer | toString }}" }} + server: |- + {{ "{{ .kubeServer | toString }}" }} config: | { "bearerToken": {{ "{{ .kubeBearer | toString }}" }}, From ef181c40fe46c60d64915975e9b3a3fdb4099776 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 15 Sep 2022 10:24:33 +1000 Subject: [PATCH 0484/1263] Fix tests --- tests/clustergroup-normal.expected.yaml | 38 ++++++++++++++++++------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index cf0218fd..1de93905 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -134,6 +134,8 @@ data: pipelines: csv: redhat-openshift-pipelines.v1.5.2 name: openshift-pipelines-operator-rh + files: + cluster_example_ca: /path/to/ca.file global: git: account: hybrid-cloud-patterns @@ -161,15 +163,7 @@ data: aws: s3Secret: test-secret cluster_example: - config: |- - { - \"bearerToken\": \"\", - \"tlsClientConfig\": { - \"insecure\": false, - \"caData\": \"\" - } - } - name: example + bearerToken: server: https://api.example.openshiftapps.com:6443 git: token: test-git-token @@ -730,9 +724,31 @@ spec: metadata: labels: argocd.argoproj.io/secret-type: cluster - dataFrom: - - extract: + data: + name: example + server: |- + {{ .kubeServer | toString }} + config: | + { + "bearerToken": {{ .kubeBearer | toString }}, + "tlsClientConfig": { + "insecure": false, + "caData": {{ .kubeCA | toString }}, + } + } + data: + - secretKey: kubeServer + remoteRef: + key: secret/data/hub/cluster_example + property: server + - secretKey: kubeBearer + remoteRef: key: secret/data/hub/cluster_example + property: bearerToken + - secretKey: kubeCA + remoteRef: + key: secret/data/hub/cluster_example_ca + property: b64content --- # Source: pattern-clustergroup/templates/operatorgroup.yaml apiVersion: operators.coreos.com/v1 From aabb8a108acf45e95d7a873b935aaf0410856a86 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 16 Sep 2022 17:21:56 +0200 Subject: [PATCH 0485/1263] Revert parts of 8e332950dc (Move the cluster provisioning into the acm chart) This will create the following PlacementRule: kind: PlacementRule metadata: labels: argocd.argoproj.io/instance: acm name: region-one-placement namespace: open-cluster-management spec: clusterConditions: - status: 'True' type: ManagedClusterConditionAvailable clusterSelector: matchLabels: clusterGroup: region-one patternGroup: region-one And since nowhere did we ever document that the remote clusters now need two labels (patternGroup *and* clusterGroup) I am reverting this for now as it makes the placement rules not work. --- acm/templates/policies/application-policies.yaml | 1 - acm/templates/provision/clusterpool.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 55fa473d..dd9a4658 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -119,7 +119,6 @@ spec: {{- range .labels }} {{ .name }}: {{ .value }} {{- end }} - patternGroup: {{ $group.name }} {{- end }} --- {{- end }} diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index 84af10be..b8cf1ade 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -74,7 +74,6 @@ metadata: {{- range $group.labels }} {{ .name }}: {{ .value }} {{- end }} - patternGroup: {{ $group.name }} spec: clusterPoolName: {{ $pool.name }} --- From 67641373070ae15f02a59b273e3149b07656b8fb Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 14 Sep 2022 09:35:06 +1000 Subject: [PATCH 0486/1263] Support argo based managed clusters --- .../policies/application-policies.yaml | 2 + clustergroup/templates/applications.yaml | 3 +- clustergroup/templates/remote-seeds.yaml | 68 +++++++++++++++++++ clustergroup/test.yaml | 6 +- clustergroup/values.yaml | 1 + 5 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 clustergroup/templates/remote-seeds.yaml diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index dd9a4658..ef9dcd37 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -1,6 +1,7 @@ # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} +{{- if not .targetClusters }} apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: @@ -122,3 +123,4 @@ spec: {{- end }} --- {{- end }} +{{- end }} diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 3ffd6a0f..672a5796 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -117,7 +117,8 @@ metadata: - resources-finalizer.argocd.argoproj.io/foreground spec: destination: - name: {{ coalesce .clusterName "in-cluster" }} + # TODO: Drop .clusterName ?? + name: {{ coalesce .clusterName $.Values.global.targetCluster $.Values.clusterGroup.targetCluster }} namespace: {{ default $namespace .namespace }} project: {{ .project }} source: diff --git a/clustergroup/templates/remote-seeds.yaml b/clustergroup/templates/remote-seeds.yaml new file mode 100644 index 00000000..d2057c4b --- /dev/null +++ b/clustergroup/templates/remote-seeds.yaml @@ -0,0 +1,68 @@ +{{- range .Values.clusterGroup.managedClusterGroups }} +{{- $group := . }} +{{- range .targetClusters }} +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: {{ $.Values.global.pattern }}-{{ $group.name }}-{{ . }} + Namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + project: default + source: + repoURL: {{ coalesce $group.repoURL $.Values.global.repoURL }} + targetRevision: {{ coalesce $group.targetRevision $.Values.global.targetRevision }} + path: {{ default "common/clustergroup" $group.path }} + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-{{ $group.name }}.yaml" + {{- range $valueFile := $group.extraValueFiles }} + - {{ $valueFile | quote }} + {{- end }} + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: {{ $.Values.global.pattern }} + - name: global.hubClusterDomain + value: {{ $.Values.global.hubClusterDomain }} + - name: global.localClusterDomain + value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' + - name: global.targetCluster + value: {{ $group.targetCluster }} + {{- range $group.helmOverrides }} + - name: {{ .name }} + value: {{ .value | quote }} + {{- end }} + {{- if $group.fileParameters }} + fileParameters: + {{- range $group.fileParameters }} + - name: {{ .name }} + path: {{ .path }} + {{- end }} + {{- end }} + destination: + name: {{ . }} + namespace: {{ $.Values.global.pattern }}-{{ $group.name }} + syncPolicy: + automated: + prune: false + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +{{- end }} +{{- end }} diff --git a/clustergroup/test.yaml b/clustergroup/test.yaml index 0ab52838..50062513 100644 --- a/clustergroup/test.yaml +++ b/clustergroup/test.yaml @@ -96,9 +96,9 @@ clusterGroup: managedClusterGroups: region-one: name: region-one - labels: - - name: clusterGroup - value: region-one + targetClusters: + - perth + - sydney helmOverrides: - name: clusterGroup.isHubCluster value: false diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index b6c802e6..bb042704 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -9,6 +9,7 @@ global: clusterGroup: name: example isHubCluster: true + targetCluster: in-cluster # Note: setting this to true stores the vault unseal keys inside a cluster secret and # is fundamentally insecure insecureUnsealVaultInsideCluster: false From c03f3ac19974e9c81e7a9cdb9380fe9d81d1acf5 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 14 Sep 2022 10:11:19 +1000 Subject: [PATCH 0487/1263] Clearly distinguish between the types of managed clusters being created --- acm/templates/policies/application-policies.yaml | 4 ++-- acm/templates/provision/clusterpool.yaml | 2 +- acm/test.yaml | 2 +- clustergroup/templates/remote-seeds.yaml | 2 +- clustergroup/test.yaml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index ef9dcd37..65fdd46f 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -1,7 +1,7 @@ # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} -{{- if not .targetClusters }} +{{- if not .argoClusterSeeds }} apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: @@ -117,7 +117,7 @@ spec: {{- else }} clusterSelector: matchLabels: - {{- range .labels }} + {{- range .acmlabels }} {{ .name }}: {{ .value }} {{- end }} {{- end }} diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index b8cf1ade..6c80559e 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -71,7 +71,7 @@ metadata: cluster.open-cluster-management.io/createmanagedcluster: "true" labels: clusterClaimName: {{ . }}-{{ $group.name }} - {{- range $group.labels }} + {{- range $group.acmlabels }} {{ .name }}: {{ .value }} {{- end }} spec: diff --git a/acm/test.yaml b/acm/test.yaml index 225f4bf8..669daf07 100644 --- a/acm/test.yaml +++ b/acm/test.yaml @@ -27,7 +27,7 @@ clusterGroup: clusters: - Two - Three - labels: + acmlabels: - name: clusterGroup value: region-one helmOverrides: diff --git a/clustergroup/templates/remote-seeds.yaml b/clustergroup/templates/remote-seeds.yaml index d2057c4b..a8ed4283 100644 --- a/clustergroup/templates/remote-seeds.yaml +++ b/clustergroup/templates/remote-seeds.yaml @@ -1,6 +1,6 @@ {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} -{{- range .targetClusters }} +{{- range .argoClusterSeeds }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: diff --git a/clustergroup/test.yaml b/clustergroup/test.yaml index 50062513..aac3bfd9 100644 --- a/clustergroup/test.yaml +++ b/clustergroup/test.yaml @@ -96,7 +96,7 @@ clusterGroup: managedClusterGroups: region-one: name: region-one - targetClusters: + argoClusterSeeds: - perth - sydney helmOverrides: From dc78425d932f28bbf1b3ec0c23e5a0d35a54a498 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 14 Sep 2022 10:25:16 +1000 Subject: [PATCH 0488/1263] Drive external argo cluster secrets from managedClusterGroups too --- clustergroup/templates/cluster-external-secrets.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/cluster-external-secrets.yaml b/clustergroup/templates/cluster-external-secrets.yaml index b01e3e3d..ac9735b3 100644 --- a/clustergroup/templates/cluster-external-secrets.yaml +++ b/clustergroup/templates/cluster-external-secrets.yaml @@ -1,6 +1,8 @@ {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} {{ if .Values.clusterGroup.isHubCluster }} -{{- range .Values.clusterGroup.externalClusters }} +{{- range .Values.clusterGroup.managedClusterGroups }} +{{- $group := . }} +{{- range .argoClusterSeeds }} --- apiVersion: "external-secrets.io/v1beta1" kind: ExternalSecret @@ -48,4 +50,5 @@ spec: key: {{ $.Values.secretsBase.key }}/cluster_{{ . }}_ca property: b64content {{- end }} -{{ end }} +{{- end }} +{{- end }} From be5bc6cfe62207e7a443b4331e5c1bded1ee80b0 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 14 Sep 2022 10:33:46 +1000 Subject: [PATCH 0489/1263] Update examples file --- examples/values-example.yaml | 59 +++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 7b3237fc..9a1297ab 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -54,25 +54,54 @@ clusterGroup: clusterName: example managedClusterGroups: - - name: edge + - name: acm-edge # Optional - Point to a different repo - # repoURL: https://github.com/dagger-refuse-cool/mySite.git + # repoURL: https://github.com/hybrid-cloud-patterns/mySite.git # Must contain values-{clustergroupname}.yaml at the top level targetRevision: main helmOverrides: # Values must be strings! - name: clusterGroup.isHubCluster value: "false" - clusterSelector: -# matchLabels: -# clusterGroup: factory - matchExpressions: - - key: vendor - operator: In - values: - - OpenShift - - # Create an ExternalSecret with a label that ArgoCD - # will detect and register as a new Cluster - externalClusters: - - example # Will read the key: cluster_example + acmlabels: + - name: clusterGroup + value: acm-region + - name: acm-provision-edge + targetRevision: main + helmOverrides: + - name: clusterGroup.isHubCluster + value: "false" + clusterPools: + exampleAWSPool: + size: 3 + name: aws-ap + openshiftVersion: 4.10.18 + baseDomain: blueprints.rhecoeng.com + platform: + aws: + region: ap-southeast-2 + clusters: + - One + exampleAzurePool: + name: azure-us + openshiftVersion: 4.10.18 + baseDomain: blueprints.rhecoeng.com + platform: + azure: + baseDomainResourceGroupName: dojo-dns-zones + region: eastus + clusters: + - Two + - Three + acmlabels: + - name: clusterGroup + value: region + - name: argo-edge + # Requires a cluster_{name} entry in your secret store containing + # ??what does it contain?? + argoClusterSeeds: + - perth + - sydney + helmOverrides: + - name: clusterGroup.isHubCluster + value: "false" From 6696d34e3ff35b0c9aa53104aeaf42a2687302e0 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 14 Sep 2022 10:34:48 +1000 Subject: [PATCH 0490/1263] Mixing spoke applications with the hub is an anti-pattern --- clustergroup/templates/applications.yaml | 3 +-- examples/values-example.yaml | 5 ----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index 672a5796..d6e3d5a8 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -117,8 +117,7 @@ metadata: - resources-finalizer.argocd.argoproj.io/foreground spec: destination: - # TODO: Drop .clusterName ?? - name: {{ coalesce .clusterName $.Values.global.targetCluster $.Values.clusterGroup.targetCluster }} + name: {{ coalesce $.Values.global.targetCluster $.Values.clusterGroup.targetCluster }} namespace: {{ default $namespace .namespace }} project: {{ .project }} source: diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 9a1297ab..9f041590 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -47,11 +47,6 @@ clusterGroup: namespace: application-ci project: datacenter path: charts/datacenter/pipelines - external: - name: external-app - namespace: demo - project: datacenter - clusterName: example managedClusterGroups: - name: acm-edge From 66717d5df9b6e9078304e95103b3413553a03a97 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 14 Sep 2022 10:37:10 +1000 Subject: [PATCH 0491/1263] Update tests --- tests/acm-normal.expected.yaml | 527 +++++++++++++++++++++++- tests/clustergroup-normal.expected.yaml | 177 ++++++-- 2 files changed, 641 insertions(+), 63 deletions(-) diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 29af29a8..3f03bcf9 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -1,4 +1,392 @@ --- +# Source: acm/templates/provision/secrets-common.yaml +apiVersion: v1 +kind: Secret +metadata: + name: aws-ap-acm-provision-edge-install-config +data: + # Base64 encoding of install-config yaml + install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXdzLWFwJyAKYmFzZURvbWFpbjogYmx1ZXByaW50cy5yaGVjb2VuZy5jb20KY29udHJvbFBsYW5lOgogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIG5hbWU6IGNvbnRyb2xQbGFuZQogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhd3M6CiAgICAgIHR5cGU6IG01LnhsYXJnZQpjb21wdXRlOgotIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIG5hbWU6ICd3b3JrZXInCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF3czoKICAgICAgdHlwZTogbTUueGxhcmdlCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT3BlblNoaWZ0U0ROCiAgc2VydmljZU5ldHdvcms6CiAgLSAxNzIuMzAuMC4wLzE2CnBsYXRmb3JtOiB7CiAgImF3cyI6IHsKICAgICJyZWdpb24iOiAiYXAtc291dGhlYXN0LTIiCiAgfQp9CnB1bGxTZWNyZXQ6ICIiICMgc2tpcCwgaGl2ZSB3aWxsIGluamVjdCBiYXNlZCBvbiBpdCdzIHNlY3JldHMKc3NoS2V5OiAiIiAgICAgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cw== +type: Opaque +--- +# Source: acm/templates/provision/secrets-common.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: aws-ap-acm-provision-edge-ssh-private-key +spec: + data: + - secretKey: sshPrivateKey + remoteRef: + key: secret/data/hub/privatekey + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-ap-acm-provision-edge-ssh-private-key + creationPolicy: Owner + template: + type: Opaque + data: + ssh-privatekey: |- + {{ .sshPrivateKey | toString }} +apiVersion: v1 +kind: Secret +metadata: + name: azure-us-acm-provision-edge-install-config +data: + # Base64 encoding of install-config yaml + install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXp1cmUtdXMnIApiYXNlRG9tYWluOiBibHVlcHJpbnRzLnJoZWNvZW5nLmNvbQpjb250cm9sUGxhbmU6CiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgbmFtZTogY29udHJvbFBsYW5lCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF6dXJlOgogICAgICB0eXBlOiBTdGFuZGFyZF9EOHNfdjMKY29tcHV0ZToKLSBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBuYW1lOiAnd29ya2VyJwogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhenVyZToKICAgICAgdHlwZTogU3RhbmRhcmRfRDhzX3YzCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT3BlblNoaWZ0U0ROCiAgc2VydmljZU5ldHdvcms6CiAgLSAxNzIuMzAuMC4wLzE2CnBsYXRmb3JtOiB7CiAgImF6dXJlIjogewogICAgImJhc2VEb21haW5SZXNvdXJjZUdyb3VwTmFtZSI6ICJkb2pvLWRucy16b25lcyIsCiAgICAicmVnaW9uIjogImVhc3R1cyIKICB9Cn0KcHVsbFNlY3JldDogIiIgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cwpzc2hLZXk6ICIiICAgICAjIHNraXAsIGhpdmUgd2lsbCBpbmplY3QgYmFzZWQgb24gaXQncyBzZWNyZXRz +type: Opaque +--- +# Source: acm/templates/provision/clusterpool.yaml +apiVersion: hive.openshift.io/v1 +kind: ClusterClaim +metadata: + name: 'One-acm-provision-edge' + annotations: + argocd.argoproj.io/sync-wave: "20" + cluster.open-cluster-management.io/createmanagedcluster: "true" + labels: + clusterClaimName: One-acm-provision-edge + clusterGroup: region + patternGroup: acm-provision-edge +spec: + clusterPoolName: aws-ap +--- +# Source: acm/templates/provision/clusterpool.yaml +apiVersion: hive.openshift.io/v1 +kind: ClusterClaim +metadata: + name: 'Two-acm-provision-edge' + annotations: + argocd.argoproj.io/sync-wave: "20" + cluster.open-cluster-management.io/createmanagedcluster: "true" + labels: + clusterClaimName: Two-acm-provision-edge + clusterGroup: region + patternGroup: acm-provision-edge +spec: + clusterPoolName: azure-us +--- +# Source: acm/templates/provision/clusterpool.yaml +apiVersion: hive.openshift.io/v1 +kind: ClusterClaim +metadata: + name: 'Three-acm-provision-edge' + annotations: + argocd.argoproj.io/sync-wave: "20" + cluster.open-cluster-management.io/createmanagedcluster: "true" + labels: + clusterClaimName: Three-acm-provision-edge + clusterGroup: region + patternGroup: acm-provision-edge +spec: + clusterPoolName: azure-us +--- +# Source: acm/templates/provision/clusterpool.yaml +apiVersion: hive.openshift.io/v1 +kind: ClusterPool +metadata: + name: "aws-ap-acm-provision-edge" + annotations: + argocd.argoproj.io/sync-wave: "10" + labels: + cloud: aws + region: 'ap-southeast-2' + vendor: OpenShift + cluster.open-cluster-management.io/clusterset: aws-ap +spec: + size: 3 + runningCount: 1 + baseDomain: blueprints.rhecoeng.com + installConfigSecretTemplateRef: + name: aws-ap-acm-provision-edge-install-config + imageSetRef: + name: img4.10.18-x86-64-appsub + pullSecretRef: + name: aws-ap-acm-provision-edge-pull-secret + sshPrivateKeySecretRef: + name: aws-ap-acm-provision-edge-ssh-private-key + skipMachinePools: true # Disable MachinePool as using custom install-config + platform: + aws: + credentialsSecretRef: + name: aws-ap-acm-provision-edge-creds + region: ap-southeast-2 +--- +# Source: acm/templates/provision/clusterpool.yaml +apiVersion: hive.openshift.io/v1 +kind: ClusterPool +metadata: + name: "azure-us-acm-provision-edge" + annotations: + argocd.argoproj.io/sync-wave: "10" + labels: + cloud: azure + region: 'eastus' + vendor: OpenShift + cluster.open-cluster-management.io/clusterset: azure-us +spec: + size: 2 + runningCount: 2 + baseDomain: blueprints.rhecoeng.com + installConfigSecretTemplateRef: + name: azure-us-acm-provision-edge-install-config + imageSetRef: + name: img4.10.18-x86-64-appsub + pullSecretRef: + name: azure-us-acm-provision-edge-pull-secret + sshPrivateKeySecretRef: + name: azure-us-acm-provision-edge-ssh-private-key + skipMachinePools: true # Disable MachinePool as using custom install-config + platform: + azure: + credentialsSecretRef: + name: azure-us-acm-provision-edge-creds + region: eastus +--- +# Source: acm/templates/provision/secrets-aws.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: aws-ap-acm-provision-edge-creds +spec: + dataFrom: + - extract: + # Expects entries called: aws_access_key_id and aws_secret_access_key + key: secret/data/hub/aws + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-ap-acm-provision-edge-creds + creationPolicy: Owner + template: + type: Opaque +--- +# Source: acm/templates/provision/secrets-aws.yaml +# For use when manually creating clusters with ACM +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: aws-ap-acm-provision-edge-infra-creds +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: secret/data/hub/openshiftPullSecret + property: content + - secretKey: awsKeyId + remoteRef: + key: secret/data/hub/aws + property: aws_access_key_id + - secretKey: awsAccessKey + remoteRef: + key: secret/data/hub/aws + property: aws_secret_access_key + - secretKey: sshPublicKey + remoteRef: + key: secret/data/hub/publickey + property: content + - secretKey: sshPrivateKey + remoteRef: + key: secret/data/hub/privatekey + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-ap-acm-provision-edge-infra-creds + creationPolicy: Owner + template: + type: Opaque + metadata: + labels: + cluster.open-cluster-management.io/credentials: "" + cluster.open-cluster-management.io/type: aws + data: + baseDomain: "blueprints.rhecoeng.com" + pullSecret: |- + {{ .openshiftPullSecret | toString }} + aws_access_key_id: |- + {{ .awsKeyId | toString }} + aws_secret_access_key: |- + {{ .awsAccessKey | toString }} + ssh-privatekey: |- + {{ .sshPrivateKey | toString }} + ssh-publickey: |- + {{ .sshPublicKey | toString }} + httpProxy: "" + httpsProxy: "" + noProxy: "" + additionalTrustBundle: "" +--- +# Source: acm/templates/provision/secrets-azure.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: azure-us-acm-provision-edge-creds +spec: + data: + - secretKey: azureOsServicePrincipal + remoteRef: + key: secret/data/hub/azureOsServicePrincipal + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: azure-us-acm-provision-edge-creds + creationPolicy: Owner + template: + type: Opaque + data: + osServicePrincipal.json: |- + {{ .azureOsServicePrincipal | toString }} +--- +# Source: acm/templates/provision/secrets-azure.yaml +# For use when manually creating clusters with ACM +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: azure-us-acm-provision-edge-infra-creds +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: secret/data/hub/openshiftPullSecret + property: content + - secretKey: sshPublicKey + remoteRef: + key: secret/data/hub/publickey + property: content + - secretKey: sshPrivateKey + remoteRef: + key: secret/data/hub/privatekey + property: content + - secretKey: azureOsServicePrincipal + remoteRef: + key: secret/data/hub/azureOsServicePrincipal + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: azure-us-acm-provision-edge-infra-creds + creationPolicy: Owner + template: + type: Opaque + metadata: + labels: + cluster.open-cluster-management.io/credentials: "" + cluster.open-cluster-management.io/type: aws + data: + cloudName: AzurePublicCloud + osServicePrincipal.json: |- + {{ .azureOsServicePrincipal | toString }} + baseDomain: "blueprints.rhecoeng.com" + baseDomainResourceGroupName: "dojo-dns-zones" + pullSecret: |- + {{ .openshiftPullSecret | toString }} + ssh-privatekey: |- + {{ .sshPrivateKey | toString }} + ssh-publickey: |- + {{ .sshPublicKey | toString }} + httpProxy: "" + httpsProxy: "" + noProxy: "" + additionalTrustBundle: "" +--- +# Source: acm/templates/provision/secrets-common.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: aws-ap-acm-provision-edge-pull-secret +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: secret/data/hub/openshiftPullSecret + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-ap-acm-provision-edge-pull-secret + creationPolicy: Owner + template: + type: kubernetes.io/dockerconfigjson + data: + .dockerconfigjson: |- + {{ .openshiftPullSecret | toString }} +--- +# Source: acm/templates/provision/secrets-common.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: azure-us-acm-provision-edge-pull-secret +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: secret/data/hub/openshiftPullSecret + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: azure-us-acm-provision-edge-pull-secret + creationPolicy: Owner + template: + type: kubernetes.io/dockerconfigjson + data: + .dockerconfigjson: |- + {{ .openshiftPullSecret | toString }} +--- +# Source: acm/templates/provision/secrets-common.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: azure-us-acm-provision-edge-ssh-private-key +spec: + data: + - secretKey: sshPrivateKey + remoteRef: + key: secret/data/hub/privatekey + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: azure-us-acm-provision-edge-ssh-private-key + creationPolicy: Owner + template: + type: Opaque + data: + ssh-privatekey: |- + {{ .sshPrivateKey | toString }} +--- +# Source: acm/templates/provision/clusterpool.yaml +apiVersion: cluster.open-cluster-management.io/v1beta1 +kind: ManagedClusterSet +metadata: + annotations: + cluster.open-cluster-management.io/submariner-broker-ns: acm-provision-edge-broker + name: acm-provision-edge +spec: + clusterSelector: + selectorType: LegacyClusterSetLabel +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub @@ -13,15 +401,31 @@ spec: {} apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding metadata: - name: edge-placement-binding + name: acm-edge-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: acm-edge-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: acm-edge-clustergroup-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/application-policies.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: acm-provision-edge-placement-binding annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true placementRef: - name: edge-placement + name: acm-provision-edge-placement kind: PlacementRule apiGroup: apps.open-cluster-management.io subjects: - - name: edge-clustergroup-policy + - name: acm-provision-edge-clustergroup-policy kind: Policy apiGroup: policy.open-cluster-management.io --- @@ -45,22 +449,29 @@ subjects: apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: - name: edge-placement + name: acm-edge-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchLabels: + clusterGroup: acm-region + patternGroup: acm-edge +--- +# Source: acm/templates/policies/application-policies.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: acm-provision-edge-placement spec: clusterConditions: - status: 'True' type: ManagedClusterConditionAvailable - clusterSelector: { - "matchExpressions": [ - { - "key": "vendor", - "operator": "In", - "values": [ - "OpenShift" - ] - } - ] -} + clusterSelector: + matchLabels: + clusterGroup: region + patternGroup: acm-provision-edge --- # Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: apps.open-cluster-management.io/v1 @@ -83,7 +494,83 @@ spec: apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: - name: edge-clustergroup-policy + name: acm-edge-clustergroup-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: acm-edge-clustergroup-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + apiVersion: argoproj.io/v1alpha1 + kind: Application + metadata: + name: mypattern-acm-edge + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground + spec: + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-acm-edge.yaml" + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.hubClusterDomain + value: hub.example.com + - name: global.localClusterDomain + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + - name: clusterGroup.isHubCluster + value: "false" + destination: + server: https://kubernetes.default.svc + namespace: mypattern-acm-edge + syncPolicy: + automated: + prune: false + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +--- +# Source: acm/templates/policies/application-policies.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: acm-provision-edge-clustergroup-policy annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true argocd.argoproj.io/compare-options: IgnoreExtraneous @@ -95,7 +582,7 @@ spec: apiVersion: policy.open-cluster-management.io/v1 kind: ConfigurationPolicy metadata: - name: edge-clustergroup-config + name: acm-provision-edge-clustergroup-config spec: remediationAction: enforce severity: medium @@ -108,7 +595,7 @@ spec: apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: mypattern-edge + name: mypattern-acm-provision-edge namespace: openshift-gitops finalizers: - resources-finalizer.argocd.argoproj.io/foreground @@ -122,7 +609,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-edge.yaml" + - "/values-acm-provision-edge.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -140,7 +627,7 @@ spec: value: "false" destination: server: https://kubernetes.default.svc - namespace: mypattern-edge + namespace: mypattern-acm-provision-edge syncPolicy: automated: prune: false diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 2af41cbb..80c45ef9 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -68,18 +68,11 @@ data: namespace: open-cluster-management path: common/acm project: datacenter - external: - clusterName: example - name: external-app - namespace: demo - project: datacenter pipe: name: pipelines namespace: application-ci path: charts/datacenter/pipelines project: datacenter - externalClusters: - - example imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role @@ -103,17 +96,51 @@ data: insecureUnsealVaultInsideCluster: true isHubCluster: true managedClusterGroups: - - clusterSelector: - matchExpressions: - - key: vendor - operator: In - values: - - OpenShift + - acmlabels: + - name: clusterGroup + value: acm-region helmOverrides: - name: clusterGroup.isHubCluster value: "false" - name: edge + name: acm-edge targetRevision: main + - acmlabels: + - name: clusterGroup + value: region + clusterPools: + exampleAWSPool: + baseDomain: blueprints.rhecoeng.com + clusters: + - One + name: aws-ap + openshiftVersion: 4.10.18 + platform: + aws: + region: ap-southeast-2 + size: 3 + exampleAzurePool: + baseDomain: blueprints.rhecoeng.com + clusters: + - Two + - Three + name: azure-us + openshiftVersion: 4.10.18 + platform: + azure: + baseDomainResourceGroupName: dojo-dns-zones + region: eastus + helmOverrides: + - name: clusterGroup.isHubCluster + value: "false" + name: acm-provision-edge + targetRevision: main + - argoClusterSeeds: + - perth + - sydney + helmOverrides: + - name: clusterGroup.isHubCluster + value: "false" + name: argo-edge name: example namespaces: - open-cluster-management @@ -505,19 +532,19 @@ spec: apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: external-app + name: pipelines namespace: mypattern-example finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: destination: - name: example - namespace: demo + name: in-cluster + namespace: application-ci project: datacenter source: repoURL: https://github.com/pattern-clone/mypattern targetRevision: - path: + path: charts/datacenter/pipelines helm: ignoreMissingValueFiles: true valueFiles: @@ -541,45 +568,109 @@ spec: automated: {} # selfHeal: true --- -# Source: pattern-clustergroup/templates/applications.yaml +# Source: pattern-clustergroup/templates/remote-seeds.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: pipelines - namespace: mypattern-example + name: mypattern-argo-edge-perth + Namespace: openshift-gitops finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-argo-edge.yaml" + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.hubClusterDomain + value: hub.example.com + - name: global.localClusterDomain + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + - name: global.targetCluster + value: + - name: clusterGroup.isHubCluster + value: "false" destination: - name: in-cluster - namespace: application-ci - project: datacenter + name: perth + namespace: mypattern-argo-edge + syncPolicy: + automated: + prune: false + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: mypattern-argo-edge-sydney + Namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + project: default source: repoURL: https://github.com/pattern-clone/mypattern targetRevision: - path: charts/datacenter/pipelines + path: common/clustergroup helm: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-example.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + - "/values-argo-edge.yaml" parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: mypattern - - name: global.hubClusterDomain - value: hub.example.com - - name: global.localClusterDomain - value: region.example.com + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.hubClusterDomain + value: hub.example.com + - name: global.localClusterDomain + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + - name: global.targetCluster + value: + - name: clusterGroup.isHubCluster + value: "false" + destination: + name: sydney + namespace: mypattern-argo-edge syncPolicy: - automated: {} - # selfHeal: true + automated: + prune: false + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status --- # Source: pattern-clustergroup/templates/argocd.yaml apiVersion: argoproj.io/v1alpha1 @@ -710,7 +801,7 @@ spec: apiVersion: "external-secrets.io/v1beta1" kind: ExternalSecret metadata: - name: example-secret + name: perth-secret namespace: mypattern-example annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true @@ -721,7 +812,7 @@ spec: name: vault-backend kind: ClusterSecretStore target: - name: example-secret + name: perth-secret template: type: Opaque metadata: From 936495fd07ac7021f3fe149f25726fbd93efd8dd Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 14 Sep 2022 16:49:32 +1000 Subject: [PATCH 0492/1263] Remove non-existant ClusterPool field --- acm/templates/provision/clusterpool.yaml | 2 -- tests/acm-normal.expected.yaml | 4 ---- 2 files changed, 6 deletions(-) diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index 6c80559e..b7960570 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -52,8 +52,6 @@ spec: name: img{{ .openshiftVersion }}-x86-64-appsub pullSecretRef: name: {{ $poolName }}-pull-secret - sshPrivateKeySecretRef: - name: {{ $poolName }}-ssh-private-key skipMachinePools: true # Disable MachinePool as using custom install-config platform: {{ $cloud }}: diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 3f03bcf9..37677c65 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -108,8 +108,6 @@ spec: name: img4.10.18-x86-64-appsub pullSecretRef: name: aws-ap-acm-provision-edge-pull-secret - sshPrivateKeySecretRef: - name: aws-ap-acm-provision-edge-ssh-private-key skipMachinePools: true # Disable MachinePool as using custom install-config platform: aws: @@ -139,8 +137,6 @@ spec: name: img4.10.18-x86-64-appsub pullSecretRef: name: azure-us-acm-provision-edge-pull-secret - sshPrivateKeySecretRef: - name: azure-us-acm-provision-edge-ssh-private-key skipMachinePools: true # Disable MachinePool as using custom install-config platform: azure: From 41a23d2eccb147daa7447522b12c18bfc3bb3224 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 14 Sep 2022 17:02:28 +1000 Subject: [PATCH 0493/1263] Ensure secret manifests are not munged together --- acm/templates/provision/secrets-azure.yaml | 3 +- acm/templates/provision/secrets-common.yaml | 3 +- tests/acm-normal.expected.yaml | 46 +++++++++++---------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/acm/templates/provision/secrets-azure.yaml b/acm/templates/provision/secrets-azure.yaml index 66b470c1..7897d61c 100644 --- a/acm/templates/provision/secrets-azure.yaml +++ b/acm/templates/provision/secrets-azure.yaml @@ -78,6 +78,7 @@ spec: httpsProxy: "" noProxy: "" additionalTrustBundle: "" +--- +{{- end }} {{- end }} {{- end }} -{{- end }} \ No newline at end of file diff --git a/acm/templates/provision/secrets-common.yaml b/acm/templates/provision/secrets-common.yaml index 62641dde..40fcb19c 100644 --- a/acm/templates/provision/secrets-common.yaml +++ b/acm/templates/provision/secrets-common.yaml @@ -56,5 +56,6 @@ spec: data: ssh-privatekey: |- {{ "{{ .sshPrivateKey | toString }}" }} +--- +{{- end }} {{- end }} -{{- end }} \ No newline at end of file diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 37677c65..c941df9b 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -10,28 +10,6 @@ data: type: Opaque --- # Source: acm/templates/provision/secrets-common.yaml -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: aws-ap-acm-provision-edge-ssh-private-key -spec: - data: - - secretKey: sshPrivateKey - remoteRef: - key: secret/data/hub/privatekey - property: content - refreshInterval: 24h0m0s - secretStoreRef: - name: vault-backend - kind: ClusterSecretStore - target: - name: aws-ap-acm-provision-edge-ssh-private-key - creationPolicy: Owner - template: - type: Opaque - data: - ssh-privatekey: |- - {{ .sshPrivateKey | toString }} apiVersion: v1 kind: Secret metadata: @@ -327,6 +305,30 @@ spec: # Source: acm/templates/provision/secrets-common.yaml apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret +metadata: + name: aws-ap-acm-provision-edge-ssh-private-key +spec: + data: + - secretKey: sshPrivateKey + remoteRef: + key: secret/data/hub/privatekey + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-ap-acm-provision-edge-ssh-private-key + creationPolicy: Owner + template: + type: Opaque + data: + ssh-privatekey: |- + {{ .sshPrivateKey | toString }} +--- +# Source: acm/templates/provision/secrets-common.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret metadata: name: azure-us-acm-provision-edge-pull-secret spec: From d911690c40d89a24f645a3ff1d8f5c9d51fced44 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 14 Sep 2022 17:05:56 +1000 Subject: [PATCH 0494/1263] Fix incorrect capitalization in metadata --- clustergroup/templates/remote-seeds.yaml | 2 +- tests/clustergroup-normal.expected.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clustergroup/templates/remote-seeds.yaml b/clustergroup/templates/remote-seeds.yaml index a8ed4283..f2b27b1b 100644 --- a/clustergroup/templates/remote-seeds.yaml +++ b/clustergroup/templates/remote-seeds.yaml @@ -5,7 +5,7 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: {{ $.Values.global.pattern }}-{{ $group.name }}-{{ . }} - Namespace: openshift-gitops + namespace: openshift-gitops finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 80c45ef9..0ace2a11 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -573,7 +573,7 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: mypattern-argo-edge-perth - Namespace: openshift-gitops + namespace: openshift-gitops finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -624,7 +624,7 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: mypattern-argo-edge-sydney - Namespace: openshift-gitops + namespace: openshift-gitops finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: From 85610be44e19743253e3bab8126622d0e9f52f90 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 14 Sep 2022 17:44:29 +1000 Subject: [PATCH 0495/1263] Drop the additional global variable as it's not required --- clustergroup/templates/applications.yaml | 2 +- clustergroup/templates/remote-seeds.yaml | 2 +- tests/clustergroup-normal.expected.yaml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/applications.yaml index d6e3d5a8..f610da71 100644 --- a/clustergroup/templates/applications.yaml +++ b/clustergroup/templates/applications.yaml @@ -117,7 +117,7 @@ metadata: - resources-finalizer.argocd.argoproj.io/foreground spec: destination: - name: {{ coalesce $.Values.global.targetCluster $.Values.clusterGroup.targetCluster }} + name: {{ $.Values.clusterGroup.targetCluster }} namespace: {{ default $namespace .namespace }} project: {{ .project }} source: diff --git a/clustergroup/templates/remote-seeds.yaml b/clustergroup/templates/remote-seeds.yaml index f2b27b1b..a3b93b1d 100644 --- a/clustergroup/templates/remote-seeds.yaml +++ b/clustergroup/templates/remote-seeds.yaml @@ -35,7 +35,7 @@ spec: value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' - - name: global.targetCluster + - name: clusterGroup.targetCluster value: {{ $group.targetCluster }} {{- range $group.helmOverrides }} - name: {{ .name }} diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 0ace2a11..dd920407 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -600,7 +600,7 @@ spec: value: hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - - name: global.targetCluster + - name: clusterGroup.targetCluster value: - name: clusterGroup.isHubCluster value: "false" @@ -651,7 +651,7 @@ spec: value: hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - - name: global.targetCluster + - name: clusterGroup.targetCluster value: - name: clusterGroup.isHubCluster value: "false" From 1beb6829c4cd1c040c0fecd6d9c16a6d1c025447 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 21 Sep 2022 07:58:04 +1000 Subject: [PATCH 0496/1263] Ensure argo secret data is quoted, and in the same namespace as the app that consumes it --- clustergroup/templates/cluster-external-secrets.yaml | 2 +- tests/clustergroup-normal.expected.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/cluster-external-secrets.yaml b/clustergroup/templates/cluster-external-secrets.yaml index ac9735b3..60ff673c 100644 --- a/clustergroup/templates/cluster-external-secrets.yaml +++ b/clustergroup/templates/cluster-external-secrets.yaml @@ -8,7 +8,7 @@ apiVersion: "external-secrets.io/v1beta1" kind: ExternalSecret metadata: name: {{ . | kebabcase }}-secret - namespace: {{ $namespace }} + namespace: openshift-gitops annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true argocd.argoproj.io/sync-wave: "100" diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index dd920407..3db27504 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -802,7 +802,7 @@ apiVersion: "external-secrets.io/v1beta1" kind: ExternalSecret metadata: name: perth-secret - namespace: mypattern-example + namespace: openshift-gitops annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true argocd.argoproj.io/sync-wave: "100" From fdc0d9a7dbad5e33540041d784f5e800bcbb87c0 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 16 Sep 2022 16:53:30 +1000 Subject: [PATCH 0497/1263] Reorganise the clusterGroup chart --- clustergroup/templates/{ => core}/catalog-sources.yaml | 0 .../templates/{ => core}/cluster-external-secrets.yaml | 0 clustergroup/templates/{ => core}/namespaces.yaml | 0 clustergroup/templates/{ => core}/operatorgroup.yaml | 0 clustergroup/templates/{ => core}/subscriptions.yaml | 0 .../templates/{ => plumbing}/applications.yaml | 0 .../templates/{ => plumbing}/argocd-super-role.yaml | 0 clustergroup/templates/{ => plumbing}/argocd.yaml | 0 .../templates/{ => plumbing}/gitops-namespace.yaml | 0 clustergroup/templates/{ => plumbing}/projects.yaml | 0 .../templates/{ => plumbing}/remote-seeds.yaml | 0 tests/clustergroup-naked.expected.yaml | 10 +++++----- 12 files changed, 5 insertions(+), 5 deletions(-) rename clustergroup/templates/{ => core}/catalog-sources.yaml (100%) rename clustergroup/templates/{ => core}/cluster-external-secrets.yaml (100%) rename clustergroup/templates/{ => core}/namespaces.yaml (100%) rename clustergroup/templates/{ => core}/operatorgroup.yaml (100%) rename clustergroup/templates/{ => core}/subscriptions.yaml (100%) rename clustergroup/templates/{ => plumbing}/applications.yaml (100%) rename clustergroup/templates/{ => plumbing}/argocd-super-role.yaml (100%) rename clustergroup/templates/{ => plumbing}/argocd.yaml (100%) rename clustergroup/templates/{ => plumbing}/gitops-namespace.yaml (100%) rename clustergroup/templates/{ => plumbing}/projects.yaml (100%) rename clustergroup/templates/{ => plumbing}/remote-seeds.yaml (100%) diff --git a/clustergroup/templates/catalog-sources.yaml b/clustergroup/templates/core/catalog-sources.yaml similarity index 100% rename from clustergroup/templates/catalog-sources.yaml rename to clustergroup/templates/core/catalog-sources.yaml diff --git a/clustergroup/templates/cluster-external-secrets.yaml b/clustergroup/templates/core/cluster-external-secrets.yaml similarity index 100% rename from clustergroup/templates/cluster-external-secrets.yaml rename to clustergroup/templates/core/cluster-external-secrets.yaml diff --git a/clustergroup/templates/namespaces.yaml b/clustergroup/templates/core/namespaces.yaml similarity index 100% rename from clustergroup/templates/namespaces.yaml rename to clustergroup/templates/core/namespaces.yaml diff --git a/clustergroup/templates/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml similarity index 100% rename from clustergroup/templates/operatorgroup.yaml rename to clustergroup/templates/core/operatorgroup.yaml diff --git a/clustergroup/templates/subscriptions.yaml b/clustergroup/templates/core/subscriptions.yaml similarity index 100% rename from clustergroup/templates/subscriptions.yaml rename to clustergroup/templates/core/subscriptions.yaml diff --git a/clustergroup/templates/applications.yaml b/clustergroup/templates/plumbing/applications.yaml similarity index 100% rename from clustergroup/templates/applications.yaml rename to clustergroup/templates/plumbing/applications.yaml diff --git a/clustergroup/templates/argocd-super-role.yaml b/clustergroup/templates/plumbing/argocd-super-role.yaml similarity index 100% rename from clustergroup/templates/argocd-super-role.yaml rename to clustergroup/templates/plumbing/argocd-super-role.yaml diff --git a/clustergroup/templates/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml similarity index 100% rename from clustergroup/templates/argocd.yaml rename to clustergroup/templates/plumbing/argocd.yaml diff --git a/clustergroup/templates/gitops-namespace.yaml b/clustergroup/templates/plumbing/gitops-namespace.yaml similarity index 100% rename from clustergroup/templates/gitops-namespace.yaml rename to clustergroup/templates/plumbing/gitops-namespace.yaml diff --git a/clustergroup/templates/projects.yaml b/clustergroup/templates/plumbing/projects.yaml similarity index 100% rename from clustergroup/templates/projects.yaml rename to clustergroup/templates/plumbing/projects.yaml diff --git a/clustergroup/templates/remote-seeds.yaml b/clustergroup/templates/plumbing/remote-seeds.yaml similarity index 100% rename from clustergroup/templates/remote-seeds.yaml rename to clustergroup/templates/plumbing/remote-seeds.yaml diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 993e6bb5..733c6a7f 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -1,5 +1,5 @@ --- -# Source: pattern-clustergroup/templates/gitops-namespace.yaml +# Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -12,7 +12,7 @@ metadata: name: common-example spec: {} --- -# Source: pattern-clustergroup/templates/argocd-super-role.yaml +# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -31,7 +31,7 @@ subjects: name: openshift-gitops-argocd-server namespace: openshift-gitops --- -# Source: pattern-clustergroup/templates/argocd-super-role.yaml +# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -56,7 +56,7 @@ subjects: name: example-gitops-argocd-dex-server namespace: common-example --- -# Source: pattern-clustergroup/templates/argocd.yaml +# Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 kind: ArgoCD metadata: @@ -167,7 +167,7 @@ spec: ca: {} status: --- -# Source: pattern-clustergroup/templates/argocd.yaml +# Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: console.openshift.io/v1 kind: ConsoleLink metadata: From c7af26809cc372ca17a7f884ef204ddad0ead760 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 16 Sep 2022 16:54:35 +1000 Subject: [PATCH 0498/1263] Update acm tests for new cluster pool examples --- tests/clustergroup-normal.expected.yaml | 145 ++++++++++++++++-------- 1 file changed, 96 insertions(+), 49 deletions(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 3db27504..35413750 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -1,45 +1,45 @@ --- -# Source: pattern-clustergroup/templates/gitops-namespace.yaml +# Source: pattern-clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: labels: - name: mypattern-example - # The name here needs to be consistent with - # - acm/templates/policies/application-policies.yaml - # - clustergroup/templates/applications.yaml - # - any references to secrets and route URLs in documentation - name: mypattern-example -spec: {} + name: pattern + argocd.argoproj.io/managed-by: mypattern-example + name: open-cluster-management +spec: --- -# Source: pattern-clustergroup/templates/imperative/namespace.yaml +# Source: pattern-clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: labels: - name: imperative + name: pattern argocd.argoproj.io/managed-by: mypattern-example - name: imperative + name: application-ci +spec: --- -# Source: pattern-clustergroup/templates/namespaces.yaml +# Source: pattern-clustergroup/templates/imperative/namespace.yaml apiVersion: v1 kind: Namespace metadata: labels: - name: pattern + name: imperative argocd.argoproj.io/managed-by: mypattern-example - name: open-cluster-management -spec: + name: imperative --- -# Source: pattern-clustergroup/templates/namespaces.yaml +# Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml apiVersion: v1 kind: Namespace metadata: labels: - name: pattern - argocd.argoproj.io/managed-by: mypattern-example - name: application-ci -spec: + name: mypattern-example + # The name here needs to be consistent with + # - acm/templates/policies/application-policies.yaml + # - clustergroup/templates/applications.yaml + # - any references to secrets and route URLs in documentation + name: mypattern-example +spec: {} --- # Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml apiVersion: v1 @@ -216,7 +216,21 @@ rules: - list - watch --- -# Source: pattern-clustergroup/templates/argocd-super-role.yaml +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: imperative-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: imperative-cluster-role +subjects: + - kind: ServiceAccount + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -235,7 +249,7 @@ subjects: name: openshift-gitops-argocd-server namespace: openshift-gitops --- -# Source: pattern-clustergroup/templates/argocd-super-role.yaml +# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -260,20 +274,6 @@ subjects: name: example-gitops-argocd-dex-server namespace: mypattern-example --- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: imperative-cluster-admin-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: imperative-cluster-role -subjects: - - kind: ServiceAccount - name: imperative-sa - namespace: imperative ---- # Source: pattern-clustergroup/templates/imperative/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -455,10 +455,10 @@ spec: name: helm-values-configmap restartPolicy: Never --- -# Source: pattern-clustergroup/templates/subscriptions.yaml +# Source: pattern-clustergroup/templates/core/subscriptions.yaml --- --- -# Source: pattern-clustergroup/templates/projects.yaml +# Source: pattern-clustergroup/templates/plumbing/projects.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -479,7 +479,7 @@ spec: - '*' status: {} --- -# Source: pattern-clustergroup/templates/applications.yaml +# Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -528,7 +528,7 @@ spec: automated: {} # selfHeal: true --- -# Source: pattern-clustergroup/templates/applications.yaml +# Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -568,7 +568,7 @@ spec: automated: {} # selfHeal: true --- -# Source: pattern-clustergroup/templates/remote-seeds.yaml +# Source: pattern-clustergroup/templates/plumbing/remote-seeds.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -672,7 +672,7 @@ spec: jsonPointers: - /status --- -# Source: pattern-clustergroup/templates/argocd.yaml +# Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 kind: ArgoCD metadata: @@ -783,7 +783,7 @@ spec: ca: {} status: --- -# Source: pattern-clustergroup/templates/argocd.yaml +# Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: console.openshift.io/v1 kind: ConsoleLink metadata: @@ -797,7 +797,7 @@ spec: location: ApplicationMenu text: 'Example ArgoCD' --- -# Source: pattern-clustergroup/templates/cluster-external-secrets.yaml +# Source: pattern-clustergroup/templates/core/cluster-external-secrets.yaml apiVersion: "external-secrets.io/v1beta1" kind: ExternalSecret metadata: @@ -841,10 +841,57 @@ spec: property: bearerToken - secretKey: kubeCA remoteRef: - key: secret/data/hub/cluster_example_ca + key: secret/data/hub/cluster_perth_ca + property: b64content +--- +# Source: pattern-clustergroup/templates/core/cluster-external-secrets.yaml +apiVersion: "external-secrets.io/v1beta1" +kind: ExternalSecret +metadata: + name: sydney-secret + namespace: openshift-gitops + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/sync-wave: "100" +spec: + refreshInterval: 15s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: sydney-secret + template: + type: Opaque + metadata: + labels: + argocd.argoproj.io/secret-type: cluster + data: + name: sydney + server: |- + {{ .kubeServer | toString }} + config: | + { + "bearerToken": "{{ .kubeBearer | toString }}", + "tlsClientConfig": { + "insecure": false, + "caData": "{{ .kubeCA | toString }}" + } + } + data: + - secretKey: kubeServer + remoteRef: + key: secret/data/hub/cluster_sydney + property: server + - secretKey: kubeBearer + remoteRef: + key: secret/data/hub/cluster_sydney + property: bearerToken + - secretKey: kubeCA + remoteRef: + key: secret/data/hub/cluster_sydney_ca property: b64content --- -# Source: pattern-clustergroup/templates/operatorgroup.yaml +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -854,7 +901,7 @@ spec: targetNamespaces: - open-cluster-management --- -# Source: pattern-clustergroup/templates/operatorgroup.yaml +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -864,7 +911,7 @@ spec: targetNamespaces: - application-ci --- -# Source: pattern-clustergroup/templates/subscriptions.yaml +# Source: pattern-clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -878,7 +925,7 @@ spec: installPlanApproval: Automatic startingCSV: advanced-cluster-management.v2.4.1 --- -# Source: pattern-clustergroup/templates/subscriptions.yaml +# Source: pattern-clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: From c0f13154266e9810ed3253150ae832efe7ff4236 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 16 Sep 2022 17:23:22 +1000 Subject: [PATCH 0499/1263] Allow parts of clusterGroup to be disabled for the remote argo usecase --- clustergroup/templates/core/catalog-sources.yaml | 3 +++ clustergroup/templates/core/cluster-external-secrets.yaml | 3 +++ clustergroup/templates/core/namespaces.yaml | 3 +++ clustergroup/templates/core/operatorgroup.yaml | 3 +++ clustergroup/templates/core/subscriptions.yaml | 3 +++ clustergroup/templates/plumbing/applications.yaml | 3 +++ clustergroup/templates/plumbing/argocd-super-role.yaml | 2 ++ clustergroup/templates/plumbing/argocd.yaml | 2 ++ clustergroup/templates/plumbing/gitops-namespace.yaml | 2 ++ clustergroup/templates/plumbing/projects.yaml | 3 +++ clustergroup/templates/plumbing/remote-seeds.yaml | 3 +++ clustergroup/values.yaml | 2 ++ tests/clustergroup-normal.expected.yaml | 2 ++ 13 files changed, 34 insertions(+) diff --git a/clustergroup/templates/core/catalog-sources.yaml b/clustergroup/templates/core/catalog-sources.yaml index 35208b37..298be405 100644 --- a/clustergroup/templates/core/catalog-sources.yaml +++ b/clustergroup/templates/core/catalog-sources.yaml @@ -1,3 +1,6 @@ +{{- if (eq .Values.enabled "plumbing") }} +{{ $_ := unset .Values.clusterGroup "indexImages" }} +{{- end }} {{- range .Values.clusterGroup.indexImages }} apiVersion: operators.coreos.com/v1alpha1 kind: CatalogSource diff --git a/clustergroup/templates/core/cluster-external-secrets.yaml b/clustergroup/templates/core/cluster-external-secrets.yaml index 60ff673c..edc462d1 100644 --- a/clustergroup/templates/core/cluster-external-secrets.yaml +++ b/clustergroup/templates/core/cluster-external-secrets.yaml @@ -1,3 +1,6 @@ +{{- if (eq .Values.enabled "plumbing") }} +{{ $_ := unset .Values.clusterGroup.managedClusterGroups "argoClusterSeeds" }} +{{- end }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} {{ if .Values.clusterGroup.isHubCluster }} {{- range .Values.clusterGroup.managedClusterGroups }} diff --git a/clustergroup/templates/core/namespaces.yaml b/clustergroup/templates/core/namespaces.yaml index b3bc86a5..3757ac9f 100644 --- a/clustergroup/templates/core/namespaces.yaml +++ b/clustergroup/templates/core/namespaces.yaml @@ -1,3 +1,6 @@ +{{- if (eq .Values.enabled "plumbing") }} +{{ $_ := unset .Values.clusterGroup "namespaces" }} +{{- end }} {{- range .Values.clusterGroup.namespaces }} apiVersion: v1 kind: Namespace diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index 0180a912..fd9814d2 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -1,3 +1,6 @@ +{{- if (eq .Values.enabled "plumbing") }} +{{ $_ := unset .Values.clusterGroup "namespaces" }} +{{- end }} {{- range .Values.clusterGroup.namespaces }} {{- if empty $.Values.clusterGroup.operatorgroupExcludes }} diff --git a/clustergroup/templates/core/subscriptions.yaml b/clustergroup/templates/core/subscriptions.yaml index 3fcd2d1a..9d0ffb58 100644 --- a/clustergroup/templates/core/subscriptions.yaml +++ b/clustergroup/templates/core/subscriptions.yaml @@ -1,3 +1,6 @@ +{{- if (eq .Values.enabled "plumbing") }} +{{ $_ := unset .Values.clusterGroup "subscriptions" }} +{{- end }} {{- range .Values.clusterGroup.subscriptions }} {{- $subs := . }} {{- $installPlanValue := .installPlanApproval }} diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index f610da71..5ee4624e 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -1,3 +1,6 @@ +{{- if (eq .Values.enabled "core") }} +{{ $_ := unset .Values.clusterGroup "applications" }} +{{- end }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} {{- range .Values.clusterGroup.applications }} {{- if or (.generators) (.generatorFile) (.useGeneratorValues) (.destinationServer) (.destinationNamespace) }} diff --git a/clustergroup/templates/plumbing/argocd-super-role.yaml b/clustergroup/templates/plumbing/argocd-super-role.yaml index 78af462d..5105337c 100644 --- a/clustergroup/templates/plumbing/argocd-super-role.yaml +++ b/clustergroup/templates/plumbing/argocd-super-role.yaml @@ -1,3 +1,4 @@ +{{- if not (eq .Values.enabled "core") }} # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -39,3 +40,4 @@ subjects: - kind: ServiceAccount name: {{ .Values.clusterGroup.name }}-gitops-argocd-dex-server namespace: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} +{{- end }} diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 0126c0a5..67f15fc6 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -1,3 +1,4 @@ +{{- if not (eq .Values.enabled "core") }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} apiVersion: argoproj.io/v1alpha1 kind: ArgoCD @@ -121,3 +122,4 @@ spec: href: 'https://{{ .Values.clusterGroup.name }}-gitops-server-{{ $namespace }}.{{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }}' location: ApplicationMenu text: '{{ title .Values.clusterGroup.name }} ArgoCD' +{{- end }} diff --git a/clustergroup/templates/plumbing/gitops-namespace.yaml b/clustergroup/templates/plumbing/gitops-namespace.yaml index 785ef75f..496ddf28 100644 --- a/clustergroup/templates/plumbing/gitops-namespace.yaml +++ b/clustergroup/templates/plumbing/gitops-namespace.yaml @@ -1,3 +1,4 @@ +{{- if not (eq .Values.enabled "core") }} apiVersion: v1 kind: Namespace metadata: @@ -9,3 +10,4 @@ metadata: # - any references to secrets and route URLs in documentation name: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} spec: {} +{{- end }} diff --git a/clustergroup/templates/plumbing/projects.yaml b/clustergroup/templates/plumbing/projects.yaml index d74e2cba..ee0a8e16 100644 --- a/clustergroup/templates/plumbing/projects.yaml +++ b/clustergroup/templates/plumbing/projects.yaml @@ -1,3 +1,6 @@ +{{- if (eq .Values.enabled "core") }} +{{ $_ := unset .Values.clusterGroup "projects" }} +{{- end }} {{- range .Values.clusterGroup.projects }} apiVersion: argoproj.io/v1alpha1 kind: AppProject diff --git a/clustergroup/templates/plumbing/remote-seeds.yaml b/clustergroup/templates/plumbing/remote-seeds.yaml index a3b93b1d..99d98ed6 100644 --- a/clustergroup/templates/plumbing/remote-seeds.yaml +++ b/clustergroup/templates/plumbing/remote-seeds.yaml @@ -1,3 +1,6 @@ +{{- if (eq .Values.enabled "core") }} +{{ $_ := unset .Values.clusterGroup "managedClusterGroups" }} +{{- end }} {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} {{- range .argoClusterSeeds }} diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index bb042704..dbbba87a 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -5,6 +5,8 @@ global: syncPolicy: Automatic installPlanApproval: Automatic +enabled: all + # Note that sometimes changing helm values might require a hard refresh (https://github.com/helm/helm/issues/3486) clusterGroup: name: example diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 35413750..2c15fed3 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -161,6 +161,8 @@ data: pipelines: csv: redhat-openshift-pipelines.v1.5.2 name: openshift-pipelines-operator-rh + targetCluster: in-cluster + enabled: all files: cluster_example_ca: /path/to/ca.file global: From 403befff8c5b90613985c04ff44c4a01e3e51251 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 16 Sep 2022 18:10:26 +1000 Subject: [PATCH 0500/1263] Always deliver the argo plumbing to the hub cluster --- .../templates/plumbing/remote-seeds.yaml | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/clustergroup/templates/plumbing/remote-seeds.yaml b/clustergroup/templates/plumbing/remote-seeds.yaml index 99d98ed6..9db46d8b 100644 --- a/clustergroup/templates/plumbing/remote-seeds.yaml +++ b/clustergroup/templates/plumbing/remote-seeds.yaml @@ -40,6 +40,8 @@ spec: value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' - name: clusterGroup.targetCluster value: {{ $group.targetCluster }} + - name: clusterGroup.enabled + value: core {{- range $group.helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} @@ -67,5 +69,72 @@ spec: kind: Route jsonPointers: - /status +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: {{ $.Values.global.pattern }}-{{ $group.name }}-{{ . }}-plumbing + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + project: default + source: + repoURL: {{ coalesce $group.repoURL $.Values.global.repoURL }} + targetRevision: {{ coalesce $group.targetRevision $.Values.global.targetRevision }} + path: {{ default "common/clustergroup" $group.path }} + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-{{ $group.name }}.yaml" + {{- range $valueFile := $group.extraValueFiles }} + - {{ $valueFile | quote }} + {{- end }} + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: {{ $.Values.global.pattern }} + - name: global.hubClusterDomain + value: {{ $.Values.global.hubClusterDomain }} + - name: global.localClusterDomain + value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' + - name: clusterGroup.enabled + value: plumbing + - name: clusterGroup.targetCluster + value: {{ $group.targetCluster }} + {{- range $group.helmOverrides }} + - name: {{ .name }} + value: {{ .value | quote }} + {{- end }} + {{- if $group.fileParameters }} + fileParameters: + {{- range $group.fileParameters }} + - name: {{ .name }} + path: {{ .path }} + {{- end }} + {{- end }} + destination: + name: in-cluster + namespace: {{ $.Values.global.pattern }}-{{ $group.name }} + syncPolicy: + automated: + prune: false + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +--- {{- end }} {{- end }} From 2c5feeac1b3b14e7f41f1552d1576d0e62b78563 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 01:39:04 +1000 Subject: [PATCH 0501/1263] Break global.localClusterDomain for argo seeds in order to make progress --- clustergroup/templates/plumbing/remote-seeds.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/plumbing/remote-seeds.yaml b/clustergroup/templates/plumbing/remote-seeds.yaml index 9db46d8b..c7af57c5 100644 --- a/clustergroup/templates/plumbing/remote-seeds.yaml +++ b/clustergroup/templates/plumbing/remote-seeds.yaml @@ -37,7 +37,7 @@ spec: - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain - value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' + value: "fixme.example.com" - name: clusterGroup.targetCluster value: {{ $group.targetCluster }} - name: clusterGroup.enabled @@ -103,7 +103,7 @@ spec: - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain - value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' + value: "fixme.example.com" - name: clusterGroup.enabled value: plumbing - name: clusterGroup.targetCluster From 5afa96131143b670527303d2248eeee1fefc4d5c Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 01:46:22 +1000 Subject: [PATCH 0502/1263] Force the correct cluster Group name --- acm/templates/policies/application-policies.yaml | 2 ++ clustergroup/templates/plumbing/applications.yaml | 2 ++ clustergroup/templates/plumbing/argocd.yaml | 1 + clustergroup/templates/plumbing/remote-seeds.yaml | 4 ++++ 4 files changed, 9 insertions(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 65fdd46f..33ea97d2 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -61,6 +61,8 @@ spec: value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' + - name: clusterGroup.name + value: {{ $group.name }} {{- range .helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 5ee4624e..0368ede1 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -75,6 +75,8 @@ spec: value: {{ $.Values.global.namespace }} - name: global.pattern value: {{ $.Values.global.pattern }} + - name: clusterGroup.name + value: {{ .Values.clusterGroup.name }} {{- range .extraHubClusterDomainFields }} - name: {{ . }} value: {{ $.Values.global.hubClusterDomain }} diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 67f15fc6..d5eedeb3 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -40,6 +40,7 @@ spec: --set global.pattern={{ .Values.global.pattern }} --set global.hubClusterDomain={{ .Values.global.hubClusterDomain }} --set global.localClusterDomain={{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }} + --set clusterGroup.name={{ .Values.clusterGroup.name }} --post-renderer ./kustomize"] applicationSet: resources: diff --git a/clustergroup/templates/plumbing/remote-seeds.yaml b/clustergroup/templates/plumbing/remote-seeds.yaml index c7af57c5..759692b9 100644 --- a/clustergroup/templates/plumbing/remote-seeds.yaml +++ b/clustergroup/templates/plumbing/remote-seeds.yaml @@ -42,6 +42,8 @@ spec: value: {{ $group.targetCluster }} - name: clusterGroup.enabled value: core + - name: clusterGroup.name + value: {{ $group.name }} {{- range $group.helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} @@ -108,6 +110,8 @@ spec: value: plumbing - name: clusterGroup.targetCluster value: {{ $group.targetCluster }} + - name: clusterGroup.name + value: {{ $group.name }} {{- range $group.helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} From 345c8e5d41361ff5bd5a801415f347b477949348 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 02:07:35 +1000 Subject: [PATCH 0503/1263] Fix passing of the target cluster to argo seeds --- clustergroup/templates/plumbing/remote-seeds.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/plumbing/remote-seeds.yaml b/clustergroup/templates/plumbing/remote-seeds.yaml index 759692b9..903e1939 100644 --- a/clustergroup/templates/plumbing/remote-seeds.yaml +++ b/clustergroup/templates/plumbing/remote-seeds.yaml @@ -39,7 +39,7 @@ spec: - name: global.localClusterDomain value: "fixme.example.com" - name: clusterGroup.targetCluster - value: {{ $group.targetCluster }} + value: {{ . }} - name: clusterGroup.enabled value: core - name: clusterGroup.name @@ -109,7 +109,7 @@ spec: - name: clusterGroup.enabled value: plumbing - name: clusterGroup.targetCluster - value: {{ $group.targetCluster }} + value: {{ . }} - name: clusterGroup.name value: {{ $group.name }} {{- range $group.helmOverrides }} From 12d8832d45817e561b9fa256d61fcdbd9d46276a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 02:22:46 +1000 Subject: [PATCH 0504/1263] Namespaces were not being disabled as expected --- clustergroup/templates/core/namespaces.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clustergroup/templates/core/namespaces.yaml b/clustergroup/templates/core/namespaces.yaml index 3757ac9f..ca98916a 100644 --- a/clustergroup/templates/core/namespaces.yaml +++ b/clustergroup/templates/core/namespaces.yaml @@ -1,6 +1,4 @@ -{{- if (eq .Values.enabled "plumbing") }} -{{ $_ := unset .Values.clusterGroup "namespaces" }} -{{- end }} +{{- if (eq .Values.enabled "core") }} {{- range .Values.clusterGroup.namespaces }} apiVersion: v1 kind: Namespace @@ -12,3 +10,4 @@ metadata: spec: --- {{- end }} +{{- end }} From 56f4dc2a7f1dad2c7b83db69f960bfea70339178 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 02:27:48 +1000 Subject: [PATCH 0505/1263] Disambiguate the config map when multiple clustergroups are present on the same cluster --- clustergroup/templates/imperative/configmap.yaml | 2 +- clustergroup/templates/imperative/job.yaml | 2 +- clustergroup/templates/imperative/unsealjob.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clustergroup/templates/imperative/configmap.yaml b/clustergroup/templates/imperative/configmap.yaml index 5cde2d37..88a6e7ef 100644 --- a/clustergroup/templates/imperative/configmap.yaml +++ b/clustergroup/templates/imperative/configmap.yaml @@ -5,7 +5,7 @@ apiVersion: v1 kind: ConfigMap metadata: - name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }} + name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} namespace: {{ $.Values.clusterGroup.imperative.namespace}} data: values.yaml: | diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index b237e11f..d7432c96 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -62,6 +62,6 @@ spec: emptyDir: {} - name: values-volume configMap: - name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }} + name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} restartPolicy: Never {{ end }} diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index 0fae9071..c1027479 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -54,6 +54,6 @@ spec: emptyDir: {} - name: values-volume configMap: - name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }} + name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} restartPolicy: Never {{ end }} From 8d8f05c546ee51c61373d2e2321341304a3b079a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 02:32:22 +1000 Subject: [PATCH 0506/1263] Restrict imperative elements to clustergroup 'core' mode --- clustergroup/templates/core/catalog-sources.yaml | 6 +++--- clustergroup/templates/core/cluster-external-secrets.yaml | 5 ++--- clustergroup/templates/core/namespaces.yaml | 2 +- clustergroup/templates/core/operatorgroup.yaml | 5 ++--- clustergroup/templates/core/subscriptions.yaml | 5 ++--- clustergroup/templates/imperative/clusterrole.yaml | 2 ++ clustergroup/templates/imperative/configmap.yaml | 4 +++- clustergroup/templates/imperative/job.yaml | 4 +++- clustergroup/templates/imperative/namespace.yaml | 4 +++- clustergroup/templates/imperative/rbac.yaml | 4 +++- clustergroup/templates/imperative/role.yaml | 2 ++ clustergroup/templates/imperative/serviceaccount.yaml | 2 ++ clustergroup/templates/imperative/unsealjob.yaml | 4 +++- clustergroup/templates/plumbing/applications.yaml | 2 +- clustergroup/templates/plumbing/argocd-super-role.yaml | 2 +- clustergroup/templates/plumbing/argocd.yaml | 2 +- clustergroup/templates/plumbing/gitops-namespace.yaml | 2 +- clustergroup/templates/plumbing/projects.yaml | 2 +- clustergroup/templates/plumbing/remote-seeds.yaml | 2 +- tests/acm-normal.expected.yaml | 4 ++++ tests/clustergroup-naked.expected.yaml | 1 + 21 files changed, 42 insertions(+), 24 deletions(-) diff --git a/clustergroup/templates/core/catalog-sources.yaml b/clustergroup/templates/core/catalog-sources.yaml index 298be405..b3af9612 100644 --- a/clustergroup/templates/core/catalog-sources.yaml +++ b/clustergroup/templates/core/catalog-sources.yaml @@ -1,6 +1,4 @@ -{{- if (eq .Values.enabled "plumbing") }} -{{ $_ := unset .Values.clusterGroup "indexImages" }} -{{- end }} +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{- range .Values.clusterGroup.indexImages }} apiVersion: operators.coreos.com/v1alpha1 kind: CatalogSource @@ -10,4 +8,6 @@ metadata: spec: sourceType: grpc image: {{ .image }}:{{ .version }} +--- +{{- end -}} {{- end -}} diff --git a/clustergroup/templates/core/cluster-external-secrets.yaml b/clustergroup/templates/core/cluster-external-secrets.yaml index edc462d1..bc554827 100644 --- a/clustergroup/templates/core/cluster-external-secrets.yaml +++ b/clustergroup/templates/core/cluster-external-secrets.yaml @@ -1,6 +1,4 @@ -{{- if (eq .Values.enabled "plumbing") }} -{{ $_ := unset .Values.clusterGroup.managedClusterGroups "argoClusterSeeds" }} -{{- end }} +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} {{ if .Values.clusterGroup.isHubCluster }} {{- range .Values.clusterGroup.managedClusterGroups }} @@ -55,3 +53,4 @@ spec: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/clustergroup/templates/core/namespaces.yaml b/clustergroup/templates/core/namespaces.yaml index ca98916a..a390c7f8 100644 --- a/clustergroup/templates/core/namespaces.yaml +++ b/clustergroup/templates/core/namespaces.yaml @@ -1,4 +1,4 @@ -{{- if (eq .Values.enabled "core") }} +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{- range .Values.clusterGroup.namespaces }} apiVersion: v1 kind: Namespace diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index fd9814d2..0b5eecca 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -1,6 +1,4 @@ -{{- if (eq .Values.enabled "plumbing") }} -{{ $_ := unset .Values.clusterGroup "namespaces" }} -{{- end }} +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{- range .Values.clusterGroup.namespaces }} {{- if empty $.Values.clusterGroup.operatorgroupExcludes }} @@ -26,3 +24,4 @@ spec: {{- end }} {{- end }} +{{- end }} diff --git a/clustergroup/templates/core/subscriptions.yaml b/clustergroup/templates/core/subscriptions.yaml index 9d0ffb58..ee1157b7 100644 --- a/clustergroup/templates/core/subscriptions.yaml +++ b/clustergroup/templates/core/subscriptions.yaml @@ -1,6 +1,4 @@ -{{- if (eq .Values.enabled "plumbing") }} -{{ $_ := unset .Values.clusterGroup "subscriptions" }} -{{- end }} +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{- range .Values.clusterGroup.subscriptions }} {{- $subs := . }} {{- $installPlanValue := .installPlanApproval }} @@ -68,3 +66,4 @@ spec: {{- end }} {{- end }} --- +{{- end }} diff --git a/clustergroup/templates/imperative/clusterrole.yaml b/clustergroup/templates/imperative/clusterrole.yaml index 17e33d8d..0aa5f63d 100644 --- a/clustergroup/templates/imperative/clusterrole.yaml +++ b/clustergroup/templates/imperative/clusterrole.yaml @@ -1,3 +1,4 @@ +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{/* Define this if needed (jobs defined or insecure unseal configured) */}} {{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} @@ -20,3 +21,4 @@ rules: - watch {{- end }} {{- end }} +{{- end }} diff --git a/clustergroup/templates/imperative/configmap.yaml b/clustergroup/templates/imperative/configmap.yaml index 88a6e7ef..118dab8f 100644 --- a/clustergroup/templates/imperative/configmap.yaml +++ b/clustergroup/templates/imperative/configmap.yaml @@ -1,3 +1,4 @@ +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{/* Define this if needed (jobs defined or insecure unseal configured) */}} {{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} @@ -10,4 +11,5 @@ metadata: data: values.yaml: | {{ tpl $valuesyaml . | indent 4 }} -{{ end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index d7432c96..4fee1bd8 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -1,3 +1,4 @@ +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{/* Define this if needed (jobs defined */}} {{- if (gt (len $.Values.clusterGroup.imperative.jobs) 0) -}} --- @@ -64,4 +65,5 @@ spec: configMap: name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} restartPolicy: Never -{{ end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/clustergroup/templates/imperative/namespace.yaml b/clustergroup/templates/imperative/namespace.yaml index 827bbee5..c5e4d2c1 100644 --- a/clustergroup/templates/imperative/namespace.yaml +++ b/clustergroup/templates/imperative/namespace.yaml @@ -1,3 +1,4 @@ +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{/* Define this if needed (jobs defined or insecure unseal configured) */}} {{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} @@ -8,4 +9,5 @@ metadata: name: {{ $.Values.clusterGroup.imperative.namespace }} argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} name: {{ $.Values.clusterGroup.imperative.namespace }} -{{ end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/clustergroup/templates/imperative/rbac.yaml b/clustergroup/templates/imperative/rbac.yaml index f62b23ac..4b98f62f 100644 --- a/clustergroup/templates/imperative/rbac.yaml +++ b/clustergroup/templates/imperative/rbac.yaml @@ -1,3 +1,4 @@ +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{/* Define this if needed (jobs defined or insecure unseal configured) */}} {{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} @@ -28,4 +29,5 @@ subjects: - kind: ServiceAccount name: {{ $.Values.clusterGroup.imperative.serviceAccountName }} namespace: {{ $.Values.clusterGroup.imperative.namespace }} -{{ end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/clustergroup/templates/imperative/role.yaml b/clustergroup/templates/imperative/role.yaml index f4909c76..edc54032 100644 --- a/clustergroup/templates/imperative/role.yaml +++ b/clustergroup/templates/imperative/role.yaml @@ -1,3 +1,4 @@ +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{/* Define this if needed (jobs defined or insecure unseal configured) */}} {{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} @@ -19,3 +20,4 @@ rules: - '*' {{- end }} {{- end }} +{{- end }} diff --git a/clustergroup/templates/imperative/serviceaccount.yaml b/clustergroup/templates/imperative/serviceaccount.yaml index bb500deb..0d6fc21b 100644 --- a/clustergroup/templates/imperative/serviceaccount.yaml +++ b/clustergroup/templates/imperative/serviceaccount.yaml @@ -1,3 +1,4 @@ +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{/* Define this if needed (jobs defined or insecure unseal configured) */}} {{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} @@ -9,3 +10,4 @@ metadata: namespace: {{ $.Values.clusterGroup.imperative.namespace }} {{- end }} {{- end }} +{{- end }} diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index c1027479..8790f295 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -1,3 +1,4 @@ +{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} {{/* Only define this if the values.insecureUnsealVaultInsideCluster is set to tre and we're on the cluster */}} {{- if and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster }} --- @@ -56,4 +57,5 @@ spec: configMap: name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} restartPolicy: Never -{{ end }} +{{- end }} +{{- end }} diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 0368ede1..7c8a38ac 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -1,4 +1,4 @@ -{{- if (eq .Values.enabled "core") }} +{{- if (eq .Values.clusterGroup.enabled "core") }} {{ $_ := unset .Values.clusterGroup "applications" }} {{- end }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} diff --git a/clustergroup/templates/plumbing/argocd-super-role.yaml b/clustergroup/templates/plumbing/argocd-super-role.yaml index 5105337c..40f2652a 100644 --- a/clustergroup/templates/plumbing/argocd-super-role.yaml +++ b/clustergroup/templates/plumbing/argocd-super-role.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.enabled "core") }} +{{- if not (eq .Values.clusterGroup.enabled "core") }} # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index d5eedeb3..7ca1da35 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.enabled "core") }} +{{- if not (eq .Values.clusterGroup.enabled "core") }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} apiVersion: argoproj.io/v1alpha1 kind: ArgoCD diff --git a/clustergroup/templates/plumbing/gitops-namespace.yaml b/clustergroup/templates/plumbing/gitops-namespace.yaml index 496ddf28..731aa06d 100644 --- a/clustergroup/templates/plumbing/gitops-namespace.yaml +++ b/clustergroup/templates/plumbing/gitops-namespace.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.enabled "core") }} +{{- if not (eq .Values.clusterGroup.enabled "core") }} apiVersion: v1 kind: Namespace metadata: diff --git a/clustergroup/templates/plumbing/projects.yaml b/clustergroup/templates/plumbing/projects.yaml index ee0a8e16..006da07e 100644 --- a/clustergroup/templates/plumbing/projects.yaml +++ b/clustergroup/templates/plumbing/projects.yaml @@ -1,4 +1,4 @@ -{{- if (eq .Values.enabled "core") }} +{{- if (eq .Values.clusterGroup.enabled "core") }} {{ $_ := unset .Values.clusterGroup "projects" }} {{- end }} {{- range .Values.clusterGroup.projects }} diff --git a/clustergroup/templates/plumbing/remote-seeds.yaml b/clustergroup/templates/plumbing/remote-seeds.yaml index 903e1939..66852b6f 100644 --- a/clustergroup/templates/plumbing/remote-seeds.yaml +++ b/clustergroup/templates/plumbing/remote-seeds.yaml @@ -1,4 +1,4 @@ -{{- if (eq .Values.enabled "core") }} +{{- if (eq .Values.clusterGroup.enabled "core") }} {{ $_ := unset .Values.clusterGroup "managedClusterGroups" }} {{- end }} {{- range .Values.clusterGroup.managedClusterGroups }} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index c941df9b..073778e9 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -545,6 +545,8 @@ spec: value: hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + - name: clusterGroup.name + value: acm-edge - name: clusterGroup.isHubCluster value: "false" destination: @@ -621,6 +623,8 @@ spec: value: hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + - name: clusterGroup.name + value: acm-provision-edge - name: clusterGroup.isHubCluster value: "false" destination: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 733c6a7f..8a19f66f 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -97,6 +97,7 @@ spec: --set global.pattern=common --set global.hubClusterDomain= --set global.localClusterDomain= + --set clusterGroup.name=example --post-renderer ./kustomize"] applicationSet: resources: From 058e37168068ba418c6c693158c47980b33f8168 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 02:51:12 +1000 Subject: [PATCH 0507/1263] Create the external secret as part of the argo seed --- .../cluster-external-secrets.yaml | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) rename clustergroup/templates/{core => plumbing}/cluster-external-secrets.yaml (67%) diff --git a/clustergroup/templates/core/cluster-external-secrets.yaml b/clustergroup/templates/plumbing/cluster-external-secrets.yaml similarity index 67% rename from clustergroup/templates/core/cluster-external-secrets.yaml rename to clustergroup/templates/plumbing/cluster-external-secrets.yaml index bc554827..f4480865 100644 --- a/clustergroup/templates/core/cluster-external-secrets.yaml +++ b/clustergroup/templates/plumbing/cluster-external-secrets.yaml @@ -1,14 +1,9 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if (eq .Values.clusterGroup.enabled "plumbing") }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} -{{ if .Values.clusterGroup.isHubCluster }} -{{- range .Values.clusterGroup.managedClusterGroups }} -{{- $group := . }} -{{- range .argoClusterSeeds }} ---- apiVersion: "external-secrets.io/v1beta1" kind: ExternalSecret metadata: - name: {{ . | kebabcase }}-secret + name: {{ .Values.clusterGroup.targetCluster | kebabcase }}-secret namespace: openshift-gitops annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true @@ -19,14 +14,14 @@ spec: name: {{ $.Values.secretStore.name }} kind: {{ $.Values.secretStore.kind }} target: - name: {{ . | kebabcase }}-secret + name: {{ .Values.clusterGroup.targetCluster | kebabcase }}-secret template: type: Opaque metadata: labels: argocd.argoproj.io/secret-type: cluster data: - name: {{ . }} + name: {{ .Values.clusterGroup.targetCluster }} server: |- {{ "{{ .kubeServer | toString }}" }} config: | @@ -40,17 +35,14 @@ spec: data: - secretKey: kubeServer remoteRef: - key: {{ $.Values.secretsBase.key }}/cluster_{{ . }} + key: {{ $.Values.secretsBase.key }}/cluster_{{ .Values.clusterGroup.targetCluster }} property: server - secretKey: kubeBearer remoteRef: - key: {{ $.Values.secretsBase.key }}/cluster_{{ . }} + key: {{ $.Values.secretsBase.key }}/cluster_{{ .Values.clusterGroup.targetCluster }} property: bearerToken - secretKey: kubeCA remoteRef: - key: {{ $.Values.secretsBase.key }}/cluster_{{ . }}_ca + key: {{ $.Values.secretsBase.key }}/cluster_{{ .Values.clusterGroup.targetCluster }}_ca property: b64content -{{- end }} -{{- end }} -{{- end }} -{{- end }} +{{- end }} \ No newline at end of file From 2093f205226cad1fb74e509c9dd346946b95d598 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 04:32:06 +1000 Subject: [PATCH 0508/1263] Put collect remote seeds into a group Argo project --- .../templates/plumbing/remote-seeds.yaml | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/plumbing/remote-seeds.yaml b/clustergroup/templates/plumbing/remote-seeds.yaml index 66852b6f..d0f926dc 100644 --- a/clustergroup/templates/plumbing/remote-seeds.yaml +++ b/clustergroup/templates/plumbing/remote-seeds.yaml @@ -3,6 +3,26 @@ {{- end }} {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: {{ .name }} + namespace: openshift-gitops +spec: + description: "Cluster Group {{ . }}" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- {{- range .argoClusterSeeds }} apiVersion: argoproj.io/v1alpha1 kind: Application @@ -12,7 +32,7 @@ metadata: finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: - project: default + project: {{ $group.name }} source: repoURL: {{ coalesce $group.repoURL $.Values.global.repoURL }} targetRevision: {{ coalesce $group.targetRevision $.Values.global.targetRevision }} @@ -80,7 +100,7 @@ metadata: finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: - project: default + project: {{ $group.name }} source: repoURL: {{ coalesce $group.repoURL $.Values.global.repoURL }} targetRevision: {{ coalesce $group.targetRevision $.Values.global.targetRevision }} From 8c0a4493224fe68070250e3c1a9ee8b8a0e07473 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 04:32:29 +1000 Subject: [PATCH 0509/1263] Only create the argocd super role once --- clustergroup/templates/plumbing/argocd-super-role.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/templates/plumbing/argocd-super-role.yaml b/clustergroup/templates/plumbing/argocd-super-role.yaml index 40f2652a..b6c4d2e7 100644 --- a/clustergroup/templates/plumbing/argocd-super-role.yaml +++ b/clustergroup/templates/plumbing/argocd-super-role.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "core") }} +{{- if (eq .Values.clusterGroup.enabled "all") }} # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding From c5b6224c15300d580cf69ccb97425ae1bd0384ee Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 06:44:48 +1000 Subject: [PATCH 0510/1263] Only create external secrets for remote argo clusters --- clustergroup/templates/plumbing/remote-seeds.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/clustergroup/templates/plumbing/remote-seeds.yaml b/clustergroup/templates/plumbing/remote-seeds.yaml index d0f926dc..31a8b15c 100644 --- a/clustergroup/templates/plumbing/remote-seeds.yaml +++ b/clustergroup/templates/plumbing/remote-seeds.yaml @@ -1,8 +1,7 @@ -{{- if (eq .Values.clusterGroup.enabled "core") }} -{{ $_ := unset .Values.clusterGroup "managedClusterGroups" }} -{{- end }} +{{- if (eq .Values.enabled "all") }} {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} +{{- if .argoClusterSeeds }} apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -23,6 +22,7 @@ spec: - '*' status: {} --- +{{- end }} {{- range .argoClusterSeeds }} apiVersion: argoproj.io/v1alpha1 kind: Application @@ -60,7 +60,7 @@ spec: value: "fixme.example.com" - name: clusterGroup.targetCluster value: {{ . }} - - name: clusterGroup.enabled + - name: enabled value: core - name: clusterGroup.name value: {{ $group.name }} @@ -126,7 +126,7 @@ spec: value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain value: "fixme.example.com" - - name: clusterGroup.enabled + - name: enabled value: plumbing - name: clusterGroup.targetCluster value: {{ . }} @@ -162,3 +162,4 @@ spec: --- {{- end }} {{- end }} +{{- end }} From 448fb991ec2e591c1e87bf1c1efe652de7d4369c Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 06:45:14 +1000 Subject: [PATCH 0511/1263] Fix 'enabled' mode in clusterGroup --- .../templates/core/catalog-sources.yaml | 2 +- clustergroup/templates/core/namespaces.yaml | 2 +- .../templates/core/operatorgroup.yaml | 2 +- .../templates/core/subscriptions.yaml | 2 +- .../templates/imperative/clusterrole.yaml | 2 +- .../templates/imperative/configmap.yaml | 2 +- clustergroup/templates/imperative/job.yaml | 2 +- .../templates/imperative/namespace.yaml | 2 +- clustergroup/templates/imperative/rbac.yaml | 2 +- clustergroup/templates/imperative/role.yaml | 2 +- .../templates/imperative/serviceaccount.yaml | 2 +- .../templates/imperative/unsealjob.yaml | 2 +- .../templates/plumbing/applications.yaml | 2 +- .../templates/plumbing/argocd-super-role.yaml | 2 +- clustergroup/templates/plumbing/argocd.yaml | 2 +- .../plumbing/cluster-external-secrets.yaml | 2 +- .../templates/plumbing/gitops-namespace.yaml | 2 +- clustergroup/templates/plumbing/projects.yaml | 2 +- clustergroup/values.yaml | 2 +- tests/clustergroup-normal.expected.yaml | 258 +++++++++++------- 20 files changed, 174 insertions(+), 122 deletions(-) diff --git a/clustergroup/templates/core/catalog-sources.yaml b/clustergroup/templates/core/catalog-sources.yaml index b3af9612..2f0c2a95 100644 --- a/clustergroup/templates/core/catalog-sources.yaml +++ b/clustergroup/templates/core/catalog-sources.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if not (eq .Values.enabled "plumbing") }} {{- range .Values.clusterGroup.indexImages }} apiVersion: operators.coreos.com/v1alpha1 kind: CatalogSource diff --git a/clustergroup/templates/core/namespaces.yaml b/clustergroup/templates/core/namespaces.yaml index a390c7f8..6d2ad164 100644 --- a/clustergroup/templates/core/namespaces.yaml +++ b/clustergroup/templates/core/namespaces.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if not (eq .Values.enabled "plumbing") }} {{- range .Values.clusterGroup.namespaces }} apiVersion: v1 kind: Namespace diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index 0b5eecca..74febe94 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if not (eq .Values.enabled "plumbing") }} {{- range .Values.clusterGroup.namespaces }} {{- if empty $.Values.clusterGroup.operatorgroupExcludes }} diff --git a/clustergroup/templates/core/subscriptions.yaml b/clustergroup/templates/core/subscriptions.yaml index ee1157b7..bdaeff84 100644 --- a/clustergroup/templates/core/subscriptions.yaml +++ b/clustergroup/templates/core/subscriptions.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if not (eq .Values.enabled "plumbing") }} {{- range .Values.clusterGroup.subscriptions }} {{- $subs := . }} {{- $installPlanValue := .installPlanApproval }} diff --git a/clustergroup/templates/imperative/clusterrole.yaml b/clustergroup/templates/imperative/clusterrole.yaml index 0aa5f63d..b893d0e2 100644 --- a/clustergroup/templates/imperative/clusterrole.yaml +++ b/clustergroup/templates/imperative/clusterrole.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if not (eq .Values.enabled "plumbing") }} {{/* Define this if needed (jobs defined or insecure unseal configured) */}} {{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} diff --git a/clustergroup/templates/imperative/configmap.yaml b/clustergroup/templates/imperative/configmap.yaml index 118dab8f..5abb473b 100644 --- a/clustergroup/templates/imperative/configmap.yaml +++ b/clustergroup/templates/imperative/configmap.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if not (eq .Values.enabled "plumbing") }} {{/* Define this if needed (jobs defined or insecure unseal configured) */}} {{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index 4fee1bd8..b9437c3f 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if not (eq .Values.enabled "plumbing") }} {{/* Define this if needed (jobs defined */}} {{- if (gt (len $.Values.clusterGroup.imperative.jobs) 0) -}} --- diff --git a/clustergroup/templates/imperative/namespace.yaml b/clustergroup/templates/imperative/namespace.yaml index c5e4d2c1..fd4569c6 100644 --- a/clustergroup/templates/imperative/namespace.yaml +++ b/clustergroup/templates/imperative/namespace.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if not (eq .Values.enabled "plumbing") }} {{/* Define this if needed (jobs defined or insecure unseal configured) */}} {{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} diff --git a/clustergroup/templates/imperative/rbac.yaml b/clustergroup/templates/imperative/rbac.yaml index 4b98f62f..1b73ca3e 100644 --- a/clustergroup/templates/imperative/rbac.yaml +++ b/clustergroup/templates/imperative/rbac.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if not (eq .Values.enabled "plumbing") }} {{/* Define this if needed (jobs defined or insecure unseal configured) */}} {{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} diff --git a/clustergroup/templates/imperative/role.yaml b/clustergroup/templates/imperative/role.yaml index edc54032..79b7b7a7 100644 --- a/clustergroup/templates/imperative/role.yaml +++ b/clustergroup/templates/imperative/role.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if not (eq .Values.enabled "plumbing") }} {{/* Define this if needed (jobs defined or insecure unseal configured) */}} {{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} diff --git a/clustergroup/templates/imperative/serviceaccount.yaml b/clustergroup/templates/imperative/serviceaccount.yaml index 0d6fc21b..b90ac2a4 100644 --- a/clustergroup/templates/imperative/serviceaccount.yaml +++ b/clustergroup/templates/imperative/serviceaccount.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if not (eq .Values.enabled "plumbing") }} {{/* Define this if needed (jobs defined or insecure unseal configured) */}} {{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index 8790f295..76fbd135 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if not (eq .Values.enabled "plumbing") }} {{/* Only define this if the values.insecureUnsealVaultInsideCluster is set to tre and we're on the cluster */}} {{- if and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster }} --- diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 7c8a38ac..0368ede1 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -1,4 +1,4 @@ -{{- if (eq .Values.clusterGroup.enabled "core") }} +{{- if (eq .Values.enabled "core") }} {{ $_ := unset .Values.clusterGroup "applications" }} {{- end }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} diff --git a/clustergroup/templates/plumbing/argocd-super-role.yaml b/clustergroup/templates/plumbing/argocd-super-role.yaml index b6c4d2e7..2d5f8f76 100644 --- a/clustergroup/templates/plumbing/argocd-super-role.yaml +++ b/clustergroup/templates/plumbing/argocd-super-role.yaml @@ -1,4 +1,4 @@ -{{- if (eq .Values.clusterGroup.enabled "all") }} +{{- if (eq .Values.enabled "all") }} # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 7ca1da35..d5eedeb3 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "core") }} +{{- if not (eq .Values.enabled "core") }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} apiVersion: argoproj.io/v1alpha1 kind: ArgoCD diff --git a/clustergroup/templates/plumbing/cluster-external-secrets.yaml b/clustergroup/templates/plumbing/cluster-external-secrets.yaml index f4480865..e448570e 100644 --- a/clustergroup/templates/plumbing/cluster-external-secrets.yaml +++ b/clustergroup/templates/plumbing/cluster-external-secrets.yaml @@ -1,4 +1,4 @@ -{{- if (eq .Values.clusterGroup.enabled "plumbing") }} +{{- if (eq .Values.enabled "plumbing") }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} apiVersion: "external-secrets.io/v1beta1" kind: ExternalSecret diff --git a/clustergroup/templates/plumbing/gitops-namespace.yaml b/clustergroup/templates/plumbing/gitops-namespace.yaml index 731aa06d..496ddf28 100644 --- a/clustergroup/templates/plumbing/gitops-namespace.yaml +++ b/clustergroup/templates/plumbing/gitops-namespace.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.clusterGroup.enabled "core") }} +{{- if not (eq .Values.enabled "core") }} apiVersion: v1 kind: Namespace metadata: diff --git a/clustergroup/templates/plumbing/projects.yaml b/clustergroup/templates/plumbing/projects.yaml index 006da07e..ee0a8e16 100644 --- a/clustergroup/templates/plumbing/projects.yaml +++ b/clustergroup/templates/plumbing/projects.yaml @@ -1,4 +1,4 @@ -{{- if (eq .Values.clusterGroup.enabled "core") }} +{{- if (eq .Values.enabled "core") }} {{ $_ := unset .Values.clusterGroup "projects" }} {{- end }} {{- range .Values.clusterGroup.projects }} diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index dbbba87a..117858bd 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -5,7 +5,7 @@ global: syncPolicy: Automatic installPlanApproval: Automatic -enabled: all +enabled: "all" # Note that sometimes changing helm values might require a hard refresh (https://github.com/helm/helm/issues/3486) clusterGroup: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 2c15fed3..db13a05c 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -52,7 +52,7 @@ metadata: apiVersion: v1 kind: ConfigMap metadata: - name: helm-values-configmap + name: helm-values-configmap-example namespace: imperative data: values.yaml: | @@ -376,7 +376,7 @@ spec: emptyDir: {} - name: values-volume configMap: - name: helm-values-configmap + name: helm-values-configmap-example restartPolicy: Never --- # Source: pattern-clustergroup/templates/imperative/unsealjob.yaml @@ -454,7 +454,7 @@ spec: emptyDir: {} - name: values-volume configMap: - name: helm-values-configmap + name: helm-values-configmap-example restartPolicy: Never --- # Source: pattern-clustergroup/templates/core/subscriptions.yaml @@ -481,6 +481,27 @@ spec: - '*' status: {} --- +# Source: pattern-clustergroup/templates/plumbing/remote-seeds.yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: argo-edge + namespace: openshift-gitops +spec: + description: "Cluster Group map[argoClusterSeeds:[perth sydney] helmOverrides:[map[name:clusterGroup.isHubCluster value:false]] name:argo-edge]" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application @@ -579,7 +600,7 @@ metadata: finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: - project: default + project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern targetRevision: @@ -601,9 +622,13 @@ spec: - name: global.hubClusterDomain value: hub.example.com - name: global.localClusterDomain - value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + value: "fixme.example.com" - name: clusterGroup.targetCluster - value: + value: perth + - name: enabled + value: core + - name: clusterGroup.name + value: argo-edge - name: clusterGroup.isHubCluster value: "false" destination: @@ -622,6 +647,65 @@ spec: kind: Route jsonPointers: - /status +--- +# Source: pattern-clustergroup/templates/plumbing/remote-seeds.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: mypattern-argo-edge-perth-plumbing + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + project: argo-edge + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-argo-edge.yaml" + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.hubClusterDomain + value: hub.example.com + - name: global.localClusterDomain + value: "fixme.example.com" + - name: enabled + value: plumbing + - name: clusterGroup.targetCluster + value: perth + - name: clusterGroup.name + value: argo-edge + - name: clusterGroup.isHubCluster + value: "false" + destination: + name: in-cluster + namespace: mypattern-argo-edge + syncPolicy: + automated: + prune: false + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +--- +# Source: pattern-clustergroup/templates/plumbing/remote-seeds.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -630,7 +714,7 @@ metadata: finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: - project: default + project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern targetRevision: @@ -652,9 +736,13 @@ spec: - name: global.hubClusterDomain value: hub.example.com - name: global.localClusterDomain - value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + value: "fixme.example.com" - name: clusterGroup.targetCluster - value: + value: sydney + - name: enabled + value: core + - name: clusterGroup.name + value: argo-edge - name: clusterGroup.isHubCluster value: "false" destination: @@ -674,6 +762,63 @@ spec: jsonPointers: - /status --- +# Source: pattern-clustergroup/templates/plumbing/remote-seeds.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: mypattern-argo-edge-sydney-plumbing + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + project: argo-edge + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-argo-edge.yaml" + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.hubClusterDomain + value: hub.example.com + - name: global.localClusterDomain + value: "fixme.example.com" + - name: enabled + value: plumbing + - name: clusterGroup.targetCluster + value: sydney + - name: clusterGroup.name + value: argo-edge + - name: clusterGroup.isHubCluster + value: "false" + destination: + name: in-cluster + namespace: mypattern-argo-edge + syncPolicy: + automated: + prune: false + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +--- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 kind: ArgoCD @@ -715,6 +860,7 @@ spec: --set global.pattern=mypattern --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com + --set clusterGroup.name=example --post-renderer ./kustomize"] applicationSet: resources: @@ -799,100 +945,6 @@ spec: location: ApplicationMenu text: 'Example ArgoCD' --- -# Source: pattern-clustergroup/templates/core/cluster-external-secrets.yaml -apiVersion: "external-secrets.io/v1beta1" -kind: ExternalSecret -metadata: - name: perth-secret - namespace: openshift-gitops - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - argocd.argoproj.io/sync-wave: "100" -spec: - refreshInterval: 15s - secretStoreRef: - name: vault-backend - kind: ClusterSecretStore - target: - name: perth-secret - template: - type: Opaque - metadata: - labels: - argocd.argoproj.io/secret-type: cluster - data: - name: example - server: |- - {{ .kubeServer | toString }} - config: | - { - "bearerToken": {{ .kubeBearer | toString | quote }}, - "tlsClientConfig": { - "insecure": false, - "caData": {{ .kubeCA | toString | quote }} - } - } - data: - - secretKey: kubeServer - remoteRef: - key: secret/data/hub/cluster_example - property: server - - secretKey: kubeBearer - remoteRef: - key: secret/data/hub/cluster_example - property: bearerToken - - secretKey: kubeCA - remoteRef: - key: secret/data/hub/cluster_perth_ca - property: b64content ---- -# Source: pattern-clustergroup/templates/core/cluster-external-secrets.yaml -apiVersion: "external-secrets.io/v1beta1" -kind: ExternalSecret -metadata: - name: sydney-secret - namespace: openshift-gitops - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - argocd.argoproj.io/sync-wave: "100" -spec: - refreshInterval: 15s - secretStoreRef: - name: vault-backend - kind: ClusterSecretStore - target: - name: sydney-secret - template: - type: Opaque - metadata: - labels: - argocd.argoproj.io/secret-type: cluster - data: - name: sydney - server: |- - {{ .kubeServer | toString }} - config: | - { - "bearerToken": "{{ .kubeBearer | toString }}", - "tlsClientConfig": { - "insecure": false, - "caData": "{{ .kubeCA | toString }}" - } - } - data: - - secretKey: kubeServer - remoteRef: - key: secret/data/hub/cluster_sydney - property: server - - secretKey: kubeBearer - remoteRef: - key: secret/data/hub/cluster_sydney - property: bearerToken - - secretKey: kubeCA - remoteRef: - key: secret/data/hub/cluster_sydney_ca - property: b64content ---- # Source: pattern-clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup From d04f2d31e9634c9ef8e66e7f8807d3fb9bf437a8 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 07:05:47 +1000 Subject: [PATCH 0512/1263] Ensure targetRevision is always set in clusterGroup --- clustergroup/values.yaml | 1 + tests/clustergroup-normal.expected.yaml | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 117858bd..fac5d56c 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -1,5 +1,6 @@ global: pattern: common + targetRevision: main options: useCSV: True syncPolicy: Automatic diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index db13a05c..0adcd3bd 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -180,6 +180,7 @@ data: useCSV: false pattern: mypattern repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main main: clusterGroupName: example git: @@ -335,7 +336,7 @@ spec: command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" volumeMounts: - name: git mountPath: "/git" @@ -409,7 +410,7 @@ spec: command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" volumeMounts: - name: git mountPath: "/git" @@ -517,7 +518,7 @@ spec: project: datacenter source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: + targetRevision: main path: common/acm helm: ignoreMissingValueFiles: true @@ -566,7 +567,7 @@ spec: project: datacenter source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: + targetRevision: main path: charts/datacenter/pipelines helm: ignoreMissingValueFiles: true @@ -603,7 +604,7 @@ spec: project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: + targetRevision: main path: common/clustergroup helm: ignoreMissingValueFiles: true @@ -660,7 +661,7 @@ spec: project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: + targetRevision: main path: common/clustergroup helm: ignoreMissingValueFiles: true @@ -717,7 +718,7 @@ spec: project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: + targetRevision: main path: common/clustergroup helm: ignoreMissingValueFiles: true @@ -774,7 +775,7 @@ spec: project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: + targetRevision: main path: common/clustergroup helm: ignoreMissingValueFiles: true From fd20a60b5d59995126750de6bddfd04c4aa4f79c Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 10:14:51 +1000 Subject: [PATCH 0513/1263] Argo drops prune=false from applications --- clustergroup/templates/plumbing/remote-seeds.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/clustergroup/templates/plumbing/remote-seeds.yaml b/clustergroup/templates/plumbing/remote-seeds.yaml index 31a8b15c..68ed20a9 100644 --- a/clustergroup/templates/plumbing/remote-seeds.yaml +++ b/clustergroup/templates/plumbing/remote-seeds.yaml @@ -80,7 +80,6 @@ spec: namespace: {{ $.Values.global.pattern }}-{{ $group.name }} syncPolicy: automated: - prune: false selfHeal: true ignoreDifferences: - group: apps @@ -148,7 +147,6 @@ spec: namespace: {{ $.Values.global.pattern }}-{{ $group.name }} syncPolicy: automated: - prune: false selfHeal: true ignoreDifferences: - group: apps From f5efc5b929103cb30c4a0501c3c39541f8460db3 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 10:16:02 +1000 Subject: [PATCH 0514/1263] Create the default group namespace for remote seeds on the remote cluster --- clustergroup/templates/plumbing/gitops-namespace.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/templates/plumbing/gitops-namespace.yaml b/clustergroup/templates/plumbing/gitops-namespace.yaml index 496ddf28..3cd7608d 100644 --- a/clustergroup/templates/plumbing/gitops-namespace.yaml +++ b/clustergroup/templates/plumbing/gitops-namespace.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.enabled "core") }} +{{- if not (eq .Values.enabled "plumbing") }} apiVersion: v1 kind: Namespace metadata: From 0ef65814ce3ed6789cb0bdd0159db9aea2696147 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 10:16:29 +1000 Subject: [PATCH 0515/1263] Do not define another argo for remote seeds --- clustergroup/templates/plumbing/argocd.yaml | 2 +- clustergroup/templates/plumbing/projects.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index d5eedeb3..b8b1e822 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -1,4 +1,4 @@ -{{- if not (eq .Values.enabled "core") }} +{{- if (eq .Values.enabled "all") }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} apiVersion: argoproj.io/v1alpha1 kind: ArgoCD diff --git a/clustergroup/templates/plumbing/projects.yaml b/clustergroup/templates/plumbing/projects.yaml index ee0a8e16..7170f7d4 100644 --- a/clustergroup/templates/plumbing/projects.yaml +++ b/clustergroup/templates/plumbing/projects.yaml @@ -1,4 +1,4 @@ -{{- if (eq .Values.enabled "core") }} +{{- if (eq .Values.enabled "all") }} {{ $_ := unset .Values.clusterGroup "projects" }} {{- end }} {{- range .Values.clusterGroup.projects }} From ee3421343479259c41598a64d64f244ec3521897 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 10:17:21 +1000 Subject: [PATCH 0516/1263] Remote argo apps need to be processed by the hub's cluster instance --- clustergroup/templates/plumbing/applications.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 0368ede1..0ab37ec1 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -1,7 +1,8 @@ -{{- if (eq .Values.enabled "core") }} -{{ $_ := unset .Values.clusterGroup "applications" }} -{{- end }} +{{- if not (eq .Values.enabled "core") }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} +{{- if (eq .Values.enabled "plumbing") }} +{{- $namespace := "openshift-gitops" }} +{{- end }} {{- range .Values.clusterGroup.applications }} {{- if or (.generators) (.generatorFile) (.useGeneratorValues) (.destinationServer) (.destinationNamespace) }} apiVersion: argoproj.io/v1alpha1 @@ -210,3 +211,4 @@ spec: --- {{- end }} {{- end }} +{{- end }} From 70fce05bf28c4cef20fa33a22fc97bb311653c4e Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 10:18:26 +1000 Subject: [PATCH 0517/1263] Update tests --- tests/clustergroup-normal.expected.yaml | 27 ------------------------- 1 file changed, 27 deletions(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 0adcd3bd..77ea5943 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -145,8 +145,6 @@ data: namespaces: - open-cluster-management - application-ci - projects: - - datacenter subscriptions: acm: channel: release-2.4 @@ -461,27 +459,6 @@ spec: # Source: pattern-clustergroup/templates/core/subscriptions.yaml --- --- -# Source: pattern-clustergroup/templates/plumbing/projects.yaml -apiVersion: argoproj.io/v1alpha1 -kind: AppProject -metadata: - name: datacenter - namespace: mypattern-example -spec: - description: "Pattern datacenter" - destinations: - - namespace: '*' - server: '*' - clusterResourceWhitelist: - - group: '*' - kind: '*' - namespaceResourceWhitelist: - - group: '*' - kind: '*' - sourceRepos: - - '*' -status: {} ---- # Source: pattern-clustergroup/templates/plumbing/remote-seeds.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject @@ -637,7 +614,6 @@ spec: namespace: mypattern-argo-edge syncPolicy: automated: - prune: false selfHeal: true ignoreDifferences: - group: apps @@ -694,7 +670,6 @@ spec: namespace: mypattern-argo-edge syncPolicy: automated: - prune: false selfHeal: true ignoreDifferences: - group: apps @@ -751,7 +726,6 @@ spec: namespace: mypattern-argo-edge syncPolicy: automated: - prune: false selfHeal: true ignoreDifferences: - group: apps @@ -808,7 +782,6 @@ spec: namespace: mypattern-argo-edge syncPolicy: automated: - prune: false selfHeal: true ignoreDifferences: - group: apps From 7f7b86b33482cbde7545c55038057f9f207164d7 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 11:11:46 +1000 Subject: [PATCH 0518/1263] Ensure remote seed elements on the hub end up in the openshift-gitops namesapce --- clustergroup/templates/plumbing/applications.yaml | 2 +- clustergroup/templates/plumbing/projects.yaml | 9 +++++---- clustergroup/templates/plumbing/remote-seeds.yaml | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 0ab37ec1..715c189e 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -1,7 +1,7 @@ {{- if not (eq .Values.enabled "core") }} {{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} {{- if (eq .Values.enabled "plumbing") }} -{{- $namespace := "openshift-gitops" }} +{{- $namespace = "openshift-gitops" }} {{- end }} {{- range .Values.clusterGroup.applications }} {{- if or (.generators) (.generatorFile) (.useGeneratorValues) (.destinationServer) (.destinationNamespace) }} diff --git a/clustergroup/templates/plumbing/projects.yaml b/clustergroup/templates/plumbing/projects.yaml index 7170f7d4..6f55a392 100644 --- a/clustergroup/templates/plumbing/projects.yaml +++ b/clustergroup/templates/plumbing/projects.yaml @@ -1,12 +1,12 @@ -{{- if (eq .Values.enabled "all") }} -{{ $_ := unset .Values.clusterGroup "projects" }} -{{- end }} +{{- if not (eq .Values.enabled "core") }} {{- range .Values.clusterGroup.projects }} apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: {{ . }} - namespace: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} +{{- if (eq $.Values.enabled "plumbing") }} + namespace: openshift-gitops +{{- end }} spec: description: "Pattern {{ . }}" destinations: @@ -23,3 +23,4 @@ spec: status: {} --- {{- end }} +{{- end }} diff --git a/clustergroup/templates/plumbing/remote-seeds.yaml b/clustergroup/templates/plumbing/remote-seeds.yaml index 68ed20a9..7af519b3 100644 --- a/clustergroup/templates/plumbing/remote-seeds.yaml +++ b/clustergroup/templates/plumbing/remote-seeds.yaml @@ -144,7 +144,7 @@ spec: {{- end }} destination: name: in-cluster - namespace: {{ $.Values.global.pattern }}-{{ $group.name }} + namespace: openshift-gitops syncPolicy: automated: selfHeal: true From 5c99cb4a8e11cdcb89d4aafac92bb42d2a5f23fa Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 11:27:58 +1000 Subject: [PATCH 0519/1263] Update tests --- tests/clustergroup-normal.expected.yaml | 26 +++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 77ea5943..d28f5626 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -145,6 +145,8 @@ data: namespaces: - open-cluster-management - application-ci + projects: + - datacenter subscriptions: acm: channel: release-2.4 @@ -459,6 +461,26 @@ spec: # Source: pattern-clustergroup/templates/core/subscriptions.yaml --- --- +# Source: pattern-clustergroup/templates/plumbing/projects.yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: datacenter +spec: + description: "Pattern datacenter" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- # Source: pattern-clustergroup/templates/plumbing/remote-seeds.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject @@ -667,7 +689,7 @@ spec: value: "false" destination: name: in-cluster - namespace: mypattern-argo-edge + namespace: openshift-gitops syncPolicy: automated: selfHeal: true @@ -779,7 +801,7 @@ spec: value: "false" destination: name: in-cluster - namespace: mypattern-argo-edge + namespace: openshift-gitops syncPolicy: automated: selfHeal: true From b9315823594851d4dd07599ac7d0b58a3797bc32 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 11:48:03 +1000 Subject: [PATCH 0520/1263] Fix acm tests --- tests/acm-normal.expected.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 073778e9..caf5daa6 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -30,7 +30,6 @@ metadata: labels: clusterClaimName: One-acm-provision-edge clusterGroup: region - patternGroup: acm-provision-edge spec: clusterPoolName: aws-ap --- @@ -45,7 +44,6 @@ metadata: labels: clusterClaimName: Two-acm-provision-edge clusterGroup: region - patternGroup: acm-provision-edge spec: clusterPoolName: azure-us --- @@ -60,7 +58,6 @@ metadata: labels: clusterClaimName: Three-acm-provision-edge clusterGroup: region - patternGroup: acm-provision-edge spec: clusterPoolName: azure-us --- @@ -455,7 +452,6 @@ spec: clusterSelector: matchLabels: clusterGroup: acm-region - patternGroup: acm-edge --- # Source: acm/templates/policies/application-policies.yaml apiVersion: apps.open-cluster-management.io/v1 @@ -469,7 +465,6 @@ spec: clusterSelector: matchLabels: clusterGroup: region - patternGroup: acm-provision-edge --- # Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: apps.open-cluster-management.io/v1 From 8a448271811f8dea72c48ad1c5f894aa4d55e9e7 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 17:20:42 +1000 Subject: [PATCH 0521/1263] Require a domain for argo seeds so we can set global.localClusterDomain --- .../plumbing/cluster-external-secrets.yaml | 3 +-- .../templates/plumbing/remote-seeds.yaml | 20 +++++++++------- examples/values-example.yaml | 6 +++-- tests/clustergroup-normal.expected.yaml | 24 +++++++++++++------ 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/clustergroup/templates/plumbing/cluster-external-secrets.yaml b/clustergroup/templates/plumbing/cluster-external-secrets.yaml index e448570e..4f67da56 100644 --- a/clustergroup/templates/plumbing/cluster-external-secrets.yaml +++ b/clustergroup/templates/plumbing/cluster-external-secrets.yaml @@ -22,8 +22,7 @@ spec: argocd.argoproj.io/secret-type: cluster data: name: {{ .Values.clusterGroup.targetCluster }} - server: |- - {{ "{{ .kubeServer | toString }}" }} + server: https://api.{{ .Values.global.clusterDomain }}:6443 config: | { "bearerToken": {{ "{{ .kubeBearer | toString | quote }}" }}, diff --git a/clustergroup/templates/plumbing/remote-seeds.yaml b/clustergroup/templates/plumbing/remote-seeds.yaml index 7af519b3..c9eb8846 100644 --- a/clustergroup/templates/plumbing/remote-seeds.yaml +++ b/clustergroup/templates/plumbing/remote-seeds.yaml @@ -8,7 +8,7 @@ metadata: name: {{ .name }} namespace: openshift-gitops spec: - description: "Cluster Group {{ . }}" + description: "Cluster Group {{ $group.name }}" destinations: - namespace: '*' server: '*' @@ -27,7 +27,7 @@ status: {} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: {{ $.Values.global.pattern }}-{{ $group.name }}-{{ . }} + name: {{ $.Values.global.pattern }}-{{ $group.name }}-{{ .name }} namespace: openshift-gitops finalizers: - resources-finalizer.argocd.argoproj.io/foreground @@ -57,9 +57,11 @@ spec: - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain - value: "fixme.example.com" + value: apps.{{ .domain }} + - name: global.clusterDomain + value: {{ .domain }} - name: clusterGroup.targetCluster - value: {{ . }} + value: {{ .name }} - name: enabled value: core - name: clusterGroup.name @@ -76,7 +78,7 @@ spec: {{- end }} {{- end }} destination: - name: {{ . }} + name: {{ .name }} namespace: {{ $.Values.global.pattern }}-{{ $group.name }} syncPolicy: automated: @@ -94,7 +96,7 @@ spec: apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: {{ $.Values.global.pattern }}-{{ $group.name }}-{{ . }}-plumbing + name: {{ $.Values.global.pattern }}-{{ $group.name }}-{{ .name }}-plumbing namespace: openshift-gitops finalizers: - resources-finalizer.argocd.argoproj.io/foreground @@ -124,11 +126,13 @@ spec: - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain - value: "fixme.example.com" + value: apps.{{ .domain }} + - name: global.clusterDomain + value: {{ .domain }} - name: enabled value: plumbing - name: clusterGroup.targetCluster - value: {{ . }} + value: {{ .name }} - name: clusterGroup.name value: {{ $group.name }} {{- range $group.helmOverrides }} diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 9f041590..6023f230 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -95,8 +95,10 @@ clusterGroup: # Requires a cluster_{name} entry in your secret store containing # ??what does it contain?? argoClusterSeeds: - - perth - - sydney + - name: perth + domain: beekhof.net + - name: sydney + domain: beekhof.net helmOverrides: - name: clusterGroup.isHubCluster value: "false" diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index d28f5626..322b0dc7 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -135,8 +135,10 @@ data: name: acm-provision-edge targetRevision: main - argoClusterSeeds: - - perth - - sydney + - domain: beekhof.net + name: perth + - domain: beekhof.net + name: sydney helmOverrides: - name: clusterGroup.isHubCluster value: "false" @@ -488,7 +490,7 @@ metadata: name: argo-edge namespace: openshift-gitops spec: - description: "Cluster Group map[argoClusterSeeds:[perth sydney] helmOverrides:[map[name:clusterGroup.isHubCluster value:false]] name:argo-edge]" + description: "Cluster Group argo-edge" destinations: - namespace: '*' server: '*' @@ -622,7 +624,9 @@ spec: - name: global.hubClusterDomain value: hub.example.com - name: global.localClusterDomain - value: "fixme.example.com" + value: apps.beekhof.net + - name: global.clusterDomain + value: beekhof.net - name: clusterGroup.targetCluster value: perth - name: enabled @@ -678,7 +682,9 @@ spec: - name: global.hubClusterDomain value: hub.example.com - name: global.localClusterDomain - value: "fixme.example.com" + value: apps.beekhof.net + - name: global.clusterDomain + value: beekhof.net - name: enabled value: plumbing - name: clusterGroup.targetCluster @@ -734,7 +740,9 @@ spec: - name: global.hubClusterDomain value: hub.example.com - name: global.localClusterDomain - value: "fixme.example.com" + value: apps.beekhof.net + - name: global.clusterDomain + value: beekhof.net - name: clusterGroup.targetCluster value: sydney - name: enabled @@ -790,7 +798,9 @@ spec: - name: global.hubClusterDomain value: hub.example.com - name: global.localClusterDomain - value: "fixme.example.com" + value: apps.beekhof.net + - name: global.clusterDomain + value: beekhof.net - name: enabled value: plumbing - name: clusterGroup.targetCluster From b5914392cfe0af1b49f45858ab5a04d0e31d2b8c Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Sat, 17 Sep 2022 12:53:01 -0600 Subject: [PATCH 0522/1263] Update for subscription.yaml template in common/clustergroup We added a feature, a while ago, to allow the user to disable the subscription if desired. Adding the *disable: true* key/value pair to the definition of the subscription would skip the generation of the subscription manifest. There are two ways to describe subscriptions in our values-*.yaml file. The first way is to define a subscription to be targeted to one namespace: subscriptions: seldon-dev: name: seldon-operator namespace: manuela-tst-all source: community-operators csv: seldon-operator.v1.12.0 The second way of define a subscription is to target multiple namespaces. This means that the subcription will get applied to each namespace listed: seldon: disabled: true name: seldon-operator namespaces: - manuela-ml-workspace - manuela-tst-all source: community-operators csv: seldon-operator.v1.12.0 The current template implementation for subscription would only allow disabling subscription defined in a single namespace. This commit will allow users to disable subscription definitions targeted to multiple namespaces. --- clustergroup/templates/subscriptions.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clustergroup/templates/subscriptions.yaml b/clustergroup/templates/subscriptions.yaml index 80905a95..3fcd2d1a 100644 --- a/clustergroup/templates/subscriptions.yaml +++ b/clustergroup/templates/subscriptions.yaml @@ -3,6 +3,7 @@ {{- $installPlanValue := .installPlanApproval }} {{- if $subs.namespaces }} +{{- if not $subs.disabled }} {{- range .namespaces }} apiVersion: operators.coreos.com/v1alpha1 kind: Subscription @@ -32,6 +33,7 @@ spec: {{- end }} --- {{- end }} +{{- end }} {{- else if not $subs.disabled }} apiVersion: operators.coreos.com/v1alpha1 kind: Subscription From ac7e1c6483313191b8da5ee33281aedc13dc49b9 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 19 Sep 2022 11:39:03 +0300 Subject: [PATCH 0523/1263] Create an example blank chart (#143) * Create an example blank chart --- examples/blank/Chart.yaml | 6 ++++++ examples/blank/templates/manifest.yaml | 4 ++++ examples/blank/values.yaml | 2 ++ tests/examples-blank-naked.expected.yaml | 6 ++++++ tests/examples-blank-normal.expected.yaml | 6 ++++++ 5 files changed, 24 insertions(+) create mode 100644 examples/blank/Chart.yaml create mode 100644 examples/blank/templates/manifest.yaml create mode 100644 examples/blank/values.yaml create mode 100644 tests/examples-blank-naked.expected.yaml create mode 100644 tests/examples-blank-normal.expected.yaml diff --git a/examples/blank/Chart.yaml b/examples/blank/Chart.yaml new file mode 100644 index 00000000..c552610d --- /dev/null +++ b/examples/blank/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +description: An empty Helm chart +keywords: +- pattern +name: blank +version: 0.0.1 diff --git a/examples/blank/templates/manifest.yaml b/examples/blank/templates/manifest.yaml new file mode 100644 index 00000000..3f160b02 --- /dev/null +++ b/examples/blank/templates/manifest.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: example diff --git a/examples/blank/values.yaml b/examples/blank/values.yaml new file mode 100644 index 00000000..35e4a6f4 --- /dev/null +++ b/examples/blank/values.yaml @@ -0,0 +1,2 @@ +tree: + of: "values" diff --git a/tests/examples-blank-naked.expected.yaml b/tests/examples-blank-naked.expected.yaml new file mode 100644 index 00000000..51a92e5d --- /dev/null +++ b/tests/examples-blank-naked.expected.yaml @@ -0,0 +1,6 @@ +--- +# Source: blank/templates/manifest.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: example diff --git a/tests/examples-blank-normal.expected.yaml b/tests/examples-blank-normal.expected.yaml new file mode 100644 index 00000000..51a92e5d --- /dev/null +++ b/tests/examples-blank-normal.expected.yaml @@ -0,0 +1,6 @@ +--- +# Source: blank/templates/manifest.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: example From 374ea4148e16774627896d8bd4597c9b6368708b Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 20 Sep 2022 16:21:30 +1000 Subject: [PATCH 0524/1263] Avoid race condition before ESO operator is deployed --- clustergroup/templates/cluster-external-secrets.yaml | 3 +++ tests/clustergroup-normal.expected.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/clustergroup/templates/cluster-external-secrets.yaml b/clustergroup/templates/cluster-external-secrets.yaml index df386c35..f44167d2 100644 --- a/clustergroup/templates/cluster-external-secrets.yaml +++ b/clustergroup/templates/cluster-external-secrets.yaml @@ -7,6 +7,9 @@ kind: ExternalSecret metadata: name: {{ . | kebabcase }}-secret namespace: {{ $namespace }} + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/sync-wave: "100" spec: refreshInterval: 15s secretStoreRef: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 1de93905..41e28a92 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -712,6 +712,9 @@ kind: ExternalSecret metadata: name: example-secret namespace: mypattern-example + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/sync-wave: "100" spec: refreshInterval: 15s secretStoreRef: From 9e2b533014ba026fefa186bc31d8849593270fee Mon Sep 17 00:00:00 2001 From: ruromero Date: Wed, 21 Sep 2022 16:51:02 +0200 Subject: [PATCH 0525/1263] Fix external secrets template Signed-off-by: ruromero --- clustergroup/templates/cluster-external-secrets.yaml | 4 ++-- tests/clustergroup-normal.expected.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clustergroup/templates/cluster-external-secrets.yaml b/clustergroup/templates/cluster-external-secrets.yaml index f44167d2..b01e3e3d 100644 --- a/clustergroup/templates/cluster-external-secrets.yaml +++ b/clustergroup/templates/cluster-external-secrets.yaml @@ -28,10 +28,10 @@ spec: {{ "{{ .kubeServer | toString }}" }} config: | { - "bearerToken": {{ "{{ .kubeBearer | toString }}" }}, + "bearerToken": {{ "{{ .kubeBearer | toString | quote }}" }}, "tlsClientConfig": { "insecure": false, - "caData": {{ "{{ .kubeCA | toString }}" }}, + "caData": {{ "{{ .kubeCA | toString | quote }}" }} } } data: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 41e28a92..2af41cbb 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -733,10 +733,10 @@ spec: {{ .kubeServer | toString }} config: | { - "bearerToken": {{ .kubeBearer | toString }}, + "bearerToken": {{ .kubeBearer | toString | quote }}, "tlsClientConfig": { "insecure": false, - "caData": {{ .kubeCA | toString }}, + "caData": {{ .kubeCA | toString | quote }} } } data: From 170d87f75ddb3433e733b70246562a45077b93eb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 23 Sep 2022 11:47:05 +0200 Subject: [PATCH 0526/1263] Explicitely ignore ssh pubkeyacceptedalgorithms config option When a user has an .ssh/config file that leverages recent options, like pubkeyacceptedalgorithms, 'make install' using the wrapper will fail with: Please make sure you have the correct access rights and the repository exists. /root/.ssh/config: line 643: Bad configuration option: pubkeyacceptedalgorithms This is because my host has openssh-8.8p1-1.fc36.1.x86_64 whereas the container has the older openssh-8.0p1-16.el8.x86_64 version which is not aware of the above option. Since pubkeyacceptedalgorithms is a common setting in newer ssh's that allows you to connect to older and deprecated systems, let's explicitely disable it. --- scripts/pattern-util.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index d2be2e97..4e475a64 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -12,6 +12,7 @@ fi podman run -it \ --security-opt label=disable \ -e KUBECONFIG="${KUBECONFIG}" \ + -e GIT_SSH_COMMAND="ssh -o IgnoreUnknown=pubkeyacceptedalgorithms" \ -v ${HOME}:/home/runner \ -v ${HOME}:${HOME} \ -v ${HOME}:/root \ From e9daf17abd66594f53d02e2068fbb604102a48d5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 23 Sep 2022 12:07:54 +0200 Subject: [PATCH 0527/1263] Bind mount the ssh-agent socket if it is set Otherwise we get prompted like follows: make -f common/Makefile operator-deploy make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Enter passphrase for key '/root/.ssh/id_rsa': With this we correctly ssh to the remote git repo to check for repo and branch and it all works. I also tested it with SSH_AUTH_SOCK being unset and the previous behaviour is maintained. --- scripts/pattern-util.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 4e475a64..a51dd711 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -8,10 +8,16 @@ fi # /home/runner is the normal homedir # $HOME is mounted as itself for any files that are referenced with absolute paths # $HOME is mounted to /root because the UID in the container is 0 and that's where SSH looks for credentials +# We bind mount the SSH_AUTH_SOCK socket if it is set, so ssh works without user prompting +SSH_SOCK_MOUNTS="" +if [ -n "$SSH_AUTH_SOCK" ]; then + SSH_SOCK_MOUNTS="-v $(readlink -f $SSH_AUTH_SOCK):/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent" +fi podman run -it \ --security-opt label=disable \ -e KUBECONFIG="${KUBECONFIG}" \ + ${SSH_SOCK_MOUNTS} \ -e GIT_SSH_COMMAND="ssh -o IgnoreUnknown=pubkeyacceptedalgorithms" \ -v ${HOME}:/home/runner \ -v ${HOME}:${HOME} \ From cad0645f807a1883fcb57b3135061c82c0c3a8d8 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Sat, 17 Sep 2022 17:49:00 +1000 Subject: [PATCH 0528/1263] Consistently define a new variable for the non-apps cluster domain --- Makefile | 2 +- .../policies/application-policies.yaml | 3 ++ .../templates/plumbing/applications.yaml | 4 +++ clustergroup/templates/plumbing/argocd.yaml | 1 + tests/acm-normal.expected.yaml | 8 +++-- tests/clustergroup-naked.expected.yaml | 1 + tests/clustergroup-normal.expected.yaml | 32 +++++++++++-------- ...lang-external-secrets-normal.expected.yaml | 2 +- tests/hashicorp-vault-normal.expected.yaml | 2 +- tests/install-normal.expected.yaml | 2 +- 10 files changed, 38 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 3f371774..39fa1c16 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set ma --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" \ --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" \ - --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com \ + --set global.namespace="pattern-namespace" --set global.hubClusterDomain=apps.hub.example.com --set global.localClusterDomain=apps.region.example.com --set global.clusterDomain=region.example.com\ --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" \ --set clusterGroup.insecureUnsealVaultInsideCluster=true PATTERN_OPTS=-f common/examples/values-example.yaml diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 33ea97d2..67452acc 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -61,6 +61,9 @@ spec: value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' + # Requires ACM 2.6 or higher + - name: global.clusterDomain + value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}` }}' - name: clusterGroup.name value: {{ $group.name }} {{- range .helmOverrides }} diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 715c189e..552b569a 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -64,6 +64,8 @@ spec: {{ `{{ values }}` }} {{- end }} parameters: + - name: global.clusterDomain + value: {{ $.Values.global.clusterDomain }} - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain @@ -155,6 +157,8 @@ spec: value: $ARGOCD_APP_NAMESPACE - name: global.pattern value: {{ $.Values.global.pattern }} + - name: global.clusterDomain + value: {{ $.Values.global.clusterDomain }} - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index b8b1e822..832a7e13 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -38,6 +38,7 @@ spec: --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern={{ .Values.global.pattern }} + --set global.clusterDomain={{ .Values.global.clusterDomain }} --set global.hubClusterDomain={{ .Values.global.hubClusterDomain }} --set global.localClusterDomain={{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }} --set clusterGroup.name={{ .Values.clusterGroup.name }} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index caf5daa6..9a90e020 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -537,9 +537,11 @@ spec: - name: global.pattern value: mypattern - name: global.hubClusterDomain - value: hub.example.com + value: apps.hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + - name: global.clusterDomain + value: 'fixme.{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - name: clusterGroup.name value: acm-edge - name: clusterGroup.isHubCluster @@ -615,9 +617,11 @@ spec: - name: global.pattern value: mypattern - name: global.hubClusterDomain - value: hub.example.com + value: apps.hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + - name: global.clusterDomain + value: 'fixme.{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - name: clusterGroup.name value: acm-provision-edge - name: clusterGroup.isHubCluster diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 8a19f66f..4d5c5eee 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -95,6 +95,7 @@ spec: --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern=common + --set global.clusterDomain= --set global.hubClusterDomain= --set global.localClusterDomain= --set clusterGroup.name=example diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 322b0dc7..b0bab215 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -168,13 +168,14 @@ data: files: cluster_example_ca: /path/to/ca.file global: + clusterDomain: region.example.com git: account: hybrid-cloud-patterns dev_revision: main email: someone@somewhere.com hostname: github.com - hubClusterDomain: hub.example.com - localClusterDomain: region.example.com + hubClusterDomain: apps.hub.example.com + localClusterDomain: apps.region.example.com namespace: pattern-namespace options: installPlanApproval: Automatic @@ -536,10 +537,12 @@ spec: value: $ARGOCD_APP_NAMESPACE - name: global.pattern value: mypattern + - name: global.clusterDomain + value: region.example.com - name: global.hubClusterDomain - value: hub.example.com + value: apps.hub.example.com - name: global.localClusterDomain - value: region.example.com + value: apps.region.example.com ignoreDifferences: [ { "group": "internal.open-cluster-management.io", @@ -585,10 +588,12 @@ spec: value: $ARGOCD_APP_NAMESPACE - name: global.pattern value: mypattern + - name: global.clusterDomain + value: region.example.com - name: global.hubClusterDomain - value: hub.example.com + value: apps.hub.example.com - name: global.localClusterDomain - value: region.example.com + value: apps.region.example.com syncPolicy: automated: {} # selfHeal: true @@ -622,7 +627,7 @@ spec: - name: global.pattern value: mypattern - name: global.hubClusterDomain - value: hub.example.com + value: apps.hub.example.com - name: global.localClusterDomain value: apps.beekhof.net - name: global.clusterDomain @@ -680,7 +685,7 @@ spec: - name: global.pattern value: mypattern - name: global.hubClusterDomain - value: hub.example.com + value: apps.hub.example.com - name: global.localClusterDomain value: apps.beekhof.net - name: global.clusterDomain @@ -738,7 +743,7 @@ spec: - name: global.pattern value: mypattern - name: global.hubClusterDomain - value: hub.example.com + value: apps.hub.example.com - name: global.localClusterDomain value: apps.beekhof.net - name: global.clusterDomain @@ -796,7 +801,7 @@ spec: - name: global.pattern value: mypattern - name: global.hubClusterDomain - value: hub.example.com + value: apps.hub.example.com - name: global.localClusterDomain value: apps.beekhof.net - name: global.clusterDomain @@ -864,8 +869,9 @@ spec: --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE --set global.pattern=mypattern - --set global.hubClusterDomain=hub.example.com - --set global.localClusterDomain=region.example.com + --set global.clusterDomain=region.example.com + --set global.hubClusterDomain=apps.hub.example.com + --set global.localClusterDomain=apps.region.example.com --set clusterGroup.name=example --post-renderer ./kustomize"] applicationSet: @@ -947,7 +953,7 @@ spec: applicationMenu: section: OpenShift GitOps imageURL: data:image/png;base64,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 - href: 'https://example-gitops-server-mypattern-example.region.example.com' + href: 'https://example-gitops-server-mypattern-example.apps.region.example.com' location: ApplicationMenu text: 'Example ArgoCD' --- diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 7d9fa628..872b07de 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -5827,7 +5827,7 @@ metadata: spec: provider: vault: - server: https://vault-vault.hub.example.com + server: https://vault-vault.apps.hub.example.com path: secret # Version of KV backend version: v2 diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index d2af3d1b..9d707c16 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -340,7 +340,7 @@ spec: applicationMenu: section: HashiCorp Vault imageURL: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAf0AAAHhCAQAAADO0a/jAAAcYElEQVR42u2dB5hU5fWHzy5NmoBd7LH3WIIFFVS6goJRFBVjwYqKIhgbCFgQO2LBBoKKBQWFYIt/E2NHo4SABERAaui9LOzm/41DCLA7M7ffr7zv++Qxj48c7j3n++3Mztz7XZGh8h9EdMyhIodKGY1AdMoylXvFAFqB6JQD5Ff2lhKageiMJSrzG7mHdiA64z2yiZ1kFQ1BdMJVKu+b0Y2WIDphN9mCbWURTUG03kUq61txJW1BtN4rpRzVZBaNQbTaWSrnFdCe1iBabXupkEoymeYgWutklfEcNKc9iNbaXPLwHQ1CtNLvJC9NaRGilTaVAoykSYjWOVIKchxtQrTO48QDL9EoRKt8STxxiJTSLERrLFWZ9kh/2oVojf3FM3vKOhqGaIXrVJ590IeWIVphH/HFjrKSpiEa70qVZZ90pW2IxttVfFNbFtI4RKNdqHIcgCtoHaLRXiGBqCYzaR6isc6seGMOL5xL+xCN9VwJTLFMooGIRjpJ5TcEzWghopE2k5CMpYmIxjlWQnM6bUQ0ztMlAkbQSESjHCGR8DtaiWiUv5OIGEwzEY1xsETGQWzegWiIpSqvEfIYLUU0wsckUvZg8w5EA1ynshoxvWgrovb2ksjZQVbQWEStXaFyGgM301pErb1ZYqGWLKC5iNq6QGU0Ji6nvYjaernERlWZQYMRtXSGymeMnEOLEbX0HImVYplIkxG1c2K4jTm80IQ2I2pnE0mAr2k0olZ+LYlwKq1G1MpTJSHeotmI2viWJMYxtBtRG4+RBHmRhiNq4YuSKAfIBpqOmLobVBYT5lHajpi6j0ri7C5raTxiqq5VOUyBnrQeMVV7SipsJ8tpPmJqLlcZTIkutB8xNbtIatSU+QwAMRXnq/ylyKWMADEVL5VUqSLTGQJi4k5X2UuZdowBMXHbSeoUywQGgZioE+LfmMMLZzEKxEQ9SzThI4aBmJgfiTY0YhyIidlINGI4A0FMxOGiFUcxEsREPEo043mGghi7z4t27M/mHYgxu0HlTEMeZjSIsfqwaEl9WcNwEGNzjcqYpvRgPIix2UO0pZ4sY0CIsbhM5UtjbmREiLF4o2hNDZnHkBAjd57KluZcwpgQI/cS0Z4q8jODQozUn9PfmMMLZzMqxEg9W4ygSMYzLMTIHK8yZQitGRdiZLYWg/iAgSFG4gdiFCczMsRIPFkM4w2GhhjaN8Q4jpQyBocYyjKVIwN5jtEhhvI5MZJ9ZT3DQwzsepUhQ3mQ8SEG9kExll1lNQNEDORqlR+DuZMRIgbyTjGaurKEISL6donKjuFczxgRfXu9GE91mcsgEX05V+XGAjoySkRfdhQrqCxTGSaiZ6eqzFhCG8aJ6Nk2Yg1FMo6BInpynDkbc3ihFSNF9GQrsYz3GSpiQd8X62jIWBEL2lAs5DUGi5jX18RKjmDzDsQ8lqmMWMpAxouY04FiLb9h8w7EHJaofFhMP0aMWKH9xGp2kVUMGbGcq1Q2LOcOxoxYzjvEeurIYgaNuIWLVS4c4DpGjbiF14kTVJc5DBtxk3Ps2JjDCxcx7kAXfCyUf8rH8lf5lyzTuGbWNTJNvpAP5QeZKxuYXl4vEmeoLJMZuA+XyMtygdTbood7yNUyStZpVTPrFLlfGmxx4+k20kqelJlMskIn27MxhxdaM3KPrpA+eT4C2lNeDPCaGkfNrAvkKinOUbeK+sEym4mWs7U4RZF8ydA9+L7sVLCXh8qPqdfM+sFW7yIq+pynP1Pdwi/t2pjDCy0Ye0EflkqeermtjE61ZtZX1eu6Fy4P/SuFTbYQBxnD4PPa00cvi2VkajWzjvLx6tWGezg3Okac5ARGn8fhPt8I1pZ/plIz63TZzlfl25nwr54gjjKM4edwmtT03c39C7yRjqPmfz3Jd+X3mLJa/85yGG/8chjsEQz9E6+Z9a0AdQ+XUuev0zhMHOZpYl6BE3J+QZafnWVlojWzrpcDA1V+xfEpPy1Os7eUEPVyBn/U4kuJ1sz6ZsC6Jzo94xK19h2nL1EvZ/DdWs5NtGbW5gHrFstCh2fcV5xnp4JvKF3zxxDdrJNjC7Q4amadEfAXiQwvOzvjlR4uq3KA24j7VpfGhGFyYjWzPhKibldnZ3wbsc9eN7aIwEcUJpFPE6uZtVmIuhc6OuFFas3Dr1xL4Dfz1lC9fCOxmhlXyzYh6p7u6ISvJfL/parMIvKbvCFUL/8UQ82X8nxlGAY3H8Y2S6132EQHIr/Je0N9JLcu8pr5HpW6MFTddk7OtwNx35xKbN6xyRdC9LFjDDVFfshzRVqVEHVd/EVvssc7Jx3iDEK/0Y9DdPGjGGoWy5I8R7tfiMouPpDlDKJenu+I/a+uDXCbTZZjY6gpclzeow2zl+w452b7HTGviGbEfqNnBezg2zHUFOmT91iD33G+h4OTbUbMK2Y0sQ9xAU6DvPdBBr2op7jAVl1rZEcu6PHoaCIe7K2lO5bKob57V0XGR14zQ+GN0x8NVLeW/Nu5uR5HxHPzCsH/1RG+O9czhpqZHyhTPXw2sUeAync6N9NXiHc+DnF+A4dg3/628LR9tv9vlB/2dKx/9r2T/FHOPXG5VK1tyMtTxH7j79DHeu7Z4bI88pr5rhIo7+O+6u4kM5yb51NEuxC7s1XzRmer10YvHOHjwRZea2Zoq97Kez/aOz3X3UXGOjfLdWpdQ0HuJ/YbXSXnefhKdHnkNTOPSOnpe9/E16SGp2sPXLxf435i7YUdZAWx3+TQPPvr1JIH826jEaRmhuPlL4GOdZK0y1u3nvRVv3S4N8MVak2DJ24l8ptZIs/IyeWu/K4jV4Z4gGXFNUW9bp8p74Q62rFykWxf4a8lvWWpoxO8lUh7pbbMIfJbuVhel/vkBvV2/Q9ytwyP5NXzfzUvlK7ST8ZE9JpcKp/Lo2rBXyLnyDXSSwY5/ZTdOWo9g2euJuxoiVcTZz9U42nsaIUz1VoGX1zAskELvIAo+6WSTGLhoOFOYmOOILRi6aDhtiLGwRjL4kGDHUuEg9KE5YMG24QIB2cUCwgNdRTxDcORLKGNrpe/ySDpK13kFnlIXs6zS266NbP+LK/L43KbXC995Dl537lbdDMeQXzDMZTYy7vSXuqW68zucqV8o1XNjNOkewV3pm8jLdSPAJceqD6U6IblYE+bUNjrxwW2dWorE7SomXGedM77ZJl9ZIgjW7FsUOsWQjPA2dhvkBs99KeKDEy5Ztb/q/C2na1pkXdXf1scQGyjYE8nb/T8j4qI90+Ir/X43iiOmlmf9LxJ1wHWP2lpTaDdCqEC7nXyFd/fV0NdU6qZ1d9W3/vIQqtndy+RjYodfO5EY4M3+u7SkFRqZvxWqvus2zjQFiNmuJyNOaKku3Mf7vmnRoFdDuKomX0vcWCAyvY+b687cY2SWrLAqegHe1jDNYnXzPh8oLrbWbpnzwK1ViFSrnLqe/xgVJHpidbMWBL4I62eVs7uSqIaNdUc2ru9feAu9Uu0ZtBfI7LsZ+HkZrAxRxy0d+aS3bqBe9QowZpZbw4x0X/xQxu8UFzggZK2+LcQPaqc47uQOGpmDXPV2mOWTW68WqMQCy2ciP6gUD36R2I1s1YNUbezZZNrQUTj42sHoh/uSS0fJVYz4+JQdX9v1dy+Jp5xcoID0e8SqkNDE6uZcWKouidbNbcTiGe8vGN99G8J1Z/hMdR8LeexTglV9zSLpvYO0YybY62P/kOh+vNtDDU/yXmsK0LV7WDR1I4hmvEzxPLovxyqOwtiqPljnqOtGaJuV2tmNoRYJsEBlm/e8UOI3uwbQ82asjbP0TYIUXmwNXdZHkAsk+EJy1/3dwvcmd4x1GyT91h7Ba5bLPMtmdcTRDIpds/7OuTudeBFMi2Ga8sHFrhh1/Vva9aq9QiJ0cfq6H8TsCsXx1CzjizOe6xlcrTjb/f7EMck2V6WWR3+toF6siDymiL3FDzW9wLVPdSSDTqXedqRECLkFqujP0Gq+O7I0Bhq1peVHo721ADzG81VGBCMWtZ8SFSxA332o0cMNavK556Odbbs6rNyN0umNJ+NOdKgk+Wf81/roxedY6gp8oLnY/3K133qZ1izG38nYpgGVQvsH2P+t8VdPfWhkvSVsohrZrr7gq+j/avs6LFyB1ltyYSmh7pvEUJwnvUX9Q6RGgV/G/9L5DUzVT8PEITCn/VXtmo7zvOIYFoUB3xAlEnOkWtyfjy3rfTx9DGcn5qZr/PuCVQ1855ikOyV56qDDvKTVR/FsjFHijRz4CbezCMs+0mjLZ5uUyynSP9QD7IoXzNzyW4bGVjge/xCrpPX5eKtdqIvkt9JL5lo2VSaEb90+cSJ8GdcLv+Qj2SovClfy6KIaw5TffwxwmskS9Xr+2cyXAbLGPk+sqPVyU+IXto0dib6qJONiV76jGAhYsKOIHY6cDRLERP2aGKnB4NZjJigg4mcLuxv+eYdqNelVvsTOX14nCWJCfk4cdOJnWWN1cttvXwi3eUcaSj7ykHSSNpLH/m7hjWzTpX+cpE0kcNkb2kgbaSzjAx4kZB+rlFrDbSit7Wx/0U6SZ0Kz7m+9Ai4b0EcNbM/TgbmeBRXNTlLvrNgGr2Jmm5sF/IqND1dKV0K3BO3vTzi+ead+GpmfVv2y1u3SM5VP3RMnscitc5AO2628ALeIzydeZu8D8SMv2b2+r3unuruJH8zeCI3EzMdqSmzrAr+t1tdCZ+PQ2ReajUzlsiZnutWlWGGTmRWqKcOQIxcbtUde/V9nXtDWZdKzaxX+6pbTb40ciaXEzFdqSJTLQl+iRzv++w7pVAz6zO+6+6ifgyZNpOpAfY2hMSw5bHNAwKd/ZeJ18y4ULYNUPdS42bye+KlM8Uy3oLgr1KvikE4NeGaWYPtSVsp75P89HM8G3PoTlOnrxf7LNGaGRdL9YB1Oxo1k6ZES38+Nz76jQOf+y2J1sw4LHDdegbdefE5sTKBUwwP/tKtts3yw0EJ1sx6YYhJfWrMTE4hVmbwttHRHx3q3OcmVjNr/RB1exkykbeJlCn8NtBlqLr4TKhzH5tYzewNrGE+/DLjISplaj2BMQwyOPp3hzrzUYnVzDg3VN3WRsxjEHEyib1kvbHR7xzqzAfHUHNQnq+8wnCCEbdL70WczOJRY6N/Z6jzHhNDzRF5bv4NQ0sDpvEoUTKNXY19stsToc57Qgw1c1/Rty5UXf2v6Fvt+xnCoAF3Gxr94SHOuSjHbjjDQ3VyWp6j3T5E3Tss/9wFUqKeLDEy+rNDnHOjGGrWz3u0rSP/5UQfl6g1BEZyk6Gv+0cFPuMXY6jZKaavImtG+KCveLyJCJlKDfVqZ2L0ewQ831p5dtbpEbiL7+Q91pnql4xgtNP+3VcNImQulxkZ/XkBd4N5OIaaBxe80v6CgLP5SvMpXEZ8TMbUzTtuC3CuRxcI6W2BOvhWwWOdEuj+AN1f89mYw3jaGRn9JbKHz/OsLeMir+nlbv2MXX3XrS2TNZ9AO6JjOkUFI6Gn3/m6D76afBx5zcw1kQs8XvF2mq+6xfKu5t0fF/gTDNCIZoZ+zv+6VPIc/JGR1xSpKz94PtaFcqCPiTykfe+bERs7+MjQ8P/Z0wMf9vH1VJs/e3yIxEHyL1/HuthjXKrLq9r3/SMiYwvHGXs9/09ycoFfZy70feFSoZrZqkt9H+sGuUO2KVD5SCMevnUckbGH4QbfxDtGjs5xVi0CPx4zd02RM+T7wMc6Uzrl/GT8ABlmxD4Kw4mLTRxh0D5wFfmj+v34dNlr46vqTnKS9JOfI61ZS/aTs+W5CC6CWipvSEc5eONFsFVkN2kovSN6hm/8bvD4YDIwhhcM37Hvf8FaE0PNeB6BvVYWGbdf0gtExTZ+IyWWhB/js0StE7COR1jaWMBHiImN7CKrWNyYx6BPKALt6cHyxhjumATtqSuLWeCY86KkukTEXm5kiWMObyQeNlPdwOe6YxLOCfzYUDCEP7DME/te3yT/QDRsp5JMIeyxXs1nolN83NEIxtKW2Oe5hr8o1DX8ptqWWLhAUZ7HR7pgXHfumetYNuZwhZYOBz+u+/VNtiWRcIcPHQ1+XLv0mOyHxMElTnQy+HHtzWe2JxIHt3jTueDHtyOvyb5JFFzjMCl1LPpx7cNvsqVqHYBzPOdU8ON7+o7JPkcMXGQvpzbviOuZeyZbotYAOMlDDkU/riftmuxDRMBVdnbm2vXZIbpU39KerFTzB2e5y5Hoh9tgepqVPbmL5e8ydWSRE9F/IlSXvrSwI4vU7MFpbnAi+neE6tEICztyA0vfdbaR6Q5Ev3OoHg22rh/TCz4iDBygowPRvztUh0Zb14+OLHsQqSyTrY/+M6E6ZNtNzpPVzAEUZ1kf/dGh+jPXsm6cxZKHLEVGPPo5jEtDvM4dZN39i2zMAZtobv3rfuPAvbnFsk40Z7nD5rxvefQfD9yZz6zqw/ssddiS4y2PftDnydl2x/7xLHXYmtctD/+AQF2x60q+11nmUJ6DLd+8oyTAK55dd+2VqhkDVMCzlr/uz5H6vvrRUNZZdf7PssShYna3bKmX91vZwXM3DpF5Vp37OjVfgBz0s/5LvmlyhKdOtJHllp15P5Y35GZ7WWF9+FdKF6lWoAuPSJllZ71CnRVAHu5w4ibeX6RTjvvV60sPWcZty+Ae28q/Hdm5Z718It2lnTSUfeVAaSTtpY/83dJz/beaK0ABOjsSfZfszLKGwmwjMwmLVc5kYw7wxsXExSovZkmDNyrJJAJjjZN8PF0YnKc1kbHG1ixn8MNYQmOFY1nK4I8mxMYKm7CUwS/vERzjfY9lDP5pQHSMtwHLGIIwjPAY7TCWMATjYNlAgIx1AxtzQHCeIULG+gzLF4Kzp6wlREa6Vs0OIAQPECMjfYClC+HYwbrdalxwuY+NyABycDtRMs7bWbYQntqygDAZ5QI1M4AIuJY4GeW1LFmIhmoyg0AZ44wCm44C+OBCImWMF7JcIToqyURCZYQT2ZgDouVMYmWEZ7JUIWq+IVja+w3LFKLnNKKlvaexTCEO/kS4tPZPLFGIh2OJl9YeyxKFuHiVgGnrqyxPiI8D2bxD2405DmR5Qpw8Rcy09CmWJsTLHmzeoeXGHLuzNCFu7idq2nk/yxLiZ3tZRti0cpmaCUAC/JG4aeUfWZKQDLVkPoHTxvlqHgAJcQ2R08ZrWI6QHFVlOqHTwulqFgAJcgGx08ILWIqQLMUygeCl7gQ1B4CEaUX0UrcVyxDS4CvCl6pfsQQhHRoTv1RtzBKEtBhFAFNzFMsP0uNoKSOEqVimeg+QIi8Tw1R8maUH6XKArCeIibte9R0gZZ4kion7JMsO0mc3WUMYE3WN6jmABtxHHBP1PpYc6MF2spRAJuZS1W8ATehOJBOzO8sN9KGmzCOUiThP9RpAI64ilol4FUsN9KKq/EwwY/dnNuYA/TifaMbu+Swz0I9iGU84Y3U8G3OAnrQknrHakiUGuvIFAY3NL1heoC+nENHYPIXlBTrzLiGNxXdZWqA3v2Xzjlg25vgtSwt0ZwhRjdwhLCvQn/3YvCPyjTn2Y1mBCTxBXCP1CZYUmEF9WU1gI3O16ieAIdxDZCPzHpYTmEM9WUJoI3GJ6iWAQXQjtpHYjaUEZlFD5hLc0M5VfQQwjE5EN7SdWEZgHlVkKuEN5VTVQwADOY/4hvI8lhCYSZGMI8CBHaf6B2AozYlwYJuzfMBkPiPEgfyMpQNmcxIxDuRJLB0wnZEE2bcjWTZgPkeyeYfvjTmOZNmADbxEnH35EksG7GBfKSHQni1R/QKwhP5E2rP9WS5gD7vKKkLtyVWyC8sFbKI3sfZkb5YK2EVdWUywC7pY9QnAMroS7YJ2ZZmAfVSXOYQ7r3NUjwAs5ArindcrWCJgJ5VlCgHP6RTVHwBLOZeI5/RclgfYS5F8T8gr9Hs25gC7aUrMK7QpSwNs51OCXs5PWRZgPycS9XKeyLIAF3ibsG/h2ywJcIPDpZTAb7JU9QPAEQYR+U0OYjmAO+zD5h2bNubYh+UALvEYsf/Vx1gK4BY7y0qCr3qwM0sBXKMX0Vc9AHCOOrLI8eAvUj0AcJCbHI/+TSwBcJPqMsvh4M9iYw5wl8scjv5ljB/cpbJMdjT4k9mYA9zmHEejfw6jB7cpkr87GPzv2JgDoImD0W/C2AFEPnEs+J8wcoAMxzsW/eMZOUCWtxwK/luMG+C/HOrM5h2l6lwBYBMvOhL9Fxk1wObsLescCP46dZ4AsAWPOBD9RxgzwNbsJCssD/4KdY4AUI6elke/JyMGqIhtZaHFwV+ozg8AKqSLxdHvwngBcrGNzLQ0+DPVuQFATi61NPqXMlqAfFSSSRYGf5I6LwDIS1sLo9+WsQIUoki+tSz437IxB4AXmloW/aaMFMAbH1oU/A8ZJ4BXGlgU/QaME8A7b1oS/DcZJYAfDpYNFgR/gzoPAPDF8xZE/3nGCOCXPWWt4cFfq84BAHzzsOHRf5gRAgRhR1lucPCXq+MHgEDcZXD072J8AEGpLQsMDf4CdewAEJgbDI3+DYwOIAzV5BcDg/+LOm4ACMUlBkb/EsYGEJZK8qNhwZ/IxhwAUXC2YdE/m5EBRMM3BgX/G8YFEBWnGxT90xkXQHR8YEjwP2BUAFFyrCHRP5ZRAUTLGwYE/w3GBBA1B2m/eccGdYwAEDnPah79ZxkRQBzsofXmHWvV8QFALDyocfQfZDwAcbGDLNM0+MvUsQFAbNypafTvZDQAcVJL5msY/PnquAAgVq7XMPrXMxaAuKkq0zUL/nR1TAAQOxdrFv2LGQlAEhTLBI2CP0EdDwAkQhuNot+GcQAkx1eaBP8rRgGQJKdqEv1TGQVAsrynQfDfYwwASXOMlKUc/DJ1DACQOK+lHP3XGAFAGhwg61MM/nr19wNAKgxMMfoDaT9AWuwma1IK/hr1dwNAajyQUvQfoPUAabKdLE0h+EvV3wsAqXJ7CtG/nbYDpE1NmZdw8OepvxMAUue6hKN/HS0H0IGqMi3B4E9jYw4AXbgowehfRLsBdKFYxicU/PFszAGgE2cmFP0zaTWAXnyRQPC/oM0AutEogeg3os0A+jEm5uCPocUAOnJUrJt3lKn6AKAlr8YY/VdpL4Cu7B/b5h3rVW0A0JanY4r+07QWQGfqy+oYgr9a1QUArekbQ/T70lYA3aknSyIO/hJVEwC057aIo38bLQUwgRoyN8Lgz1X1AMAIrokw+tfQTgBTqCJTIwr+VFULAIyhQ0TR70ArAUyiWMZFEPxxbMwBYBpnRBD9M2gjgHl8FjL4n9FCABM5OWT0T6aFAGYyOkTwR9M+AFM5MvDmHWXqzwKAsbwSMPqv0DoAk9lXSgIEv0T9OQAwmicDRP9J2gZgOrvKKp/BX6X+DAAYz30+o38fLQOwgbqy2EfwF6v/HgCs4FYf0b+VdgHYQg2Z4zH4c9iYA8AmrvYY/atpFYBNVJGfPAT/JzbmALCN8z1E/3zaBGAbRfJDgeD/oP4bALCOlgWi35IWAdjJp3mC/yntAbCVhnmi35D2ANjLuzmC/y6tAbCZw6W0guCXqn8PAFYztILoD6UtALazT7nNO0rUvwMA6xmwVfQH0BIAF9hFVm4W/JWyMy0BcIN7N4v+vbQDwBXqyKKNwV+k/j8AOEO3jdHvRisAXKK6zFbBn63+CQBOcaWK/pW0AcA1Ksto9T8AcA525HGY/wcAxiEwaH/ifAAAAABJRU5ErkJggg== - href: 'https://vault-vault.region.example.com' + href: 'https://vault-vault.apps.region.example.com' location: ApplicationMenu text: 'Vault' --- diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yaml index d115d8ae..faaef3cc 100644 --- a/tests/install-normal.expected.yaml +++ b/tests/install-normal.expected.yaml @@ -40,7 +40,7 @@ spec: - name: global.pattern value: install - name: global.hubClusterDomain - value: hub.example.com + value: apps.hub.example.com syncPolicy: automated: {} --- From 9e78be12e281687586ab267897fbe26ae78d334b Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 28 Sep 2022 21:08:43 +1000 Subject: [PATCH 0529/1263] kubeserver is now calculated from the managed cluster group for remote argo clusters --- .../templates/plumbing/cluster-external-secrets.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/clustergroup/templates/plumbing/cluster-external-secrets.yaml b/clustergroup/templates/plumbing/cluster-external-secrets.yaml index 4f67da56..52f5ca51 100644 --- a/clustergroup/templates/plumbing/cluster-external-secrets.yaml +++ b/clustergroup/templates/plumbing/cluster-external-secrets.yaml @@ -32,10 +32,6 @@ spec: } } data: - - secretKey: kubeServer - remoteRef: - key: {{ $.Values.secretsBase.key }}/cluster_{{ .Values.clusterGroup.targetCluster }} - property: server - secretKey: kubeBearer remoteRef: key: {{ $.Values.secretsBase.key }}/cluster_{{ .Values.clusterGroup.targetCluster }} @@ -44,4 +40,4 @@ spec: remoteRef: key: {{ $.Values.secretsBase.key }}/cluster_{{ .Values.clusterGroup.targetCluster }}_ca property: b64content -{{- end }} \ No newline at end of file +{{- end }} From dfc22199229a9c710257a860e6424a807784dc3d Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 28 Sep 2022 21:10:52 +1000 Subject: [PATCH 0530/1263] Ensure app projects land in the namespaced argo instance --- clustergroup/templates/plumbing/projects.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clustergroup/templates/plumbing/projects.yaml b/clustergroup/templates/plumbing/projects.yaml index 6f55a392..ab67508a 100644 --- a/clustergroup/templates/plumbing/projects.yaml +++ b/clustergroup/templates/plumbing/projects.yaml @@ -1,4 +1,5 @@ {{- if not (eq .Values.enabled "core") }} +{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} {{- range .Values.clusterGroup.projects }} apiVersion: argoproj.io/v1alpha1 kind: AppProject @@ -6,6 +7,8 @@ metadata: name: {{ . }} {{- if (eq $.Values.enabled "plumbing") }} namespace: openshift-gitops +{{- else }} + namespace: {{ $namespace }} {{- end }} spec: description: "Pattern {{ . }}" From b0d1cb7e3974f8c9a95d23833aa946bd7bab617e Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 28 Sep 2022 21:23:53 +1000 Subject: [PATCH 0531/1263] Update tests --- tests/acm-normal.expected.yaml | 6 ++++-- tests/clustergroup-normal.expected.yaml | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 9a90e020..42b9452b 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -540,8 +540,9 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + # Requires ACM 2.6 or higher - name: global.clusterDomain - value: 'fixme.{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' - name: clusterGroup.name value: acm-edge - name: clusterGroup.isHubCluster @@ -620,8 +621,9 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + # Requires ACM 2.6 or higher - name: global.clusterDomain - value: 'fixme.{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' - name: clusterGroup.name value: acm-provision-edge - name: clusterGroup.isHubCluster diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index b0bab215..c01d4e8c 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -469,6 +469,7 @@ apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: datacenter + namespace: mypattern-example spec: description: "Pattern datacenter" destinations: From 161730dcd30f00a4914adb375613e8ca31c78a0e Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 30 Sep 2022 17:52:56 +1000 Subject: [PATCH 0532/1263] Settle on 'hosted argo site' terminology --- .../policies/application-policies.yaml | 2 +- .../{remote-seeds.yaml => hosted-sites.yaml} | 4 +-- clustergroup/test.yaml | 2 +- examples/values-example.yaml | 2 +- tests/clustergroup-normal.expected.yaml | 32 +++++++++---------- 5 files changed, 21 insertions(+), 21 deletions(-) rename clustergroup/templates/plumbing/{remote-seeds.yaml => hosted-sites.yaml} (98%) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 67452acc..2f9e4f89 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -1,7 +1,7 @@ # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} -{{- if not .argoClusterSeeds }} +{{- if not .hostedArgoSites }} apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: diff --git a/clustergroup/templates/plumbing/remote-seeds.yaml b/clustergroup/templates/plumbing/hosted-sites.yaml similarity index 98% rename from clustergroup/templates/plumbing/remote-seeds.yaml rename to clustergroup/templates/plumbing/hosted-sites.yaml index c9eb8846..cdb8c153 100644 --- a/clustergroup/templates/plumbing/remote-seeds.yaml +++ b/clustergroup/templates/plumbing/hosted-sites.yaml @@ -1,7 +1,7 @@ {{- if (eq .Values.enabled "all") }} {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} -{{- if .argoClusterSeeds }} +{{- if .hostedArgoSites }} apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -23,7 +23,7 @@ spec: status: {} --- {{- end }} -{{- range .argoClusterSeeds }} +{{- range .hostedArgoSites }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: diff --git a/clustergroup/test.yaml b/clustergroup/test.yaml index aac3bfd9..5db2e4a6 100644 --- a/clustergroup/test.yaml +++ b/clustergroup/test.yaml @@ -96,7 +96,7 @@ clusterGroup: managedClusterGroups: region-one: name: region-one - argoClusterSeeds: + hostedArgoSites: - perth - sydney helmOverrides: diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 6023f230..bd7d7721 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -94,7 +94,7 @@ clusterGroup: - name: argo-edge # Requires a cluster_{name} entry in your secret store containing # ??what does it contain?? - argoClusterSeeds: + hostedArgoSites: - name: perth domain: beekhof.net - name: sydney diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index c01d4e8c..c027af71 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -134,14 +134,14 @@ data: value: "false" name: acm-provision-edge targetRevision: main - - argoClusterSeeds: + - helmOverrides: + - name: clusterGroup.isHubCluster + value: "false" + hostedArgoSites: - domain: beekhof.net name: perth - domain: beekhof.net name: sydney - helmOverrides: - - name: clusterGroup.isHubCluster - value: "false" name: argo-edge name: example namespaces: @@ -464,14 +464,14 @@ spec: # Source: pattern-clustergroup/templates/core/subscriptions.yaml --- --- -# Source: pattern-clustergroup/templates/plumbing/projects.yaml +# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: - name: datacenter - namespace: mypattern-example + name: argo-edge + namespace: openshift-gitops spec: - description: "Pattern datacenter" + description: "Cluster Group argo-edge" destinations: - namespace: '*' server: '*' @@ -485,14 +485,14 @@ spec: - '*' status: {} --- -# Source: pattern-clustergroup/templates/plumbing/remote-seeds.yaml +# Source: pattern-clustergroup/templates/plumbing/projects.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: - name: argo-edge - namespace: openshift-gitops + name: datacenter + namespace: mypattern-example spec: - description: "Cluster Group argo-edge" + description: "Pattern datacenter" destinations: - namespace: '*' server: '*' @@ -599,7 +599,7 @@ spec: automated: {} # selfHeal: true --- -# Source: pattern-clustergroup/templates/plumbing/remote-seeds.yaml +# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -657,7 +657,7 @@ spec: jsonPointers: - /status --- -# Source: pattern-clustergroup/templates/plumbing/remote-seeds.yaml +# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -715,7 +715,7 @@ spec: jsonPointers: - /status --- -# Source: pattern-clustergroup/templates/plumbing/remote-seeds.yaml +# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -773,7 +773,7 @@ spec: jsonPointers: - /status --- -# Source: pattern-clustergroup/templates/plumbing/remote-seeds.yaml +# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: From 259b2417189607b3fbf0943b254007c8f124eb64 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 30 Sep 2022 21:07:26 +1000 Subject: [PATCH 0533/1263] Avoid magic naming schemes and require explicit vault keys --- .../plumbing/cluster-external-secrets.yaml | 4 +- .../templates/plumbing/hosted-sites.yaml | 16 ++++-- examples/values-example.yaml | 8 ++- tests/clustergroup-normal.expected.yaml | 56 +++++++++++++------ 4 files changed, 58 insertions(+), 26 deletions(-) diff --git a/clustergroup/templates/plumbing/cluster-external-secrets.yaml b/clustergroup/templates/plumbing/cluster-external-secrets.yaml index 52f5ca51..8e0bbfc5 100644 --- a/clustergroup/templates/plumbing/cluster-external-secrets.yaml +++ b/clustergroup/templates/plumbing/cluster-external-secrets.yaml @@ -34,10 +34,10 @@ spec: data: - secretKey: kubeBearer remoteRef: - key: {{ $.Values.secretsBase.key }}/cluster_{{ .Values.clusterGroup.targetCluster }} + key: {{ $.Values.clusterGroup.hostedSite.bearer_secret_key }} property: bearerToken - secretKey: kubeCA remoteRef: - key: {{ $.Values.secretsBase.key }}/cluster_{{ .Values.clusterGroup.targetCluster }}_ca + key: {{ $.Values.clusterGroup.hostedSite.ca_secret_key }} property: b64content {{- end }} diff --git a/clustergroup/templates/plumbing/hosted-sites.yaml b/clustergroup/templates/plumbing/hosted-sites.yaml index cdb8c153..b9434a90 100644 --- a/clustergroup/templates/plumbing/hosted-sites.yaml +++ b/clustergroup/templates/plumbing/hosted-sites.yaml @@ -60,12 +60,16 @@ spec: value: apps.{{ .domain }} - name: global.clusterDomain value: {{ .domain }} - - name: clusterGroup.targetCluster - value: {{ .name }} - name: enabled value: core - name: clusterGroup.name value: {{ $group.name }} + - name: clusterGroup.targetCluster + value: {{ .name }} + - name: clusterGroup.hostedSite.bearer_secret_key + value: {{ .bearer_secret_key }} + - name: clusterGroup.hostedSite.ca_secret_key + value: {{ .ca_secret_key }} {{- range $group.helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} @@ -131,10 +135,14 @@ spec: value: {{ .domain }} - name: enabled value: plumbing - - name: clusterGroup.targetCluster - value: {{ .name }} - name: clusterGroup.name value: {{ $group.name }} + - name: clusterGroup.targetCluster + value: {{ .name }} + - name: clusterGroup.hostedSite.bearer_secret_key + value: {{ .bearer_secret_key }} + - name: clusterGroup.hostedSite.ca_secret_key + value: {{ .ca_secret_key }} {{- range $group.helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} diff --git a/examples/values-example.yaml b/examples/values-example.yaml index bd7d7721..7618a21d 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -96,9 +96,13 @@ clusterGroup: # ??what does it contain?? hostedArgoSites: - name: perth - domain: beekhof.net + domain: perth1.beekhof.net + bearer_secret_key: secret/data/hub/cluster_perth + ca_secret_key: secret/data/hub/cluster_perth_ca - name: sydney - domain: beekhof.net + domain: syd.beekhof.net + bearer_secret_key: secret/data/hub/cluster_sydney + ca_secret_key: secret/data/hub/cluster_syndey_ca helmOverrides: - name: clusterGroup.isHubCluster value: "false" diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index c027af71..d23bb757 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -138,9 +138,13 @@ data: - name: clusterGroup.isHubCluster value: "false" hostedArgoSites: - - domain: beekhof.net + - bearer_secret_key: secret/data/hub/cluster_perth + ca_secret_key: secret/data/hub/cluster_perth_ca + domain: perth1.beekhof.net name: perth - - domain: beekhof.net + - bearer_secret_key: secret/data/hub/cluster_sydney + ca_secret_key: secret/data/hub/cluster_syndey_ca + domain: syd.beekhof.net name: sydney name: argo-edge name: example @@ -630,15 +634,19 @@ spec: - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain - value: apps.beekhof.net + value: apps.perth1.beekhof.net - name: global.clusterDomain - value: beekhof.net - - name: clusterGroup.targetCluster - value: perth + value: perth1.beekhof.net - name: enabled value: core - name: clusterGroup.name value: argo-edge + - name: clusterGroup.targetCluster + value: perth + - name: clusterGroup.hostedSite.bearer_secret_key + value: secret/data/hub/cluster_perth + - name: clusterGroup.hostedSite.ca_secret_key + value: secret/data/hub/cluster_perth_ca - name: clusterGroup.isHubCluster value: "false" destination: @@ -688,15 +696,19 @@ spec: - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain - value: apps.beekhof.net + value: apps.perth1.beekhof.net - name: global.clusterDomain - value: beekhof.net + value: perth1.beekhof.net - name: enabled value: plumbing - - name: clusterGroup.targetCluster - value: perth - name: clusterGroup.name value: argo-edge + - name: clusterGroup.targetCluster + value: perth + - name: clusterGroup.hostedSite.bearer_secret_key + value: secret/data/hub/cluster_perth + - name: clusterGroup.hostedSite.ca_secret_key + value: secret/data/hub/cluster_perth_ca - name: clusterGroup.isHubCluster value: "false" destination: @@ -746,15 +758,19 @@ spec: - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain - value: apps.beekhof.net + value: apps.syd.beekhof.net - name: global.clusterDomain - value: beekhof.net - - name: clusterGroup.targetCluster - value: sydney + value: syd.beekhof.net - name: enabled value: core - name: clusterGroup.name value: argo-edge + - name: clusterGroup.targetCluster + value: sydney + - name: clusterGroup.hostedSite.bearer_secret_key + value: secret/data/hub/cluster_sydney + - name: clusterGroup.hostedSite.ca_secret_key + value: secret/data/hub/cluster_syndey_ca - name: clusterGroup.isHubCluster value: "false" destination: @@ -804,15 +820,19 @@ spec: - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain - value: apps.beekhof.net + value: apps.syd.beekhof.net - name: global.clusterDomain - value: beekhof.net + value: syd.beekhof.net - name: enabled value: plumbing - - name: clusterGroup.targetCluster - value: sydney - name: clusterGroup.name value: argo-edge + - name: clusterGroup.targetCluster + value: sydney + - name: clusterGroup.hostedSite.bearer_secret_key + value: secret/data/hub/cluster_sydney + - name: clusterGroup.hostedSite.ca_secret_key + value: secret/data/hub/cluster_syndey_ca - name: clusterGroup.isHubCluster value: "false" destination: From d42169fe775271c161678ca29a080ca4b0a1dbd6 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 30 Sep 2022 21:11:31 +1000 Subject: [PATCH 0534/1263] Drop outdated comment --- examples/values-example.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 7618a21d..d53ba72c 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -92,8 +92,6 @@ clusterGroup: - name: clusterGroup value: region - name: argo-edge - # Requires a cluster_{name} entry in your secret store containing - # ??what does it contain?? hostedArgoSites: - name: perth domain: perth1.beekhof.net From 89590a88cb39f1b2699c9229ceb3f2d8c255f12b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 2 Oct 2022 15:35:32 +0200 Subject: [PATCH 0535/1263] Drop readlink -f usage And replace it with an echo + pwd combo that works both on Linux and on Mac OSX. Note that at this point the Mac OSX support is still not working with ssh auth due to podman not being able to bind mount host dirs into the container on the podman machine vm (or at least not /private/tmp) Tested on both Fedora and Mac OSX --- scripts/pattern-util.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index a51dd711..5d2c9bae 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -4,6 +4,12 @@ if [ -z "$PATTERN_UTILITY_CONTAINER" ]; then PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/hybridcloudpatterns-utility-ee" fi +# This is one of the most concise ways to get a readlink -f command work without going too complicated +# Across Linux and MacOSX +function real_path() { + echo $(cd $(dirname $1) ; pwd -P) +} + # Copy Kubeconfig from current environment. The utilities will pick up ~/.kube/config if set so it's not mandatory # /home/runner is the normal homedir # $HOME is mounted as itself for any files that are referenced with absolute paths @@ -11,7 +17,7 @@ fi # We bind mount the SSH_AUTH_SOCK socket if it is set, so ssh works without user prompting SSH_SOCK_MOUNTS="" if [ -n "$SSH_AUTH_SOCK" ]; then - SSH_SOCK_MOUNTS="-v $(readlink -f $SSH_AUTH_SOCK):/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent" + SSH_SOCK_MOUNTS="-v $(real_path $SSH_AUTH_SOCK):/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent" fi podman run -it \ From 6c08362f16630a25790925bb664d305afa2cbe07 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 2 Oct 2022 19:37:14 +0200 Subject: [PATCH 0536/1263] Fix CI New ansible linter ran by superlinter is erroring out. Let's disable it there and only let it run on the ansible-lint job. That way we do not lose coverage in any way and reduce the amount of useless work. --- .ansible-lint | 4 ++++ .github/workflows/ansible-lint.yml | 4 ++-- .github/workflows/superlinter.yml | 2 +- ansible/roles/vault_utils/tasks/push_secrets.yaml | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.ansible-lint b/.ansible-lint index 138ae765..f8066ff0 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -1,3 +1,7 @@ # Vim filetype=yaml --- offline: false +skip_list: + - name[template] # Allow Jinja templating inside task and play names + - template-instead-of-copy # Templated files should use template instead of copy + - yaml[line-length] # too long lines diff --git a/.github/workflows/ansible-lint.yml b/.github/workflows/ansible-lint.yml index f0943b53..ae3e9caf 100644 --- a/.github/workflows/ansible-lint.yml +++ b/.github/workflows/ansible-lint.yml @@ -11,8 +11,8 @@ jobs: - uses: actions/checkout@v2 - name: Lint Ansible Playbook - # Using the latest as of today (2022-06-23) v6.2.1 - uses: ansible/ansible-lint-action@v6.2.1 + # Using the latest as of today (2022-09-02) v6.6.1 + uses: ansible/ansible-lint-action@v6.6.1 # Let's point it to the path with: path: "ansible/" diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index 0141598c..a3e22028 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -27,11 +27,11 @@ jobs: DEFAULT_BRANCH: main GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # These are the validation we disable atm + VALIDATE_ANSIBLE: false VALIDATE_BASH: false VALIDATE_JSCPD: false VALIDATE_KUBERNETES_KUBEVAL: false VALIDATE_YAML: false - # VALIDATE_ANSIBLE: false # VALIDATE_DOCKERFILE_HADOLINT: false # VALIDATE_MARKDOWN: false # VALIDATE_NATURAL_LANGUAGE: false diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 3569d24a..784da4d2 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -65,12 +65,12 @@ loop: "{{ file_secrets | dict2items }}" -- name: debug file_stat +- name: Debug file_stat ansible.builtin.debug: var: file_stat when: debug | default(False) | bool -- name: debug file_values +- name: Debug file_values ansible.builtin.debug: var: file_values when: debug | default(False) | bool From 1a1aedb0ce5e10e3dab7c60344f8b446d9b2bb29 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 10:37:55 +1100 Subject: [PATCH 0537/1263] Allow for a sensible default when constructing the argo cluster secret --- .../plumbing/cluster-external-secrets.yaml | 4 +-- .../templates/plumbing/hosted-sites.yaml | 18 ++++++------ examples/values-example.yaml | 6 ++-- tests/clustergroup-normal.expected.yaml | 28 +++++++++---------- 4 files changed, 27 insertions(+), 29 deletions(-) diff --git a/clustergroup/templates/plumbing/cluster-external-secrets.yaml b/clustergroup/templates/plumbing/cluster-external-secrets.yaml index 8e0bbfc5..ee2fbb79 100644 --- a/clustergroup/templates/plumbing/cluster-external-secrets.yaml +++ b/clustergroup/templates/plumbing/cluster-external-secrets.yaml @@ -34,10 +34,10 @@ spec: data: - secretKey: kubeBearer remoteRef: - key: {{ $.Values.clusterGroup.hostedSite.bearer_secret_key }} + key: {{ $.Values.clusterGroup.hostedSite.bearerKeyPath }} property: bearerToken - secretKey: kubeCA remoteRef: - key: {{ $.Values.clusterGroup.hostedSite.ca_secret_key }} + key: {{ $.Values.clusterGroup.hostedSite.caKeyPath }} property: b64content {{- end }} diff --git a/clustergroup/templates/plumbing/hosted-sites.yaml b/clustergroup/templates/plumbing/hosted-sites.yaml index b9434a90..1f11dbe4 100644 --- a/clustergroup/templates/plumbing/hosted-sites.yaml +++ b/clustergroup/templates/plumbing/hosted-sites.yaml @@ -24,6 +24,8 @@ status: {} --- {{- end }} {{- range .hostedArgoSites }} +{{ $bearerDefault := print "secret/data/hub/cluster_" .name }} +{{ $caDefault := print "secret/data/hub/cluster_" .name "_ca" }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -66,10 +68,10 @@ spec: value: {{ $group.name }} - name: clusterGroup.targetCluster value: {{ .name }} - - name: clusterGroup.hostedSite.bearer_secret_key - value: {{ .bearer_secret_key }} - - name: clusterGroup.hostedSite.ca_secret_key - value: {{ .ca_secret_key }} + - name: clusterGroup.hostedSite.bearerKeyPath + value: {{ default $bearerDefault .bearerKeyPath }} + - name: clusterGroup.hostedSite.caKeyPath + value: {{ default $caDefault .caKeyPath }} {{- range $group.helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} @@ -139,10 +141,10 @@ spec: value: {{ $group.name }} - name: clusterGroup.targetCluster value: {{ .name }} - - name: clusterGroup.hostedSite.bearer_secret_key - value: {{ .bearer_secret_key }} - - name: clusterGroup.hostedSite.ca_secret_key - value: {{ .ca_secret_key }} + - name: clusterGroup.hostedSite.bearerKeyPath + value: {{ default $bearerDefault .bearerKeyPath }} + - name: clusterGroup.hostedSite.caKeyPath + value: {{ default $caDefault .caKeyPath }} {{- range $group.helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} diff --git a/examples/values-example.yaml b/examples/values-example.yaml index d53ba72c..81bbcf10 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -95,12 +95,10 @@ clusterGroup: hostedArgoSites: - name: perth domain: perth1.beekhof.net - bearer_secret_key: secret/data/hub/cluster_perth - ca_secret_key: secret/data/hub/cluster_perth_ca + bearerKeyPath: secret/data/hub/cluster_perth + caKeyPath: secret/data/hub/cluster_perth_ca - name: sydney domain: syd.beekhof.net - bearer_secret_key: secret/data/hub/cluster_sydney - ca_secret_key: secret/data/hub/cluster_syndey_ca helmOverrides: - name: clusterGroup.isHubCluster value: "false" diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index d23bb757..50a8fb46 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -138,13 +138,11 @@ data: - name: clusterGroup.isHubCluster value: "false" hostedArgoSites: - - bearer_secret_key: secret/data/hub/cluster_perth - ca_secret_key: secret/data/hub/cluster_perth_ca + - bearerKeyPath: secret/data/hub/cluster_perth + caKeyPath: secret/data/hub/cluster_perth_ca domain: perth1.beekhof.net name: perth - - bearer_secret_key: secret/data/hub/cluster_sydney - ca_secret_key: secret/data/hub/cluster_syndey_ca - domain: syd.beekhof.net + - domain: syd.beekhof.net name: sydney name: argo-edge name: example @@ -643,9 +641,9 @@ spec: value: argo-edge - name: clusterGroup.targetCluster value: perth - - name: clusterGroup.hostedSite.bearer_secret_key + - name: clusterGroup.hostedSite.bearerKeyPath value: secret/data/hub/cluster_perth - - name: clusterGroup.hostedSite.ca_secret_key + - name: clusterGroup.hostedSite.caKeyPath value: secret/data/hub/cluster_perth_ca - name: clusterGroup.isHubCluster value: "false" @@ -705,9 +703,9 @@ spec: value: argo-edge - name: clusterGroup.targetCluster value: perth - - name: clusterGroup.hostedSite.bearer_secret_key + - name: clusterGroup.hostedSite.bearerKeyPath value: secret/data/hub/cluster_perth - - name: clusterGroup.hostedSite.ca_secret_key + - name: clusterGroup.hostedSite.caKeyPath value: secret/data/hub/cluster_perth_ca - name: clusterGroup.isHubCluster value: "false" @@ -767,10 +765,10 @@ spec: value: argo-edge - name: clusterGroup.targetCluster value: sydney - - name: clusterGroup.hostedSite.bearer_secret_key + - name: clusterGroup.hostedSite.bearerKeyPath value: secret/data/hub/cluster_sydney - - name: clusterGroup.hostedSite.ca_secret_key - value: secret/data/hub/cluster_syndey_ca + - name: clusterGroup.hostedSite.caKeyPath + value: secret/data/hub/cluster_sydney_ca - name: clusterGroup.isHubCluster value: "false" destination: @@ -829,10 +827,10 @@ spec: value: argo-edge - name: clusterGroup.targetCluster value: sydney - - name: clusterGroup.hostedSite.bearer_secret_key + - name: clusterGroup.hostedSite.bearerKeyPath value: secret/data/hub/cluster_sydney - - name: clusterGroup.hostedSite.ca_secret_key - value: secret/data/hub/cluster_syndey_ca + - name: clusterGroup.hostedSite.caKeyPath + value: secret/data/hub/cluster_sydney_ca - name: clusterGroup.isHubCluster value: "false" destination: From e9e3adfc43b2689503eddac1c7501eb826f275e8 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 10:43:08 +1100 Subject: [PATCH 0538/1263] Replace 'cat' with the simpler 'print' helm function --- acm/templates/provision/clusterpool.yaml | 2 +- acm/templates/provision/secrets-aws.yaml | 2 +- acm/templates/provision/secrets-azure.yaml | 2 +- acm/templates/provision/secrets-common.yaml | 2 +- clustergroup/templates/plumbing/applications.yaml | 2 +- clustergroup/templates/plumbing/argocd.yaml | 2 +- clustergroup/templates/plumbing/cluster-external-secrets.yaml | 2 +- clustergroup/templates/plumbing/projects.yaml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index b7960570..a5aa327e 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -14,7 +14,7 @@ spec: {{- range .clusterPools }} {{- $pool := . }} -{{- $poolName := cat .name $group.name | replace " " "-" }} +{{- $poolName := print .name "-" $group.name }} {{- $cloud := "None" }} {{- $region := "None" }} diff --git a/acm/templates/provision/secrets-aws.yaml b/acm/templates/provision/secrets-aws.yaml index 99022df6..002c9247 100644 --- a/acm/templates/provision/secrets-aws.yaml +++ b/acm/templates/provision/secrets-aws.yaml @@ -1,7 +1,7 @@ {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} {{- range .clusterPools }} -{{- $poolName := cat .name $group.name | replace " " "-" }} +{{- $poolName := print .name "-" $group.name }} {{- if .platform.aws }} apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret diff --git a/acm/templates/provision/secrets-azure.yaml b/acm/templates/provision/secrets-azure.yaml index 7897d61c..7fe6271b 100644 --- a/acm/templates/provision/secrets-azure.yaml +++ b/acm/templates/provision/secrets-azure.yaml @@ -1,7 +1,7 @@ {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} {{- range .clusterPools }} -{{- $poolName := cat .name $group.name | replace " " "-" }} +{{- $poolName := print .name "-" $group.name }} {{- if .platform.azure }} apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret diff --git a/acm/templates/provision/secrets-common.yaml b/acm/templates/provision/secrets-common.yaml index 40fcb19c..21a03b73 100644 --- a/acm/templates/provision/secrets-common.yaml +++ b/acm/templates/provision/secrets-common.yaml @@ -1,7 +1,7 @@ {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} {{- range .clusterPools }} -{{- $poolName := cat .name $group.name | replace " " "-" }} +{{- $poolName := print .name "-" $group.name }} apiVersion: v1 kind: Secret metadata: diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 552b569a..c9a6667d 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -1,5 +1,5 @@ {{- if not (eq .Values.enabled "core") }} -{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} +{{- $namespace := print $.Values.global.pattern "-" $.Values.clusterGroup.name }} {{- if (eq .Values.enabled "plumbing") }} {{- $namespace = "openshift-gitops" }} {{- end }} diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 832a7e13..68400b95 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -1,5 +1,5 @@ {{- if (eq .Values.enabled "all") }} -{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} +{{- $namespace := print $.Values.global.pattern "-" $.Values.clusterGroup.name }} apiVersion: argoproj.io/v1alpha1 kind: ArgoCD metadata: diff --git a/clustergroup/templates/plumbing/cluster-external-secrets.yaml b/clustergroup/templates/plumbing/cluster-external-secrets.yaml index ee2fbb79..dfb6bc6b 100644 --- a/clustergroup/templates/plumbing/cluster-external-secrets.yaml +++ b/clustergroup/templates/plumbing/cluster-external-secrets.yaml @@ -1,5 +1,5 @@ {{- if (eq .Values.enabled "plumbing") }} -{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} +{{- $namespace := print $.Values.global.pattern "-" $.Values.clusterGroup.name }} apiVersion: "external-secrets.io/v1beta1" kind: ExternalSecret metadata: diff --git a/clustergroup/templates/plumbing/projects.yaml b/clustergroup/templates/plumbing/projects.yaml index ab67508a..7f3b8c22 100644 --- a/clustergroup/templates/plumbing/projects.yaml +++ b/clustergroup/templates/plumbing/projects.yaml @@ -1,5 +1,5 @@ {{- if not (eq .Values.enabled "core") }} -{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} +{{- $namespace := print $.Values.global.pattern "-" $.Values.clusterGroup.name }} {{- range .Values.clusterGroup.projects }} apiVersion: argoproj.io/v1alpha1 kind: AppProject From e94ecb28b980c7f42b3908ddde7ec393265b9656 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 10:51:14 +1100 Subject: [PATCH 0539/1263] Start a changes file --- Changes.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Changes.md diff --git a/Changes.md b/Changes.md new file mode 100644 index 00000000..d9d7f227 --- /dev/null +++ b/Changes.md @@ -0,0 +1,21 @@ +# Changes + +## October 3, 2022 +* ACM 2.6 required for ACM-based managed sites +* Introduced global.clusterDomain template variable (without the `apps.` prefix) +* Removed the ability to send specific charts to another cluster, use hosted argo sites instead +* Added the ability to have the hub host `values-{site}.yaml` for spoke clusters. + + The following example would deploy the namespaces, subscriptions, and + applications defined in `values-group-one.yaml` to the `perth` cluster + directly from ArgoCD on the hub. + + ``` + managedClusterGroups: + - name: group-one + hostedArgoSites: + - name: perth + domain: perth1.beekhof.net + bearerKeyPath: secret/data/hub/cluster_perth + caKeyPath: secret/data/hub/cluster_perth_ca + ``` From 8482c1905da96b9c4cae94fe6011a540dcb9b0c9 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 11:21:08 +1100 Subject: [PATCH 0540/1263] Sanely handle ACM clusters without an explicit label --- acm/templates/policies/application-policies.yaml | 8 ++++++++ acm/templates/provision/clusterpool.yaml | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 2f9e4f89..dfb29a90 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -119,6 +119,14 @@ spec: type: ManagedClusterConditionAvailable {{- if .clusterSelector }} clusterSelector: {{ .clusterSelector | toPrettyJson }} + {{- else if (not $group.acmlabels) }} + clusterSelector: + matchLabels: + clusterGroup: {{ $group.name }} + {{- else if eq (len $group.acmlabels) 0 }} + clusterSelector: + matchLabels: + clusterGroup: {{ $group.name }} {{- else }} clusterSelector: matchLabels: diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index a5aa327e..0ac851c5 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -69,9 +69,15 @@ metadata: cluster.open-cluster-management.io/createmanagedcluster: "true" labels: clusterClaimName: {{ . }}-{{ $group.name }} + {{- if (not $group.acmlabels) }} + clusterGroup: {{ $group.name }} + {{- else if eq (len $group.acmlabels) 0 }} + clusterGroup: {{ $group.name }} + {{- else }} {{- range $group.acmlabels }} {{ .name }}: {{ .value }} {{- end }} + {{- end }} spec: clusterPoolName: {{ $pool.name }} --- From c106b23921942bb127261a31808526787bed2c7f Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 11:31:17 +1100 Subject: [PATCH 0541/1263] Track expected differences bewteen naked and normal test --- scripts/test.sh | 22 +- tests/acm-normal.expected.yaml.variant | 633 ++++++++++++ .../clustergroup-normal.expected.yaml.variant | 930 ++++++++++++++++++ ...xamples-blank-normal.expected.yaml.variant | 0 ...mize-renderer-normal.expected.yaml.variant | 19 + ...ernal-secrets-normal.expected.yaml.variant | 11 + ...shicorp-vault-normal.expected.yaml.variant | 11 + tests/install-normal.expected.yaml.variant | 43 + ...rator-install-normal.expected.yaml.variant | 11 + 9 files changed, 1671 insertions(+), 9 deletions(-) create mode 100644 tests/acm-normal.expected.yaml.variant create mode 100644 tests/clustergroup-normal.expected.yaml.variant create mode 100644 tests/examples-blank-normal.expected.yaml.variant create mode 100644 tests/examples-kustomize-renderer-normal.expected.yaml.variant create mode 100644 tests/golang-external-secrets-normal.expected.yaml.variant create mode 100644 tests/hashicorp-vault-normal.expected.yaml.variant create mode 100644 tests/install-normal.expected.yaml.variant create mode 100644 tests/operator-install-normal.expected.yaml.variant diff --git a/scripts/test.sh b/scripts/test.sh index 5790b5de..31ef74b8 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -23,28 +23,32 @@ if [ $rc -ne 0 ]; then echo "FAIL on helm template $target --name-template $name ${CHART_OPTS}" exit 1 fi -#cp ${OUTPUT} ${REFERENCE} if [ ! -e ${REFERENCE} ]; then - touch ${REFERENCE} + cp ${OUTPUT} ${REFERENCE} fi diff -u ${REFERENCE} ${OUTPUT} rc=$? -if [ $rc = 0 ]; then - rm -f ${OUTPUT} -fi if [ $TEST_VARIANT = normal -a $rc = 0 ]; then # Another method of finding variables missing from values.yaml, eg. # - name: -datacenter # + name: pattern-name-datacenter - # Alas we can't make it fatal because there *should* be some differences - diff -u ${TESTDIR}/${name}-naked.expected.yaml ${TESTDIR}/${name}-normal.expected.yaml + diff -u ${TESTDIR}/${name}-naked.expected.yaml ${TESTDIR}/${name}-normal.expected.yaml | sed 's/20[0-9][0-9]-[09][0-9].*//' > ${OUTPUT}.variant + + if [ ! -e ${REFERENCE}.variant ]; then + cp ${OUTPUT}.variant ${REFERENCE}.variant + fi + + diff -u ${REFERENCE}.variant ${OUTPUT}.variant + rc=$? fi if [ $rc = 0 ]; then - echo "PASS on $target $TEST_VARIANT with opts [$CHART_OPTS]" + rm -f ${OUTPUT} + rm -f ${OUTPUT}.variant + echo "PASS on $target $TEST_VARIANT with opts [$CHART_OPTS]" else - echo "FAIL on $target $TEST_VARIANT with opts [$CHART_OPTS]" + echo "FAIL on $target $TEST_VARIANT with opts [$CHART_OPTS]" fi exit $rc diff --git a/tests/acm-normal.expected.yaml.variant b/tests/acm-normal.expected.yaml.variant new file mode 100644 index 00000000..3e1c4dae --- /dev/null +++ b/tests/acm-normal.expected.yaml.variant @@ -0,0 +1,633 @@ +--- tests/acm-naked.expected.yaml ++++ tests/acm-normal.expected.yaml 2022-10-03 11:27:20.418172850 +1100 +@@ -1,6 +1,386 @@ + --- +-# Source: acm/templates/policies/application-policies.yaml +-# TODO: Also create a GitOpsCluster.apps.open-cluster-management.io ++# Source: acm/templates/provision/secrets-common.yaml ++apiVersion: v1 ++kind: Secret ++metadata: ++ name: aws-ap-acm-provision-edge-install-config ++data: ++ # Base64 encoding of install-config yaml ++ install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXdzLWFwJyAKYmFzZURvbWFpbjogYmx1ZXByaW50cy5yaGVjb2VuZy5jb20KY29udHJvbFBsYW5lOgogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIG5hbWU6IGNvbnRyb2xQbGFuZQogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhd3M6CiAgICAgIHR5cGU6IG01LnhsYXJnZQpjb21wdXRlOgotIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIG5hbWU6ICd3b3JrZXInCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF3czoKICAgICAgdHlwZTogbTUueGxhcmdlCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT3BlblNoaWZ0U0ROCiAgc2VydmljZU5ldHdvcms6CiAgLSAxNzIuMzAuMC4wLzE2CnBsYXRmb3JtOiB7CiAgImF3cyI6IHsKICAgICJyZWdpb24iOiAiYXAtc291dGhlYXN0LTIiCiAgfQp9CnB1bGxTZWNyZXQ6ICIiICMgc2tpcCwgaGl2ZSB3aWxsIGluamVjdCBiYXNlZCBvbiBpdCdzIHNlY3JldHMKc3NoS2V5OiAiIiAgICAgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cw== ++type: Opaque ++--- ++# Source: acm/templates/provision/secrets-common.yaml ++apiVersion: v1 ++kind: Secret ++metadata: ++ name: azure-us-acm-provision-edge-install-config ++data: ++ # Base64 encoding of install-config yaml ++ install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXp1cmUtdXMnIApiYXNlRG9tYWluOiBibHVlcHJpbnRzLnJoZWNvZW5nLmNvbQpjb250cm9sUGxhbmU6CiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgbmFtZTogY29udHJvbFBsYW5lCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF6dXJlOgogICAgICB0eXBlOiBTdGFuZGFyZF9EOHNfdjMKY29tcHV0ZToKLSBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBuYW1lOiAnd29ya2VyJwogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhenVyZToKICAgICAgdHlwZTogU3RhbmRhcmRfRDhzX3YzCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT3BlblNoaWZ0U0ROCiAgc2VydmljZU5ldHdvcms6CiAgLSAxNzIuMzAuMC4wLzE2CnBsYXRmb3JtOiB7CiAgImF6dXJlIjogewogICAgImJhc2VEb21haW5SZXNvdXJjZUdyb3VwTmFtZSI6ICJkb2pvLWRucy16b25lcyIsCiAgICAicmVnaW9uIjogImVhc3R1cyIKICB9Cn0KcHVsbFNlY3JldDogIiIgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cwpzc2hLZXk6ICIiICAgICAjIHNraXAsIGhpdmUgd2lsbCBpbmplY3QgYmFzZWQgb24gaXQncyBzZWNyZXRz ++type: Opaque ++--- ++# Source: acm/templates/provision/clusterpool.yaml ++apiVersion: hive.openshift.io/v1 ++kind: ClusterClaim ++metadata: ++ name: 'One-acm-provision-edge' ++ annotations: ++ argocd.argoproj.io/sync-wave: "20" ++ cluster.open-cluster-management.io/createmanagedcluster: "true" ++ labels: ++ clusterClaimName: One-acm-provision-edge ++ clusterGroup: region ++spec: ++ clusterPoolName: aws-ap ++--- ++# Source: acm/templates/provision/clusterpool.yaml ++apiVersion: hive.openshift.io/v1 ++kind: ClusterClaim ++metadata: ++ name: 'Two-acm-provision-edge' ++ annotations: ++ argocd.argoproj.io/sync-wave: "20" ++ cluster.open-cluster-management.io/createmanagedcluster: "true" ++ labels: ++ clusterClaimName: Two-acm-provision-edge ++ clusterGroup: region ++spec: ++ clusterPoolName: azure-us ++--- ++# Source: acm/templates/provision/clusterpool.yaml ++apiVersion: hive.openshift.io/v1 ++kind: ClusterClaim ++metadata: ++ name: 'Three-acm-provision-edge' ++ annotations: ++ argocd.argoproj.io/sync-wave: "20" ++ cluster.open-cluster-management.io/createmanagedcluster: "true" ++ labels: ++ clusterClaimName: Three-acm-provision-edge ++ clusterGroup: region ++spec: ++ clusterPoolName: azure-us ++--- ++# Source: acm/templates/provision/clusterpool.yaml ++apiVersion: hive.openshift.io/v1 ++kind: ClusterPool ++metadata: ++ name: "aws-ap-acm-provision-edge" ++ annotations: ++ argocd.argoproj.io/sync-wave: "10" ++ labels: ++ cloud: aws ++ region: 'ap-southeast-2' ++ vendor: OpenShift ++ cluster.open-cluster-management.io/clusterset: aws-ap ++spec: ++ size: 3 ++ runningCount: 1 ++ baseDomain: blueprints.rhecoeng.com ++ installConfigSecretTemplateRef: ++ name: aws-ap-acm-provision-edge-install-config ++ imageSetRef: ++ name: img4.10.18-x86-64-appsub ++ pullSecretRef: ++ name: aws-ap-acm-provision-edge-pull-secret ++ skipMachinePools: true # Disable MachinePool as using custom install-config ++ platform: ++ aws: ++ credentialsSecretRef: ++ name: aws-ap-acm-provision-edge-creds ++ region: ap-southeast-2 ++--- ++# Source: acm/templates/provision/clusterpool.yaml ++apiVersion: hive.openshift.io/v1 ++kind: ClusterPool ++metadata: ++ name: "azure-us-acm-provision-edge" ++ annotations: ++ argocd.argoproj.io/sync-wave: "10" ++ labels: ++ cloud: azure ++ region: 'eastus' ++ vendor: OpenShift ++ cluster.open-cluster-management.io/clusterset: azure-us ++spec: ++ size: 2 ++ runningCount: 2 ++ baseDomain: blueprints.rhecoeng.com ++ installConfigSecretTemplateRef: ++ name: azure-us-acm-provision-edge-install-config ++ imageSetRef: ++ name: img4.10.18-x86-64-appsub ++ pullSecretRef: ++ name: azure-us-acm-provision-edge-pull-secret ++ skipMachinePools: true # Disable MachinePool as using custom install-config ++ platform: ++ azure: ++ credentialsSecretRef: ++ name: azure-us-acm-provision-edge-creds ++ region: eastus ++--- ++# Source: acm/templates/provision/secrets-aws.yaml ++apiVersion: external-secrets.io/v1beta1 ++kind: ExternalSecret ++metadata: ++ name: aws-ap-acm-provision-edge-creds ++spec: ++ dataFrom: ++ - extract: ++ # Expects entries called: aws_access_key_id and aws_secret_access_key ++ key: secret/data/hub/aws ++ refreshInterval: 24h0m0s ++ secretStoreRef: ++ name: vault-backend ++ kind: ClusterSecretStore ++ target: ++ name: aws-ap-acm-provision-edge-creds ++ creationPolicy: Owner ++ template: ++ type: Opaque ++--- ++# Source: acm/templates/provision/secrets-aws.yaml ++# For use when manually creating clusters with ACM ++apiVersion: external-secrets.io/v1beta1 ++kind: ExternalSecret ++metadata: ++ name: aws-ap-acm-provision-edge-infra-creds ++spec: ++ data: ++ - secretKey: openshiftPullSecret ++ remoteRef: ++ key: secret/data/hub/openshiftPullSecret ++ property: content ++ - secretKey: awsKeyId ++ remoteRef: ++ key: secret/data/hub/aws ++ property: aws_access_key_id ++ - secretKey: awsAccessKey ++ remoteRef: ++ key: secret/data/hub/aws ++ property: aws_secret_access_key ++ - secretKey: sshPublicKey ++ remoteRef: ++ key: secret/data/hub/publickey ++ property: content ++ - secretKey: sshPrivateKey ++ remoteRef: ++ key: secret/data/hub/privatekey ++ property: content ++ refreshInterval: 24h0m0s ++ secretStoreRef: ++ name: vault-backend ++ kind: ClusterSecretStore ++ target: ++ name: aws-ap-acm-provision-edge-infra-creds ++ creationPolicy: Owner ++ template: ++ type: Opaque ++ metadata: ++ labels: ++ cluster.open-cluster-management.io/credentials: "" ++ cluster.open-cluster-management.io/type: aws ++ data: ++ baseDomain: "blueprints.rhecoeng.com" ++ pullSecret: |- ++ {{ .openshiftPullSecret | toString }} ++ aws_access_key_id: |- ++ {{ .awsKeyId | toString }} ++ aws_secret_access_key: |- ++ {{ .awsAccessKey | toString }} ++ ssh-privatekey: |- ++ {{ .sshPrivateKey | toString }} ++ ssh-publickey: |- ++ {{ .sshPublicKey | toString }} ++ httpProxy: "" ++ httpsProxy: "" ++ noProxy: "" ++ additionalTrustBundle: "" ++--- ++# Source: acm/templates/provision/secrets-azure.yaml ++apiVersion: external-secrets.io/v1beta1 ++kind: ExternalSecret ++metadata: ++ name: azure-us-acm-provision-edge-creds ++spec: ++ data: ++ - secretKey: azureOsServicePrincipal ++ remoteRef: ++ key: secret/data/hub/azureOsServicePrincipal ++ property: content ++ refreshInterval: 24h0m0s ++ secretStoreRef: ++ name: vault-backend ++ kind: ClusterSecretStore ++ target: ++ name: azure-us-acm-provision-edge-creds ++ creationPolicy: Owner ++ template: ++ type: Opaque ++ data: ++ osServicePrincipal.json: |- ++ {{ .azureOsServicePrincipal | toString }} ++--- ++# Source: acm/templates/provision/secrets-azure.yaml ++# For use when manually creating clusters with ACM ++apiVersion: external-secrets.io/v1beta1 ++kind: ExternalSecret ++metadata: ++ name: azure-us-acm-provision-edge-infra-creds ++spec: ++ data: ++ - secretKey: openshiftPullSecret ++ remoteRef: ++ key: secret/data/hub/openshiftPullSecret ++ property: content ++ - secretKey: sshPublicKey ++ remoteRef: ++ key: secret/data/hub/publickey ++ property: content ++ - secretKey: sshPrivateKey ++ remoteRef: ++ key: secret/data/hub/privatekey ++ property: content ++ - secretKey: azureOsServicePrincipal ++ remoteRef: ++ key: secret/data/hub/azureOsServicePrincipal ++ property: content ++ refreshInterval: 24h0m0s ++ secretStoreRef: ++ name: vault-backend ++ kind: ClusterSecretStore ++ target: ++ name: azure-us-acm-provision-edge-infra-creds ++ creationPolicy: Owner ++ template: ++ type: Opaque ++ metadata: ++ labels: ++ cluster.open-cluster-management.io/credentials: "" ++ cluster.open-cluster-management.io/type: aws ++ data: ++ cloudName: AzurePublicCloud ++ osServicePrincipal.json: |- ++ {{ .azureOsServicePrincipal | toString }} ++ baseDomain: "blueprints.rhecoeng.com" ++ baseDomainResourceGroupName: "dojo-dns-zones" ++ pullSecret: |- ++ {{ .openshiftPullSecret | toString }} ++ ssh-privatekey: |- ++ {{ .sshPrivateKey | toString }} ++ ssh-publickey: |- ++ {{ .sshPublicKey | toString }} ++ httpProxy: "" ++ httpsProxy: "" ++ noProxy: "" ++ additionalTrustBundle: "" ++--- ++# Source: acm/templates/provision/secrets-common.yaml ++apiVersion: external-secrets.io/v1beta1 ++kind: ExternalSecret ++metadata: ++ name: aws-ap-acm-provision-edge-pull-secret ++spec: ++ data: ++ - secretKey: openshiftPullSecret ++ remoteRef: ++ key: secret/data/hub/openshiftPullSecret ++ property: content ++ refreshInterval: 24h0m0s ++ secretStoreRef: ++ name: vault-backend ++ kind: ClusterSecretStore ++ target: ++ name: aws-ap-acm-provision-edge-pull-secret ++ creationPolicy: Owner ++ template: ++ type: kubernetes.io/dockerconfigjson ++ data: ++ .dockerconfigjson: |- ++ {{ .openshiftPullSecret | toString }} ++--- ++# Source: acm/templates/provision/secrets-common.yaml ++apiVersion: external-secrets.io/v1beta1 ++kind: ExternalSecret ++metadata: ++ name: aws-ap-acm-provision-edge-ssh-private-key ++spec: ++ data: ++ - secretKey: sshPrivateKey ++ remoteRef: ++ key: secret/data/hub/privatekey ++ property: content ++ refreshInterval: 24h0m0s ++ secretStoreRef: ++ name: vault-backend ++ kind: ClusterSecretStore ++ target: ++ name: aws-ap-acm-provision-edge-ssh-private-key ++ creationPolicy: Owner ++ template: ++ type: Opaque ++ data: ++ ssh-privatekey: |- ++ {{ .sshPrivateKey | toString }} ++--- ++# Source: acm/templates/provision/secrets-common.yaml ++apiVersion: external-secrets.io/v1beta1 ++kind: ExternalSecret ++metadata: ++ name: azure-us-acm-provision-edge-pull-secret ++spec: ++ data: ++ - secretKey: openshiftPullSecret ++ remoteRef: ++ key: secret/data/hub/openshiftPullSecret ++ property: content ++ refreshInterval: 24h0m0s ++ secretStoreRef: ++ name: vault-backend ++ kind: ClusterSecretStore ++ target: ++ name: azure-us-acm-provision-edge-pull-secret ++ creationPolicy: Owner ++ template: ++ type: kubernetes.io/dockerconfigjson ++ data: ++ .dockerconfigjson: |- ++ {{ .openshiftPullSecret | toString }} ++--- ++# Source: acm/templates/provision/secrets-common.yaml ++apiVersion: external-secrets.io/v1beta1 ++kind: ExternalSecret ++metadata: ++ name: azure-us-acm-provision-edge-ssh-private-key ++spec: ++ data: ++ - secretKey: sshPrivateKey ++ remoteRef: ++ key: secret/data/hub/privatekey ++ property: content ++ refreshInterval: 24h0m0s ++ secretStoreRef: ++ name: vault-backend ++ kind: ClusterSecretStore ++ target: ++ name: azure-us-acm-provision-edge-ssh-private-key ++ creationPolicy: Owner ++ template: ++ type: Opaque ++ data: ++ ssh-privatekey: |- ++ {{ .sshPrivateKey | toString }} ++--- ++# Source: acm/templates/provision/clusterpool.yaml ++apiVersion: cluster.open-cluster-management.io/v1beta1 ++kind: ManagedClusterSet ++metadata: ++ annotations: ++ cluster.open-cluster-management.io/submariner-broker-ns: acm-provision-edge-broker ++ name: acm-provision-edge ++spec: ++ clusterSelector: ++ selectorType: LegacyClusterSetLabel + --- + # Source: acm/templates/multiclusterhub.yaml + apiVersion: operator.open-cluster-management.io/v1 +@@ -12,6 +392,38 @@ + argocd.argoproj.io/sync-wave: "-1" + spec: {} + --- ++# Source: acm/templates/policies/application-policies.yaml ++apiVersion: policy.open-cluster-management.io/v1 ++kind: PlacementBinding ++metadata: ++ name: acm-edge-placement-binding ++ annotations: ++ argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true ++placementRef: ++ name: acm-edge-placement ++ kind: PlacementRule ++ apiGroup: apps.open-cluster-management.io ++subjects: ++ - name: acm-edge-clustergroup-policy ++ kind: Policy ++ apiGroup: policy.open-cluster-management.io ++--- ++# Source: acm/templates/policies/application-policies.yaml ++apiVersion: policy.open-cluster-management.io/v1 ++kind: PlacementBinding ++metadata: ++ name: acm-provision-edge-placement-binding ++ annotations: ++ argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true ++placementRef: ++ name: acm-provision-edge-placement ++ kind: PlacementRule ++ apiGroup: apps.open-cluster-management.io ++subjects: ++ - name: acm-provision-edge-clustergroup-policy ++ kind: Policy ++ apiGroup: policy.open-cluster-management.io ++--- + # Source: acm/templates/policies/ocp-gitops-policy.yaml + apiVersion: policy.open-cluster-management.io/v1 + kind: PlacementBinding +@@ -28,6 +440,32 @@ + kind: Policy + apiGroup: policy.open-cluster-management.io + --- ++# Source: acm/templates/policies/application-policies.yaml ++apiVersion: apps.open-cluster-management.io/v1 ++kind: PlacementRule ++metadata: ++ name: acm-edge-placement ++spec: ++ clusterConditions: ++ - status: 'True' ++ type: ManagedClusterConditionAvailable ++ clusterSelector: ++ matchLabels: ++ clusterGroup: acm-region ++--- ++# Source: acm/templates/policies/application-policies.yaml ++apiVersion: apps.open-cluster-management.io/v1 ++kind: PlacementRule ++metadata: ++ name: acm-provision-edge-placement ++spec: ++ clusterConditions: ++ - status: 'True' ++ type: ManagedClusterConditionAvailable ++ clusterSelector: ++ matchLabels: ++ clusterGroup: region ++--- + # Source: acm/templates/policies/ocp-gitops-policy.yaml + apiVersion: apps.open-cluster-management.io/v1 + kind: PlacementRule +@@ -44,6 +482,169 @@ + values: + - OpenShift + --- ++# Source: acm/templates/policies/application-policies.yaml ++# TODO: Also create a GitOpsCluster.apps.open-cluster-management.io ++apiVersion: policy.open-cluster-management.io/v1 ++kind: Policy ++metadata: ++ name: acm-edge-clustergroup-policy ++ annotations: ++ argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true ++ argocd.argoproj.io/compare-options: IgnoreExtraneous ++spec: ++ remediationAction: enforce ++ disabled: false ++ policy-templates: ++ - objectDefinition: ++ apiVersion: policy.open-cluster-management.io/v1 ++ kind: ConfigurationPolicy ++ metadata: ++ name: acm-edge-clustergroup-config ++ spec: ++ remediationAction: enforce ++ severity: medium ++ namespaceSelector: ++ include: ++ - default ++ object-templates: ++ - complianceType: mustonlyhave ++ objectDefinition: ++ apiVersion: argoproj.io/v1alpha1 ++ kind: Application ++ metadata: ++ name: mypattern-acm-edge ++ namespace: openshift-gitops ++ finalizers: ++ - resources-finalizer.argocd.argoproj.io/foreground ++ spec: ++ project: default ++ source: ++ repoURL: https://github.com/pattern-clone/mypattern ++ targetRevision: main ++ path: common/clustergroup ++ helm: ++ ignoreMissingValueFiles: true ++ valueFiles: ++ - "/values-global.yaml" ++ - "/values-acm-edge.yaml" ++ parameters: ++ - name: global.repoURL ++ value: $ARGOCD_APP_SOURCE_REPO_URL ++ - name: global.targetRevision ++ value: $ARGOCD_APP_SOURCE_TARGET_REVISION ++ - name: global.namespace ++ value: $ARGOCD_APP_NAMESPACE ++ - name: global.pattern ++ value: mypattern ++ - name: global.hubClusterDomain ++ value: apps.hub.example.com ++ - name: global.localClusterDomain ++ value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' ++ # Requires ACM 2.6 or higher ++ - name: global.clusterDomain ++ value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' ++ - name: clusterGroup.name ++ value: acm-edge ++ - name: clusterGroup.isHubCluster ++ value: "false" ++ destination: ++ server: https://kubernetes.default.svc ++ namespace: mypattern-acm-edge ++ syncPolicy: ++ automated: ++ prune: false ++ selfHeal: true ++ ignoreDifferences: ++ - group: apps ++ kind: Deployment ++ jsonPointers: ++ - /spec/replicas ++ - group: route.openshift.io ++ kind: Route ++ jsonPointers: ++ - /status ++--- ++# Source: acm/templates/policies/application-policies.yaml ++apiVersion: policy.open-cluster-management.io/v1 ++kind: Policy ++metadata: ++ name: acm-provision-edge-clustergroup-policy ++ annotations: ++ argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true ++ argocd.argoproj.io/compare-options: IgnoreExtraneous ++spec: ++ remediationAction: enforce ++ disabled: false ++ policy-templates: ++ - objectDefinition: ++ apiVersion: policy.open-cluster-management.io/v1 ++ kind: ConfigurationPolicy ++ metadata: ++ name: acm-provision-edge-clustergroup-config ++ spec: ++ remediationAction: enforce ++ severity: medium ++ namespaceSelector: ++ include: ++ - default ++ object-templates: ++ - complianceType: mustonlyhave ++ objectDefinition: ++ apiVersion: argoproj.io/v1alpha1 ++ kind: Application ++ metadata: ++ name: mypattern-acm-provision-edge ++ namespace: openshift-gitops ++ finalizers: ++ - resources-finalizer.argocd.argoproj.io/foreground ++ spec: ++ project: default ++ source: ++ repoURL: https://github.com/pattern-clone/mypattern ++ targetRevision: main ++ path: common/clustergroup ++ helm: ++ ignoreMissingValueFiles: true ++ valueFiles: ++ - "/values-global.yaml" ++ - "/values-acm-provision-edge.yaml" ++ parameters: ++ - name: global.repoURL ++ value: $ARGOCD_APP_SOURCE_REPO_URL ++ - name: global.targetRevision ++ value: $ARGOCD_APP_SOURCE_TARGET_REVISION ++ - name: global.namespace ++ value: $ARGOCD_APP_NAMESPACE ++ - name: global.pattern ++ value: mypattern ++ - name: global.hubClusterDomain ++ value: apps.hub.example.com ++ - name: global.localClusterDomain ++ value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' ++ # Requires ACM 2.6 or higher ++ - name: global.clusterDomain ++ value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' ++ - name: clusterGroup.name ++ value: acm-provision-edge ++ - name: clusterGroup.isHubCluster ++ value: "false" ++ destination: ++ server: https://kubernetes.default.svc ++ namespace: mypattern-acm-provision-edge ++ syncPolicy: ++ automated: ++ prune: false ++ selfHeal: true ++ ignoreDifferences: ++ - group: apps ++ kind: Deployment ++ jsonPointers: ++ - /spec/replicas ++ - group: route.openshift.io ++ kind: Route ++ jsonPointers: ++ - /status ++--- + # Source: acm/templates/policies/ocp-gitops-policy.yaml + apiVersion: policy.open-cluster-management.io/v1 + kind: Policy diff --git a/tests/clustergroup-normal.expected.yaml.variant b/tests/clustergroup-normal.expected.yaml.variant new file mode 100644 index 00000000..36996c57 --- /dev/null +++ b/tests/clustergroup-normal.expected.yaml.variant @@ -0,0 +1,930 @@ +--- tests/clustergroup-naked.expected.yaml 2022-10-03 11:27:20.418172850 +1100 ++++ tests/clustergroup-normal.expected.yaml 2022-10-03 11:27:20.418172850 +1100 +@@ -1,17 +1,243 @@ + --- ++# Source: pattern-clustergroup/templates/core/namespaces.yaml ++apiVersion: v1 ++kind: Namespace ++metadata: ++ labels: ++ name: pattern ++ argocd.argoproj.io/managed-by: mypattern-example ++ name: open-cluster-management ++spec: ++--- ++# Source: pattern-clustergroup/templates/core/namespaces.yaml ++apiVersion: v1 ++kind: Namespace ++metadata: ++ labels: ++ name: pattern ++ argocd.argoproj.io/managed-by: mypattern-example ++ name: application-ci ++spec: ++--- ++# Source: pattern-clustergroup/templates/imperative/namespace.yaml ++apiVersion: v1 ++kind: Namespace ++metadata: ++ labels: ++ name: imperative ++ argocd.argoproj.io/managed-by: mypattern-example ++ name: imperative ++--- + # Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml + apiVersion: v1 + kind: Namespace + metadata: + labels: +- name: common-example ++ name: mypattern-example + # The name here needs to be consistent with + # - acm/templates/policies/application-policies.yaml + # - clustergroup/templates/applications.yaml + # - any references to secrets and route URLs in documentation +- name: common-example ++ name: mypattern-example + spec: {} + --- ++# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml ++apiVersion: v1 ++kind: ServiceAccount ++metadata: ++ name: imperative-sa ++ namespace: imperative ++--- ++# Source: pattern-clustergroup/templates/imperative/configmap.yaml ++apiVersion: v1 ++kind: ConfigMap ++metadata: ++ name: helm-values-configmap-example ++ namespace: imperative ++data: ++ values.yaml: | ++ clusterGroup: ++ applications: ++ acm: ++ ignoreDifferences: ++ - group: internal.open-cluster-management.io ++ jsonPointers: ++ - /spec/loggingCA ++ kind: ManagedClusterInfo ++ name: acm ++ namespace: open-cluster-management ++ path: common/acm ++ project: datacenter ++ pipe: ++ name: pipelines ++ namespace: application-ci ++ path: charts/datacenter/pipelines ++ project: datacenter ++ imperative: ++ activeDeadlineSeconds: 3600 ++ clusterRoleName: imperative-cluster-role ++ clusterRoleYaml: "" ++ cronJobName: imperative-cronjob ++ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest ++ imagePullPolicy: Always ++ insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' ++ jobName: imperative-job ++ jobs: ++ - name: test ++ playbook: ansible/test.yml ++ namespace: imperative ++ roleName: imperative-role ++ roleYaml: "" ++ schedule: '*/10 * * * *' ++ serviceAccountCreate: true ++ serviceAccountName: imperative-sa ++ valuesConfigMap: helm-values-configmap ++ verbosity: "" ++ insecureUnsealVaultInsideCluster: true ++ isHubCluster: true ++ managedClusterGroups: ++ - acmlabels: ++ - name: clusterGroup ++ value: acm-region ++ helmOverrides: ++ - name: clusterGroup.isHubCluster ++ value: "false" ++ name: acm-edge ++ targetRevision: main ++ - acmlabels: ++ - name: clusterGroup ++ value: region ++ clusterPools: ++ exampleAWSPool: ++ baseDomain: blueprints.rhecoeng.com ++ clusters: ++ - One ++ name: aws-ap ++ openshiftVersion: 4.10.18 ++ platform: ++ aws: ++ region: ap-southeast-2 ++ size: 3 ++ exampleAzurePool: ++ baseDomain: blueprints.rhecoeng.com ++ clusters: ++ - Two ++ - Three ++ name: azure-us ++ openshiftVersion: 4.10.18 ++ platform: ++ azure: ++ baseDomainResourceGroupName: dojo-dns-zones ++ region: eastus ++ helmOverrides: ++ - name: clusterGroup.isHubCluster ++ value: "false" ++ name: acm-provision-edge ++ targetRevision: main ++ - helmOverrides: ++ - name: clusterGroup.isHubCluster ++ value: "false" ++ hostedArgoSites: ++ - bearerKeyPath: secret/data/hub/cluster_perth ++ caKeyPath: secret/data/hub/cluster_perth_ca ++ domain: perth1.beekhof.net ++ name: perth ++ - domain: syd.beekhof.net ++ name: sydney ++ name: argo-edge ++ name: example ++ namespaces: ++ - open-cluster-management ++ - application-ci ++ projects: ++ - datacenter ++ subscriptions: ++ acm: ++ channel: release-2.4 ++ csv: advanced-cluster-management.v2.4.1 ++ name: advanced-cluster-management ++ namespace: open-cluster-management ++ odh: ++ csv: opendatahub-operator.v1.1.0 ++ disabled: true ++ name: opendatahub-operator ++ source: community-operators ++ pipelines: ++ csv: redhat-openshift-pipelines.v1.5.2 ++ name: openshift-pipelines-operator-rh ++ targetCluster: in-cluster ++ enabled: all ++ files: ++ cluster_example_ca: /path/to/ca.file ++ global: ++ clusterDomain: region.example.com ++ git: ++ account: hybrid-cloud-patterns ++ dev_revision: main ++ email: someone@somewhere.com ++ hostname: github.com ++ hubClusterDomain: apps.hub.example.com ++ localClusterDomain: apps.region.example.com ++ namespace: pattern-namespace ++ options: ++ installPlanApproval: Automatic ++ syncPolicy: Automatic ++ useCSV: false ++ pattern: mypattern ++ repoURL: https://github.com/pattern-clone/mypattern ++ targetRevision: main ++ main: ++ clusterGroupName: example ++ git: ++ repoURL: https://github.com/pattern-clone/mypattern ++ revision: main ++ secretStore: ++ kind: ClusterSecretStore ++ name: vault-backend ++ secrets: ++ aws: ++ s3Secret: test-secret ++ cluster_example: ++ bearerToken: ++ server: https://api.example.openshiftapps.com:6443 ++ git: ++ token: test-git-token ++ username: test-user ++ imageregistry: ++ account: test-account ++ token: test-quay-token ++ secretsBase: ++ key: secret/data/hub ++--- ++# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml ++apiVersion: rbac.authorization.k8s.io/v1 ++kind: ClusterRole ++metadata: ++ name: imperative-cluster-role ++rules: ++ - apiGroups: ++ - '*' ++ resources: ++ - '*' ++ verbs: ++ - get ++ - list ++ - watch ++--- ++# Source: pattern-clustergroup/templates/imperative/rbac.yaml ++apiVersion: rbac.authorization.k8s.io/v1 ++kind: ClusterRoleBinding ++metadata: ++ name: imperative-cluster-admin-rolebinding ++roleRef: ++ apiGroup: rbac.authorization.k8s.io ++ kind: ClusterRole ++ name: imperative-cluster-role ++subjects: ++ - kind: ServiceAccount ++ name: imperative-sa ++ namespace: imperative ++--- + # Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml + # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS + apiVersion: rbac.authorization.k8s.io/v1 +@@ -36,7 +262,7 @@ + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: +- name: common-example-cluster-admin-rolebinding ++ name: mypattern-example-cluster-admin-rolebinding + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole +@@ -45,16 +271,583 @@ + - kind: ServiceAccount + # This is the {ArgoCD.name}-argocd-application-controller + name: example-gitops-argocd-application-controller +- namespace: common-example ++ namespace: mypattern-example + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + # This is the {ArgoCD.name}-argocd-server + name: example-gitops-argocd-server +- namespace: common-example ++ namespace: mypattern-example + # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) + - kind: ServiceAccount + name: example-gitops-argocd-dex-server +- namespace: common-example ++ namespace: mypattern-example ++--- ++# Source: pattern-clustergroup/templates/imperative/role.yaml ++apiVersion: rbac.authorization.k8s.io/v1 ++kind: Role ++metadata: ++ name: imperative-role ++ namespace: imperative ++rules: ++ - apiGroups: ++ - '*' ++ resources: ++ - '*' ++ verbs: ++ - '*' ++--- ++# Source: pattern-clustergroup/templates/imperative/rbac.yaml ++apiVersion: rbac.authorization.k8s.io/v1 ++kind: RoleBinding ++metadata: ++ name: imperative-admin-rolebinding ++ namespace: imperative ++roleRef: ++ apiGroup: rbac.authorization.k8s.io ++ kind: Role ++ name: imperative-role ++subjects: ++ - kind: ServiceAccount ++ name: imperative-sa ++ namespace: imperative ++--- ++# Source: pattern-clustergroup/templates/imperative/job.yaml ++apiVersion: batch/v1 ++kind: CronJob ++metadata: ++ name: imperative-cronjob ++ namespace: imperative ++spec: ++ schedule: "*/10 * * * *" ++ # if previous Job is still running, skip execution of a new Job ++ concurrencyPolicy: Forbid ++ jobTemplate: ++ spec: ++ activeDeadlineSeconds: 3600 ++ template: ++ metadata: ++ name: imperative-job ++ spec: ++ serviceAccountName: imperative-sa ++ initContainers: ++ # git init happens in /git/repo so that we can set the folder to 0770 permissions ++ # reason for that is ansible refuses to create temporary folders in there ++ - name: git-init ++ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest ++ imagePullPolicy: Always ++ env: ++ - name: HOME ++ value: /git/home ++ command: ++ - 'sh' ++ - '-c' ++ - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" ++ volumeMounts: ++ - name: git ++ mountPath: "/git" ++ - name: test ++ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest ++ imagePullPolicy: Always ++ env: ++ - name: HOME ++ value: /git/home ++ workingDir: /git/repo ++ # We have a default timeout of 600s for each playbook. Can be overridden ++ # on a per-job basis ++ command: ++ - timeout ++ - "600" ++ - ansible-playbook ++ - -e ++ - "@/values/values.yaml" ++ - ansible/test.yml ++ volumeMounts: ++ - name: git ++ mountPath: "/git" ++ - name: values-volume ++ mountPath: /values/values.yaml ++ subPath: values.yaml ++ containers: ++ - name: "done" ++ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest ++ imagePullPolicy: Always ++ command: ++ - 'sh' ++ - '-c' ++ - 'echo' ++ - 'done' ++ - '\n' ++ volumes: ++ - name: git ++ emptyDir: {} ++ - name: values-volume ++ configMap: ++ name: helm-values-configmap-example ++ restartPolicy: Never ++--- ++# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml ++apiVersion: batch/v1 ++kind: CronJob ++metadata: ++ name: unsealvault-cronjob ++ namespace: imperative ++spec: ++ schedule: "*/5 * * * *" ++ # if previous Job is still running, skip execution of a new Job ++ concurrencyPolicy: Forbid ++ jobTemplate: ++ spec: ++ activeDeadlineSeconds: 3600 ++ template: ++ metadata: ++ name: unsealvault-job ++ spec: ++ serviceAccountName: imperative-sa ++ initContainers: ++ # git init happens in /git/repo so that we can set the folder to 0770 permissions ++ # reason for that is ansible refuses to create temporary folders in there ++ - name: git-init ++ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest ++ imagePullPolicy: Always ++ env: ++ - name: HOME ++ value: /git/home ++ command: ++ - 'sh' ++ - '-c' ++ - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" ++ volumeMounts: ++ - name: git ++ mountPath: "/git" ++ - name: unseal-playbook ++ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest ++ imagePullPolicy: Always ++ env: ++ - name: HOME ++ value: /git/home ++ workingDir: /git/repo ++ # We have a default timeout of 600s for each playbook. Can be overridden ++ # on a per-job basis ++ command: ++ - timeout ++ - "600" ++ - ansible-playbook ++ - -e ++ - "@/values/values.yaml" ++ - -e ++ - '{"file_unseal": false}' ++ - -t ++ - 'vault_init,vault_unseal,vault_secrets_init' ++ - "common/ansible/playbooks/vault/vault.yaml" ++ volumeMounts: ++ - name: git ++ mountPath: "/git" ++ - name: values-volume ++ mountPath: /values/values.yaml ++ subPath: values.yaml ++ containers: ++ - name: "done" ++ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest ++ imagePullPolicy: Always ++ command: ++ - 'sh' ++ - '-c' ++ - 'echo' ++ - 'done' ++ - '\n' ++ volumes: ++ - name: git ++ emptyDir: {} ++ - name: values-volume ++ configMap: ++ name: helm-values-configmap-example ++ restartPolicy: Never ++--- ++# Source: pattern-clustergroup/templates/core/subscriptions.yaml ++--- ++--- ++# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml ++apiVersion: argoproj.io/v1alpha1 ++kind: AppProject ++metadata: ++ name: argo-edge ++ namespace: openshift-gitops ++spec: ++ description: "Cluster Group argo-edge" ++ destinations: ++ - namespace: '*' ++ server: '*' ++ clusterResourceWhitelist: ++ - group: '*' ++ kind: '*' ++ namespaceResourceWhitelist: ++ - group: '*' ++ kind: '*' ++ sourceRepos: ++ - '*' ++status: {} ++--- ++# Source: pattern-clustergroup/templates/plumbing/projects.yaml ++apiVersion: argoproj.io/v1alpha1 ++kind: AppProject ++metadata: ++ name: datacenter ++ namespace: mypattern-example ++spec: ++ description: "Pattern datacenter" ++ destinations: ++ - namespace: '*' ++ server: '*' ++ clusterResourceWhitelist: ++ - group: '*' ++ kind: '*' ++ namespaceResourceWhitelist: ++ - group: '*' ++ kind: '*' ++ sourceRepos: ++ - '*' ++status: {} ++--- ++# Source: pattern-clustergroup/templates/plumbing/applications.yaml ++apiVersion: argoproj.io/v1alpha1 ++kind: Application ++metadata: ++ name: acm ++ namespace: mypattern-example ++ finalizers: ++ - resources-finalizer.argocd.argoproj.io/foreground ++spec: ++ destination: ++ name: in-cluster ++ namespace: open-cluster-management ++ project: datacenter ++ source: ++ repoURL: https://github.com/pattern-clone/mypattern ++ targetRevision: main ++ path: common/acm ++ helm: ++ ignoreMissingValueFiles: true ++ valueFiles: ++ - "/values-global.yaml" ++ - "/values-example.yaml" ++ # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly ++ parameters: ++ - name: global.repoURL ++ value: $ARGOCD_APP_SOURCE_REPO_URL ++ - name: global.targetRevision ++ value: $ARGOCD_APP_SOURCE_TARGET_REVISION ++ - name: global.namespace ++ value: $ARGOCD_APP_NAMESPACE ++ - name: global.pattern ++ value: mypattern ++ - name: global.clusterDomain ++ value: region.example.com ++ - name: global.hubClusterDomain ++ value: apps.hub.example.com ++ - name: global.localClusterDomain ++ value: apps.region.example.com ++ ignoreDifferences: [ ++ { ++ "group": "internal.open-cluster-management.io", ++ "jsonPointers": [ ++ "/spec/loggingCA" ++ ], ++ "kind": "ManagedClusterInfo" ++ } ++] ++ syncPolicy: ++ automated: {} ++ # selfHeal: true ++--- ++# Source: pattern-clustergroup/templates/plumbing/applications.yaml ++apiVersion: argoproj.io/v1alpha1 ++kind: Application ++metadata: ++ name: pipelines ++ namespace: mypattern-example ++ finalizers: ++ - resources-finalizer.argocd.argoproj.io/foreground ++spec: ++ destination: ++ name: in-cluster ++ namespace: application-ci ++ project: datacenter ++ source: ++ repoURL: https://github.com/pattern-clone/mypattern ++ targetRevision: main ++ path: charts/datacenter/pipelines ++ helm: ++ ignoreMissingValueFiles: true ++ valueFiles: ++ - "/values-global.yaml" ++ - "/values-example.yaml" ++ # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly ++ parameters: ++ - name: global.repoURL ++ value: $ARGOCD_APP_SOURCE_REPO_URL ++ - name: global.targetRevision ++ value: $ARGOCD_APP_SOURCE_TARGET_REVISION ++ - name: global.namespace ++ value: $ARGOCD_APP_NAMESPACE ++ - name: global.pattern ++ value: mypattern ++ - name: global.clusterDomain ++ value: region.example.com ++ - name: global.hubClusterDomain ++ value: apps.hub.example.com ++ - name: global.localClusterDomain ++ value: apps.region.example.com ++ syncPolicy: ++ automated: {} ++ # selfHeal: true ++--- ++# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml ++apiVersion: argoproj.io/v1alpha1 ++kind: Application ++metadata: ++ name: mypattern-argo-edge-perth ++ namespace: openshift-gitops ++ finalizers: ++ - resources-finalizer.argocd.argoproj.io/foreground ++spec: ++ project: argo-edge ++ source: ++ repoURL: https://github.com/pattern-clone/mypattern ++ targetRevision: main ++ path: common/clustergroup ++ helm: ++ ignoreMissingValueFiles: true ++ valueFiles: ++ - "/values-global.yaml" ++ - "/values-argo-edge.yaml" ++ parameters: ++ - name: global.repoURL ++ value: $ARGOCD_APP_SOURCE_REPO_URL ++ - name: global.targetRevision ++ value: $ARGOCD_APP_SOURCE_TARGET_REVISION ++ - name: global.namespace ++ value: $ARGOCD_APP_NAMESPACE ++ - name: global.pattern ++ value: mypattern ++ - name: global.hubClusterDomain ++ value: apps.hub.example.com ++ - name: global.localClusterDomain ++ value: apps.perth1.beekhof.net ++ - name: global.clusterDomain ++ value: perth1.beekhof.net ++ - name: enabled ++ value: core ++ - name: clusterGroup.name ++ value: argo-edge ++ - name: clusterGroup.targetCluster ++ value: perth ++ - name: clusterGroup.hostedSite.bearerKeyPath ++ value: secret/data/hub/cluster_perth ++ - name: clusterGroup.hostedSite.caKeyPath ++ value: secret/data/hub/cluster_perth_ca ++ - name: clusterGroup.isHubCluster ++ value: "false" ++ destination: ++ name: perth ++ namespace: mypattern-argo-edge ++ syncPolicy: ++ automated: ++ selfHeal: true ++ ignoreDifferences: ++ - group: apps ++ kind: Deployment ++ jsonPointers: ++ - /spec/replicas ++ - group: route.openshift.io ++ kind: Route ++ jsonPointers: ++ - /status ++--- ++# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml ++apiVersion: argoproj.io/v1alpha1 ++kind: Application ++metadata: ++ name: mypattern-argo-edge-perth-plumbing ++ namespace: openshift-gitops ++ finalizers: ++ - resources-finalizer.argocd.argoproj.io/foreground ++spec: ++ project: argo-edge ++ source: ++ repoURL: https://github.com/pattern-clone/mypattern ++ targetRevision: main ++ path: common/clustergroup ++ helm: ++ ignoreMissingValueFiles: true ++ valueFiles: ++ - "/values-global.yaml" ++ - "/values-argo-edge.yaml" ++ parameters: ++ - name: global.repoURL ++ value: $ARGOCD_APP_SOURCE_REPO_URL ++ - name: global.targetRevision ++ value: $ARGOCD_APP_SOURCE_TARGET_REVISION ++ - name: global.namespace ++ value: $ARGOCD_APP_NAMESPACE ++ - name: global.pattern ++ value: mypattern ++ - name: global.hubClusterDomain ++ value: apps.hub.example.com ++ - name: global.localClusterDomain ++ value: apps.perth1.beekhof.net ++ - name: global.clusterDomain ++ value: perth1.beekhof.net ++ - name: enabled ++ value: plumbing ++ - name: clusterGroup.name ++ value: argo-edge ++ - name: clusterGroup.targetCluster ++ value: perth ++ - name: clusterGroup.hostedSite.bearerKeyPath ++ value: secret/data/hub/cluster_perth ++ - name: clusterGroup.hostedSite.caKeyPath ++ value: secret/data/hub/cluster_perth_ca ++ - name: clusterGroup.isHubCluster ++ value: "false" ++ destination: ++ name: in-cluster ++ namespace: openshift-gitops ++ syncPolicy: ++ automated: ++ selfHeal: true ++ ignoreDifferences: ++ - group: apps ++ kind: Deployment ++ jsonPointers: ++ - /spec/replicas ++ - group: route.openshift.io ++ kind: Route ++ jsonPointers: ++ - /status ++--- ++# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml ++apiVersion: argoproj.io/v1alpha1 ++kind: Application ++metadata: ++ name: mypattern-argo-edge-sydney ++ namespace: openshift-gitops ++ finalizers: ++ - resources-finalizer.argocd.argoproj.io/foreground ++spec: ++ project: argo-edge ++ source: ++ repoURL: https://github.com/pattern-clone/mypattern ++ targetRevision: main ++ path: common/clustergroup ++ helm: ++ ignoreMissingValueFiles: true ++ valueFiles: ++ - "/values-global.yaml" ++ - "/values-argo-edge.yaml" ++ parameters: ++ - name: global.repoURL ++ value: $ARGOCD_APP_SOURCE_REPO_URL ++ - name: global.targetRevision ++ value: $ARGOCD_APP_SOURCE_TARGET_REVISION ++ - name: global.namespace ++ value: $ARGOCD_APP_NAMESPACE ++ - name: global.pattern ++ value: mypattern ++ - name: global.hubClusterDomain ++ value: apps.hub.example.com ++ - name: global.localClusterDomain ++ value: apps.syd.beekhof.net ++ - name: global.clusterDomain ++ value: syd.beekhof.net ++ - name: enabled ++ value: core ++ - name: clusterGroup.name ++ value: argo-edge ++ - name: clusterGroup.targetCluster ++ value: sydney ++ - name: clusterGroup.hostedSite.bearerKeyPath ++ value: secret/data/hub/cluster_sydney ++ - name: clusterGroup.hostedSite.caKeyPath ++ value: secret/data/hub/cluster_sydney_ca ++ - name: clusterGroup.isHubCluster ++ value: "false" ++ destination: ++ name: sydney ++ namespace: mypattern-argo-edge ++ syncPolicy: ++ automated: ++ selfHeal: true ++ ignoreDifferences: ++ - group: apps ++ kind: Deployment ++ jsonPointers: ++ - /spec/replicas ++ - group: route.openshift.io ++ kind: Route ++ jsonPointers: ++ - /status ++--- ++# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml ++apiVersion: argoproj.io/v1alpha1 ++kind: Application ++metadata: ++ name: mypattern-argo-edge-sydney-plumbing ++ namespace: openshift-gitops ++ finalizers: ++ - resources-finalizer.argocd.argoproj.io/foreground ++spec: ++ project: argo-edge ++ source: ++ repoURL: https://github.com/pattern-clone/mypattern ++ targetRevision: main ++ path: common/clustergroup ++ helm: ++ ignoreMissingValueFiles: true ++ valueFiles: ++ - "/values-global.yaml" ++ - "/values-argo-edge.yaml" ++ parameters: ++ - name: global.repoURL ++ value: $ARGOCD_APP_SOURCE_REPO_URL ++ - name: global.targetRevision ++ value: $ARGOCD_APP_SOURCE_TARGET_REVISION ++ - name: global.namespace ++ value: $ARGOCD_APP_NAMESPACE ++ - name: global.pattern ++ value: mypattern ++ - name: global.hubClusterDomain ++ value: apps.hub.example.com ++ - name: global.localClusterDomain ++ value: apps.syd.beekhof.net ++ - name: global.clusterDomain ++ value: syd.beekhof.net ++ - name: enabled ++ value: plumbing ++ - name: clusterGroup.name ++ value: argo-edge ++ - name: clusterGroup.targetCluster ++ value: sydney ++ - name: clusterGroup.hostedSite.bearerKeyPath ++ value: secret/data/hub/cluster_sydney ++ - name: clusterGroup.hostedSite.caKeyPath ++ value: secret/data/hub/cluster_sydney_ca ++ - name: clusterGroup.isHubCluster ++ value: "false" ++ destination: ++ name: in-cluster ++ namespace: openshift-gitops ++ syncPolicy: ++ automated: ++ selfHeal: true ++ ignoreDifferences: ++ - group: apps ++ kind: Deployment ++ jsonPointers: ++ - /spec/replicas ++ - group: route.openshift.io ++ kind: Route ++ jsonPointers: ++ - /status + --- + # Source: pattern-clustergroup/templates/plumbing/argocd.yaml + apiVersion: argoproj.io/v1alpha1 +@@ -65,7 +858,7 @@ + # Changing the name affects the ClusterRoleBinding, the generated secret, + # route URL, and argocd.argoproj.io/managed-by annotations + name: example-gitops +- namespace: common-example ++ namespace: mypattern-example + annotations: + argocd.argoproj.io/compare-options: IgnoreExtraneous + spec: +@@ -94,10 +887,10 @@ + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE +- --set global.pattern=common +- --set global.clusterDomain= +- --set global.hubClusterDomain= +- --set global.localClusterDomain= ++ --set global.pattern=mypattern ++ --set global.clusterDomain=region.example.com ++ --set global.hubClusterDomain=apps.hub.example.com ++ --set global.localClusterDomain=apps.region.example.com + --set clusterGroup.name=example + --post-renderer ./kustomize"] + applicationSet: +@@ -174,11 +967,59 @@ + kind: ConsoleLink + metadata: + name: example-gitops-link +- namespace: common-example ++ namespace: mypattern-example + spec: + applicationMenu: + section: OpenShift GitOps + imageURL: data:image/png;base64,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 +- href: 'https://example-gitops-server-common-example.' ++ href: 'https://example-gitops-server-mypattern-example.apps.region.example.com' + location: ApplicationMenu + text: 'Example ArgoCD' ++--- ++# Source: pattern-clustergroup/templates/core/operatorgroup.yaml ++apiVersion: operators.coreos.com/v1 ++kind: OperatorGroup ++metadata: ++ name: open-cluster-management-operator-group ++ namespace: open-cluster-management ++spec: ++ targetNamespaces: ++ - open-cluster-management ++--- ++# Source: pattern-clustergroup/templates/core/operatorgroup.yaml ++apiVersion: operators.coreos.com/v1 ++kind: OperatorGroup ++metadata: ++ name: application-ci-operator-group ++ namespace: application-ci ++spec: ++ targetNamespaces: ++ - application-ci ++--- ++# Source: pattern-clustergroup/templates/core/subscriptions.yaml ++apiVersion: operators.coreos.com/v1alpha1 ++kind: Subscription ++metadata: ++ name: advanced-cluster-management ++ namespace: open-cluster-management ++spec: ++ name: advanced-cluster-management ++ source: redhat-operators ++ sourceNamespace: openshift-marketplace ++ channel: release-2.4 ++ installPlanApproval: Automatic ++ startingCSV: advanced-cluster-management.v2.4.1 ++--- ++# Source: pattern-clustergroup/templates/core/subscriptions.yaml ++apiVersion: operators.coreos.com/v1alpha1 ++kind: Subscription ++metadata: ++ name: openshift-pipelines-operator-rh ++ namespace: openshift-operators ++spec: ++ name: openshift-pipelines-operator-rh ++ source: redhat-operators ++ sourceNamespace: openshift-marketplace ++ channel: stable ++ installPlanApproval: Automatic ++ startingCSV: redhat-openshift-pipelines.v1.5.2 diff --git a/tests/examples-blank-normal.expected.yaml.variant b/tests/examples-blank-normal.expected.yaml.variant new file mode 100644 index 00000000..e69de29b diff --git a/tests/examples-kustomize-renderer-normal.expected.yaml.variant b/tests/examples-kustomize-renderer-normal.expected.yaml.variant new file mode 100644 index 00000000..47be768e --- /dev/null +++ b/tests/examples-kustomize-renderer-normal.expected.yaml.variant @@ -0,0 +1,19 @@ +--- tests/examples-kustomize-renderer-naked.expected.yaml ++++ tests/examples-kustomize-renderer-normal.expected.yaml +@@ -7,12 +7,12 @@ + data: + IMAGE_PROVIDER: + IMAGE_ACCOUNT: PLAINTEXT +- GIT_EMAIL: SOMEWHERE@EXAMPLE.COM +- GIT_DEV_REPO_URL: https:///PLAINTEXT/manuela-dev.git ++ GIT_EMAIL: someone@somewhere.com ++ GIT_DEV_REPO_URL: https://github.com/hybrid-cloud-patterns/manuela-dev.git + GIT_DEV_REPO_REVISION: main +- GIT_OPS_REPO_TEST_URL: ++ GIT_OPS_REPO_TEST_URL: https://github.com/pattern-clone/mypattern + GIT_OPS_REPO_TEST_REVISION: +- GIT_OPS_REPO_PROD_URL: ++ GIT_OPS_REPO_PROD_URL: https://github.com/pattern-clone/mypattern + GIT_OPS_REPO_PROD_REVISION: + IOT_CONSUMER_IMAGE: iot-consumer + IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag diff --git a/tests/golang-external-secrets-normal.expected.yaml.variant b/tests/golang-external-secrets-normal.expected.yaml.variant new file mode 100644 index 00000000..d9420c80 --- /dev/null +++ b/tests/golang-external-secrets-normal.expected.yaml.variant @@ -0,0 +1,11 @@ +--- tests/golang-external-secrets-naked.expected.yaml ++++ tests/golang-external-secrets-normal.expected.yaml 2022-10-03 11:27:20.419172850 +1100 +@@ -5827,7 +5827,7 @@ + spec: + provider: + vault: +- server: https://vault-vault.hub.example.com ++ server: https://vault-vault.apps.hub.example.com + path: secret + # Version of KV backend + version: v2 diff --git a/tests/hashicorp-vault-normal.expected.yaml.variant b/tests/hashicorp-vault-normal.expected.yaml.variant new file mode 100644 index 00000000..254ff7a6 --- /dev/null +++ b/tests/hashicorp-vault-normal.expected.yaml.variant @@ -0,0 +1,11 @@ +--- tests/hashicorp-vault-naked.expected.yaml 2022-10-03 11:27:20.420172850 +1100 ++++ tests/hashicorp-vault-normal.expected.yaml 2022-10-03 11:27:20.420172850 +1100 +@@ -340,7 +340,7 @@ + applicationMenu: + section: HashiCorp Vault + imageURL: data:image/png;base64,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 +- href: 'https://vault-vault.apps.foo.cluster.com' ++ href: 'https://vault-vault.apps.region.example.com' + location: ApplicationMenu + text: 'Vault' + --- diff --git a/tests/install-normal.expected.yaml.variant b/tests/install-normal.expected.yaml.variant new file mode 100644 index 00000000..612d0975 --- /dev/null +++ b/tests/install-normal.expected.yaml.variant @@ -0,0 +1,43 @@ +--- tests/install-naked.expected.yaml ++++ tests/install-normal.expected.yaml 2022-10-03 11:27:20.420172850 +1100 +@@ -11,14 +11,14 @@ + apiVersion: argoproj.io/v1alpha1 + kind: Application + metadata: +- name: install-default ++ name: install-example + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground + spec: + destination: + name: in-cluster +- namespace: install-default ++ namespace: install-example + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern +@@ -28,7 +28,7 @@ + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" +- - "/values-default.yaml" ++ - "/values-example.yaml" + # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 + parameters: + - name: global.repoURL +@@ -40,7 +40,7 @@ + - name: global.pattern + value: install + - name: global.hubClusterDomain +- value: ++ value: apps.hub.example.com + syncPolicy: + automated: {} + --- +@@ -61,4 +61,4 @@ + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES +- value: install-default,openshift-gitops ++ value: install-example,openshift-gitops diff --git a/tests/operator-install-normal.expected.yaml.variant b/tests/operator-install-normal.expected.yaml.variant new file mode 100644 index 00000000..a7095356 --- /dev/null +++ b/tests/operator-install-normal.expected.yaml.variant @@ -0,0 +1,11 @@ +--- tests/operator-install-naked.expected.yaml ++++ tests/operator-install-normal.expected.yaml +@@ -6,7 +6,7 @@ + name: operator-install + namespace: openshift-operators + spec: +- clusterGroupName: default ++ clusterGroupName: example + gitSpec: + targetRepo: https://github.com/pattern-clone/mypattern + targetRevision: main From 01de50f1a2b2eaad4a5b4c5a3703dddff445ce65 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 12:25:09 +1100 Subject: [PATCH 0542/1263] Group tests by chart for clarity, rename outputs for when searching templates --- Makefile | 5 +- scripts/test.sh | 111 ++++++++++++------ ...d.expected.yaml => acm-naked.expected.yml} | 0 ....expected.yaml => acm-normal.expected.yml} | 0 ...xpected.yaml.variant => acm.expected.diff} | 4 +- ...d.yaml => clustergroup-naked.expected.yml} | 0 ....yaml => clustergroup-normal.expected.yml} | 0 ...aml.variant => clustergroup.expected.diff} | 4 +- ...yaml => examples-blank-naked.expected.yml} | 0 ...aml => examples-blank-normal.expected.yml} | 0 ...l.variant => examples-blank.expected.diff} | 0 ...les-kustomize-renderer-naked.expected.yml} | 0 ...es-kustomize-renderer-normal.expected.yml} | 0 ...examples-kustomize-renderer.expected.diff} | 4 +- ...olang-external-secrets-naked.expected.yml} | 0 ...lang-external-secrets-normal.expected.yml} | 0 ... => golang-external-secrets.expected.diff} | 4 +- ...aml => hashicorp-vault-naked.expected.yml} | 0 ...ml => hashicorp-vault-normal.expected.yml} | 0 ....variant => hashicorp-vault.expected.diff} | 4 +- ...pected.yaml => install-naked.expected.yml} | 0 ...ected.yaml => install-normal.expected.yml} | 0 ...ted.yaml.variant => install.expected.diff} | 4 +- ...ml => operator-install-naked.expected.yml} | 0 ...l => operator-install-normal.expected.yml} | 0 ...variant => operator-install.expected.diff} | 4 +- 26 files changed, 93 insertions(+), 51 deletions(-) rename tests/{acm-naked.expected.yaml => acm-naked.expected.yml} (100%) rename tests/{acm-normal.expected.yaml => acm-normal.expected.yml} (100%) rename tests/{acm-normal.expected.yaml.variant => acm.expected.diff} (99%) rename tests/{clustergroup-naked.expected.yaml => clustergroup-naked.expected.yml} (100%) rename tests/{clustergroup-normal.expected.yaml => clustergroup-normal.expected.yml} (100%) rename tests/{clustergroup-normal.expected.yaml.variant => clustergroup.expected.diff} (99%) rename tests/{examples-blank-naked.expected.yaml => examples-blank-naked.expected.yml} (100%) rename tests/{examples-blank-normal.expected.yaml => examples-blank-normal.expected.yml} (100%) rename tests/{examples-blank-normal.expected.yaml.variant => examples-blank.expected.diff} (100%) rename tests/{examples-kustomize-renderer-naked.expected.yaml => examples-kustomize-renderer-naked.expected.yml} (100%) rename tests/{examples-kustomize-renderer-normal.expected.yaml => examples-kustomize-renderer-normal.expected.yml} (100%) rename tests/{examples-kustomize-renderer-normal.expected.yaml.variant => examples-kustomize-renderer.expected.diff} (84%) rename tests/{golang-external-secrets-naked.expected.yaml => golang-external-secrets-naked.expected.yml} (100%) rename tests/{golang-external-secrets-normal.expected.yaml => golang-external-secrets-normal.expected.yml} (100%) rename tests/{golang-external-secrets-normal.expected.yaml.variant => golang-external-secrets.expected.diff} (61%) rename tests/{hashicorp-vault-naked.expected.yaml => hashicorp-vault-naked.expected.yml} (100%) rename tests/{hashicorp-vault-normal.expected.yaml => hashicorp-vault-normal.expected.yml} (100%) rename tests/{hashicorp-vault-normal.expected.yaml.variant => hashicorp-vault.expected.diff} (98%) rename tests/{install-naked.expected.yaml => install-naked.expected.yml} (100%) rename tests/{install-normal.expected.yaml => install-normal.expected.yml} (100%) rename tests/{install-normal.expected.yaml.variant => install.expected.diff} (90%) rename tests/{operator-install-naked.expected.yaml => operator-install-naked.expected.yml} (100%) rename tests/{operator-install-normal.expected.yaml => operator-install-normal.expected.yml} (100%) rename tests/{operator-install-normal.expected.yaml.variant => operator-install.expected.diff} (71%) diff --git a/Makefile b/Makefile index 39fa1c16..8901e3ae 100644 --- a/Makefile +++ b/Makefile @@ -29,10 +29,7 @@ show: ## show the starting template without installing it CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | sed -e 's/.\///') test: ## run helm tests -# Test that all values used by the chart are in values.yaml with the same defaults as the pattern - @for t in $(CHARTS); do common/scripts/test.sh $$t naked ""; if [ $$? != 0 ]; then exit 1; fi; done -# Test the charts as the pattern would drive them - @for t in $(CHARTS); do common/scripts/test.sh $$t normal "$(TEST_OPTS) $(PATTERN_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done + @for t in $(CHARTS); do common/scripts/test.sh $$t all "$(TEST_OPTS) $(PATTERN_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done helmlint: ## run helm lint @for t in $(CHARTS); do helm lint $(TEST_OPTS) $(PATTERN_OPTS) $$t; if [ $$? != 0 ]; then exit 1; fi; done diff --git a/scripts/test.sh b/scripts/test.sh index 31ef74b8..4cc292e1 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -7,48 +7,93 @@ unset KUBECONFIG target=$1 name=$(echo $1 | sed -e s@/@-@g -e s@charts-@@) -TEST_VARIANT="$2" -CHART_OPTS="$3" - -TESTDIR=tests -REFERENCE=${TESTDIR}/${name}-${TEST_VARIANT}.expected.yaml -OUTPUT=${TESTDIR}/.${name}-${TEST_VARIANT}.expected.yaml -#REFERENCE=${TESTDIR}/${name}.expected.yaml -#OUTPUT=${TESTDIR}/.${name}.expected.yaml - -echo "Testing $1 chart (${TEST_VARIANT})" >&2 -helm template --kubeconfig /tmp/doesnotexistever $target --name-template $name ${CHART_OPTS} > ${OUTPUT} -rc=$? -if [ $rc -ne 0 ]; then - echo "FAIL on helm template $target --name-template $name ${CHART_OPTS}" - exit 1 -fi -if [ ! -e ${REFERENCE} ]; then - cp ${OUTPUT} ${REFERENCE} -fi -diff -u ${REFERENCE} ${OUTPUT} -rc=$? -if [ $TEST_VARIANT = normal -a $rc = 0 ]; then +function doTest() { + TEST_VARIANT=$1 + CHART_OPTS="$2" + TESTDIR=tests + TEST=${name}-${TEST_VARIANT} + FILENAME=${TEST}.expected.yml + OUTPUT=${TESTDIR}/.${FILENAME} + REFERENCE=${TESTDIR}/${FILENAME} + + echo "Testing $name chart (${TEST_VARIANT})" >&2 + helm template --kubeconfig /tmp/doesnotexistever $target --name-template $name ${CHART_OPTS} > ${OUTPUT} + rc=$? + if [ $rc -ne 0 ]; then + echo "FAIL on helm template $target --name-template $name ${CHART_OPTS}" + exit 1 + fi + if [ ! -e ${REFERENCE} ]; then + cp ${OUTPUT} ${REFERENCE} + echo -e "\n\n#### Created test output\007\n#### Now add ${REFERENCE} to Git\n\n\007" + exit 2 + fi + diff -u ${REFERENCE} ${OUTPUT} + rc=$? + if [ $rc = 0 ]; then + rm -f ${TESTDIR}/.${name}.* + echo -e "PASS on $name $TEST_VARIANT with opts [$CHART_OPTS]\n" + else + echo -e "FAIL on $name $TEST_VARIANT with opts [$CHART_OPTS]\n" + exit $rc + fi +} + +function doTestCompare() { + TEST_VARIANT="differences" + TESTDIR=tests + TEST=${name} + FILENAME=${TEST}.expected.yml + OUTPUT=${TESTDIR}/.${FILENAME} + REFERENCE=${TESTDIR}/${FILENAME} + + echo "Testing $name chart (${TEST_VARIANT})" >&2 # Another method of finding variables missing from values.yaml, eg. # - name: -datacenter # + name: pattern-name-datacenter - diff -u ${TESTDIR}/${name}-naked.expected.yaml ${TESTDIR}/${name}-normal.expected.yaml | sed 's/20[0-9][0-9]-[09][0-9].*//' > ${OUTPUT}.variant - if [ ! -e ${REFERENCE}.variant ]; then - cp ${OUTPUT}.variant ${REFERENCE}.variant + TEST=${name} + FILENAME=${TEST}.expected.diff + OUTPUT=${TESTDIR}/.${FILENAME} + REFERENCE=${TESTDIR}/${FILENAME} + + # Drop the date from the diff output, it will not be stable + diff -u ${TESTDIR}/${name}-naked.expected.yml ${TESTDIR}/${name}-normal.expected.yml | sed 's/\.yml.*20[0-9][0-9].*/.yml/g' > ${OUTPUT} + + if [ ! -e ${REFERENCE} ]; then + cp ${OUTPUT} ${REFERENCE} + echo -e "\n\n#### Created test output\007\n#### Now add ${REFERENCE} to Git\n\n\007" + exit 2 fi - diff -u ${REFERENCE}.variant ${OUTPUT}.variant + diff -u ${REFERENCE} ${OUTPUT} rc=$? -fi -if [ $rc = 0 ]; then - rm -f ${OUTPUT} - rm -f ${OUTPUT}.variant - echo "PASS on $target $TEST_VARIANT with opts [$CHART_OPTS]" + if [ $rc = 0 ]; then + rm -f ${TESTDIR}/.${name}.* + echo -e "PASS on $name $TEST_VARIANT with opts [$CHART_OPTS]\n" + else + echo -e "FAIL on $name $TEST_VARIANT with opts [$CHART_OPTS]\n" + exit $rc + fi +} + +if [ $2 = "all" ]; then + echo -e "\n#####################" + echo -e "### ${name}" + echo -e "#####################\n" + + # Test that all values used by the chart are in values.yaml with the same defaults as the pattern + doTest naked + + # Test the charts as the pattern would drive them + doTest normal "$3" + + # Ensure the differences between the two results are also stable + doTestCompare else - echo "FAIL on $target $TEST_VARIANT with opts [$CHART_OPTS]" + doTest $2 "$3" fi -exit $rc +exit 0 diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yml similarity index 100% rename from tests/acm-naked.expected.yaml rename to tests/acm-naked.expected.yml diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yml similarity index 100% rename from tests/acm-normal.expected.yaml rename to tests/acm-normal.expected.yml diff --git a/tests/acm-normal.expected.yaml.variant b/tests/acm.expected.diff similarity index 99% rename from tests/acm-normal.expected.yaml.variant rename to tests/acm.expected.diff index 3e1c4dae..9cf23621 100644 --- a/tests/acm-normal.expected.yaml.variant +++ b/tests/acm.expected.diff @@ -1,5 +1,5 @@ ---- tests/acm-naked.expected.yaml -+++ tests/acm-normal.expected.yaml 2022-10-03 11:27:20.418172850 +1100 +--- tests/acm-naked.expected.yml ++++ tests/acm-normal.expected.yml @@ -1,6 +1,386 @@ --- -# Source: acm/templates/policies/application-policies.yaml diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yml similarity index 100% rename from tests/clustergroup-naked.expected.yaml rename to tests/clustergroup-naked.expected.yml diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yml similarity index 100% rename from tests/clustergroup-normal.expected.yaml rename to tests/clustergroup-normal.expected.yml diff --git a/tests/clustergroup-normal.expected.yaml.variant b/tests/clustergroup.expected.diff similarity index 99% rename from tests/clustergroup-normal.expected.yaml.variant rename to tests/clustergroup.expected.diff index 36996c57..b1bb3239 100644 --- a/tests/clustergroup-normal.expected.yaml.variant +++ b/tests/clustergroup.expected.diff @@ -1,5 +1,5 @@ ---- tests/clustergroup-naked.expected.yaml 2022-10-03 11:27:20.418172850 +1100 -+++ tests/clustergroup-normal.expected.yaml 2022-10-03 11:27:20.418172850 +1100 +--- tests/clustergroup-naked.expected.yml ++++ tests/clustergroup-normal.expected.yml @@ -1,17 +1,243 @@ --- +# Source: pattern-clustergroup/templates/core/namespaces.yaml diff --git a/tests/examples-blank-naked.expected.yaml b/tests/examples-blank-naked.expected.yml similarity index 100% rename from tests/examples-blank-naked.expected.yaml rename to tests/examples-blank-naked.expected.yml diff --git a/tests/examples-blank-normal.expected.yaml b/tests/examples-blank-normal.expected.yml similarity index 100% rename from tests/examples-blank-normal.expected.yaml rename to tests/examples-blank-normal.expected.yml diff --git a/tests/examples-blank-normal.expected.yaml.variant b/tests/examples-blank.expected.diff similarity index 100% rename from tests/examples-blank-normal.expected.yaml.variant rename to tests/examples-blank.expected.diff diff --git a/tests/examples-kustomize-renderer-naked.expected.yaml b/tests/examples-kustomize-renderer-naked.expected.yml similarity index 100% rename from tests/examples-kustomize-renderer-naked.expected.yaml rename to tests/examples-kustomize-renderer-naked.expected.yml diff --git a/tests/examples-kustomize-renderer-normal.expected.yaml b/tests/examples-kustomize-renderer-normal.expected.yml similarity index 100% rename from tests/examples-kustomize-renderer-normal.expected.yaml rename to tests/examples-kustomize-renderer-normal.expected.yml diff --git a/tests/examples-kustomize-renderer-normal.expected.yaml.variant b/tests/examples-kustomize-renderer.expected.diff similarity index 84% rename from tests/examples-kustomize-renderer-normal.expected.yaml.variant rename to tests/examples-kustomize-renderer.expected.diff index 47be768e..cee20d02 100644 --- a/tests/examples-kustomize-renderer-normal.expected.yaml.variant +++ b/tests/examples-kustomize-renderer.expected.diff @@ -1,5 +1,5 @@ ---- tests/examples-kustomize-renderer-naked.expected.yaml -+++ tests/examples-kustomize-renderer-normal.expected.yaml +--- tests/examples-kustomize-renderer-naked.expected.yml ++++ tests/examples-kustomize-renderer-normal.expected.yml @@ -7,12 +7,12 @@ data: IMAGE_PROVIDER: diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yml similarity index 100% rename from tests/golang-external-secrets-naked.expected.yaml rename to tests/golang-external-secrets-naked.expected.yml diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yml similarity index 100% rename from tests/golang-external-secrets-normal.expected.yaml rename to tests/golang-external-secrets-normal.expected.yml diff --git a/tests/golang-external-secrets-normal.expected.yaml.variant b/tests/golang-external-secrets.expected.diff similarity index 61% rename from tests/golang-external-secrets-normal.expected.yaml.variant rename to tests/golang-external-secrets.expected.diff index d9420c80..299cd616 100644 --- a/tests/golang-external-secrets-normal.expected.yaml.variant +++ b/tests/golang-external-secrets.expected.diff @@ -1,5 +1,5 @@ ---- tests/golang-external-secrets-naked.expected.yaml -+++ tests/golang-external-secrets-normal.expected.yaml 2022-10-03 11:27:20.419172850 +1100 +--- tests/golang-external-secrets-naked.expected.yml ++++ tests/golang-external-secrets-normal.expected.yml @@ -5827,7 +5827,7 @@ spec: provider: diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yml similarity index 100% rename from tests/hashicorp-vault-naked.expected.yaml rename to tests/hashicorp-vault-naked.expected.yml diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yml similarity index 100% rename from tests/hashicorp-vault-normal.expected.yaml rename to tests/hashicorp-vault-normal.expected.yml diff --git a/tests/hashicorp-vault-normal.expected.yaml.variant b/tests/hashicorp-vault.expected.diff similarity index 98% rename from tests/hashicorp-vault-normal.expected.yaml.variant rename to tests/hashicorp-vault.expected.diff index 254ff7a6..dbc3442f 100644 --- a/tests/hashicorp-vault-normal.expected.yaml.variant +++ b/tests/hashicorp-vault.expected.diff @@ -1,5 +1,5 @@ ---- tests/hashicorp-vault-naked.expected.yaml 2022-10-03 11:27:20.420172850 +1100 -+++ tests/hashicorp-vault-normal.expected.yaml 2022-10-03 11:27:20.420172850 +1100 +--- tests/hashicorp-vault-naked.expected.yml ++++ tests/hashicorp-vault-normal.expected.yml @@ -340,7 +340,7 @@ applicationMenu: section: HashiCorp Vault diff --git a/tests/install-naked.expected.yaml b/tests/install-naked.expected.yml similarity index 100% rename from tests/install-naked.expected.yaml rename to tests/install-naked.expected.yml diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yml similarity index 100% rename from tests/install-normal.expected.yaml rename to tests/install-normal.expected.yml diff --git a/tests/install-normal.expected.yaml.variant b/tests/install.expected.diff similarity index 90% rename from tests/install-normal.expected.yaml.variant rename to tests/install.expected.diff index 612d0975..563de641 100644 --- a/tests/install-normal.expected.yaml.variant +++ b/tests/install.expected.diff @@ -1,5 +1,5 @@ ---- tests/install-naked.expected.yaml -+++ tests/install-normal.expected.yaml 2022-10-03 11:27:20.420172850 +1100 +--- tests/install-naked.expected.yml ++++ tests/install-normal.expected.yml @@ -11,14 +11,14 @@ apiVersion: argoproj.io/v1alpha1 kind: Application diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yml similarity index 100% rename from tests/operator-install-naked.expected.yaml rename to tests/operator-install-naked.expected.yml diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yml similarity index 100% rename from tests/operator-install-normal.expected.yaml rename to tests/operator-install-normal.expected.yml diff --git a/tests/operator-install-normal.expected.yaml.variant b/tests/operator-install.expected.diff similarity index 71% rename from tests/operator-install-normal.expected.yaml.variant rename to tests/operator-install.expected.diff index a7095356..9f5bdedc 100644 --- a/tests/operator-install-normal.expected.yaml.variant +++ b/tests/operator-install.expected.diff @@ -1,5 +1,5 @@ ---- tests/operator-install-naked.expected.yaml -+++ tests/operator-install-normal.expected.yaml +--- tests/operator-install-naked.expected.yml ++++ tests/operator-install-normal.expected.yml @@ -6,7 +6,7 @@ name: operator-install namespace: openshift-operators From edcd4495dc759582ca9cdd548201596cf376793a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 12:30:59 +1100 Subject: [PATCH 0543/1263] Tweak output --- scripts/test.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index 4cc292e1..c806d896 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -17,7 +17,7 @@ function doTest() { OUTPUT=${TESTDIR}/.${FILENAME} REFERENCE=${TESTDIR}/${FILENAME} - echo "Testing $name chart (${TEST_VARIANT})" >&2 + echo -e "\nTesting $name chart (${TEST_VARIANT}) with opts [$CHART_OPTS]" >&2 helm template --kubeconfig /tmp/doesnotexistever $target --name-template $name ${CHART_OPTS} > ${OUTPUT} rc=$? if [ $rc -ne 0 ]; then @@ -33,9 +33,9 @@ function doTest() { rc=$? if [ $rc = 0 ]; then rm -f ${TESTDIR}/.${name}.* - echo -e "PASS on $name $TEST_VARIANT with opts [$CHART_OPTS]\n" + echo "PASS" >&2 else - echo -e "FAIL on $name $TEST_VARIANT with opts [$CHART_OPTS]\n" + echo "FAIL" >&2 exit $rc fi } @@ -48,7 +48,7 @@ function doTestCompare() { OUTPUT=${TESTDIR}/.${FILENAME} REFERENCE=${TESTDIR}/${FILENAME} - echo "Testing $name chart (${TEST_VARIANT})" >&2 + echo -e "\nTesting $name chart (${TEST_VARIANT})" >&2 # Another method of finding variables missing from values.yaml, eg. # - name: -datacenter # + name: pattern-name-datacenter @@ -72,17 +72,17 @@ function doTestCompare() { if [ $rc = 0 ]; then rm -f ${TESTDIR}/.${name}.* - echo -e "PASS on $name $TEST_VARIANT with opts [$CHART_OPTS]\n" + echo "PASS" >&2 else - echo -e "FAIL on $name $TEST_VARIANT with opts [$CHART_OPTS]\n" + echo "FAIL" >&2 exit $rc fi } if [ $2 = "all" ]; then echo -e "\n#####################" - echo -e "### ${name}" - echo -e "#####################\n" + echo "### ${name}" + echo "#####################" # Test that all values used by the chart are in values.yaml with the same defaults as the pattern doTest naked From c095954cd2800b395736ae6e25266c560403c0fe Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 12:33:02 +1100 Subject: [PATCH 0544/1263] Update changes with implications for pattern authors --- Changes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changes.md b/Changes.md index d9d7f227..4706f903 100644 --- a/Changes.md +++ b/Changes.md @@ -1,6 +1,7 @@ # Changes ## October 3, 2022 +* Revised tests (new output and filenames, requires adding new result files to git) * ACM 2.6 required for ACM-based managed sites * Introduced global.clusterDomain template variable (without the `apps.` prefix) * Removed the ability to send specific charts to another cluster, use hosted argo sites instead From d2b4d8ec887fa09452caa009bd66bc220f469efb Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 12:41:02 +1100 Subject: [PATCH 0545/1263] Interactively prompt to update expected test outputs --- scripts/test.sh | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index c806d896..3f2d6fff 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -4,7 +4,6 @@ # This won't protect us if a user has ~/.kube # Also call helm template with a non existing --kubeconfig while we're at it unset KUBECONFIG - target=$1 name=$(echo $1 | sed -e s@/@-@g -e s@charts-@@) @@ -34,7 +33,18 @@ function doTest() { if [ $rc = 0 ]; then rm -f ${TESTDIR}/.${name}.* echo "PASS" >&2 - else + elif [ -z $GITHUB_ACTIONS ]; then + read -p "Are these changes expected? [y/N] " EXPECTED + case $EXPECTED in + y*|Y*) + echo "Updating ${REFERENCE}" + cp ${OUTPUT} ${REFERENCE} + rc=0 + ;; + *) ;; + esac + fi + if [ $rc != 0 ]; then echo "FAIL" >&2 exit $rc fi @@ -73,7 +83,18 @@ function doTestCompare() { if [ $rc = 0 ]; then rm -f ${TESTDIR}/.${name}.* echo "PASS" >&2 - else + elif [ -z $GITHUB_ACTIONS ]; then + read -p "Are these changes expected? [y/N] " EXPECTED + case $EXPECTED in + y*|Y*) + echo "Updating ${REFERENCE}" + cp ${OUTPUT} ${REFERENCE} + rc=0 + ;; + *) ;; + esac + fi + if [ $rc != 0 ]; then echo "FAIL" >&2 exit $rc fi From 128b8654876170137a77ae6ecc22140cb17d6e43 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 12:46:49 +1100 Subject: [PATCH 0546/1263] Consistent use of stderr --- scripts/test.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index 3f2d6fff..5aa754ef 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -20,12 +20,12 @@ function doTest() { helm template --kubeconfig /tmp/doesnotexistever $target --name-template $name ${CHART_OPTS} > ${OUTPUT} rc=$? if [ $rc -ne 0 ]; then - echo "FAIL on helm template $target --name-template $name ${CHART_OPTS}" + echo "FAIL on helm template $target --name-template $name ${CHART_OPTS}" >&2 exit 1 fi if [ ! -e ${REFERENCE} ]; then cp ${OUTPUT} ${REFERENCE} - echo -e "\n\n#### Created test output\007\n#### Now add ${REFERENCE} to Git\n\n\007" + echo -e "\n\n#### Created test output\007\n#### Now add ${REFERENCE} to Git\n\n\007" >&2 exit 2 fi diff -u ${REFERENCE} ${OUTPUT} @@ -73,7 +73,7 @@ function doTestCompare() { if [ ! -e ${REFERENCE} ]; then cp ${OUTPUT} ${REFERENCE} - echo -e "\n\n#### Created test output\007\n#### Now add ${REFERENCE} to Git\n\n\007" + echo -e "\n\n#### Created test output\007\n#### Now add ${REFERENCE} to Git\n\n\007" >&2 exit 2 fi @@ -101,9 +101,9 @@ function doTestCompare() { } if [ $2 = "all" ]; then - echo -e "\n#####################" - echo "### ${name}" - echo "#####################" + echo -e "\n#####################" >&2 + echo "### ${name}" >&2 + echo "#####################" >&2 # Test that all values used by the chart are in values.yaml with the same defaults as the pattern doTest naked From 27ac754fc55949faa8307c3aecbc10b31df5f4be Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 13:06:40 +1100 Subject: [PATCH 0547/1263] Allow an optional TARGET_SITE variable to drive main.clusterGroupName --- Changes.md | 1 + Makefile | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Changes.md b/Changes.md index 4706f903..09d9bd0b 100644 --- a/Changes.md +++ b/Changes.md @@ -1,6 +1,7 @@ # Changes ## October 3, 2022 +* Restore the ability to install a non-default site: `make TARGET_SITE=mysite install` * Revised tests (new output and filenames, requires adding new result files to git) * ACM 2.6 required for ACM-based managed sites * Introduced global.clusterDomain template variable (without the `apps.` prefix) diff --git a/Makefile b/Makefile index 8901e3ae..7ddcc947 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,10 @@ NAME=$(shell basename `pwd`) # This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL # This is because we expect to use tokens for repo authentication as opposed to SSH keys +ifneq ($(origin TARGET_SITE), undefined) + TARGET_SITE_OPT=--set main.clusterGroupName=$(TARGET_SITE) +endif + TARGET_ORIGIN ?= origin TARGET_REPO=$(shell git remote show $(TARGET_ORIGIN) | grep Push | sed -e 's/.*URL:[[:space:]]*//' -e 's%^git@%%' -e 's%^https://%%' -e 's%:%/%' -e 's%^%https://%') # git branch --show-current is also available as of git 2.22, but we will use this for compatibility @@ -9,7 +13,7 @@ HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spe # --set values always take precedence over the contents of -f HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) \ - --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) + --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) $(TARGET_SITE_OPT) TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" \ --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" \ --set global.namespace="pattern-namespace" --set global.hubClusterDomain=apps.hub.example.com --set global.localClusterDomain=apps.region.example.com --set global.clusterDomain=region.example.com\ From 5a6e9b0ca6692857fcb2386f5f2911a06f01cee2 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 13:39:39 +1100 Subject: [PATCH 0548/1263] Automatically add new tests to git --- scripts/test.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index 5aa754ef..040a4b17 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -71,10 +71,10 @@ function doTestCompare() { # Drop the date from the diff output, it will not be stable diff -u ${TESTDIR}/${name}-naked.expected.yml ${TESTDIR}/${name}-normal.expected.yml | sed 's/\.yml.*20[0-9][0-9].*/.yml/g' > ${OUTPUT} - if [ ! -e ${REFERENCE} ]; then + if [ ! -e ${REFERENCE} -a -z $GITHUB_ACTIONS ]; then cp ${OUTPUT} ${REFERENCE} - echo -e "\n\n#### Created test output\007\n#### Now add ${REFERENCE} to Git\n\n\007" >&2 - exit 2 + git add ${REFERENCE} + echo -e "\n\n#### Created test output\007\n\n\007" >&2 fi diff -u ${REFERENCE} ${OUTPUT} From 7a1b4f4382581e16396e515effcffeb3f859654c Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 13:46:47 +1100 Subject: [PATCH 0549/1263] Better test cleanup --- scripts/test.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index 040a4b17..a4077fb6 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -31,7 +31,7 @@ function doTest() { diff -u ${REFERENCE} ${OUTPUT} rc=$? if [ $rc = 0 ]; then - rm -f ${TESTDIR}/.${name}.* + rm -f ${OUTPUT} echo "PASS" >&2 elif [ -z $GITHUB_ACTIONS ]; then read -p "Are these changes expected? [y/N] " EXPECTED @@ -39,6 +39,7 @@ function doTest() { y*|Y*) echo "Updating ${REFERENCE}" cp ${OUTPUT} ${REFERENCE} + rm -f ${OUTPUT} rc=0 ;; *) ;; @@ -81,7 +82,7 @@ function doTestCompare() { rc=$? if [ $rc = 0 ]; then - rm -f ${TESTDIR}/.${name}.* + rm -f ${OUTPUT} echo "PASS" >&2 elif [ -z $GITHUB_ACTIONS ]; then read -p "Are these changes expected? [y/N] " EXPECTED @@ -89,6 +90,7 @@ function doTestCompare() { y*|Y*) echo "Updating ${REFERENCE}" cp ${OUTPUT} ${REFERENCE} + rm -f ${OUTPUT} rc=0 ;; *) ;; From 4ac39d05a2738fdb7fa7406a3f0a35f201022d0d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 3 Oct 2022 09:28:22 +0200 Subject: [PATCH 0550/1263] Fix common/ CI --- Changes.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Changes.md b/Changes.md index 09d9bd0b..0c1f9731 100644 --- a/Changes.md +++ b/Changes.md @@ -1,6 +1,7 @@ -# Changes +# Changes ## October 3, 2022 + * Restore the ability to install a non-default site: `make TARGET_SITE=mysite install` * Revised tests (new output and filenames, requires adding new result files to git) * ACM 2.6 required for ACM-based managed sites @@ -11,9 +12,9 @@ The following example would deploy the namespaces, subscriptions, and applications defined in `values-group-one.yaml` to the `perth` cluster directly from ArgoCD on the hub. - - ``` - managedClusterGroups: + + ```yaml + managedClusterGroups: - name: group-one hostedArgoSites: - name: perth From ecbd2f6ce0605c0166bc7a0818252d5ab7f46cdc Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 3 Oct 2022 20:23:57 +1100 Subject: [PATCH 0551/1263] Add all missing tests to git --- scripts/test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index a4077fb6..1280423d 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -25,8 +25,8 @@ function doTest() { fi if [ ! -e ${REFERENCE} ]; then cp ${OUTPUT} ${REFERENCE} - echo -e "\n\n#### Created test output\007\n#### Now add ${REFERENCE} to Git\n\n\007" >&2 - exit 2 + git add ${REFERENCE} + echo -e "\n\n#### Created test output\007\n\n\007" >&2 fi diff -u ${REFERENCE} ${OUTPUT} rc=$? From 0eee654d29d31a4430546d3414bf21e5260e1dea Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 3 Oct 2022 15:41:00 +0200 Subject: [PATCH 0552/1263] Rename tests back to .yaml They got renamed I think by accident to .yml. By leaving them with the .yaml we create less churn when updating common in the verious patterns --- scripts/test.sh | 6 +++--- tests/{acm-naked.expected.yml => acm-naked.expected.yaml} | 0 tests/{acm-normal.expected.yml => acm-normal.expected.yaml} | 0 tests/acm.expected.diff | 4 ++-- ...-naked.expected.yml => clustergroup-naked.expected.yaml} | 0 ...ormal.expected.yml => clustergroup-normal.expected.yaml} | 0 tests/clustergroup.expected.diff | 4 ++-- ...aked.expected.yml => examples-blank-naked.expected.yaml} | 0 ...mal.expected.yml => examples-blank-normal.expected.yaml} | 0 ....yml => examples-kustomize-renderer-naked.expected.yaml} | 0 ...yml => examples-kustomize-renderer-normal.expected.yaml} | 0 tests/examples-kustomize-renderer.expected.diff | 4 ++-- ...cted.yml => golang-external-secrets-naked.expected.yaml} | 0 ...ted.yml => golang-external-secrets-normal.expected.yaml} | 0 tests/golang-external-secrets.expected.diff | 4 ++-- ...ked.expected.yml => hashicorp-vault-naked.expected.yaml} | 0 ...al.expected.yml => hashicorp-vault-normal.expected.yaml} | 0 tests/hashicorp-vault.expected.diff | 4 ++-- ...stall-naked.expected.yml => install-naked.expected.yaml} | 0 ...all-normal.expected.yml => install-normal.expected.yaml} | 0 tests/install.expected.diff | 4 ++-- ...ed.expected.yml => operator-install-naked.expected.yaml} | 0 ...l.expected.yml => operator-install-normal.expected.yaml} | 0 tests/operator-install.expected.diff | 4 ++-- 24 files changed, 17 insertions(+), 17 deletions(-) rename tests/{acm-naked.expected.yml => acm-naked.expected.yaml} (100%) rename tests/{acm-normal.expected.yml => acm-normal.expected.yaml} (100%) rename tests/{clustergroup-naked.expected.yml => clustergroup-naked.expected.yaml} (100%) rename tests/{clustergroup-normal.expected.yml => clustergroup-normal.expected.yaml} (100%) rename tests/{examples-blank-naked.expected.yml => examples-blank-naked.expected.yaml} (100%) rename tests/{examples-blank-normal.expected.yml => examples-blank-normal.expected.yaml} (100%) rename tests/{examples-kustomize-renderer-naked.expected.yml => examples-kustomize-renderer-naked.expected.yaml} (100%) rename tests/{examples-kustomize-renderer-normal.expected.yml => examples-kustomize-renderer-normal.expected.yaml} (100%) rename tests/{golang-external-secrets-naked.expected.yml => golang-external-secrets-naked.expected.yaml} (100%) rename tests/{golang-external-secrets-normal.expected.yml => golang-external-secrets-normal.expected.yaml} (100%) rename tests/{hashicorp-vault-naked.expected.yml => hashicorp-vault-naked.expected.yaml} (100%) rename tests/{hashicorp-vault-normal.expected.yml => hashicorp-vault-normal.expected.yaml} (100%) rename tests/{install-naked.expected.yml => install-naked.expected.yaml} (100%) rename tests/{install-normal.expected.yml => install-normal.expected.yaml} (100%) rename tests/{operator-install-naked.expected.yml => operator-install-naked.expected.yaml} (100%) rename tests/{operator-install-normal.expected.yml => operator-install-normal.expected.yaml} (100%) diff --git a/scripts/test.sh b/scripts/test.sh index 1280423d..07fab6a4 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -12,7 +12,7 @@ function doTest() { CHART_OPTS="$2" TESTDIR=tests TEST=${name}-${TEST_VARIANT} - FILENAME=${TEST}.expected.yml + FILENAME=${TEST}.expected.yaml OUTPUT=${TESTDIR}/.${FILENAME} REFERENCE=${TESTDIR}/${FILENAME} @@ -55,7 +55,7 @@ function doTestCompare() { TEST_VARIANT="differences" TESTDIR=tests TEST=${name} - FILENAME=${TEST}.expected.yml + FILENAME=${TEST}.expected.yaml OUTPUT=${TESTDIR}/.${FILENAME} REFERENCE=${TESTDIR}/${FILENAME} @@ -70,7 +70,7 @@ function doTestCompare() { REFERENCE=${TESTDIR}/${FILENAME} # Drop the date from the diff output, it will not be stable - diff -u ${TESTDIR}/${name}-naked.expected.yml ${TESTDIR}/${name}-normal.expected.yml | sed 's/\.yml.*20[0-9][0-9].*/.yml/g' > ${OUTPUT} + diff -u ${TESTDIR}/${name}-naked.expected.yaml ${TESTDIR}/${name}-normal.expected.yaml | sed 's/\.yaml.*20[0-9][0-9].*/.yaml/g' > ${OUTPUT} if [ ! -e ${REFERENCE} -a -z $GITHUB_ACTIONS ]; then cp ${OUTPUT} ${REFERENCE} diff --git a/tests/acm-naked.expected.yml b/tests/acm-naked.expected.yaml similarity index 100% rename from tests/acm-naked.expected.yml rename to tests/acm-naked.expected.yaml diff --git a/tests/acm-normal.expected.yml b/tests/acm-normal.expected.yaml similarity index 100% rename from tests/acm-normal.expected.yml rename to tests/acm-normal.expected.yaml diff --git a/tests/acm.expected.diff b/tests/acm.expected.diff index 9cf23621..9e95aeda 100644 --- a/tests/acm.expected.diff +++ b/tests/acm.expected.diff @@ -1,5 +1,5 @@ ---- tests/acm-naked.expected.yml -+++ tests/acm-normal.expected.yml +--- tests/acm-naked.expected.yaml ++++ tests/acm-normal.expected.yaml @@ -1,6 +1,386 @@ --- -# Source: acm/templates/policies/application-policies.yaml diff --git a/tests/clustergroup-naked.expected.yml b/tests/clustergroup-naked.expected.yaml similarity index 100% rename from tests/clustergroup-naked.expected.yml rename to tests/clustergroup-naked.expected.yaml diff --git a/tests/clustergroup-normal.expected.yml b/tests/clustergroup-normal.expected.yaml similarity index 100% rename from tests/clustergroup-normal.expected.yml rename to tests/clustergroup-normal.expected.yaml diff --git a/tests/clustergroup.expected.diff b/tests/clustergroup.expected.diff index b1bb3239..a963250f 100644 --- a/tests/clustergroup.expected.diff +++ b/tests/clustergroup.expected.diff @@ -1,5 +1,5 @@ ---- tests/clustergroup-naked.expected.yml -+++ tests/clustergroup-normal.expected.yml +--- tests/clustergroup-naked.expected.yaml ++++ tests/clustergroup-normal.expected.yaml @@ -1,17 +1,243 @@ --- +# Source: pattern-clustergroup/templates/core/namespaces.yaml diff --git a/tests/examples-blank-naked.expected.yml b/tests/examples-blank-naked.expected.yaml similarity index 100% rename from tests/examples-blank-naked.expected.yml rename to tests/examples-blank-naked.expected.yaml diff --git a/tests/examples-blank-normal.expected.yml b/tests/examples-blank-normal.expected.yaml similarity index 100% rename from tests/examples-blank-normal.expected.yml rename to tests/examples-blank-normal.expected.yaml diff --git a/tests/examples-kustomize-renderer-naked.expected.yml b/tests/examples-kustomize-renderer-naked.expected.yaml similarity index 100% rename from tests/examples-kustomize-renderer-naked.expected.yml rename to tests/examples-kustomize-renderer-naked.expected.yaml diff --git a/tests/examples-kustomize-renderer-normal.expected.yml b/tests/examples-kustomize-renderer-normal.expected.yaml similarity index 100% rename from tests/examples-kustomize-renderer-normal.expected.yml rename to tests/examples-kustomize-renderer-normal.expected.yaml diff --git a/tests/examples-kustomize-renderer.expected.diff b/tests/examples-kustomize-renderer.expected.diff index cee20d02..cbb72a99 100644 --- a/tests/examples-kustomize-renderer.expected.diff +++ b/tests/examples-kustomize-renderer.expected.diff @@ -1,5 +1,5 @@ ---- tests/examples-kustomize-renderer-naked.expected.yml -+++ tests/examples-kustomize-renderer-normal.expected.yml +--- tests/examples-kustomize-renderer-naked.expected.yaml ++++ tests/examples-kustomize-renderer-normal.expected.yaml @@ -7,12 +7,12 @@ data: IMAGE_PROVIDER: diff --git a/tests/golang-external-secrets-naked.expected.yml b/tests/golang-external-secrets-naked.expected.yaml similarity index 100% rename from tests/golang-external-secrets-naked.expected.yml rename to tests/golang-external-secrets-naked.expected.yaml diff --git a/tests/golang-external-secrets-normal.expected.yml b/tests/golang-external-secrets-normal.expected.yaml similarity index 100% rename from tests/golang-external-secrets-normal.expected.yml rename to tests/golang-external-secrets-normal.expected.yaml diff --git a/tests/golang-external-secrets.expected.diff b/tests/golang-external-secrets.expected.diff index 299cd616..9925606f 100644 --- a/tests/golang-external-secrets.expected.diff +++ b/tests/golang-external-secrets.expected.diff @@ -1,5 +1,5 @@ ---- tests/golang-external-secrets-naked.expected.yml -+++ tests/golang-external-secrets-normal.expected.yml +--- tests/golang-external-secrets-naked.expected.yaml ++++ tests/golang-external-secrets-normal.expected.yaml @@ -5827,7 +5827,7 @@ spec: provider: diff --git a/tests/hashicorp-vault-naked.expected.yml b/tests/hashicorp-vault-naked.expected.yaml similarity index 100% rename from tests/hashicorp-vault-naked.expected.yml rename to tests/hashicorp-vault-naked.expected.yaml diff --git a/tests/hashicorp-vault-normal.expected.yml b/tests/hashicorp-vault-normal.expected.yaml similarity index 100% rename from tests/hashicorp-vault-normal.expected.yml rename to tests/hashicorp-vault-normal.expected.yaml diff --git a/tests/hashicorp-vault.expected.diff b/tests/hashicorp-vault.expected.diff index dbc3442f..699e17d2 100644 --- a/tests/hashicorp-vault.expected.diff +++ b/tests/hashicorp-vault.expected.diff @@ -1,5 +1,5 @@ ---- tests/hashicorp-vault-naked.expected.yml -+++ tests/hashicorp-vault-normal.expected.yml +--- tests/hashicorp-vault-naked.expected.yaml ++++ tests/hashicorp-vault-normal.expected.yaml @@ -340,7 +340,7 @@ applicationMenu: section: HashiCorp Vault diff --git a/tests/install-naked.expected.yml b/tests/install-naked.expected.yaml similarity index 100% rename from tests/install-naked.expected.yml rename to tests/install-naked.expected.yaml diff --git a/tests/install-normal.expected.yml b/tests/install-normal.expected.yaml similarity index 100% rename from tests/install-normal.expected.yml rename to tests/install-normal.expected.yaml diff --git a/tests/install.expected.diff b/tests/install.expected.diff index 563de641..d96baecb 100644 --- a/tests/install.expected.diff +++ b/tests/install.expected.diff @@ -1,5 +1,5 @@ ---- tests/install-naked.expected.yml -+++ tests/install-normal.expected.yml +--- tests/install-naked.expected.yaml ++++ tests/install-normal.expected.yaml @@ -11,14 +11,14 @@ apiVersion: argoproj.io/v1alpha1 kind: Application diff --git a/tests/operator-install-naked.expected.yml b/tests/operator-install-naked.expected.yaml similarity index 100% rename from tests/operator-install-naked.expected.yml rename to tests/operator-install-naked.expected.yaml diff --git a/tests/operator-install-normal.expected.yml b/tests/operator-install-normal.expected.yaml similarity index 100% rename from tests/operator-install-normal.expected.yml rename to tests/operator-install-normal.expected.yaml diff --git a/tests/operator-install.expected.diff b/tests/operator-install.expected.diff index 9f5bdedc..3f73da90 100644 --- a/tests/operator-install.expected.diff +++ b/tests/operator-install.expected.diff @@ -1,5 +1,5 @@ ---- tests/operator-install-naked.expected.yml -+++ tests/operator-install-normal.expected.yml +--- tests/operator-install-naked.expected.yaml ++++ tests/operator-install-normal.expected.yaml @@ -6,7 +6,7 @@ name: operator-install namespace: openshift-operators From 02f62c21c5f5241e9930ce2a93a33a4ed324532f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 10:39:13 +0200 Subject: [PATCH 0553/1263] Add new ignores for python module --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index fd2282bc..9e5051a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +__pycache__/ +*.py[cod] *~ *.swp *.swo @@ -7,3 +9,4 @@ pattern-vault.init pattern-vault.init.bak super-linter.log golang-external-secrets/Chart.lock +hashicorp-vault/Chart.lock From c72a547ca9bbbf8d32a9190aef648127c02ffb67 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 10:39:31 +0200 Subject: [PATCH 0554/1263] Add python module implementing the push-secret functionality This module rewrites the push_secrets.yaml functionality in python. The reason for this rewrite is that it just became too complext to manage in pure ansible and we really reached the limits of ansible and it became an unreadable mess. Also add an ansible-unittest makefile target to unittest the module. For the time being we leave it out of CI until it is a bit more refined/generic. --- Makefile | 3 + ansible/plugins/__init__.py | 0 ansible/plugins/modules/vault_load_secrets.py | 349 ++++++++++++++++++ ansible/tests/unit/test_vault_load_secrets.py | 109 ++++++ 4 files changed, 461 insertions(+) create mode 100644 ansible/plugins/__init__.py create mode 100644 ansible/plugins/modules/vault_load_secrets.py create mode 100644 ansible/tests/unit/test_vault_load_secrets.py diff --git a/Makefile b/Makefile index 7ddcc947..7607d49a 100644 --- a/Makefile +++ b/Makefile @@ -98,5 +98,8 @@ super-linter: ## Runs super linter locally ansible-lint: ## run ansible lint on ansible/ folder podman run -it -v $(PWD):/workspace:rw,z --workdir /workspace --entrypoint "/usr/local/bin/ansible-lint" quay.io/ansible/creator-ee:latest "-vvv" "ansible/" +ansible-unittest: ## run ansible unit tests + pytest -r a --fulltrace --color yes ansible/tests/unit/test_*.py + .phony: install test diff --git a/ansible/plugins/__init__.py b/ansible/plugins/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py new file mode 100644 index 00000000..d0948f61 --- /dev/null +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -0,0 +1,349 @@ +# Copyright 2022 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Ansible plugin module that loads secrets from a yaml file and pushes them +inside the HashiCorp Vault in an OCP cluster. The values-secrets.yaml file is +expected to be in the following format: +--- +# version is optional. When not specified it is assumed it is 1.0 +version: 1.0 + +# These secrets will be pushed in the vault at secret/hub/test The vault will +# have secret/hub/test with secret1 and secret2 as keys with their associated +# values (secrets) +secrets: + test: + secret1: foo + secret2: bar + +# This will create the vault key secret/hub/testfoo which will have two +# properties 'b64content' and 'content' which will be the base64-encoded +# content and the normal content respectively +files: + testfoo: ~/ca.crt + +# These secrets will be pushed in the vault at secret/region1/test The vault will +# have secret/region1/test with secret1 and secret2 as keys with their associated +# values (secrets) +secrets.region1: + test: + secret1: foo1 + secret2: bar1 + +# This will create the vault key secret/region2/testbar which will have two +# properties 'b64content' and 'content' which will be the base64-encoded +# content and the normal content respectively +files.region2: + testbar: ~/ca.crt +""" + +import base64 +import os +import subprocess +import yaml + +from ansible.module_utils.basic import AnsibleModule + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: vault_load_secrets +short_description: Loads secrets into the HashiCorp Vault +version_added: "2.11" +author: "Michele Baldessari" +description: + - Takes a values-secret.yaml file and uploads the secrets into the HashiCorp Vault +options: + values_secrets: + description: + - Path to the values-secrets file + required: true + type: str + namespace: + description: + - Namespace where the vault is running + required: false + type: str + default: vault + pod: + description: + - Name of the vault pod to use to inject secrets + required: false + type: str + default: vault-0 + basepath: + description: + - Vault's kv initial part of the path + required: false + type: str + default: secret +''' + +RETURN = ''' +''' + +EXAMPLES = ''' +- name: Loads secrets file into the vault of a cluster + vault_load_secrets: + values_secrets: ~/values-secret.yaml +''' + +def parse_values(values_file): + ''' + Parses a values-secrets.yaml file (usually placed in ~) + and returns a Python Obect with the parsed yaml. + + Parameters: + values_file(str): The path of the values-secrets.yaml file + to be parsed. + + Returns: + secrets_yaml(obj): The python object containing the parsed yaml + ''' + with open(values_file, 'r', encoding='utf-8') as file: + secrets_yaml = yaml.safe_load(file.read()) + return secrets_yaml + +def get_version(syaml): + ''' + Return the version: of the parsed yaml object. If it does not exist + return 1.0 + + Returns: + ret(str): The version value in of the top-level 'version:' key + ''' + return syaml.get('version', '1.0') + +def run_command(command): + ''' + Runs a command on the host ansible is running on. A failing command + will raise an exception in this function directly (due to check=True) + + Parameters: + command(str): The command to be run. + + Returns: + ret(subprocess.CompletedProcess): The return value from run() + ''' + ret = subprocess.run(command, shell=True, env=os.environ.copy(), + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True, check=True) + return ret + +def sanitize_values(module, syaml): + ''' + Sanitizes the secrets YAML object. If a specific secret key has + s3.accessKey and s3.secretKey but not s3Secret, the latter will be + generated as the base64 encoding of both s3.accessKey and s3.secretKey. + + secrets: + test: + s3.accessKey: "1234" + s3.secretKey: "4321" + + will push three secrets at 'secret/hub/test': + + s3.accessKey: 1234 + s3.secretKey: 4321 + s3Secret: czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ== + + Parameters: + module(AnsibleModule): The current AnsibleModule being used + + syaml(obj): The parsed yaml object representing the secrets + + Returns: + syaml(obj): The parsed yaml object sanitized + ''' + if not ('secrets' in syaml or 'files' in syaml): + module.fail_json(f"Values secrets file does not contain 'secrets' or" \ + f"'files' keys: {syaml}") + + secrets = syaml.get('secrets', {}) + files = syaml.get('files', {}) + if len(secrets) == 0 and len(files) == 0: + module.fail_json(f"Neither 'secrets' nor 'files have any secrets to " \ + f"be parsed: {syaml}") + + for secret in secrets: + if not isinstance(secrets[secret], dict): + module.fail_json(f"Each key under 'secrets' needs to point to " \ + f"a dictionary of key value pairs: {syaml}") + + for file in files: + path = files[file] + if not os.path.isfile(path): + module.fail_json(f"File {path} does not exist") + + # If s3Secret key does not exist but s3.accessKey and s3.secretKey do exist + # generate s3Secret so a user does not need to do it manually which tends to be error-prone + for secret in secrets: + tmp = secrets[secret] + if 's3.accessKey' in tmp and 's3.secretKey' in tmp and 's3Secret' not in tmp: + s3secret = (f"s3.accessKey: {tmp['s3.accessKey']}\n" + f"s3.secretKey: {tmp['s3.secretKey']}") + s3secretb64 = base64.b64encode(s3secret.encode()) + secrets[secret]['s3Secret'] = s3secretb64.decode("utf-8") + + return syaml + +def get_secrets_vault_paths(module, syaml, keyname): + ''' + Walks a secrets yaml object to look for all top-level keys that start with + 'keyname' and returns a list of tuples [(keyname1, path1), (keyname2, path2)...] + where the path is the relative vault path + For example, given a yaml with the following: + secrets: + foo: bar + secrets.region1: + foo: baz + secrets.region2: + foo: barbaz + + a call with keyname set to 'secrets' will return the following: + [('secrets', 'hub'), ('secrets', 'region1'), ('secrets', 'region2')] + + Parameters: + module(AnsibleModule): The current AnsibleModule being used + + syaml(obj): The parsed yaml object representing the secrets + + keyname(str): The keytypes to look for either usually 'secrets' or 'files' + + Returns: + keys_paths(list): List of tuples containing (keyname, relative-vault-path) + ''' + all_keys = syaml.keys() + keys_paths = [] + for key in all_keys: + # We skip any key that does not start with 'secrets' or 'files' + # (We should probably bail out in the presence of unexpected top-level keys) + if not key.startswith(keyname): + continue + + # If there is no '.' after secrets or files, assume the secrets need to + # go to the hub vault path + if key == keyname: + keys_paths.append((key, 'hub')) + continue + + # We are in the presence of either 'secrets.region-one' or 'files.cluster1' top-level keys + tmp = key.split('.') + if len(tmp) != 2: + module.fail_json(f"values-secrets.yaml key is non-conformant: {key}") + + keys_paths.append((key, tmp[1])) + + return keys_paths + +# NOTE(bandini): we shell out to oc exec it because of https://github.com/ansible-collections/kubernetes.core/issues/506 +# and https://github.com/kubernetes/kubernetes/issues/89899. Until those are solved it makes little sense +# to invoke the APIs via the python wrappers +def inject_secrets(module, syaml, namespace, pod, basepath): + ''' + Walks a secrets yaml object and injects all the secrets into the vault via 'oc exec' calls + + Parameters: + module(AnsibleModule): The current AnsibleModule being used + + syaml(obj): The parsed yaml object representing the secrets + + namespace(str): The namespace in which the vault is + + pod(str): The pod name where the vault is + + basepath(str): The base string to which we concatenate the vault + relative paths + + Returns: + counter(int): The number of secrets injected + ''' + counter = 0 + for i in get_secrets_vault_paths(module, syaml, 'secrets'): + path = f"{basepath}/{i[1]}" + for secret in syaml[i[0]]: + properties = '' + for key, value in syaml[i[0]][secret].items(): + properties += f"{key}='{value}' " + properties = properties.rstrip() + cmd = (f"oc exec -n {namespace} {pod} -i -- sh -c " + f"\"vault kv put '{path}/{secret}' {properties}\"") + run_command(cmd) + counter += 1 + + for i in get_secrets_vault_paths(module, syaml, 'files'): + path = f"{basepath}/{i[1]}" + for filekey in syaml[i[0]]: + file = os.path.expanduser(syaml[i[0]][filekey]) + cmd = (f"cat '{file}' | oc exec -n {namespace} {pod} -i -- sh -c " + f"'cat - > /tmp/vcontent'; " + f"oc exec -n {namespace} {pod} -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | " + f"vault kv put {path}/{filekey} b64content=- content=@/tmp/vcontent; " + f"rm /tmp/vcontent'" + ) + run_command(cmd) + counter += 1 + return counter + +def run(module): + '''Main ansible module entry point''' + results = dict( + changed=False + ) + + args = module.params + values_secrets = os.path.expanduser(args.get('values_secrets')) + basepath = args.get('basepath') + namespace = args.get('namespace') + pod = args.get('pod') + + if not os.path.exists(values_secrets): + results['failed'] = True + results['error'] = "Missing values-secrets.yaml file" + results['msg'] = f"Values secrets file does not exist: {values_secrets}" + module.exit_json(**results) + + syaml = parse_values(values_secrets) + version = get_version(syaml) + + if version != '1.0': + module.fail_json(f"Version {version} is currently not supported") + + # In the future we can use the version field to manage different formats if needed + secrets = sanitize_values(module, syaml) + nr_secrets = inject_secrets(module, secrets, namespace, pod, basepath) + results['failed'] = False + results['changed'] = True + results['msg'] = f"{nr_secrets} secrets injected" + module.exit_json(**results) + + +def main(): + '''Main entry point where the AnsibleModule class is instantiated''' + module = AnsibleModule( + argument_spec=yaml.safe_load(DOCUMENTATION)['options'], + supports_check_mode=False, + ) + run(module) + + +if __name__ == '__main__': + main() diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py new file mode 100644 index 00000000..470f4ddf --- /dev/null +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -0,0 +1,109 @@ +import json +import os +import sys +import unittest + +sys.path.insert(1, './ansible/ansible_plugins/modules') +from unittest.mock import patch, call +from ansible.module_utils import basic +from ansible.module_utils.common.text.converters import to_bytes + +import vault_load_secrets + + +def set_module_args(args): + """prepare arguments so that they will be picked up during module creation""" + args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + """Exception class to be raised by module.exit_json and caught by the test case""" + pass + + +class AnsibleFailJson(Exception): + """Exception class to be raised by module.fail_json and caught by the test case""" + pass + + +def exit_json(*args, **kwargs): + """function to patch over exit_json; package return data into an exception""" + if 'changed' not in kwargs: + kwargs['changed'] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + """function to patch over fail_json; package return data into an exception""" + kwargs['failed'] = True + raise AnsibleFailJson(kwargs) + + +def get_bin_path(self, arg, required=False): + """Mock AnsibleModule.get_bin_path""" + if arg.endswith('my_command'): + return '/usr/bin/my_command' + else: + if required: + fail_json(msg='%r not found !' % arg) + + +class TestMyModule(unittest.TestCase): + + def setUp(self): + self.mock_module_helper = patch.multiple(basic.AnsibleModule, + exit_json=exit_json, + fail_json=fail_json, + get_bin_path=get_bin_path) + self.mock_module_helper.start() + self.addCleanup(self.mock_module_helper.stop) + + def test_module_fail_when_required_args_missing(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + vault_load_secrets.main() + + def test_module_fail_when_values_secret_not_existing(self): + with self.assertRaises(AnsibleExitJson) as ansible_err: + set_module_args({ + 'values_secrets': "/tmp/nonexisting", + }) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret['failed'], True) + self.assertEqual(ret['error'], 'Missing values-secrets.yaml file') + self.assertEqual(ret['msg'], 'Values secrets file does not exist: /tmp/nonexisting') + + def test_ensure_command_called(self): + set_module_args({ + 'values_secrets': os.path.join(os.path.dirname(os.path.abspath(__file__)), 'values-secret.yaml') + }) + + with patch.object(vault_load_secrets, 'run_command') as mock_run_command: + stdout = 'configuration updated' + stderr = '' + rc = 0 + mock_run_command.return_value = rc, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue(result.exception.args[0]['changed']) # ensure result is changed + assert mock_run_command.call_count == 9 + + calls = [ + call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/config-demo\' secret=\'demo123\'"'), + call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/googleapi\' key=\'lskdjflskjdflsdjflsdkjfldsjkfldsj\'"'), + call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/cluster_alejandro\' name=\'alejandro\' bearerToken=\'sha256~bumxi-012345678901233455675678678098-abcdef\'"'), + call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/test\' s3.accessKey=\'1234\' s3.secretKey=\'4321\' s3Secret=\'czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ==\'"'), + call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/test2\' s3.accessKey=\'accessKey\' s3.secretKey=\'secretKey\' s3Secret=\'fooo\'"'), + call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/test3\' s3.accessKey=\'aaaaa\' s3.secretKey=\'bbbbbbbb\' s3Secret=\'czMuYWNjZXNzS2V5OiBhYWFhYQpzMy5zZWNyZXRLZXk6IGJiYmJiYmJi\'"'), + call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/region-one/config-demo\' secret=\'region123\'"'), + call("cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/cluster_alejandro_ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'"), + call("cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/region-one/ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'") + ] + mock_run_command.assert_has_calls(calls) + +if __name__ == '__main__': + unittest.main() From 4b384d1b3155b89cb94e3f0e2fb0979af93e3527 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 10:42:30 +0200 Subject: [PATCH 0555/1263] Switch push_secrets.yaml to the python module implementation This effectively makes it a lot simpler to extend and to troubleshoot in the future and makes the code more maintainable. --- ansible/ansible.cfg | 1 + .../roles/vault_utils/tasks/push_secrets.yaml | 177 +----------------- ansible/tests/unit/test_vault_load_secrets.py | 2 +- 3 files changed, 6 insertions(+), 174 deletions(-) diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index c8bec627..9a742d0a 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -1,4 +1,5 @@ [defaults] display_skipped_hosts=False localhost_warning=False +library=./plugins/modules:~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules roles_path=./roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 784da4d2..a7b838f7 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -2,6 +2,7 @@ - include_tasks: pre_check.yaml - include_tasks: vault_status.yaml + # Unfortunately we cannot loop vault_status and just check if the vault is unsealed # https://github.com/ansible/proposals/issues/136 # So here we keep running the 'vault status' command until sealed is set to false @@ -16,158 +17,6 @@ delay: 45 failed_when: "'stdout_lines' not in vault_status_json" -- name: Check for existence of "{{ values_secret }}" - ansible.builtin.stat: - path: "{{ values_secret }}" - register: result - failed_when: not result.stat.exists - -- name: Parse "{{ values_secret }}" - ansible.builtin.set_fact: - all_values: "{{ lookup('file', values_secret) | from_yaml | default({}, true) }}" - -- name: Check for secrets keys - ansible.builtin.assert: - that: - - "('secrets' in all_values.keys()) or ('files' in all_values.keys())" - fail_msg: "Was not able to parse any secrets from file {{ values_secret }}. Either 'secrets:' or 'files:' top-level keys (or both) must exist" - -- name: Set secrets and file_secrets facts - ansible.builtin.set_fact: - secrets: "{{ all_values['secrets'] | default({}) }}" - file_secrets: "{{ all_values['files'] | default({}) }}" - -- name: Verify we have any secrets at all - ansible.builtin.fail: - msg: "Was not able to parse any secrets from file {{ values_secret }}: {{ all_values }}" - failed_when: - secrets | combine(file_secrets) | length == 0 - -- name: Check the value-secret.yaml file for errors in the secrets section - ansible.builtin.fail: - msg: > - "{{ item }}" is not properly formatted. Each key under 'secrets:' - needs to point to a dictionary of key, value pairs. See values-secret.yaml.template. - when: > - item.key | length == 0 or - item.value is not mapping - loop: - "{{ secrets | dict2items }}" - loop_control: - label: "{{ item.key }}" - -- name: Validate file references in the files section of value-secret.yaml - ansible.builtin.stat: - path: '{{ file_stat.value }}' - register: file_values - loop_control: - loop_var: file_stat - loop: - "{{ file_secrets | dict2items }}" - -- name: Debug file_stat - ansible.builtin.debug: - var: file_stat - when: debug | default(False) | bool - -- name: Debug file_values - ansible.builtin.debug: - var: file_values - when: debug | default(False) | bool - -- name: Fail if referenced file does not exist - ansible.builtin.fail: - msg: > - "file {{ item.file_stat.key }} {{ item.file_stat.value }}" must exist and be a file - when: > - not item.stat.exists - loop: - "{{ file_values.results }}" - -# Detect here if we have only the following two keys under a password -# s3.accessKey: -# s3.secretKey: -# If we do, then detect it and calculate the b64 s3Secret token -# Note: the vars: line is due to https://github.com/ansible/ansible/issues/40239 -- name: Check if any of the passwords has only s3.[accessKey,secretKey] and generate the combined s3Secret in that case - ansible.builtin.set_fact: - s3keys: "{{ s3keys | default({}) | combine({ item.key: {'s3Secret': s3secret | b64encode } }) }}" - vars: - s3secret: "{{ 's3.accessKey: ' + item.value['s3.accessKey'] + '\ns3.secretKey: ' + item.value['s3.secretKey'] }}" - when: - - '"s3.accessKey" in item.value.keys()' - - '"s3.secretKey" in item.value.keys()' - - '"s3Secret" not in item.value.keys()' - loop: - "{{ secrets | dict2items }}" - loop_control: - label: "{{ item.key }}" - -- name: Merge any s3Secret into the secrets dictionary if we have any - ansible.builtin.set_fact: - secrets: "{{ secrets | combine(s3keys) }}" - when: - s3keys is defined and s3keys | length > 0 - -# The values-secret.yaml file is in the fairly loose form of: -# secrets: -# group1: -# key1: value1 -# key2: value2 -# group2: -# key1: valueA -# key4: valueC -# The above will generate: -# vault kv put 'secret/hub/group1' key1='value1' key2='value2' -# vault kv put 'secret/hub/group2' key1='valueA' key4='valueC' -# Below we loop on the top level keys (group1, group2) and for each -# sub-top-level key (key1, key2) we create a single 'key1=value1 key2=value2' string -# -# Special note is to be given to the regex_replace which is run against each value aka password: -# A. The need for it is to quote the passwords -# B. Since it needs to cater to multiline strings (certs) and ansible offers no way -# to specify re.DOTALL and re.MULTILINE we need to use (?ms) at the beginning -# See https://docs.python.org/3/library/re.html and (?aiLmsux) section -# and https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/filter/core.py#L124 -# C. The \\A and \\Z match the beginning and end of a string (in case of multiline strings -# do not want to match every line) - -- name: Set defaults for vault_cmds and secret_files_vault_cmds to prevent undefined variable errors - ansible.builtin.set_fact: - vault_cmds: "{{ vault_cmds | default({}) }}" - secret_files_vault_cmds: "{{ secret_files_vault_cmds | default({}) }}" - -- name: Set secrets vault commands fact - ansible.builtin.set_fact: - vault_cmds: "{{ vault_cmds | combine({ item.key: vault_cmd}) }}" - vars: - vault_cmd: "vault kv put '{{ vault_path }}/{{ item.key }}' - {{ item.value.keys() | zip(item.value.values() | map('regex_replace', '(?ms)\\A(.*)\\Z', \"'\\1'\")) | map('join', '=') | list | join(' ') }}" - loop: - "{{ secrets | dict2items }}" - loop_control: - label: "{{ item.key }}" - -- name: Set files vault commands fact - ansible.builtin.set_fact: - secret_files_vault_cmds: "{{ secret_files_vault_cmds | combine({ item.key: vault_cmd}) }}" - vars: - vault_cmd: "cat '{{ item.value | expanduser }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'cat - > /tmp/vault_content'; oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'base64 --wrap=0 /tmp/vault_content | vault kv put {{ vault_path }}/{{ item.key }} b64content=- content=@/tmp/vault_content; rm /tmp/vault_content'" # noqa yaml - loop: - "{{ file_secrets | dict2items }}" - loop_control: - label: "{{ item.key }}" - -- name: Debug vault commands - ansible.builtin.debug: - msg: "{{ vault_cmds }}" - when: debug | default(False) | bool - -- name: Debug files vault commands - ansible.builtin.debug: - msg: "{{ secret_files_vault_cmds }}" - when: debug | default(False) | bool - # This step is not really needed when running make vault-init + load-secrets as # everything is sequential # It is needed when the vault is unsealed/configured inside the cluster and load-secrets @@ -185,24 +34,6 @@ delay: 45 changed_when: false -- name: Add the actual secrets to the vault - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: | - sh -c "{{ item.value }}" - loop: - "{{ vault_cmds | dict2items }}" - loop_control: - label: "{{ item.key }}" - when: not debug | default(False) | bool - - # This has to be shell because of the use of stdin and pipes -- name: Add the file secrets to the vault - ansible.builtin.shell: # noqa: command-instead-of-shell - "{{ item.value }}" - loop: - "{{ secret_files_vault_cmds | dict2items }}" - loop_control: - label: "{{ item.key }}" - when: not debug | default(False) | bool +- name: Loads secrets file into the vault of a cluster + vault_load_secrets: + values_secrets: ~/values-secret.yaml diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 470f4ddf..0cdff728 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -3,7 +3,7 @@ import sys import unittest -sys.path.insert(1, './ansible/ansible_plugins/modules') +sys.path.insert(1, './ansible/plugins/modules') from unittest.mock import patch, call from ansible.module_utils import basic from ansible.module_utils.common.text.converters import to_bytes From 3f2f25569e8063503f79b97d7a8df32464de10d3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 10:46:52 +0200 Subject: [PATCH 0556/1263] Add more detail to the values-secret.yaml example Also cover the new support of the secrets.xyz and files.xyz keys --- examples/values-secret.yaml | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/examples/values-secret.yaml b/examples/values-secret.yaml index 1b6fec08..0dafeebf 100644 --- a/examples/values-secret.yaml +++ b/examples/values-secret.yaml @@ -1,7 +1,9 @@ -main: - git: - repoURL: https://github.com/example/common +# version is optional. When not specified it is assumed it is 1.0 +version: 1.0 +# These secrets will be pushed in the vault at secret/hub/test The vault will +# have secret/hub/imageregistry with account and token as keys with their associated +# values (secrets) secrets: # NEVER COMMIT THESE VALUES TO GIT imageregistry: @@ -28,5 +30,22 @@ secrets: server: https://api.example.openshiftapps.com:6443 bearerToken: +# This will create the vault key secret/hub/cluster_example_ca which will have two +# properties 'b64content' and 'content' which will be the base64-encoded +# content and the normal content respectively files: cluster_example_ca: /path/to/ca.file + +# These secrets will be pushed in the vault at secret/region1/test The vault will +# have secret/region1/test with secret1 and secret2 as keys with their associated +# values (secrets) +secrets.region1: + test: + secret1: foo1 + secret2: bar1 + +# This will create the vault key secret/region2/testbar which will have two +# properties 'b64content' and 'content' which will be the base64-encoded +# content and the normal content respectively +files.region2: + testbar: ~/ca.crt From fed81b2033a34e05243e32623c3d968e40f610cf Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 10:47:59 +0200 Subject: [PATCH 0557/1263] Fix up tests after the secrets example change --- tests/clustergroup-normal.expected.yaml | 7 +++++++ tests/clustergroup.expected.diff | 19 +++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 50a8fb46..eaf254f7 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -169,6 +169,8 @@ data: enabled: all files: cluster_example_ca: /path/to/ca.file + files.region2: + testbar: ~/ca.crt global: clusterDomain: region.example.com git: @@ -206,8 +208,13 @@ data: imageregistry: account: test-account token: test-quay-token + secrets.region1: + test: + secret1: foo1 + secret2: bar1 secretsBase: key: secret/data/hub + version: 1 --- # Source: pattern-clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 diff --git a/tests/clustergroup.expected.diff b/tests/clustergroup.expected.diff index a963250f..8346b27f 100644 --- a/tests/clustergroup.expected.diff +++ b/tests/clustergroup.expected.diff @@ -1,6 +1,6 @@ --- tests/clustergroup-naked.expected.yaml +++ tests/clustergroup-normal.expected.yaml -@@ -1,17 +1,243 @@ +@@ -1,17 +1,250 @@ --- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 @@ -174,6 +174,8 @@ + enabled: all + files: + cluster_example_ca: /path/to/ca.file ++ files.region2: ++ testbar: ~/ca.crt + global: + clusterDomain: region.example.com + git: @@ -211,8 +213,13 @@ + imageregistry: + account: test-account + token: test-quay-token ++ secrets.region1: ++ test: ++ secret1: foo1 ++ secret2: bar1 + secretsBase: + key: secret/data/hub ++ version: 1 +--- +# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 @@ -246,7 +253,7 @@ # Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 -@@ -36,7 +262,7 @@ +@@ -36,7 +269,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -255,7 +262,7 @@ roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole -@@ -45,16 +271,583 @@ +@@ -45,16 +278,583 @@ - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-application-controller name: example-gitops-argocd-application-controller @@ -842,7 +849,7 @@ --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 -@@ -65,7 +858,7 @@ +@@ -65,7 +865,7 @@ # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: example-gitops @@ -851,7 +858,7 @@ annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: -@@ -94,10 +887,10 @@ +@@ -94,10 +894,10 @@ --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE @@ -866,7 +873,7 @@ --set clusterGroup.name=example --post-renderer ./kustomize"] applicationSet: -@@ -174,11 +967,59 @@ +@@ -174,11 +974,59 @@ kind: ConsoleLink metadata: name: example-gitops-link From 07316dbc3f443f50d3944cfb8fa7ea3944f1191a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 10:59:41 +0200 Subject: [PATCH 0558/1263] Tests cleanups --- ansible/plugins/modules/vault_load_secrets.py | 16 +++++--- ansible/tests/unit/test_vault_load_secrets.py | 41 +++++++++++-------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index d0948f61..e0c51f41 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -106,6 +106,7 @@ values_secrets: ~/values-secret.yaml ''' + def parse_values(values_file): ''' Parses a values-secrets.yaml file (usually placed in ~) @@ -122,6 +123,7 @@ def parse_values(values_file): secrets_yaml = yaml.safe_load(file.read()) return secrets_yaml + def get_version(syaml): ''' Return the version: of the parsed yaml object. If it does not exist @@ -132,6 +134,7 @@ def get_version(syaml): ''' return syaml.get('version', '1.0') + def run_command(command): ''' Runs a command on the host ansible is running on. A failing command @@ -144,10 +147,11 @@ def run_command(command): ret(subprocess.CompletedProcess): The return value from run() ''' ret = subprocess.run(command, shell=True, env=os.environ.copy(), - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True, check=True) + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True, check=True) return ret + def sanitize_values(module, syaml): ''' Sanitizes the secrets YAML object. If a specific secret key has @@ -205,6 +209,7 @@ def sanitize_values(module, syaml): return syaml + def get_secrets_vault_paths(module, syaml, keyname): ''' Walks a secrets yaml object to look for all top-level keys that start with @@ -254,9 +259,10 @@ def get_secrets_vault_paths(module, syaml, keyname): return keys_paths -# NOTE(bandini): we shell out to oc exec it because of https://github.com/ansible-collections/kubernetes.core/issues/506 -# and https://github.com/kubernetes/kubernetes/issues/89899. Until those are solved it makes little sense -# to invoke the APIs via the python wrappers +# NOTE(bandini): we shell out to oc exec it because of +# https://github.com/ansible-collections/kubernetes.core/issues/506 and +# https://github.com/kubernetes/kubernetes/issues/89899. Until those are solved +# it makes little sense to invoke the APIs via the python wrappers def inject_secrets(module, syaml, namespace, pod, basepath): ''' Walks a secrets yaml object and injects all the secrets into the vault via 'oc exec' calls diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 0cdff728..a762e51c 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -1,13 +1,32 @@ +# Copyright 2022 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Simple module to test vault_load_secrets +""" + import json import os import sys import unittest -sys.path.insert(1, './ansible/plugins/modules') from unittest.mock import patch, call from ansible.module_utils import basic from ansible.module_utils.common.text.converters import to_bytes +sys.path.insert(1, './ansible/plugins/modules') import vault_load_secrets @@ -40,22 +59,11 @@ def fail_json(*args, **kwargs): raise AnsibleFailJson(kwargs) -def get_bin_path(self, arg, required=False): - """Mock AnsibleModule.get_bin_path""" - if arg.endswith('my_command'): - return '/usr/bin/my_command' - else: - if required: - fail_json(msg='%r not found !' % arg) - - class TestMyModule(unittest.TestCase): - def setUp(self): self.mock_module_helper = patch.multiple(basic.AnsibleModule, exit_json=exit_json, - fail_json=fail_json, - get_bin_path=get_bin_path) + fail_json=fail_json) self.mock_module_helper.start() self.addCleanup(self.mock_module_helper.stop) @@ -78,14 +86,15 @@ def test_module_fail_when_values_secret_not_existing(self): def test_ensure_command_called(self): set_module_args({ - 'values_secrets': os.path.join(os.path.dirname(os.path.abspath(__file__)), 'values-secret.yaml') + 'values_secrets': os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'values-secret.yaml') }) with patch.object(vault_load_secrets, 'run_command') as mock_run_command: stdout = 'configuration updated' stderr = '' - rc = 0 - mock_run_command.return_value = rc, stdout, stderr # successful execution + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution with self.assertRaises(AnsibleExitJson) as result: vault_load_secrets.main() From 7bfb3cb63249686069102c21923cd7de7f831b0b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 11:03:42 +0200 Subject: [PATCH 0559/1263] Some more linting fixes --- ansible/plugins/modules/vault_load_secrets.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index e0c51f41..96c216db 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -178,18 +178,18 @@ def sanitize_values(module, syaml): syaml(obj): The parsed yaml object sanitized ''' if not ('secrets' in syaml or 'files' in syaml): - module.fail_json(f"Values secrets file does not contain 'secrets' or" \ + module.fail_json(f"Values secrets file does not contain 'secrets' or" f"'files' keys: {syaml}") secrets = syaml.get('secrets', {}) files = syaml.get('files', {}) if len(secrets) == 0 and len(files) == 0: - module.fail_json(f"Neither 'secrets' nor 'files have any secrets to " \ + module.fail_json(f"Neither 'secrets' nor 'files have any secrets to " f"be parsed: {syaml}") for secret in secrets: if not isinstance(secrets[secret], dict): - module.fail_json(f"Each key under 'secrets' needs to point to " \ + module.fail_json(f"Each key under 'secrets' needs to point to " f"a dictionary of key value pairs: {syaml}") for file in files: @@ -259,6 +259,7 @@ def get_secrets_vault_paths(module, syaml, keyname): return keys_paths + # NOTE(bandini): we shell out to oc exec it because of # https://github.com/ansible-collections/kubernetes.core/issues/506 and # https://github.com/kubernetes/kubernetes/issues/89899. Until those are solved @@ -303,12 +304,12 @@ def inject_secrets(module, syaml, namespace, pod, basepath): f"'cat - > /tmp/vcontent'; " f"oc exec -n {namespace} {pod} -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | " f"vault kv put {path}/{filekey} b64content=- content=@/tmp/vcontent; " - f"rm /tmp/vcontent'" - ) + f"rm /tmp/vcontent'") run_command(cmd) counter += 1 return counter + def run(module): '''Main ansible module entry point''' results = dict( From 82cf8951fb31fcadc730d0af8dfbc38719bad701 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 11:09:10 +0200 Subject: [PATCH 0560/1263] Some more linting fixes pt2 --- ansible/plugins/modules/vault_load_secrets.py | 2 +- ansible/tests/unit/test_vault_load_secrets.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 96c216db..cc9b69bb 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -53,8 +53,8 @@ import base64 import os import subprocess -import yaml +import yaml from ansible.module_utils.basic import AnsibleModule ANSIBLE_METADATA = { diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index a762e51c..92342caf 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -21,8 +21,8 @@ import os import sys import unittest +from unittest.mock import call, patch -from unittest.mock import patch, call from ansible.module_utils import basic from ansible.module_utils.common.text.converters import to_bytes @@ -98,7 +98,7 @@ def test_ensure_command_called(self): with self.assertRaises(AnsibleExitJson) as result: vault_load_secrets.main() - self.assertTrue(result.exception.args[0]['changed']) # ensure result is changed + self.assertTrue(result.exception.args[0]['changed']) # ensure result is changed assert mock_run_command.call_count == 9 calls = [ @@ -114,5 +114,6 @@ def test_ensure_command_called(self): ] mock_run_command.assert_has_calls(calls) + if __name__ == '__main__': unittest.main() From 8856687428c7543f3f3f2411003ecba46563345a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 11:12:56 +0200 Subject: [PATCH 0561/1263] Pass black --skip-string-normalization over the python plugin Makes the formatting more consistent in general --- ansible/plugins/modules/vault_load_secrets.py | 59 ++++++++++++------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index cc9b69bb..9f2bba43 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -60,7 +60,7 @@ ANSIBLE_METADATA = { 'metadata_version': '1.1', 'status': ['preview'], - 'supported_by': 'community' + 'supported_by': 'community', } DOCUMENTATION = ''' @@ -146,9 +146,15 @@ def run_command(command): Returns: ret(subprocess.CompletedProcess): The return value from run() ''' - ret = subprocess.run(command, shell=True, env=os.environ.copy(), - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True, check=True) + ret = subprocess.run( + command, + shell=True, + env=os.environ.copy(), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + check=True, + ) return ret @@ -178,19 +184,24 @@ def sanitize_values(module, syaml): syaml(obj): The parsed yaml object sanitized ''' if not ('secrets' in syaml or 'files' in syaml): - module.fail_json(f"Values secrets file does not contain 'secrets' or" - f"'files' keys: {syaml}") + module.fail_json( + f"Values secrets file does not contain 'secrets' or" + f"'files' keys: {syaml}" + ) secrets = syaml.get('secrets', {}) files = syaml.get('files', {}) if len(secrets) == 0 and len(files) == 0: - module.fail_json(f"Neither 'secrets' nor 'files have any secrets to " - f"be parsed: {syaml}") + module.fail_json( + f"Neither 'secrets' nor 'files have any secrets to " f"be parsed: {syaml}" + ) for secret in secrets: if not isinstance(secrets[secret], dict): - module.fail_json(f"Each key under 'secrets' needs to point to " - f"a dictionary of key value pairs: {syaml}") + module.fail_json( + f"Each key under 'secrets' needs to point to " + f"a dictionary of key value pairs: {syaml}" + ) for file in files: path = files[file] @@ -202,8 +213,10 @@ def sanitize_values(module, syaml): for secret in secrets: tmp = secrets[secret] if 's3.accessKey' in tmp and 's3.secretKey' in tmp and 's3Secret' not in tmp: - s3secret = (f"s3.accessKey: {tmp['s3.accessKey']}\n" - f"s3.secretKey: {tmp['s3.secretKey']}") + s3secret = ( + f"s3.accessKey: {tmp['s3.accessKey']}\n" + f"s3.secretKey: {tmp['s3.secretKey']}" + ) s3secretb64 = base64.b64encode(s3secret.encode()) secrets[secret]['s3Secret'] = s3secretb64.decode("utf-8") @@ -291,8 +304,10 @@ def inject_secrets(module, syaml, namespace, pod, basepath): for key, value in syaml[i[0]][secret].items(): properties += f"{key}='{value}' " properties = properties.rstrip() - cmd = (f"oc exec -n {namespace} {pod} -i -- sh -c " - f"\"vault kv put '{path}/{secret}' {properties}\"") + cmd = ( + f"oc exec -n {namespace} {pod} -i -- sh -c " + f"\"vault kv put '{path}/{secret}' {properties}\"" + ) run_command(cmd) counter += 1 @@ -300,11 +315,13 @@ def inject_secrets(module, syaml, namespace, pod, basepath): path = f"{basepath}/{i[1]}" for filekey in syaml[i[0]]: file = os.path.expanduser(syaml[i[0]][filekey]) - cmd = (f"cat '{file}' | oc exec -n {namespace} {pod} -i -- sh -c " - f"'cat - > /tmp/vcontent'; " - f"oc exec -n {namespace} {pod} -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | " - f"vault kv put {path}/{filekey} b64content=- content=@/tmp/vcontent; " - f"rm /tmp/vcontent'") + cmd = ( + f"cat '{file}' | oc exec -n {namespace} {pod} -i -- sh -c " + f"'cat - > /tmp/vcontent'; " + f"oc exec -n {namespace} {pod} -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | " + f"vault kv put {path}/{filekey} b64content=- content=@/tmp/vcontent; " + f"rm /tmp/vcontent'" + ) run_command(cmd) counter += 1 return counter @@ -312,9 +329,7 @@ def inject_secrets(module, syaml, namespace, pod, basepath): def run(module): '''Main ansible module entry point''' - results = dict( - changed=False - ) + results = dict(changed=False) args = module.params values_secrets = os.path.expanduser(args.get('values_secrets')) From 7277a0fff4dc5c5508f7dfe20c4ad8d1e5042cf1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 11:13:41 +0200 Subject: [PATCH 0562/1263] Pass black --skip-string-normalization over ansible unit test --- ansible/tests/unit/test_vault_load_secrets.py | 71 +++++++++++++------ 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 92342caf..3adc9d40 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -38,11 +38,13 @@ def set_module_args(args): class AnsibleExitJson(Exception): """Exception class to be raised by module.exit_json and caught by the test case""" + pass class AnsibleFailJson(Exception): """Exception class to be raised by module.fail_json and caught by the test case""" + pass @@ -61,9 +63,9 @@ def fail_json(*args, **kwargs): class TestMyModule(unittest.TestCase): def setUp(self): - self.mock_module_helper = patch.multiple(basic.AnsibleModule, - exit_json=exit_json, - fail_json=fail_json) + self.mock_module_helper = patch.multiple( + basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json + ) self.mock_module_helper.start() self.addCleanup(self.mock_module_helper.stop) @@ -74,21 +76,28 @@ def test_module_fail_when_required_args_missing(self): def test_module_fail_when_values_secret_not_existing(self): with self.assertRaises(AnsibleExitJson) as ansible_err: - set_module_args({ - 'values_secrets': "/tmp/nonexisting", - }) + set_module_args( + { + 'values_secrets': "/tmp/nonexisting", + } + ) vault_load_secrets.main() ret = ansible_err.exception.args[0] self.assertEqual(ret['failed'], True) self.assertEqual(ret['error'], 'Missing values-secrets.yaml file') - self.assertEqual(ret['msg'], 'Values secrets file does not exist: /tmp/nonexisting') + self.assertEqual( + ret['msg'], 'Values secrets file does not exist: /tmp/nonexisting' + ) def test_ensure_command_called(self): - set_module_args({ - 'values_secrets': os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'values-secret.yaml') - }) + set_module_args( + { + 'values_secrets': os.path.join( + os.path.dirname(os.path.abspath(__file__)), 'values-secret.yaml' + ) + } + ) with patch.object(vault_load_secrets, 'run_command') as mock_run_command: stdout = 'configuration updated' @@ -98,19 +107,39 @@ def test_ensure_command_called(self): with self.assertRaises(AnsibleExitJson) as result: vault_load_secrets.main() - self.assertTrue(result.exception.args[0]['changed']) # ensure result is changed + self.assertTrue( + result.exception.args[0]['changed'] + ) # ensure result is changed assert mock_run_command.call_count == 9 calls = [ - call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/config-demo\' secret=\'demo123\'"'), - call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/googleapi\' key=\'lskdjflskjdflsdjflsdkjfldsjkfldsj\'"'), - call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/cluster_alejandro\' name=\'alejandro\' bearerToken=\'sha256~bumxi-012345678901233455675678678098-abcdef\'"'), - call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/test\' s3.accessKey=\'1234\' s3.secretKey=\'4321\' s3Secret=\'czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ==\'"'), - call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/test2\' s3.accessKey=\'accessKey\' s3.secretKey=\'secretKey\' s3Secret=\'fooo\'"'), - call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/test3\' s3.accessKey=\'aaaaa\' s3.secretKey=\'bbbbbbbb\' s3Secret=\'czMuYWNjZXNzS2V5OiBhYWFhYQpzMy5zZWNyZXRLZXk6IGJiYmJiYmJi\'"'), - call('oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/region-one/config-demo\' secret=\'region123\'"'), - call("cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/cluster_alejandro_ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'"), - call("cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/region-one/ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'") + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/config-demo\' secret=\'demo123\'"' + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/googleapi\' key=\'lskdjflskjdflsdjflsdkjfldsjkfldsj\'"' + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/cluster_alejandro\' name=\'alejandro\' bearerToken=\'sha256~bumxi-012345678901233455675678678098-abcdef\'"' + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/test\' s3.accessKey=\'1234\' s3.secretKey=\'4321\' s3Secret=\'czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ==\'"' + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/test2\' s3.accessKey=\'accessKey\' s3.secretKey=\'secretKey\' s3Secret=\'fooo\'"' + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/test3\' s3.accessKey=\'aaaaa\' s3.secretKey=\'bbbbbbbb\' s3Secret=\'czMuYWNjZXNzS2V5OiBhYWFhYQpzMy5zZWNyZXRLZXk6IGJiYmJiYmJi\'"' + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/region-one/config-demo\' secret=\'region123\'"' + ), + call( + "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/cluster_alejandro_ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'" + ), + call( + "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/region-one/ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'" + ), ] mock_run_command.assert_has_calls(calls) From 9c5c835a60a45cf94b5199bcdb90eac869cae9c7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 11:19:08 +0200 Subject: [PATCH 0563/1263] Switched to full black formatting support That means we standardize on double quotes for any string --- ansible/plugins/modules/vault_load_secrets.py | 96 +++++++++---------- ansible/tests/unit/test_vault_load_secrets.py | 46 ++++----- 2 files changed, 71 insertions(+), 71 deletions(-) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 9f2bba43..e039c27d 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -58,12 +58,12 @@ from ansible.module_utils.basic import AnsibleModule ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community', + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", } -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: vault_load_secrets short_description: Loads secrets into the HashiCorp Vault @@ -95,20 +95,20 @@ required: false type: str default: secret -''' +""" -RETURN = ''' -''' +RETURN = """ +""" -EXAMPLES = ''' +EXAMPLES = """ - name: Loads secrets file into the vault of a cluster vault_load_secrets: values_secrets: ~/values-secret.yaml -''' +""" def parse_values(values_file): - ''' + """ Parses a values-secrets.yaml file (usually placed in ~) and returns a Python Obect with the parsed yaml. @@ -118,25 +118,25 @@ def parse_values(values_file): Returns: secrets_yaml(obj): The python object containing the parsed yaml - ''' - with open(values_file, 'r', encoding='utf-8') as file: + """ + with open(values_file, "r", encoding="utf-8") as file: secrets_yaml = yaml.safe_load(file.read()) return secrets_yaml def get_version(syaml): - ''' + """ Return the version: of the parsed yaml object. If it does not exist return 1.0 Returns: ret(str): The version value in of the top-level 'version:' key - ''' - return syaml.get('version', '1.0') + """ + return syaml.get("version", "1.0") def run_command(command): - ''' + """ Runs a command on the host ansible is running on. A failing command will raise an exception in this function directly (due to check=True) @@ -145,7 +145,7 @@ def run_command(command): Returns: ret(subprocess.CompletedProcess): The return value from run() - ''' + """ ret = subprocess.run( command, shell=True, @@ -159,7 +159,7 @@ def run_command(command): def sanitize_values(module, syaml): - ''' + """ Sanitizes the secrets YAML object. If a specific secret key has s3.accessKey and s3.secretKey but not s3Secret, the latter will be generated as the base64 encoding of both s3.accessKey and s3.secretKey. @@ -182,15 +182,15 @@ def sanitize_values(module, syaml): Returns: syaml(obj): The parsed yaml object sanitized - ''' - if not ('secrets' in syaml or 'files' in syaml): + """ + if not ("secrets" in syaml or "files" in syaml): module.fail_json( f"Values secrets file does not contain 'secrets' or" f"'files' keys: {syaml}" ) - secrets = syaml.get('secrets', {}) - files = syaml.get('files', {}) + secrets = syaml.get("secrets", {}) + files = syaml.get("files", {}) if len(secrets) == 0 and len(files) == 0: module.fail_json( f"Neither 'secrets' nor 'files have any secrets to " f"be parsed: {syaml}" @@ -212,19 +212,19 @@ def sanitize_values(module, syaml): # generate s3Secret so a user does not need to do it manually which tends to be error-prone for secret in secrets: tmp = secrets[secret] - if 's3.accessKey' in tmp and 's3.secretKey' in tmp and 's3Secret' not in tmp: + if "s3.accessKey" in tmp and "s3.secretKey" in tmp and "s3Secret" not in tmp: s3secret = ( f"s3.accessKey: {tmp['s3.accessKey']}\n" f"s3.secretKey: {tmp['s3.secretKey']}" ) s3secretb64 = base64.b64encode(s3secret.encode()) - secrets[secret]['s3Secret'] = s3secretb64.decode("utf-8") + secrets[secret]["s3Secret"] = s3secretb64.decode("utf-8") return syaml def get_secrets_vault_paths(module, syaml, keyname): - ''' + """ Walks a secrets yaml object to look for all top-level keys that start with 'keyname' and returns a list of tuples [(keyname1, path1), (keyname2, path2)...] where the path is the relative vault path @@ -248,7 +248,7 @@ def get_secrets_vault_paths(module, syaml, keyname): Returns: keys_paths(list): List of tuples containing (keyname, relative-vault-path) - ''' + """ all_keys = syaml.keys() keys_paths = [] for key in all_keys: @@ -260,11 +260,11 @@ def get_secrets_vault_paths(module, syaml, keyname): # If there is no '.' after secrets or files, assume the secrets need to # go to the hub vault path if key == keyname: - keys_paths.append((key, 'hub')) + keys_paths.append((key, "hub")) continue # We are in the presence of either 'secrets.region-one' or 'files.cluster1' top-level keys - tmp = key.split('.') + tmp = key.split(".") if len(tmp) != 2: module.fail_json(f"values-secrets.yaml key is non-conformant: {key}") @@ -278,7 +278,7 @@ def get_secrets_vault_paths(module, syaml, keyname): # https://github.com/kubernetes/kubernetes/issues/89899. Until those are solved # it makes little sense to invoke the APIs via the python wrappers def inject_secrets(module, syaml, namespace, pod, basepath): - ''' + """ Walks a secrets yaml object and injects all the secrets into the vault via 'oc exec' calls Parameters: @@ -295,12 +295,12 @@ def inject_secrets(module, syaml, namespace, pod, basepath): Returns: counter(int): The number of secrets injected - ''' + """ counter = 0 - for i in get_secrets_vault_paths(module, syaml, 'secrets'): + for i in get_secrets_vault_paths(module, syaml, "secrets"): path = f"{basepath}/{i[1]}" for secret in syaml[i[0]]: - properties = '' + properties = "" for key, value in syaml[i[0]][secret].items(): properties += f"{key}='{value}' " properties = properties.rstrip() @@ -311,7 +311,7 @@ def inject_secrets(module, syaml, namespace, pod, basepath): run_command(cmd) counter += 1 - for i in get_secrets_vault_paths(module, syaml, 'files'): + for i in get_secrets_vault_paths(module, syaml, "files"): path = f"{basepath}/{i[1]}" for filekey in syaml[i[0]]: file = os.path.expanduser(syaml[i[0]][filekey]) @@ -328,44 +328,44 @@ def inject_secrets(module, syaml, namespace, pod, basepath): def run(module): - '''Main ansible module entry point''' + """Main ansible module entry point""" results = dict(changed=False) args = module.params - values_secrets = os.path.expanduser(args.get('values_secrets')) - basepath = args.get('basepath') - namespace = args.get('namespace') - pod = args.get('pod') + values_secrets = os.path.expanduser(args.get("values_secrets")) + basepath = args.get("basepath") + namespace = args.get("namespace") + pod = args.get("pod") if not os.path.exists(values_secrets): - results['failed'] = True - results['error'] = "Missing values-secrets.yaml file" - results['msg'] = f"Values secrets file does not exist: {values_secrets}" + results["failed"] = True + results["error"] = "Missing values-secrets.yaml file" + results["msg"] = f"Values secrets file does not exist: {values_secrets}" module.exit_json(**results) syaml = parse_values(values_secrets) version = get_version(syaml) - if version != '1.0': + if version != "1.0": module.fail_json(f"Version {version} is currently not supported") # In the future we can use the version field to manage different formats if needed secrets = sanitize_values(module, syaml) nr_secrets = inject_secrets(module, secrets, namespace, pod, basepath) - results['failed'] = False - results['changed'] = True - results['msg'] = f"{nr_secrets} secrets injected" + results["failed"] = False + results["changed"] = True + results["msg"] = f"{nr_secrets} secrets injected" module.exit_json(**results) def main(): - '''Main entry point where the AnsibleModule class is instantiated''' + """Main entry point where the AnsibleModule class is instantiated""" module = AnsibleModule( - argument_spec=yaml.safe_load(DOCUMENTATION)['options'], + argument_spec=yaml.safe_load(DOCUMENTATION)["options"], supports_check_mode=False, ) run(module) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 3adc9d40..e00ef823 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -26,13 +26,13 @@ from ansible.module_utils import basic from ansible.module_utils.common.text.converters import to_bytes -sys.path.insert(1, './ansible/plugins/modules') +sys.path.insert(1, "./ansible/plugins/modules") import vault_load_secrets def set_module_args(args): """prepare arguments so that they will be picked up during module creation""" - args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) basic._ANSIBLE_ARGS = to_bytes(args) @@ -50,14 +50,14 @@ class AnsibleFailJson(Exception): def exit_json(*args, **kwargs): """function to patch over exit_json; package return data into an exception""" - if 'changed' not in kwargs: - kwargs['changed'] = False + if "changed" not in kwargs: + kwargs["changed"] = False raise AnsibleExitJson(kwargs) def fail_json(*args, **kwargs): """function to patch over fail_json; package return data into an exception""" - kwargs['failed'] = True + kwargs["failed"] = True raise AnsibleFailJson(kwargs) @@ -78,61 +78,61 @@ def test_module_fail_when_values_secret_not_existing(self): with self.assertRaises(AnsibleExitJson) as ansible_err: set_module_args( { - 'values_secrets': "/tmp/nonexisting", + "values_secrets": "/tmp/nonexisting", } ) vault_load_secrets.main() ret = ansible_err.exception.args[0] - self.assertEqual(ret['failed'], True) - self.assertEqual(ret['error'], 'Missing values-secrets.yaml file') + self.assertEqual(ret["failed"], True) + self.assertEqual(ret["error"], "Missing values-secrets.yaml file") self.assertEqual( - ret['msg'], 'Values secrets file does not exist: /tmp/nonexisting' + ret["msg"], "Values secrets file does not exist: /tmp/nonexisting" ) def test_ensure_command_called(self): set_module_args( { - 'values_secrets': os.path.join( - os.path.dirname(os.path.abspath(__file__)), 'values-secret.yaml' + "values_secrets": os.path.join( + os.path.dirname(os.path.abspath(__file__)), "values-secret.yaml" ) } ) - with patch.object(vault_load_secrets, 'run_command') as mock_run_command: - stdout = 'configuration updated' - stderr = '' + with patch.object(vault_load_secrets, "run_command") as mock_run_command: + stdout = "configuration updated" + stderr = "" ret = 0 mock_run_command.return_value = ret, stdout, stderr # successful execution with self.assertRaises(AnsibleExitJson) as result: vault_load_secrets.main() self.assertTrue( - result.exception.args[0]['changed'] + result.exception.args[0]["changed"] ) # ensure result is changed assert mock_run_command.call_count == 9 calls = [ call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/config-demo\' secret=\'demo123\'"' + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/config-demo' secret='demo123'\"" ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/googleapi\' key=\'lskdjflskjdflsdjflsdkjfldsjkfldsj\'"' + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/googleapi' key='lskdjflskjdflsdjflsdkjfldsjkfldsj'\"" ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/cluster_alejandro\' name=\'alejandro\' bearerToken=\'sha256~bumxi-012345678901233455675678678098-abcdef\'"' + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/cluster_alejandro' name='alejandro' bearerToken='sha256~bumxi-012345678901233455675678678098-abcdef'\"" ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/test\' s3.accessKey=\'1234\' s3.secretKey=\'4321\' s3Secret=\'czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ==\'"' + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test' s3.accessKey='1234' s3.secretKey='4321' s3Secret='czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ=='\"" ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/test2\' s3.accessKey=\'accessKey\' s3.secretKey=\'secretKey\' s3Secret=\'fooo\'"' + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test2' s3.accessKey='accessKey' s3.secretKey='secretKey' s3Secret='fooo'\"" ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/hub/test3\' s3.accessKey=\'aaaaa\' s3.secretKey=\'bbbbbbbb\' s3Secret=\'czMuYWNjZXNzS2V5OiBhYWFhYQpzMy5zZWNyZXRLZXk6IGJiYmJiYmJi\'"' + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test3' s3.accessKey='aaaaa' s3.secretKey='bbbbbbbb' s3Secret='czMuYWNjZXNzS2V5OiBhYWFhYQpzMy5zZWNyZXRLZXk6IGJiYmJiYmJi'\"" ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv put \'secret/region-one/config-demo\' secret=\'region123\'"' + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/region-one/config-demo' secret='region123'\"" ), call( "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/cluster_alejandro_ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'" @@ -144,5 +144,5 @@ def test_ensure_command_called(self): mock_run_command.assert_has_calls(calls) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() From b419ed8b4bbf18308ace4369f73c28fe91c34961 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 11:24:56 +0200 Subject: [PATCH 0564/1263] Add some noqa: comments to ignore flake8 --- ansible/tests/unit/test_vault_load_secrets.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index e00ef823..22910cb7 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -27,7 +27,7 @@ from ansible.module_utils.common.text.converters import to_bytes sys.path.insert(1, "./ansible/plugins/modules") -import vault_load_secrets +import vault_load_secrets # noqa: E402 def set_module_args(args): @@ -114,31 +114,31 @@ def test_ensure_command_called(self): calls = [ call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/config-demo' secret='demo123'\"" + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/config-demo' secret='demo123'\"" # noqa: E501 ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/googleapi' key='lskdjflskjdflsdjflsdkjfldsjkfldsj'\"" + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/googleapi' key='lskdjflskjdflsdjflsdkjfldsjkfldsj'\"" # noqa: E501 ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/cluster_alejandro' name='alejandro' bearerToken='sha256~bumxi-012345678901233455675678678098-abcdef'\"" + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/cluster_alejandro' name='alejandro' bearerToken='sha256~bumxi-012345678901233455675678678098-abcdef'\"" # noqa: E501 ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test' s3.accessKey='1234' s3.secretKey='4321' s3Secret='czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ=='\"" + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test' s3.accessKey='1234' s3.secretKey='4321' s3Secret='czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ=='\"" # noqa: E501 ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test2' s3.accessKey='accessKey' s3.secretKey='secretKey' s3Secret='fooo'\"" + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test2' s3.accessKey='accessKey' s3.secretKey='secretKey' s3Secret='fooo'\"" # noqa: E501 ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test3' s3.accessKey='aaaaa' s3.secretKey='bbbbbbbb' s3Secret='czMuYWNjZXNzS2V5OiBhYWFhYQpzMy5zZWNyZXRLZXk6IGJiYmJiYmJi'\"" + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test3' s3.accessKey='aaaaa' s3.secretKey='bbbbbbbb' s3Secret='czMuYWNjZXNzS2V5OiBhYWFhYQpzMy5zZWNyZXRLZXk6IGJiYmJiYmJi'\"" # noqa: E501 ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/region-one/config-demo' secret='region123'\"" + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/region-one/config-demo' secret='region123'\"" # noqa: E501 ), call( - "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/cluster_alejandro_ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'" + "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/cluster_alejandro_ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 ), call( - "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/region-one/ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'" + "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/region-one/ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 ), ] mock_run_command.assert_has_calls(calls) From 2e64138327167c99bce4f80e0206f3fc819b9eb9 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 11:40:00 +0200 Subject: [PATCH 0565/1263] Add .gitleaks.toml --- .gitleaks.toml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitleaks.toml diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 00000000..0749298a --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,7 @@ +[whitelist] +# As of v4, gitleaks only matches against filename, not path in the +# files directive. Leaving content for backwards compatibility. +files = [ + "ansible/tests/unit/test_*.py", + "ansible/tests/unit/*.yaml", +] From 8a1d06e69aabdb1ed609c25880b521e4bcca15a5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 11:44:02 +0200 Subject: [PATCH 0566/1263] Also ignore python modules in gitleaks as they contain secrets examples in the comments --- .gitleaks.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitleaks.toml b/.gitleaks.toml index 0749298a..7f59b235 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -2,6 +2,7 @@ # As of v4, gitleaks only matches against filename, not path in the # files directive. Leaving content for backwards compatibility. files = [ + "ansible/plugins/modules/*.py", "ansible/tests/unit/test_*.py", "ansible/tests/unit/*.yaml", ] From e259ef2cf6570209ffc3e4f0cb07fcba2d5f6745 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 11:53:29 +0200 Subject: [PATCH 0567/1263] Move .gitleaks.toml under .github/linters --- .gitleaks.toml => .github/linters/.gitleaks.toml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .gitleaks.toml => .github/linters/.gitleaks.toml (100%) diff --git a/.gitleaks.toml b/.github/linters/.gitleaks.toml similarity index 100% rename from .gitleaks.toml rename to .github/linters/.gitleaks.toml From 6a1fe4e6dbe61357f83924df90e01c913c5f34db Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 15:33:48 +0200 Subject: [PATCH 0568/1263] Add a CHANGES.md entry for the push_secrets rewrite --- Changes.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Changes.md b/Changes.md index 0c1f9731..d5ce8614 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,13 @@ # Changes +## October 4, 2022 + +* Extended the values-secret.yaml file to support multiple vault paths and re-wrote + the push_secrets feature as python module plugin. This requires the following line + in a pattern's ansible.cfg's '[defaults]' stanza: + + `library=~/.ansible/plugins/modules:./ansible/plugins/modules:./common/ansible/plugins/modules:/usr/share/ansible/plugins/modules` + ## October 3, 2022 * Restore the ability to install a non-default site: `make TARGET_SITE=mysite install` From 21f12c9b99c9c45fe756d8bef74fbf0d35dd7ba7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Oct 2022 18:32:56 +0200 Subject: [PATCH 0569/1263] Drop -f example/values-secrets.yaml from make test We should *never* ever pass values-secrets.yaml to helm. As that file is injected to vault out-of-band and has nothing to do with helm at all. --- Makefile | 2 +- tests/clustergroup-normal.expected.yaml | 21 ---------------- tests/clustergroup.expected.diff | 33 +++++-------------------- 3 files changed, 7 insertions(+), 49 deletions(-) diff --git a/Makefile b/Makefile index 7607d49a..049b0e1b 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spe # --set values always take precedence over the contents of -f HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) \ --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) $(TARGET_SITE_OPT) -TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" \ +TEST_OPTS= -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" \ --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" \ --set global.namespace="pattern-namespace" --set global.hubClusterDomain=apps.hub.example.com --set global.localClusterDomain=apps.region.example.com --set global.clusterDomain=region.example.com\ --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" \ diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index eaf254f7..4d740ac8 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -167,10 +167,6 @@ data: name: openshift-pipelines-operator-rh targetCluster: in-cluster enabled: all - files: - cluster_example_ca: /path/to/ca.file - files.region2: - testbar: ~/ca.crt global: clusterDomain: region.example.com git: @@ -196,25 +192,8 @@ data: secretStore: kind: ClusterSecretStore name: vault-backend - secrets: - aws: - s3Secret: test-secret - cluster_example: - bearerToken: - server: https://api.example.openshiftapps.com:6443 - git: - token: test-git-token - username: test-user - imageregistry: - account: test-account - token: test-quay-token - secrets.region1: - test: - secret1: foo1 - secret2: bar1 secretsBase: key: secret/data/hub - version: 1 --- # Source: pattern-clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 diff --git a/tests/clustergroup.expected.diff b/tests/clustergroup.expected.diff index 8346b27f..45fca30b 100644 --- a/tests/clustergroup.expected.diff +++ b/tests/clustergroup.expected.diff @@ -1,6 +1,6 @@ --- tests/clustergroup-naked.expected.yaml +++ tests/clustergroup-normal.expected.yaml -@@ -1,17 +1,250 @@ +@@ -1,17 +1,229 @@ --- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 @@ -172,10 +172,6 @@ + name: openshift-pipelines-operator-rh + targetCluster: in-cluster + enabled: all -+ files: -+ cluster_example_ca: /path/to/ca.file -+ files.region2: -+ testbar: ~/ca.crt + global: + clusterDomain: region.example.com + git: @@ -201,25 +197,8 @@ + secretStore: + kind: ClusterSecretStore + name: vault-backend -+ secrets: -+ aws: -+ s3Secret: test-secret -+ cluster_example: -+ bearerToken: -+ server: https://api.example.openshiftapps.com:6443 -+ git: -+ token: test-git-token -+ username: test-user -+ imageregistry: -+ account: test-account -+ token: test-quay-token -+ secrets.region1: -+ test: -+ secret1: foo1 -+ secret2: bar1 + secretsBase: + key: secret/data/hub -+ version: 1 +--- +# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 @@ -253,7 +232,7 @@ # Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 -@@ -36,7 +269,7 @@ +@@ -36,7 +248,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -262,7 +241,7 @@ roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole -@@ -45,16 +278,583 @@ +@@ -45,16 +257,583 @@ - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-application-controller name: example-gitops-argocd-application-controller @@ -849,7 +828,7 @@ --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 -@@ -65,7 +865,7 @@ +@@ -65,7 +844,7 @@ # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: example-gitops @@ -858,7 +837,7 @@ annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: -@@ -94,10 +894,10 @@ +@@ -94,10 +873,10 @@ --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE @@ -873,7 +852,7 @@ --set clusterGroup.name=example --post-renderer ./kustomize"] applicationSet: -@@ -174,11 +974,59 @@ +@@ -174,11 +953,59 @@ kind: ConsoleLink metadata: name: example-gitops-link From ed7c43d9e5ab70d9454d50748853e4cde9df73b4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 5 Oct 2022 08:52:00 +0200 Subject: [PATCH 0570/1263] Drop GIT_SSH_COMMAND from the wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It breaks on Silverblue with: ./common/scripts/pattern-util.sh make push-secrets ─╯ Error: unknown shorthand flag: 'o' in -o See 'podman run --help' --- scripts/pattern-util.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 5d2c9bae..b2cfcac0 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -24,7 +24,6 @@ podman run -it \ --security-opt label=disable \ -e KUBECONFIG="${KUBECONFIG}" \ ${SSH_SOCK_MOUNTS} \ - -e GIT_SSH_COMMAND="ssh -o IgnoreUnknown=pubkeyacceptedalgorithms" \ -v ${HOME}:/home/runner \ -v ${HOME}:${HOME} \ -v ${HOME}:/root \ From 78a8f28c1e17d84f9bc1934bfe2d55a74b3dc2fe Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 5 Oct 2022 22:19:25 +0200 Subject: [PATCH 0571/1263] Return {} when parsing an empty yaml file Currently the yaml module returns None on an empty module. Let's keep an empty dictionary so we do not need to special case things more than needed. --- ansible/plugins/modules/vault_load_secrets.py | 2 ++ ansible/tests/unit/values-secret-broken1.yaml | 6 ++++++ ansible/tests/unit/values-secret-broken2.yaml | 6 ++++++ .../tests/unit/values-secret-empty-files.yaml | 16 ++++++++++++++++ .../tests/unit/values-secret-empty-secrets.yaml | 16 ++++++++++++++++ 5 files changed, 46 insertions(+) create mode 100644 ansible/tests/unit/values-secret-broken1.yaml create mode 100644 ansible/tests/unit/values-secret-broken2.yaml create mode 100644 ansible/tests/unit/values-secret-empty-files.yaml create mode 100644 ansible/tests/unit/values-secret-empty-secrets.yaml diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index e039c27d..aed819de 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -121,6 +121,8 @@ def parse_values(values_file): """ with open(values_file, "r", encoding="utf-8") as file: secrets_yaml = yaml.safe_load(file.read()) + if secrets_yaml == None: + return {} return secrets_yaml diff --git a/ansible/tests/unit/values-secret-broken1.yaml b/ansible/tests/unit/values-secret-broken1.yaml new file mode 100644 index 00000000..ecfc9df4 --- /dev/null +++ b/ansible/tests/unit/values-secret-broken1.yaml @@ -0,0 +1,6 @@ +--- +secrets: + # empty + +files: + # empty diff --git a/ansible/tests/unit/values-secret-broken2.yaml b/ansible/tests/unit/values-secret-broken2.yaml new file mode 100644 index 00000000..11c53d05 --- /dev/null +++ b/ansible/tests/unit/values-secret-broken2.yaml @@ -0,0 +1,6 @@ +--- +#secrets: + # empty + +#files: + # empty diff --git a/ansible/tests/unit/values-secret-empty-files.yaml b/ansible/tests/unit/values-secret-empty-files.yaml new file mode 100644 index 00000000..afcd245a --- /dev/null +++ b/ansible/tests/unit/values-secret-empty-files.yaml @@ -0,0 +1,16 @@ +--- +secrets: + # NEVER COMMIT THESE VALUES TO GIT + config-demo: + # Secret used for demonstrating vault storage, external secrets, and ACM distribution + secret: VALUE + + # Required for automated spoke deployment + aws: + access_key_id: VALUE + secret_access_key: VALUE + +# Required for automated spoke deployment +files: + # # ssh-rsa AAA... + # publickey: ~/.ssh/id_rsa.pub diff --git a/ansible/tests/unit/values-secret-empty-secrets.yaml b/ansible/tests/unit/values-secret-empty-secrets.yaml new file mode 100644 index 00000000..0624ecb5 --- /dev/null +++ b/ansible/tests/unit/values-secret-empty-secrets.yaml @@ -0,0 +1,16 @@ +--- +secrets: + # NEVER COMMIT THESE VALUES TO GIT + # config-demo: + # # Secret used for demonstrating vault storage, external secrets, and ACM distribution + # secret: VALUE + + # # Required for automated spoke deployment + # aws: + # access_key_id: VALUE + # secret_access_key: VALUE + +# Required for automated spoke deployment +files: + # # ssh-rsa AAA... + publickey: ~/.ssh/id_rsa.pub From 74c46d4e2cb8aba9912e3c2986bf29a821a33268 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 5 Oct 2022 22:20:42 +0200 Subject: [PATCH 0572/1263] Make sure to skip loops that have None in their range This way we avoid barfing in case the values-secret file contains empty 'files:' or empty 'secrets:' keys. Those are turned as None by the yaml module and not as {} --- ansible/plugins/modules/vault_load_secrets.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index aed819de..ee5632df 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -192,7 +192,14 @@ def sanitize_values(module, syaml): ) secrets = syaml.get("secrets", {}) + # We need to explicitely check for None because the file might contain the + # top-level 'secrets:' or 'files:' key but have nothing else under it which will + # return None and not {} + if secrets == None: + secrets = {} files = syaml.get("files", {}) + if files == None: + files = {} if len(secrets) == 0 and len(files) == 0: module.fail_json( f"Neither 'secrets' nor 'files have any secrets to " f"be parsed: {syaml}" @@ -301,7 +308,7 @@ def inject_secrets(module, syaml, namespace, pod, basepath): counter = 0 for i in get_secrets_vault_paths(module, syaml, "secrets"): path = f"{basepath}/{i[1]}" - for secret in syaml[i[0]]: + for secret in syaml[i[0]] or []: properties = "" for key, value in syaml[i[0]][secret].items(): properties += f"{key}='{value}' " @@ -315,7 +322,7 @@ def inject_secrets(module, syaml, namespace, pod, basepath): for i in get_secrets_vault_paths(module, syaml, "files"): path = f"{basepath}/{i[1]}" - for filekey in syaml[i[0]]: + for filekey in syaml[i[0]] or []: file = os.path.expanduser(syaml[i[0]][filekey]) cmd = ( f"cat '{file}' | oc exec -n {namespace} {pod} -i -- sh -c " From 5c26d87de3aa08197467d522c08971c57e3d5110 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 5 Oct 2022 22:21:59 +0200 Subject: [PATCH 0573/1263] Make sure to expand user path when checking for file existence --- ansible/plugins/modules/vault_load_secrets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index ee5632df..85ec7afd 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -214,7 +214,7 @@ def sanitize_values(module, syaml): for file in files: path = files[file] - if not os.path.isfile(path): + if not os.path.isfile(os.path.expanduser(path)): module.fail_json(f"File {path} does not exist") # If s3Secret key does not exist but s3.accessKey and s3.secretKey do exist From a5faeebb7f70b5b87079a7b61b5a42c02a08910f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 5 Oct 2022 22:22:18 +0200 Subject: [PATCH 0574/1263] Add a number of testcases that cover corner cases in the values files This should cover a rather large surface of values-secrets.yaml files. --- ansible/tests/unit/test_vault_load_secrets.py | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 22910cb7..402cb306 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -90,6 +90,94 @@ def test_module_fail_when_values_secret_not_existing(self): ret["msg"], "Values secrets file does not exist: /tmp/nonexisting" ) + def test_ensure_empty_files_but_not_secrets_is_ok(self): + set_module_args( + { + "values_secrets": os.path.join( + os.path.dirname(os.path.abspath(__file__)), "values-secret-empty-files.yaml" + ) + } + ) + + with patch.object(vault_load_secrets, "run_command") as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 2 + + calls = [ + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/config-demo' secret='VALUE'\"" # noqa: E501 + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/aws' access_key_id='VALUE' secret_access_key='VALUE'\"" # noqa: E501 + ), + ] + mock_run_command.assert_has_calls(calls) + + def test_ensure_broken1_file_fails(self): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + os.path.dirname(os.path.abspath(__file__)), "values-secret-broken1.yaml" + ) + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + + def test_ensure_broken2_file_fails(self): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + os.path.dirname(os.path.abspath(__file__)), "values-secret-broken2.yaml" + ) + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + + def test_ensure_empty_secrets_but_not_files_is_ok(self): + set_module_args( + { + "values_secrets": os.path.join( + os.path.dirname(os.path.abspath(__file__)), "values-secret-empty-secrets.yaml" + ) + } + ) + + with patch.object(vault_load_secrets, "run_command") as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 1 + + calls = [ + call( + "cat '/home/michele/.ssh/id_rsa.pub' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/publickey b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 + ), + ] + mock_run_command.assert_has_calls(calls) def test_ensure_command_called(self): set_module_args( { From e462b94910fde4b979e94d9fd8e26c1c2416e8f7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 5 Oct 2022 22:24:33 +0200 Subject: [PATCH 0575/1263] Fix up flake8 and black warnings --- ansible/plugins/modules/vault_load_secrets.py | 6 +++--- ansible/tests/unit/test_vault_load_secrets.py | 13 +++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 85ec7afd..d9616838 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -121,7 +121,7 @@ def parse_values(values_file): """ with open(values_file, "r", encoding="utf-8") as file: secrets_yaml = yaml.safe_load(file.read()) - if secrets_yaml == None: + if secrets_yaml is None: return {} return secrets_yaml @@ -195,10 +195,10 @@ def sanitize_values(module, syaml): # We need to explicitely check for None because the file might contain the # top-level 'secrets:' or 'files:' key but have nothing else under it which will # return None and not {} - if secrets == None: + if secrets is None: secrets = {} files = syaml.get("files", {}) - if files == None: + if files is None: files = {} if len(secrets) == 0 and len(files) == 0: module.fail_json( diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 402cb306..9f2b1f4f 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -94,7 +94,8 @@ def test_ensure_empty_files_but_not_secrets_is_ok(self): set_module_args( { "values_secrets": os.path.join( - os.path.dirname(os.path.abspath(__file__)), "values-secret-empty-files.yaml" + os.path.dirname(os.path.abspath(__file__)), + "values-secret-empty-files.yaml", ) } ) @@ -127,7 +128,8 @@ def test_ensure_broken1_file_fails(self): set_module_args( { "values_secrets": os.path.join( - os.path.dirname(os.path.abspath(__file__)), "values-secret-broken1.yaml" + os.path.dirname(os.path.abspath(__file__)), + "values-secret-broken1.yaml", ) } ) @@ -141,7 +143,8 @@ def test_ensure_broken2_file_fails(self): set_module_args( { "values_secrets": os.path.join( - os.path.dirname(os.path.abspath(__file__)), "values-secret-broken2.yaml" + os.path.dirname(os.path.abspath(__file__)), + "values-secret-broken2.yaml", ) } ) @@ -154,7 +157,8 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): set_module_args( { "values_secrets": os.path.join( - os.path.dirname(os.path.abspath(__file__)), "values-secret-empty-secrets.yaml" + os.path.dirname(os.path.abspath(__file__)), + "values-secret-empty-secrets.yaml", ) } ) @@ -178,6 +182,7 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): ), ] mock_run_command.assert_has_calls(calls) + def test_ensure_command_called(self): set_module_args( { From 8e3221e8bb639e9ba865687c3bf11615066bcacc Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 5 Oct 2022 22:29:07 +0200 Subject: [PATCH 0576/1263] Fix ansible-lint --- ansible/tests/unit/values-secret-broken2.yaml | 8 ++++---- ansible/tests/unit/values-secret-empty-files.yaml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ansible/tests/unit/values-secret-broken2.yaml b/ansible/tests/unit/values-secret-broken2.yaml index 11c53d05..82477acd 100644 --- a/ansible/tests/unit/values-secret-broken2.yaml +++ b/ansible/tests/unit/values-secret-broken2.yaml @@ -1,6 +1,6 @@ --- -#secrets: - # empty +# secrets: +# empty -#files: - # empty +# files: +# empty diff --git a/ansible/tests/unit/values-secret-empty-files.yaml b/ansible/tests/unit/values-secret-empty-files.yaml index afcd245a..b609c1fe 100644 --- a/ansible/tests/unit/values-secret-empty-files.yaml +++ b/ansible/tests/unit/values-secret-empty-files.yaml @@ -7,8 +7,8 @@ secrets: # Required for automated spoke deployment aws: - access_key_id: VALUE - secret_access_key: VALUE + access_key_id: VALUE + secret_access_key: VALUE # Required for automated spoke deployment files: From df432b4b878c5e76ee78c39b1fc307ed66e8f88d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 6 Oct 2022 11:14:21 +0200 Subject: [PATCH 0577/1263] Cleanup tests and bail out properly when files/secrets: are lists --- ansible/plugins/modules/vault_load_secrets.py | 8 ++- ansible/tests/unit/test_vault_load_secrets.py | 50 ++++++------------- ansible/tests/unit/values-secret-broken3.yaml | 9 ++++ 3 files changed, 31 insertions(+), 36 deletions(-) create mode 100644 ansible/tests/unit/values-secret-broken3.yaml diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index d9616838..891daa9c 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -202,9 +202,15 @@ def sanitize_values(module, syaml): files = {} if len(secrets) == 0 and len(files) == 0: module.fail_json( - f"Neither 'secrets' nor 'files have any secrets to " f"be parsed: {syaml}" + f"Neither 'secrets' nor 'files have any secrets to be parsed: {syaml}" ) + if isinstance(secrets, list) or isinstance(files, list): + module.fail_json( + f"Neither 'secrets' nor 'files can be lists: {syaml}" + ) + + for secret in secrets: if not isinstance(secrets[secret], dict): module.fail_json( diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 9f2b1f4f..92134409 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -68,6 +68,7 @@ def setUp(self): ) self.mock_module_helper.start() self.addCleanup(self.mock_module_helper.stop) + self.testdir = os.path.dirname(os.path.abspath(__file__)) def test_module_fail_when_required_args_missing(self): with self.assertRaises(AnsibleFailJson): @@ -94,7 +95,7 @@ def test_ensure_empty_files_but_not_secrets_is_ok(self): set_module_args( { "values_secrets": os.path.join( - os.path.dirname(os.path.abspath(__file__)), + self.testdir, "values-secret-empty-files.yaml", ) } @@ -123,41 +124,24 @@ def test_ensure_empty_files_but_not_secrets_is_ok(self): ] mock_run_command.assert_has_calls(calls) - def test_ensure_broken1_file_fails(self): - with self.assertRaises(AnsibleFailJson) as ansible_err: - set_module_args( - { - "values_secrets": os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "values-secret-broken1.yaml", - ) - } - ) - vault_load_secrets.main() - - ret = ansible_err.exception.args[0] - self.assertEqual(ret["failed"], True) - - def test_ensure_broken2_file_fails(self): - with self.assertRaises(AnsibleFailJson) as ansible_err: - set_module_args( - { - "values_secrets": os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "values-secret-broken2.yaml", - ) - } - ) - vault_load_secrets.main() + def test_ensure_broken_files_fail(self): + for i in ( + "values-secret-broken1.yaml", + "values-secret-broken2.yaml", + "values-secret-broken3.yaml", + ): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args({"values_secrets": os.path.join(self.testdir, i)}) + vault_load_secrets.main() - ret = ansible_err.exception.args[0] - self.assertEqual(ret["failed"], True) + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) def test_ensure_empty_secrets_but_not_files_is_ok(self): set_module_args( { "values_secrets": os.path.join( - os.path.dirname(os.path.abspath(__file__)), + self.testdir, "values-secret-empty-secrets.yaml", ) } @@ -185,11 +169,7 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): def test_ensure_command_called(self): set_module_args( - { - "values_secrets": os.path.join( - os.path.dirname(os.path.abspath(__file__)), "values-secret.yaml" - ) - } + {"values_secrets": os.path.join(self.testdir, "values-secret.yaml")} ) with patch.object(vault_load_secrets, "run_command") as mock_run_command: diff --git a/ansible/tests/unit/values-secret-broken3.yaml b/ansible/tests/unit/values-secret-broken3.yaml new file mode 100644 index 00000000..6d7295ba --- /dev/null +++ b/ansible/tests/unit/values-secret-broken3.yaml @@ -0,0 +1,9 @@ +--- +secrets: + - borked1 + - borked2 + +files: + foo: + - broken + - broken2 From db36fbbfe18e695351dd359c0a0482d161c746de Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 6 Oct 2022 11:20:17 +0200 Subject: [PATCH 0578/1263] Reformat isinstance list fail message --- ansible/plugins/modules/vault_load_secrets.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 891daa9c..b33a8a4f 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -206,10 +206,7 @@ def sanitize_values(module, syaml): ) if isinstance(secrets, list) or isinstance(files, list): - module.fail_json( - f"Neither 'secrets' nor 'files can be lists: {syaml}" - ) - + module.fail_json(f"Neither 'secrets' nor 'files can be lists: {syaml}") for secret in secrets: if not isinstance(secrets[secret], dict): From 36c62354661337367db9930d1c9c4ed9e14eff73 Mon Sep 17 00:00:00 2001 From: day0hero Date: Thu, 6 Oct 2022 12:36:04 -0500 Subject: [PATCH 0579/1263] Added callbacks to ansible.cfg The profile_roles and profile_tasks callbacks provide timinng information for tasks and roles to execute. --- ansible/ansible.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index 9a742d0a..463c4706 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -3,3 +3,4 @@ display_skipped_hosts=False localhost_warning=False library=./plugins/modules:~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules roles_path=./roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles +callback_whitelist = profile_roles, profile_tasks From 4f548720a43b79bcc8f0f438193b2eff4bcd7017 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Oct 2022 11:27:51 +0200 Subject: [PATCH 0580/1263] Add a pattern_dir variable to the playbook This allows us to reference any files inside the pattern easily from ansible. The first user of this will be the vault module to read the values-secret.yaml.template file to check if all needed secrets are defined or not. --- scripts/vault-utils.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 2f014a45..3650466c 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -7,8 +7,9 @@ get_abs_filename() { } SCRIPT=$(get_abs_filename "$0") -SCRIPTPATH=$(dirname "$SCRIPT") -COMMONPATH=$(dirname "$SCRIPTPATH") +SCRIPTPATH=$(dirname "${SCRIPT}") +COMMONPATH=$(dirname "${SCRIPTPATH}") +PATTERNPATH=$(dirname "${COMMONPATH}") ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" PLAYBOOKPATH="${ANSIBLEPATH}/playbooks" export ANSIBLE_CONFIG="${ANSIBLEPATH}/ansible.cfg" @@ -27,4 +28,4 @@ if [ -z ${TASK} ]; then exit 1 fi -ansible-playbook -t "${TASK}" -e output_file="${OUTFILE}" "${PLAYBOOKPATH}/vault/vault.yaml" +ansible-playbook -t "${TASK}" -e pattern_dir="${PATTERNPATH}" -e output_file="${OUTFILE}" "${PLAYBOOKPATH}/vault/vault.yaml" From cd0c8b51b0aa0c94245237b9ea729659adc09d57 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Oct 2022 11:32:07 +0200 Subject: [PATCH 0581/1263] Add a check a check_missing_secrets parameter to vault_load_secrets When this is set to true we read the 'values_secret_template' parameter, parse the yaml file pointed to it and make sure that al the keys in there exist in the values-secret.yaml file. The way the implementation works is by flattening the dictionary like the following. This is the starting yaml/dictionary: secrets: config-demo: secret: demo123 cluster_alejandro: name: alejandro bearerToken: foo test: s3.accessKey: "1234" s3.secretKey: "4321" files: cluster_alejandro_ca: /home/michele/ca.crt This will generate the following flattended structure: {'secrets.config-demo.secret': 'demo123', 'secrets.cluster_alejandro.name': 'alejandro', 'secrets.cluster_alejandro.bearerToken': 'foo', 'secrets.test.s3.accessKey': '1234', 'secrets.test.s3.secretKey': '4321', 'files.cluster_alejandro_ca': '/home/michele/ca.crt'} We then take the keys of the flattened structure and compare them as sets. If the keys of the values-secret.yaml.template file are a subset of those in ~/values-secret.yaml it means all of them are present. --- ansible/plugins/modules/vault_load_secrets.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index b33a8a4f..d8b9eadb 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -51,6 +51,7 @@ """ import base64 +from collections.abc import MutableMapping import os import subprocess @@ -95,6 +96,19 @@ required: false type: str default: secret + check_missing_secrets: + description: + - Validate the ~/values-secret.yaml file against the top-level + values-secret-template.yaml and error out if secrets are missing + required: false + type: bool + default: False + values_secret_template: + description: + - Path of the values-secret-template.yaml file of the pattern + required: false + type: str + default: "" """ RETURN = """ @@ -160,6 +174,38 @@ def run_command(command): return ret +def flatten(dictionary, parent_key=False, separator="."): + """ + Turn a nested dictionary into a flattened dictionary and also + drop any key that has 'None' as their value + + Parameters: + dictionary(dict): The dictionary to flatten + + parent_key(str): The string to prepend to dictionary's keys + + separator(str): The string used to separate flattened keys + + Returns: + + dictionary: A flattened dictionary where the keys represent the + path to reach the leaves + """ + + items = [] + for key, value in dictionary.items(): + new_key = str(parent_key) + separator + key if parent_key else key + if isinstance(value, MutableMapping): + items.extend(flatten(value, new_key, separator).items()) + elif isinstance(value, list): + for k, v in enumerate(value): + items.extend(flatten({str(k): v}, new_key).items()) + else: + if value is not None: + items.append((new_key, value)) + return dict(items) + + def sanitize_values(module, syaml): """ Sanitizes the secrets YAML object. If a specific secret key has @@ -339,6 +385,27 @@ def inject_secrets(module, syaml, namespace, pod, basepath): return counter +def check_for_missing_secrets(module, syaml, values_secret_template): + with open(values_secret_template, "r", encoding="utf-8") as file: + template_yaml = yaml.safe_load(file.read()) + if template_yaml is None: + module.fail_json(f"Template {values_secret_template} is empty") + + syaml_flat = flatten(syaml) + template_flat = flatten(template_yaml) + + syaml_keys = set(syaml_flat.keys()) + template_keys = set(template_flat.keys()) + + if template_keys <= syaml_keys: + return + + diff = template_keys - syaml_keys + module.fail_json( + f"Values secret yaml is missing needed secrets from the templates: {diff}" + ) + + def run(module): """Main ansible module entry point""" results = dict(changed=False) @@ -348,6 +415,12 @@ def run(module): basepath = args.get("basepath") namespace = args.get("namespace") pod = args.get("pod") + check_missing_secrets = args.get("check_missing_secrets") + values_secret_template = args.get("values_secret_template") + if check_missing_secrets and values_secret_template == "": + module.fail_json( + "No values_secret_template defined and check_missing_secrets set to True" + ) if not os.path.exists(values_secrets): results["failed"] = True @@ -363,6 +436,12 @@ def run(module): # In the future we can use the version field to manage different formats if needed secrets = sanitize_values(module, syaml) + + # If the user specified check_for_missing_secrets then we read values_secret_template + # and check if there are any missing secrets + if check_missing_secrets: + check_for_missing_secrets(module, syaml, values_secret_template) + nr_secrets = inject_secrets(module, secrets, namespace, pod, basepath) results["failed"] = False results["changed"] = True From 11c954186f42e28aad68bd07d03e61e07ad28798 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Oct 2022 11:44:24 +0200 Subject: [PATCH 0582/1263] Add a number of unit tests to verify the new check_missing_secrets functionality We add two tests: - One that is expected to pass because the values-secret file has all the keys of the template - One that is expected to fail because the values-secret file is missing at least one key that is specified in the template --- ansible/tests/unit/mcg-values-secret.yaml | 27 +++++++++ ansible/tests/unit/template-mcg-missing.yaml | 28 +++++++++ ansible/tests/unit/template-mcg-working.yaml | 26 +++++++++ ansible/tests/unit/test_vault_load_secrets.py | 57 +++++++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 ansible/tests/unit/mcg-values-secret.yaml create mode 100644 ansible/tests/unit/template-mcg-missing.yaml create mode 100644 ansible/tests/unit/template-mcg-working.yaml diff --git a/ansible/tests/unit/mcg-values-secret.yaml b/ansible/tests/unit/mcg-values-secret.yaml new file mode 100644 index 00000000..e540e0e6 --- /dev/null +++ b/ansible/tests/unit/mcg-values-secret.yaml @@ -0,0 +1,27 @@ +--- +secrets: + # NEVER COMMIT THESE VALUES TO GIT + config-demo: + # Secret used for demonstrating vault storage, external secrets, and ACM distribution + secret: VALUE + additionalsecret: test + + # Required for automated spoke deployment + #aws: + # access_key_id: VALUE + # secret_access_key: VALUE + +# Required for automated spoke deployment +files: + # # ssh-rsa AAA... + # publickey: ~/.ssh/id_rsa.pub + # + # # -----BEGIN RSA PRIVATE KEY + # # ... + # # -----END RSA PRIVATE KEY + # privatekey: ~/.ssh/id_rsa + # + # # {"auths":{"cloud.openshift.com":{"auth":"b3Blb... }}} + # openshiftPullSecret: ~/.dockerconfigjson + # + # azureOsServicePrincipal: ~/osServicePrincipal.json diff --git a/ansible/tests/unit/template-mcg-missing.yaml b/ansible/tests/unit/template-mcg-missing.yaml new file mode 100644 index 00000000..048c6133 --- /dev/null +++ b/ansible/tests/unit/template-mcg-missing.yaml @@ -0,0 +1,28 @@ +--- +# This template file +secrets: + # NEVER COMMIT THESE VALUES TO GIT + config-demo: + # Secret used for demonstrating vault storage, external secrets, and ACM distribution + secret: VALUE + foo: bar + + # Required for automated spoke deployment + #aws: + # access_key_id: VALUE + # secret_access_key: VALUE + +# Required for automated spoke deployment +files: + # # ssh-rsa AAA... + # publickey: ~/.ssh/id_rsa.pub + # + # # -----BEGIN RSA PRIVATE KEY + # # ... + # # -----END RSA PRIVATE KEY + # privatekey: ~/.ssh/id_rsa + # + # # {"auths":{"cloud.openshift.com":{"auth":"b3Blb... }}} + # openshiftPullSecret: ~/.dockerconfigjson + # + # azureOsServicePrincipal: ~/osServicePrincipal.json diff --git a/ansible/tests/unit/template-mcg-working.yaml b/ansible/tests/unit/template-mcg-working.yaml new file mode 100644 index 00000000..52d2d567 --- /dev/null +++ b/ansible/tests/unit/template-mcg-working.yaml @@ -0,0 +1,26 @@ +--- +secrets: + # NEVER COMMIT THESE VALUES TO GIT + config-demo: + # Secret used for demonstrating vault storage, external secrets, and ACM distribution + secret: VALUE + + # Required for automated spoke deployment + #aws: + # access_key_id: VALUE + # secret_access_key: VALUE + +# Required for automated spoke deployment +files: + # # ssh-rsa AAA... + # publickey: ~/.ssh/id_rsa.pub + # + # # -----BEGIN RSA PRIVATE KEY + # # ... + # # -----END RSA PRIVATE KEY + # privatekey: ~/.ssh/id_rsa + # + # # {"auths":{"cloud.openshift.com":{"auth":"b3Blb... }}} + # openshiftPullSecret: ~/.dockerconfigjson + # + # azureOsServicePrincipal: ~/osServicePrincipal.json diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 92134409..c71e60fa 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -58,6 +58,7 @@ def exit_json(*args, **kwargs): def fail_json(*args, **kwargs): """function to patch over fail_json; package return data into an exception""" kwargs["failed"] = True + kwargs["args"] = args raise AnsibleFailJson(kwargs) @@ -216,6 +217,62 @@ def test_ensure_command_called(self): ] mock_run_command.assert_has_calls(calls) + def test_ensure_good_template_checking(self): + set_module_args( + { + "values_secrets": os.path.join(self.testdir, "mcg-values-secret.yaml"), + "check_missing_secrets": True, + "values_secret_template": os.path.join( + self.testdir, "template-mcg-working.yaml" + ), + } + ) + with patch.object(vault_load_secrets, "run_command") as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 1 + + calls = [ + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/config-demo' secret='VALUE' additionalsecret='test'\"" # noqa: E501 + ), + ] + mock_run_command.assert_has_calls(calls) + + def test_ensure_bad_template_checking(self): + set_module_args( + { + "values_secrets": os.path.join(self.testdir, "mcg-values-secret.yaml"), + "check_missing_secrets": True, + "values_secret_template": os.path.join( + self.testdir, "template-mcg-missing.yaml" + ), + } + ) + with patch.object(vault_load_secrets, "run_command") as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr + + with self.assertRaises(AnsibleFailJson) as result: + vault_load_secrets.main() + self.assertTrue(result.exception.args[0]["failed"]) + # In case of failure args[1] contains the msg of the failure + assert ( + result.exception.args[0]["args"][1] + == "Values secret yaml is missing needed secrets from the templates: {'secrets.config-demo.foo'}" + ) + assert mock_run_command.call_count == 0 + if __name__ == "__main__": unittest.main() From 605413fb1dc1329e79262a47d5ad90a87656e6df Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Oct 2022 11:46:38 +0200 Subject: [PATCH 0583/1263] Add all the code to actually verify secrets against values-secret-template.yaml but keep it disabled This adds the last piece to actually be able to flip the check_missing_secrets switch to True. So as is this is still not being run. The idea here is to socialize this feature a bit more and then flip it one. Once set to True, the 'values-secret-template.yaml' becomes a *required* part of the pattern. --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index a7b838f7..cae7952e 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -34,6 +34,12 @@ delay: 45 changed_when: false +- name: Set secret_template fact + ansible.builtin.set_fact: + secret_template: "{{ pattern_dir }}/values-secret.yaml.template" + - name: Loads secrets file into the vault of a cluster vault_load_secrets: values_secrets: ~/values-secret.yaml + check_missing_secrets: False + values_secret_template: "{{ secret_template }}" From a02a7d018e847b2100927888334f554ffe97af6c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Oct 2022 11:52:31 +0200 Subject: [PATCH 0584/1263] Fix a couple of linting issues --- ansible/plugins/modules/vault_load_secrets.py | 2 +- ansible/roles/vault_utils/tasks/push_secrets.yaml | 2 +- ansible/tests/unit/mcg-values-secret.yaml | 2 +- ansible/tests/unit/template-mcg-missing.yaml | 3 +-- ansible/tests/unit/template-mcg-working.yaml | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index d8b9eadb..7f037e46 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -51,9 +51,9 @@ """ import base64 -from collections.abc import MutableMapping import os import subprocess +from collections.abc import MutableMapping import yaml from ansible.module_utils.basic import AnsibleModule diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index cae7952e..8d1bba5e 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -41,5 +41,5 @@ - name: Loads secrets file into the vault of a cluster vault_load_secrets: values_secrets: ~/values-secret.yaml - check_missing_secrets: False + check_missing_secrets: false values_secret_template: "{{ secret_template }}" diff --git a/ansible/tests/unit/mcg-values-secret.yaml b/ansible/tests/unit/mcg-values-secret.yaml index e540e0e6..8586f1a6 100644 --- a/ansible/tests/unit/mcg-values-secret.yaml +++ b/ansible/tests/unit/mcg-values-secret.yaml @@ -7,7 +7,7 @@ secrets: additionalsecret: test # Required for automated spoke deployment - #aws: + # aws: # access_key_id: VALUE # secret_access_key: VALUE diff --git a/ansible/tests/unit/template-mcg-missing.yaml b/ansible/tests/unit/template-mcg-missing.yaml index 048c6133..eca36b2e 100644 --- a/ansible/tests/unit/template-mcg-missing.yaml +++ b/ansible/tests/unit/template-mcg-missing.yaml @@ -1,5 +1,4 @@ --- -# This template file secrets: # NEVER COMMIT THESE VALUES TO GIT config-demo: @@ -8,7 +7,7 @@ secrets: foo: bar # Required for automated spoke deployment - #aws: + # aws: # access_key_id: VALUE # secret_access_key: VALUE diff --git a/ansible/tests/unit/template-mcg-working.yaml b/ansible/tests/unit/template-mcg-working.yaml index 52d2d567..8445c6f3 100644 --- a/ansible/tests/unit/template-mcg-working.yaml +++ b/ansible/tests/unit/template-mcg-working.yaml @@ -6,7 +6,7 @@ secrets: secret: VALUE # Required for automated spoke deployment - #aws: + # aws: # access_key_id: VALUE # secret_access_key: VALUE From 3bcfa959d56512f9fd3b37de7b1314e6b4948d2c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 13 Oct 2022 10:31:01 +0200 Subject: [PATCH 0585/1263] Tweak the SSH paths so ssh-agent works again Newer ssh versions (worked for me in F36, broke in F37) seem to dislike if the ssh-agent socket is in /ssh-agent in the container. The ssh-agent on will just refuse the connections from the container. Let's bind the original host path in the same place in the container. That way we can also drop the real_path function(). Tested and now ssh agent correctly works again. --- scripts/pattern-util.sh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index b2cfcac0..fe926ec4 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -4,12 +4,6 @@ if [ -z "$PATTERN_UTILITY_CONTAINER" ]; then PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/hybridcloudpatterns-utility-ee" fi -# This is one of the most concise ways to get a readlink -f command work without going too complicated -# Across Linux and MacOSX -function real_path() { - echo $(cd $(dirname $1) ; pwd -P) -} - # Copy Kubeconfig from current environment. The utilities will pick up ~/.kube/config if set so it's not mandatory # /home/runner is the normal homedir # $HOME is mounted as itself for any files that are referenced with absolute paths @@ -17,7 +11,7 @@ function real_path() { # We bind mount the SSH_AUTH_SOCK socket if it is set, so ssh works without user prompting SSH_SOCK_MOUNTS="" if [ -n "$SSH_AUTH_SOCK" ]; then - SSH_SOCK_MOUNTS="-v $(real_path $SSH_AUTH_SOCK):/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent" + SSH_SOCK_MOUNTS="-v ${SSH_AUTH_SOCK}:${SSH_AUTH_SOCK} -e SSH_AUTH_SOCK=${SSH_AUTH_SOCK}" fi podman run -it \ From babdddb523dc4493f3d3a17661448efae5aa02a1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 13 Oct 2022 11:40:00 +0200 Subject: [PATCH 0586/1263] Pass -e KUBECONFIG *only* if it is set Without this patch the wrapper breaks when KUBECONFIG is unset and ~/.kube/config exists. We must pass -e KUBECONFIG *only* if it is set, otherwise we end up passing KUBECONFIG="" which then will confuse ansible. Tested and now 'make load-secrets' works correctly via the wrapper whenever KUBECONFIG is unset and ~/.kube/config exists. --- scripts/pattern-util.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index fe926ec4..c2e130cb 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -14,9 +14,16 @@ if [ -n "$SSH_AUTH_SOCK" ]; then SSH_SOCK_MOUNTS="-v ${SSH_AUTH_SOCK}:${SSH_AUTH_SOCK} -e SSH_AUTH_SOCK=${SSH_AUTH_SOCK}" fi +# We must pass -e KUBECONFIG *only* if it is set, otherwise we end up passing +# KUBECONFIG="" which then will confuse ansible +KUBECONF_ENV="" +if [ -n "$KUBECONFIG" ]; then + KUBECONF_ENV="-e KUBECONFIG=${KUBECONFIG}" +fi + podman run -it \ --security-opt label=disable \ - -e KUBECONFIG="${KUBECONFIG}" \ + ${KUBECONF_ENV} \ ${SSH_SOCK_MOUNTS} \ -v ${HOME}:/home/runner \ -v ${HOME}:${HOME} \ From d4ee601fe8eef06fe147c0a1ad45930dc20497fc Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 13 Oct 2022 22:08:35 +0200 Subject: [PATCH 0587/1263] Get the correct pattern name when the path has a folder Spotted by Jonny. Before: ~/test me/multicloud-gitops -> NAME=$(shell basename "`pwd`") -> "test" After: ~/test me/multicloud-gitops -> NAME=$(shell basename "`pwd`") -> "multicloud-gitops" --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 049b0e1b..1767cf66 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -NAME=$(shell basename `pwd`) +NAME=$(shell basename "`pwd`") # This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL # This is because we expect to use tokens for repo authentication as opposed to SSH keys ifneq ($(origin TARGET_SITE), undefined) From f97ad0672fda6acb31b81632fd6f345d46a845ed Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 11 Oct 2022 20:02:27 +0200 Subject: [PATCH 0588/1263] Implement global.clusterVersion We add global.clusterVersion variable (OCP major.minor) and pass it on. Then we add a /values--.yaml in the helm parameter list. We wrap every inclusion of /values--.yaml in an '{{ if .global.clusterVersion }}' to avoid including anything in case the variable is not defined or would end up having a default value which would end up being undesirable. This will allow us to have per-ocp version overrides and potentially avoid having to use git branches more than needed. Tested on MCG (w/ACM) and observed the correct global.clusterVersion on both argo instances on hub and spoke. Also the new values includes looked correct. I tested this as follows: diff --git a/charts/all/config-demo/templates/config-demo-cm.yaml b/charts/all/config-demo/templates/config-demo-cm.yaml index ac7fe991..795c235b 100644 --- a/charts/all/config-demo/templates/config-demo-cm.yaml +++ b/charts/all/config-demo/templates/config-demo-cm.yaml @@ -5,6 +5,7 @@ metadata: labels: app.kubernetes.io/instance: config-demo data: + foo: {{ .Values.global.bandini }} "index.html": |- diff --git a/values-4.10-hub.yaml b/values-4.10-hub.yaml new file mode 100644 index 00000000..350b40ec --- /dev/null +++ b/values-4.10-hub.yaml @@ -0,0 +1,3 @@ +--- +global: + bandini: "4.10 is the best on the hub" diff --git a/values-4.9-group-one.yaml b/values-4.9-group-one.yaml new file mode 100644 index 00000000..df704d75 --- /dev/null +++ b/values-4.9.yaml @@ -0,0 +1,3 @@ +--- +global: + bandini: "ZOMG I love 4.9 on the group-one spoke" foo was "ZOMG I love 4.9 on the group-one spoke" on ocp 4.9.48 and "4.10 is the best on the hub" on 4.10.32 NOTE: This is for install-legacy *only* atm. A separate PR is needed to add this as a top-level object in the operator. --- Changes.md | 8 ++++++++ Makefile | 3 ++- acm/templates/policies/application-policies.yaml | 6 ++++++ clustergroup/templates/plumbing/applications.yaml | 10 ++++++++++ install/templates/argocd/application.yaml | 5 +++++ tests/acm-normal.expected.yaml | 6 ++++++ tests/acm.expected.diff | 8 +++++++- tests/clustergroup-normal.expected.yaml | 4 ++++ tests/clustergroup.expected.diff | 12 ++++++++---- tests/install-naked.expected.yaml | 2 ++ tests/install-normal.expected.yaml | 2 ++ tests/install.expected.diff | 6 +++--- 12 files changed, 63 insertions(+), 9 deletions(-) diff --git a/Changes.md b/Changes.md index d5ce8614..89f83c79 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,13 @@ # Changes +## October 13, 2022 + +* Added global.clusterVersion as a new helm variable which represents the OCP + Major.Minor cluster version. By default now a user can add a + values--.yaml file to have specific cluster version + overrides (e.g. values-4.10-hub.yaml). Will need Validated Patterns >= 0.0.6 + when deploying with the operator. + ## October 4, 2022 * Extended the values-secret.yaml file to support multiple vault paths and re-wrote diff --git a/Makefile b/Makefile index 049b0e1b..f62a7d7b 100644 --- a/Makefile +++ b/Makefile @@ -10,10 +10,11 @@ TARGET_REPO=$(shell git remote show $(TARGET_ORIGIN) | grep Push | sed -e 's/.*U # git branch --show-current is also available as of git 2.22, but we will use this for compatibility TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spec.domain}) +HUBCLUSTER_VERSION=$(shell oc get OpenShiftControllerManager/cluster -o jsonpath='{.status.version}' | sed -n -E 's/([0-9]+).([0-9]+).*/\1.\2/p') # --set values always take precedence over the contents of -f HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) \ - --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) $(TARGET_SITE_OPT) + --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) --set global.clusterVersion="$(HUBCLUSTER_VERSION)" $(TARGET_SITE_OPT) TEST_OPTS= -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" \ --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" \ --set global.namespace="pattern-namespace" --set global.hubClusterDomain=apps.hub.example.com --set global.localClusterDomain=apps.region.example.com --set global.clusterDomain=region.example.com\ diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index dfb29a90..0a398e20 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -45,6 +45,9 @@ spec: valueFiles: - "/values-global.yaml" - "/values-{{ .name }}.yaml" + {{- if $.Values.global.clusterVersion }} + - "/values-{{ $.Values.global.clusterVersion }}-{{ .name }}.yaml" + {{- end }} {{- range $valueFile := .extraValueFiles }} - {{ $valueFile | quote }} {{- end }} @@ -64,6 +67,9 @@ spec: # Requires ACM 2.6 or higher - name: global.clusterDomain value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}` }}' + # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) + - name: global.clusterVersion + value: '{{ `{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}` }}' - name: clusterGroup.name value: {{ $group.name }} {{- range .helmOverrides }} diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index c9a6667d..f7e165d8 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -56,6 +56,9 @@ spec: ignoreMissingValueFiles: true valueFiles: - "values.yaml" + {{- if $.Values.global.clusterVersion }} + - "/values-{{ $.Values.global.clusterVersion }}-{{ $.Values.clusterGroup.name }}.yaml" + {{- end }} {{- range .extraValueFiles }} - {{ . | quote }} {{- end }} @@ -66,6 +69,8 @@ spec: parameters: - name: global.clusterDomain value: {{ $.Values.global.clusterDomain }} + - name: global.clusterVersion + value: "{{ $.Values.global.clusterVersion }}" - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain @@ -144,6 +149,9 @@ spec: valueFiles: - "/values-global.yaml" - "/values-{{ $.Values.clusterGroup.name }}.yaml" + {{- if $.Values.global.clusterVersion }} + - "/values-{{ $.Values.global.clusterVersion }}-{{ $.Values.clusterGroup.name }}.yaml" + {{- end }} {{- range $valueFile := .extraValueFiles }} - {{ $valueFile | quote }} {{- end }} @@ -159,6 +167,8 @@ spec: value: {{ $.Values.global.pattern }} - name: global.clusterDomain value: {{ $.Values.global.clusterDomain }} + - name: global.clusterVersion + value: "{{ $.Values.global.clusterVersion }}" - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml index 324e1f32..f1a6a84d 100644 --- a/install/templates/argocd/application.yaml +++ b/install/templates/argocd/application.yaml @@ -19,6 +19,9 @@ spec: valueFiles: - "/values-global.yaml" - "/values-{{ .Values.main.clusterGroupName }}.yaml" + {{- if .Values.global.clusterVersion }} + - "/values-{{ .Values.global.clusterVersion }}-{{ .Values.main.clusterGroupName }}.yaml" + {{- end }} # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 parameters: - name: global.repoURL @@ -31,6 +34,8 @@ spec: value: {{ .Release.Name }} - name: global.hubClusterDomain value: {{ .Values.global.hubClusterDomain }} + - name: global.clusterVersion + value: "{{ .Values.global.clusterVersion }}" {{- if eq .Values.main.options.syncPolicy "Automatic" }} syncPolicy: automated: {} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 42b9452b..25b2cb2d 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -543,6 +543,9 @@ spec: # Requires ACM 2.6 or higher - name: global.clusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' + # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) + - name: global.clusterVersion + value: '{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}' - name: clusterGroup.name value: acm-edge - name: clusterGroup.isHubCluster @@ -624,6 +627,9 @@ spec: # Requires ACM 2.6 or higher - name: global.clusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' + # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) + - name: global.clusterVersion + value: '{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}' - name: clusterGroup.name value: acm-provision-edge - name: clusterGroup.isHubCluster diff --git a/tests/acm.expected.diff b/tests/acm.expected.diff index 9e95aeda..3944a3b3 100644 --- a/tests/acm.expected.diff +++ b/tests/acm.expected.diff @@ -461,7 +461,7 @@ # Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule -@@ -44,6 +482,169 @@ +@@ -44,6 +482,175 @@ values: - OpenShift --- @@ -526,6 +526,9 @@ + # Requires ACM 2.6 or higher + - name: global.clusterDomain + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' ++ # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) ++ - name: global.clusterVersion ++ value: '{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}' + - name: clusterGroup.name + value: acm-edge + - name: clusterGroup.isHubCluster @@ -607,6 +610,9 @@ + # Requires ACM 2.6 or higher + - name: global.clusterDomain + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' ++ # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) ++ - name: global.clusterVersion ++ value: '{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}' + - name: clusterGroup.name + value: acm-provision-edge + - name: clusterGroup.isHubCluster diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 4d740ac8..ae1f7fc6 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -528,6 +528,8 @@ spec: value: mypattern - name: global.clusterDomain value: region.example.com + - name: global.clusterVersion + value: "" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -579,6 +581,8 @@ spec: value: mypattern - name: global.clusterDomain value: region.example.com + - name: global.clusterVersion + value: "" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain diff --git a/tests/clustergroup.expected.diff b/tests/clustergroup.expected.diff index 45fca30b..02498ef3 100644 --- a/tests/clustergroup.expected.diff +++ b/tests/clustergroup.expected.diff @@ -241,7 +241,7 @@ roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole -@@ -45,16 +257,583 @@ +@@ -45,16 +257,587 @@ - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-application-controller name: example-gitops-argocd-application-controller @@ -519,6 +519,8 @@ + value: mypattern + - name: global.clusterDomain + value: region.example.com ++ - name: global.clusterVersion ++ value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain @@ -570,6 +572,8 @@ + value: mypattern + - name: global.clusterDomain + value: region.example.com ++ - name: global.clusterVersion ++ value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain @@ -828,7 +832,7 @@ --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 -@@ -65,7 +844,7 @@ +@@ -65,7 +848,7 @@ # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: example-gitops @@ -837,7 +841,7 @@ annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: -@@ -94,10 +873,10 @@ +@@ -94,10 +877,10 @@ --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE @@ -852,7 +856,7 @@ --set clusterGroup.name=example --post-renderer ./kustomize"] applicationSet: -@@ -174,11 +953,59 @@ +@@ -174,11 +957,59 @@ kind: ConsoleLink metadata: name: example-gitops-link diff --git a/tests/install-naked.expected.yaml b/tests/install-naked.expected.yaml index 7272f0eb..4bd38238 100644 --- a/tests/install-naked.expected.yaml +++ b/tests/install-naked.expected.yaml @@ -41,6 +41,8 @@ spec: value: install - name: global.hubClusterDomain value: + - name: global.clusterVersion + value: "" syncPolicy: automated: {} --- diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yaml index faaef3cc..0dfd0d84 100644 --- a/tests/install-normal.expected.yaml +++ b/tests/install-normal.expected.yaml @@ -41,6 +41,8 @@ spec: value: install - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.clusterVersion + value: "" syncPolicy: automated: {} --- diff --git a/tests/install.expected.diff b/tests/install.expected.diff index d96baecb..605728b4 100644 --- a/tests/install.expected.diff +++ b/tests/install.expected.diff @@ -32,10 +32,10 @@ - name: global.hubClusterDomain - value: + value: apps.hub.example.com + - name: global.clusterVersion + value: "" syncPolicy: - automated: {} - --- -@@ -61,4 +61,4 @@ +@@ -63,4 +63,4 @@ config: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES From d627cf1d5be1c9fcabafdb7ca1991015e5dc4b62 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 14 Oct 2022 21:16:54 +0200 Subject: [PATCH 0589/1263] Tweak the /values-ocpversion calculation in the acm policy This is needed otherwise the cluster-wide argo instance on the spoke will use the ocp version of the hub, because in the following snippet $.Values.global.clusterversion would come from the hub and not dynamically be resolved on the spoke. --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -45,9 +45,9 @@ spec: valueFiles: - "/values-global.yaml" - "/values-{{ .name }}.yaml" - {{- if $.Values.global.clusterVersion }} - - "/values-{{ $.Values.global.clusterVersion }}-{{ .name }}.yaml" - {{- end }} ... parameters: ... # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ `{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}` }}' This way we get the correct ocp versions on both the cluster-wide and the name-spaced argo instances of the spoke. --- acm/templates/policies/application-policies.yaml | 6 +++--- tests/acm-normal.expected.yaml | 6 ++++++ tests/acm.expected.diff | 8 +++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 0a398e20..a8c66904 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -45,9 +45,9 @@ spec: valueFiles: - "/values-global.yaml" - "/values-{{ .name }}.yaml" - {{- if $.Values.global.clusterVersion }} - - "/values-{{ $.Values.global.clusterVersion }}-{{ .name }}.yaml" - {{- end }} + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ `{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}` }}-{{ .name }}.yaml' {{- range $valueFile := .extraValueFiles }} - {{ $valueFile | quote }} {{- end }} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 25b2cb2d..1fa8133a 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -527,6 +527,9 @@ spec: valueFiles: - "/values-global.yaml" - "/values-acm-edge.yaml" + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}-acm-edge.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -611,6 +614,9 @@ spec: valueFiles: - "/values-global.yaml" - "/values-acm-provision-edge.yaml" + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}-acm-provision-edge.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL diff --git a/tests/acm.expected.diff b/tests/acm.expected.diff index 3944a3b3..fd6a4214 100644 --- a/tests/acm.expected.diff +++ b/tests/acm.expected.diff @@ -461,7 +461,7 @@ # Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule -@@ -44,6 +482,175 @@ +@@ -44,6 +482,181 @@ values: - OpenShift --- @@ -510,6 +510,9 @@ + valueFiles: + - "/values-global.yaml" + - "/values-acm-edge.yaml" ++ # We cannot use $.Values.global.clusterVersion because that gets resolved to the ++ # hub's cluster version, whereas we want to include the spoke cluster version ++ - '/values-{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}-acm-edge.yaml' + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL @@ -594,6 +597,9 @@ + valueFiles: + - "/values-global.yaml" + - "/values-acm-provision-edge.yaml" ++ # We cannot use $.Values.global.clusterVersion because that gets resolved to the ++ # hub's cluster version, whereas we want to include the spoke cluster version ++ - '/values-{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}-acm-provision-edge.yaml' + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL From ca024288258da34ad06d7723cb8008616130c8b9 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 17 Oct 2022 10:24:40 +0200 Subject: [PATCH 0590/1263] Be more precise with the Changes.md --- Changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changes.md b/Changes.md index 89f83c79..01e1d3b5 100644 --- a/Changes.md +++ b/Changes.md @@ -5,7 +5,7 @@ * Added global.clusterVersion as a new helm variable which represents the OCP Major.Minor cluster version. By default now a user can add a values--.yaml file to have specific cluster version - overrides (e.g. values-4.10-hub.yaml). Will need Validated Patterns >= 0.0.6 + overrides (e.g. values-4.10-hub.yaml). Will need Validated Patterns Operator >= 0.0.6 when deploying with the operator. ## October 4, 2022 From 3df8d73f62e9a2d3b3f6e72a2ffc5d9ae61b8569 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 18 Oct 2022 11:38:59 +0200 Subject: [PATCH 0591/1263] Use a simpler expression in ACM templates --- acm/templates/policies/application-policies.yaml | 4 ++-- tests/acm-normal.expected.yaml | 8 ++++---- tests/acm.expected.diff | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index a8c66904..5fc45d69 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -47,7 +47,7 @@ spec: - "/values-{{ .name }}.yaml" # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ `{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}` }}-{{ .name }}.yaml' + - '/values-{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}-{{ .name }}.yaml' {{- range $valueFile := .extraValueFiles }} - {{ $valueFile | quote }} {{- end }} @@ -69,7 +69,7 @@ spec: value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}` }}' # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion - value: '{{ `{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}` }}' + value: '{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}' - name: clusterGroup.name value: {{ $group.name }} {{- range .helmOverrides }} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 1fa8133a..7e72acc6 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -529,7 +529,7 @@ spec: - "/values-acm-edge.yaml" # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}-acm-edge.yaml' + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-edge.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -548,7 +548,7 @@ spec: value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion - value: '{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}' + value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' - name: clusterGroup.name value: acm-edge - name: clusterGroup.isHubCluster @@ -616,7 +616,7 @@ spec: - "/values-acm-provision-edge.yaml" # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}-acm-provision-edge.yaml' + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-provision-edge.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -635,7 +635,7 @@ spec: value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion - value: '{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}' + value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' - name: clusterGroup.name value: acm-provision-edge - name: clusterGroup.isHubCluster diff --git a/tests/acm.expected.diff b/tests/acm.expected.diff index fd6a4214..41760f3d 100644 --- a/tests/acm.expected.diff +++ b/tests/acm.expected.diff @@ -512,7 +512,7 @@ + - "/values-acm-edge.yaml" + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version -+ - '/values-{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}-acm-edge.yaml' ++ - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-edge.yaml' + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL @@ -531,7 +531,7 @@ + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' + # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) + - name: global.clusterVersion -+ value: '{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}' ++ value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + - name: clusterGroup.name + value: acm-edge + - name: clusterGroup.isHubCluster @@ -599,7 +599,7 @@ + - "/values-acm-provision-edge.yaml" + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version -+ - '/values-{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}-acm-provision-edge.yaml' ++ - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-provision-edge.yaml' + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL @@ -618,7 +618,7 @@ + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' + # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) + - name: global.clusterVersion -+ value: '{{ (cat (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major (semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) | replace " " "." }}' ++ value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + - name: clusterGroup.name + value: acm-provision-edge + - name: clusterGroup.isHubCluster From 61b3f74e5478826bad6fe297d136067cb3160d05 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 18 Oct 2022 12:27:47 +0200 Subject: [PATCH 0592/1263] Add a note about argo hub and spoke and clusterversion having to be the same in that case --- Changes.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Changes.md b/Changes.md index 01e1d3b5..8f105e52 100644 --- a/Changes.md +++ b/Changes.md @@ -6,7 +6,8 @@ Major.Minor cluster version. By default now a user can add a values--.yaml file to have specific cluster version overrides (e.g. values-4.10-hub.yaml). Will need Validated Patterns Operator >= 0.0.6 - when deploying with the operator. + when deploying with the operator. Note: When using the ArgoCD Hub and spoke model, + you cannot have spokes with a different version of OCP than the hub. ## October 4, 2022 From 4f3a2025e9f8c6a15a5cff9dc9ed2eddb0c8586f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 18 Oct 2022 19:36:54 +0200 Subject: [PATCH 0593/1263] Add possibility to retry commands in run_command() Lester reported one issue with the following backtrace: TASK [vault_utils : Loads secrets file into the vault of a cluster] *********************************************************************** An exception occurred during task execution. To see the full traceback, use -vvv. The error was: subprocess.CalledProcessError: Command 'oc exec -n vault vault-0 -i -- sh -c "vault kv put 'secret/hub/aws' s3Secret='...'"' returned non-zero exit status 1. fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n File \"/home/claudiol/.ansible/tmp/ansible-tmp-1666041131.290091-69234-174948554391046/AnsiballZ_vault_load_secrets.py\", line 107, in \n _ansiballz_main()\n File \"/home/claudiol/.ansible/tmp/ansible-tmp-1666041131.290091-69234-174948554391046/AnsiballZ_vault_load_secrets.py\", line 99, in _ansiballz_main\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n File \"/home/claudiol/.ansible/tmp/ansible-tmp-1666041131.290091-69234-174948554391046/AnsiballZ_vault_load_secrets.py\", line 47, in invoke_module\n runpy.run_module(mod_name='ansible.modules.vault_load_secrets', init_globals=dict(_module_fqn='ansible.modules.vault_load_secrets', _modlib_path=modlib_path),\n File \"/usr/lib64/python3.10/runpy.py\", line 224, in run_module\n return _run_module_code(code, init_globals, run_name, mod_spec)\n File \"/usr/lib64/python3.10/runpy.py\", line 96, in _run_module_code\n _run_code(code, mod_globals, init_globals,\n File \"/usr/lib64/python3.10/runpy.py\", line 86, in _run_code\n exec(code, run_globals)\n File \"/tmp/ansible_vault_load_secrets_payload_pwl_wc6j/ansible_vault_load_secrets_payload.zip/ansible/modules/vault_load_secrets.py\", line 462, in \n File \"/tmp/ansible_vault_load_secrets_payload_pwl_wc6j/ansible_vault_load_secrets_payload.zip/ansible/modules/vault_load_secrets.py\", line 458, in main\n File \"/tmp/ansible_vault_load_secrets_payload_pwl_wc6j/ansible_vault_load_secrets_payload.zip/ansible/modules/vault_load_secrets.py\", line 445, in run\n File \"/tmp/ansible_vault_load_secrets_payload_pwl_wc6j/ansible_vault_load_secrets_payload.zip/ansible/modules/vault_load_secrets.py\", line 369, in inject_secrets\n File \"/tmp/ansible_vault_load_secrets_payload_pwl_wc6j/ansible_vault_load_secrets_payload.zip/ansible/modules/vault_load_secrets.py\", line 165, in run_command\n File \"/usr/lib64/python3.10/subprocess.py\", line 524, in run\n raise CalledProcessError(retcode, process.args,\nsubprocess.CalledProcessError: Command 'oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/aws' s3Secret='...'\"' returned non-zero exit status 1.\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1} This most likely is caused by a vault hiccup (a retry in this case worked just fine). Let's add an attempts=1 and sleep=3 default values to run_command() and set the attempts to three when running oc commands to inject secrets in the vault. --- ansible/plugins/modules/vault_load_secrets.py | 36 ++++++++++------- ansible/tests/unit/test_vault_load_secrets.py | 39 ++++++++++++------- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 7f037e46..bea59e7b 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -53,6 +53,7 @@ import base64 import os import subprocess +import time from collections.abc import MutableMapping import yaml @@ -151,27 +152,36 @@ def get_version(syaml): return syaml.get("version", "1.0") -def run_command(command): +def run_command(command, attempts=1, sleep=3): """ Runs a command on the host ansible is running on. A failing command will raise an exception in this function directly (due to check=True) Parameters: command(str): The command to be run. + attempts(int): Number of times to retry in case of Error (defaults to 1) + sleep(int): Number of seconds to wait in between retry attempts (defaults to 3s) Returns: ret(subprocess.CompletedProcess): The return value from run() """ - ret = subprocess.run( - command, - shell=True, - env=os.environ.copy(), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - check=True, - ) - return ret + for attempt in range(attempts): + try: + ret = subprocess.run( + command, + shell=True, + env=os.environ.copy(), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + check=True, + ) + return ret + except subprocess.CalledProcessError as e: + # We reached maximum nr of retries. Re-raise the last error + if attempt >= attempts - 1: + raise e + time.sleep(sleep) def flatten(dictionary, parent_key=False, separator="."): @@ -366,7 +376,7 @@ def inject_secrets(module, syaml, namespace, pod, basepath): f"oc exec -n {namespace} {pod} -i -- sh -c " f"\"vault kv put '{path}/{secret}' {properties}\"" ) - run_command(cmd) + run_command(cmd, attempts=3) counter += 1 for i in get_secrets_vault_paths(module, syaml, "files"): @@ -380,7 +390,7 @@ def inject_secrets(module, syaml, namespace, pod, basepath): f"vault kv put {path}/{filekey} b64content=- content=@/tmp/vcontent; " f"rm /tmp/vcontent'" ) - run_command(cmd) + run_command(cmd, attempts=3) counter += 1 return counter diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index c71e60fa..108153c1 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -117,10 +117,12 @@ def test_ensure_empty_files_but_not_secrets_is_ok(self): calls = [ call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/config-demo' secret='VALUE'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/config-demo' secret='VALUE'\"", # noqa: E501 + attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/aws' access_key_id='VALUE' secret_access_key='VALUE'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/aws' access_key_id='VALUE' secret_access_key='VALUE'\"", # noqa: E501 + attempts=3, ), ] mock_run_command.assert_has_calls(calls) @@ -163,7 +165,8 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): calls = [ call( - "cat '/home/michele/.ssh/id_rsa.pub' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/publickey b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 + "cat '/home/michele/.ssh/id_rsa.pub' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/publickey b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, ), ] mock_run_command.assert_has_calls(calls) @@ -188,31 +191,40 @@ def test_ensure_command_called(self): calls = [ call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/config-demo' secret='demo123'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/config-demo' secret='demo123'\"", # noqa: E501 + attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/googleapi' key='lskdjflskjdflsdjflsdkjfldsjkfldsj'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/googleapi' key='lskdjflskjdflsdjflsdkjfldsjkfldsj'\"", # noqa: E501 + attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/cluster_alejandro' name='alejandro' bearerToken='sha256~bumxi-012345678901233455675678678098-abcdef'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/cluster_alejandro' name='alejandro' bearerToken='sha256~bumxi-012345678901233455675678678098-abcdef'\"", # noqa: E501 + attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test' s3.accessKey='1234' s3.secretKey='4321' s3Secret='czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ=='\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test' s3.accessKey='1234' s3.secretKey='4321' s3Secret='czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ=='\"", # noqa: E501 + attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test2' s3.accessKey='accessKey' s3.secretKey='secretKey' s3Secret='fooo'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test2' s3.accessKey='accessKey' s3.secretKey='secretKey' s3Secret='fooo'\"", # noqa: E501 + attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test3' s3.accessKey='aaaaa' s3.secretKey='bbbbbbbb' s3Secret='czMuYWNjZXNzS2V5OiBhYWFhYQpzMy5zZWNyZXRLZXk6IGJiYmJiYmJi'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test3' s3.accessKey='aaaaa' s3.secretKey='bbbbbbbb' s3Secret='czMuYWNjZXNzS2V5OiBhYWFhYQpzMy5zZWNyZXRLZXk6IGJiYmJiYmJi'\"", # noqa: E501 + attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/region-one/config-demo' secret='region123'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/region-one/config-demo' secret='region123'\"", # noqa: E501 + attempts=3, ), call( - "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/cluster_alejandro_ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 + "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/cluster_alejandro_ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, ), call( - "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/region-one/ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 + "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/region-one/ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, ), ] mock_run_command.assert_has_calls(calls) @@ -242,7 +254,8 @@ def test_ensure_good_template_checking(self): calls = [ call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/config-demo' secret='VALUE' additionalsecret='test'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/config-demo' secret='VALUE' additionalsecret='test'\"", # noqa: E501 + attempts=3, ), ] mock_run_command.assert_has_calls(calls) From 0d63fc621c12279ec7eaad39bf701cb80c9a9ced Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 18 Oct 2022 20:45:09 +0200 Subject: [PATCH 0594/1263] Symlink .github/linters/.gitleaks.toml to .gitleaks.toml This way splunk that runs on our repos won't barf on the example secrets in our unit tests. Use a symlink so we maintain a single copy of that file. --- .gitleaks.toml | 1 + 1 file changed, 1 insertion(+) create mode 120000 .gitleaks.toml diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 120000 index 00000000..c05303b9 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1 @@ +.github/linters/.gitleaks.toml \ No newline at end of file From 3b60775578df82da3575f58f54b9206d71999e97 Mon Sep 17 00:00:00 2001 From: day0hero Date: Tue, 18 Oct 2022 17:37:56 -0500 Subject: [PATCH 0595/1263] Updated ansible.cfg with non-deprecated feature. callback_whitelist is being deprecated. The new way to enable the callbacks is by setting callbacks_enabled= --- ansible/ansible.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index 463c4706..710a2469 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -3,4 +3,4 @@ display_skipped_hosts=False localhost_warning=False library=./plugins/modules:~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules roles_path=./roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles -callback_whitelist = profile_roles, profile_tasks +callbacks_enabled = profile_roles, profile_tasks From d0014acdbdc9db8365798e25b32e88921c052740 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 24 Oct 2022 10:04:04 +0200 Subject: [PATCH 0596/1263] Skip git ssh checks when running inside a container Getting SSH auth to work automatically inside a container seems to be a bit brittle as it is very dependent on OS and configuration. For now let's just skip the ssh tests of the 'validate-origin' Makefile target when running inside a container. Tested as follows: 1) Without container $ make validate-origin make -f common/Makefile validate-origin make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Checking repo https://github.com/mbaldessari/multicloud-gitops.git - branch test-git-ssh-disabling https://github.com/mbaldessari/multicloud-gitops.git - test-git-ssh-disabling exists make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' 2) Inside a container with the wrapper script: $ ./common/scripts/pattern-util.sh make validate-origin make -f common/Makefile validate-origin make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Running inside a container: Skipping git ssh checks make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' --- Makefile | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index ed871616..4b8b3383 100644 --- a/Makefile +++ b/Makefile @@ -54,11 +54,17 @@ validate-prereq: ## verify pre-requisites @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi @echo "OK" +# We only check the remote ssh git branch's existance if we're not running inside a container +# as getting ssh auth working inside a container seems a bit brittle validate-origin: ## verify the git origin is available @echo Checking repo $(TARGET_REPO) - branch $(TARGET_BRANCH) - @git ls-remote --exit-code --heads $(TARGET_REPO) $(TARGET_BRANCH) >/dev/null && \ - echo "$(TARGET_REPO) - $(TARGET_BRANCH) exists" || \ - (echo "$(TARGET_BRANCH) not found in $(TARGET_REPO)"; exit 1) + @if [ ! -f /run/.containerenv ]; then\ + git ls-remote --exit-code --heads $(TARGET_REPO) $(TARGET_BRANCH) >/dev/null &&\ + echo "$(TARGET_REPO) - $(TARGET_BRANCH) exists" ||\ + (echo "$(TARGET_BRANCH) not found in $(TARGET_REPO)"; exit 1);\ + else\ + echo "Running inside a container: Skipping git ssh checks";\ + fi # Default targets are "deploy" and "upgrade"; they can "move" to whichever install mechanism should be default. # legacy-deploy and legacy-upgrade should be present so that patterns don't need to depend on "deploy" and "upgrade" From ce8c560b1a520e073edf24a7abb7e3f6b6f06c63 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 25 Oct 2022 09:21:53 +0200 Subject: [PATCH 0597/1263] Lower case cluster names in values-example.yaml This will get rid of the following warning: ``` make helmlint make -f common/Makefile helmlint make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' ==> Linting common/acm [INFO] Chart.yaml: icon is recommended [WARNING] templates/provision/clusterpool.yaml: object name does not conform to Kubernetes naming requirements: "One-acm-provision-edge": metadata.name: Invalid value: "One-acm-provision-edge": a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*') [WARNING] templates/provision/clusterpool.yaml: object name does not conform to Kubernetes naming requirements: "Two-acm-provision-edge": metadata.name: Invalid value: "Two-acm-provision-edge": a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*') [WARNING] templates/provision/clusterpool.yaml: object name does not conform to Kubernetes naming requirements: "Three-acm-provision-edge": metadata.name: Invalid value: "Three-acm-provision-edge": a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*') ``` --- examples/values-example.yaml | 6 +++--- tests/acm-normal.expected.yaml | 12 ++++++------ tests/acm.expected.diff | 12 ++++++------ tests/clustergroup-normal.expected.yaml | 6 +++--- tests/clustergroup.expected.diff | 6 +++--- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 81bbcf10..54e0dd28 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -76,7 +76,7 @@ clusterGroup: aws: region: ap-southeast-2 clusters: - - One + - one exampleAzurePool: name: azure-us openshiftVersion: 4.10.18 @@ -86,8 +86,8 @@ clusterGroup: baseDomainResourceGroupName: dojo-dns-zones region: eastus clusters: - - Two - - Three + - two + - three acmlabels: - name: clusterGroup value: region diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 7e72acc6..bec7d031 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -23,12 +23,12 @@ type: Opaque apiVersion: hive.openshift.io/v1 kind: ClusterClaim metadata: - name: 'One-acm-provision-edge' + name: 'one-acm-provision-edge' annotations: argocd.argoproj.io/sync-wave: "20" cluster.open-cluster-management.io/createmanagedcluster: "true" labels: - clusterClaimName: One-acm-provision-edge + clusterClaimName: one-acm-provision-edge clusterGroup: region spec: clusterPoolName: aws-ap @@ -37,12 +37,12 @@ spec: apiVersion: hive.openshift.io/v1 kind: ClusterClaim metadata: - name: 'Two-acm-provision-edge' + name: 'two-acm-provision-edge' annotations: argocd.argoproj.io/sync-wave: "20" cluster.open-cluster-management.io/createmanagedcluster: "true" labels: - clusterClaimName: Two-acm-provision-edge + clusterClaimName: two-acm-provision-edge clusterGroup: region spec: clusterPoolName: azure-us @@ -51,12 +51,12 @@ spec: apiVersion: hive.openshift.io/v1 kind: ClusterClaim metadata: - name: 'Three-acm-provision-edge' + name: 'three-acm-provision-edge' annotations: argocd.argoproj.io/sync-wave: "20" cluster.open-cluster-management.io/createmanagedcluster: "true" labels: - clusterClaimName: Three-acm-provision-edge + clusterClaimName: three-acm-provision-edge clusterGroup: region spec: clusterPoolName: azure-us diff --git a/tests/acm.expected.diff b/tests/acm.expected.diff index 41760f3d..dd994b75 100644 --- a/tests/acm.expected.diff +++ b/tests/acm.expected.diff @@ -28,12 +28,12 @@ +apiVersion: hive.openshift.io/v1 +kind: ClusterClaim +metadata: -+ name: 'One-acm-provision-edge' ++ name: 'one-acm-provision-edge' + annotations: + argocd.argoproj.io/sync-wave: "20" + cluster.open-cluster-management.io/createmanagedcluster: "true" + labels: -+ clusterClaimName: One-acm-provision-edge ++ clusterClaimName: one-acm-provision-edge + clusterGroup: region +spec: + clusterPoolName: aws-ap @@ -42,12 +42,12 @@ +apiVersion: hive.openshift.io/v1 +kind: ClusterClaim +metadata: -+ name: 'Two-acm-provision-edge' ++ name: 'two-acm-provision-edge' + annotations: + argocd.argoproj.io/sync-wave: "20" + cluster.open-cluster-management.io/createmanagedcluster: "true" + labels: -+ clusterClaimName: Two-acm-provision-edge ++ clusterClaimName: two-acm-provision-edge + clusterGroup: region +spec: + clusterPoolName: azure-us @@ -56,12 +56,12 @@ +apiVersion: hive.openshift.io/v1 +kind: ClusterClaim +metadata: -+ name: 'Three-acm-provision-edge' ++ name: 'three-acm-provision-edge' + annotations: + argocd.argoproj.io/sync-wave: "20" + cluster.open-cluster-management.io/createmanagedcluster: "true" + labels: -+ clusterClaimName: Three-acm-provision-edge ++ clusterClaimName: three-acm-provision-edge + clusterGroup: region +spec: + clusterPoolName: azure-us diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index ae1f7fc6..e3b3371b 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -111,7 +111,7 @@ data: exampleAWSPool: baseDomain: blueprints.rhecoeng.com clusters: - - One + - one name: aws-ap openshiftVersion: 4.10.18 platform: @@ -121,8 +121,8 @@ data: exampleAzurePool: baseDomain: blueprints.rhecoeng.com clusters: - - Two - - Three + - two + - three name: azure-us openshiftVersion: 4.10.18 platform: diff --git a/tests/clustergroup.expected.diff b/tests/clustergroup.expected.diff index 02498ef3..9bcfe9b3 100644 --- a/tests/clustergroup.expected.diff +++ b/tests/clustergroup.expected.diff @@ -116,7 +116,7 @@ + exampleAWSPool: + baseDomain: blueprints.rhecoeng.com + clusters: -+ - One ++ - one + name: aws-ap + openshiftVersion: 4.10.18 + platform: @@ -126,8 +126,8 @@ + exampleAzurePool: + baseDomain: blueprints.rhecoeng.com + clusters: -+ - Two -+ - Three ++ - two ++ - three + name: azure-us + openshiftVersion: 4.10.18 + platform: From b1c394abe6fc526e4492a26fcb50ace239b7b038 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 25 Oct 2022 09:31:40 +0200 Subject: [PATCH 0598/1263] Switch to a more recent setup-helm action --- .github/workflows/linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 81eee65b..8a276b0c 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -34,7 +34,7 @@ jobs: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 - name: Setup helm - uses: azure/setup-helm@v1 + uses: azure/setup-helm@v3 # with: # version: '' # default is latest stable id: install From c702fcab605403fdb03b9ff15ef44eaca9520658 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 25 Oct 2022 11:53:07 +0200 Subject: [PATCH 0599/1263] Update ESO to 0.6.0 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.5.9.tgz | Bin 40987 -> 0 bytes .../charts/external-secrets-0.6.0.tgz | Bin 0 -> 43570 bytes ...olang-external-secrets-naked.expected.yaml | 537 ++++++++++++++++-- ...lang-external-secrets-normal.expected.yaml | 537 ++++++++++++++++-- tests/golang-external-secrets.expected.diff | 2 +- 6 files changed, 962 insertions(+), 116 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.5.9.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.6.0.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 8fbec047..ebc6a138 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.5.9" + version: "0.6.0" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.5.9.tgz b/golang-external-secrets/charts/external-secrets-0.5.9.tgz deleted file mode 100644 index 3d5b93f969793a05cbfc12ed95d1547568084e9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40987 zcmZ^qQ*fq1yRKu~nb_9Ewr$&-BwuV>6K7)Enb@{%+xEBTU$tx1uG(w$!F$#xU2pgE z^mSiN6b*v`@}B{u0iiRNQeifglIM`~pY-(BP2Qw6igGF!NMX z{>3k4Zf6g2`E}up*OE#dcQfM!_n840fBqDgX~;HRF$R_CCF{U}W=VZ?S!Z6tMD*hpb! z(hyjA%H+#G@^1+8`Br@U`}pX60`HIozhBdmO5^=~obL}mMidpkoqg^f&*Bwx?p$o zE&ja-$YdC?kR2&Wx`;8hgwPT@fGe^<_Q#$P0FLpv zw!GCWf{YXii@xDhZspBP(d` zbXN$`l?56h-EoJ49LGMEI+Qgh~zIfgQtLaV^4cq9`)efa*128CW28al85 z6skMA?fnS_0zudqDsk|NUZz!uzA$jbW*UutYmu$)=E?Q}ai`Cf%8`LuwD|Xv?9+5m z4YOy4*s<*=#^;JZoz1;PEY1Ll%IO~}WwrM^Vq|3}nm&R!QWP?dQaeae2+={1C?(=E zlynlOaFn?+D$spr3e-JV(Ez2o0t5md=s`3zAy}DAP#z}#h~Ws~K&}8VA>vy&d8Ege z>7x^cK70xMqh&q11VA`Nb$NsGpAd9q}+zzS`}T82kCR^#%)lE#mqm%BS1wz zks5NRNk=`cCsG8Falw+s@OqVmqyxw>z~l~Fkh;NH3T^QVlu&&M2S5mJ1BR)6?15K4 zd+Pm==_H=?B8ZOpS4BC>?CM$Uma8E_s$WJ%%^fYG}EI*F4>xTkHL zjovv3>ZW%z7cOs?HtSW1I^vWAPPDEoDbQpGLCA-!&4og!<4z?aWmwHL7_;zC1-H{R z*bgSw63o~|9L1jf-@OV5K2S`60OCtVTHF!1C%pL@G!ijl-q8FM^jiC%F*8 zyA)aZqoye2x);OO#ED0vrX5o5*H;=L}9mNK89bz&n)IO@nw=x{{g^M?xmS=5zqj9HAbvJ*YT8s9C2M4B}aFv;m%weqf#(}?4H9=r-W3P5h znSI|>zNSzRb`q=r;^nW}iNT22R!3Q)sR=ax@{x8*pKlD3ml#>pO7p&{V12Vd@`z}R z0AdBRgOs-uxa#D=*TXwHA7YwQnd^OW^jZ-NR*bUP3h8SE&@c@b`?jsS!;!JOd_J#d zvF-w~@qC#GAtif9vBP0Rh6m8*@=v*1A~50-xvZcbLf{iuaeRsXKz<6e@B$1>%|ozm zGHpFmf`|n1;qPuaCrAw`X8hZGKGh#T!nCg0Z~~?5S)^$fb~g1bc>>@`QFI5SM25kk zOb>k^Gt)|U$5<3UHg_=#A`|$Q*dRg4kwYYkjBu*|CN%ciieZRKOD574belkNL&o=< zKto$(EsR6*Y64PnH)h2c;K|jD0maItr!>XCvFHeV%mImu#F?6y-;*N-8MxX7Oq$$y zrA@cig3u2%J)=n7Hkyap&>>-`I?~gLeG`TdvE*s9^5nij58RYmcK9X$X;BnbISLAp zKnzjLnFN!_Z{^66T2+ojH#inc>8j#Kay??uVVhd%4Ezu&A^b7V!o~i?41uCJ?D+s9 z={As|Afhd0H#}vW`@;0FPinkOBI&4S`mYzobJbFHOF-zD3!)Rbf2v8C*b$tcmu%k0 zMIXT*2AVGWWFEOp9^(p~eP(b-i3Alqb;4N+IER!$YHb)3nLE7!m~dzO!NCkW$KN@z zb1#+3QwgZWEsl{!>4dDVt*Gb?zsbRE^yoRD^ES1rb5&Xa6|GSFxE7_mQdRq8Z%huL zyA+3IOhV!^2&uFo;PP+BD3Y+E8nQs#eHbMu#^hUXkc#azY{;2~r3h;)L5ldRMCZF7 zux+k~bs@AglW*)BKg$0adwUP$Fd$`2^$F>!HUFhfTc`ppQ)?w~WNXtdzzB^J6;Fqk zmOcwC7N8o5?-5M(F7(H(chMN{2iENSpP_G*fvC&$YS;*+kj`pjuEzh|EdvKhUV{Xo z2m=+$F)varH^&26WJW4I=&DC*>!Hh#g#0KoAzq>vQ<%f#@=k)d0GWVg`SPo#_D>d{ z>4MKi+*eHj(P0Ymg3ZS?u@Vfj`b!NLKo+A>dnf=gZq0Af|g z+l27%mynmGzVrAwa7)%nJILeXq;zee_1LW492l4%#$hFon4*sS#6l`0QT z=8-D5S2-sxK{EOG@w+lPUm-0;3OY`svciJdA4$07{Bz=y+=`TqC*40oopBKQeoFY+ zBLh}s{`z_Pl$rT(5&25)_9YPwycxS&ND2b(AX5qABY*q9+$Cq#rzQmBiVR+gV1VU7 zfbQ!>g=7ME`uZS2lSBXb&&-0MlW`pDE5GLNOUN`3!_fR76`v#5*c5m&4s3UqH5*^L)O zLh=LZ|1@RxEbSWmY5dD1ofZdpHxh{1OCI8$fGUSo3Joj?`SgUq-ZgF8k))}y49W~@ zrS7mUWfhvG0>z0!S^Uvg{0?7rD-!jtqn@C}Cg-8v`S$(g9v}tt{dxfSHM_TVu^S-Np2F-5{RVpIMFC&$UPl7IcP1V_ZX|)uj^8w?MPeA2@WaOMhH?L+3F}ILo ziV?b=!r4zppHRPGghV*a0Y@=oU^vknIFs=BzlXE~mnjnqd^>-B{C` z@dSa%iX01hDm1ft^j;El_{t66|TDV!XF zsG4O({NsnSshW?q0GpFrQpOkC(`>?x5x(L*RyUgzE&6m8BM<_Zq!b9ZN*!Ndy3v1U zR|ws}JF!|ZzFIZ1zS`<}N#K1J-YrcJ6%py=w$Wb&Vtt@^tStimqktqBn~ zgkvMsYpjue(zCNu@}p!7c9e3yfZYI}bi3^8+isqEPZ!<8>zJZ24HwU-e^(f{t!;bp zs=YAF_Gyf46QY*NLB3!~gjGqBbPYyxACD_D4kyYGlrR8ii$?d|n^^-IcvLwU1kbG3 z#;y-v#L46aUc(k2nVyBNcjEOm<1qU89CG`(hq&X^#=Oi_cO%WjDF%O))`5?#{S_Oz z1hIe4rBci}z7mWl0={}g0DIC(Gu+Gv$EVQ24$_IL6_FfRU=_FEhcVHZL%?!M?qXx- zBL|`@$uSZj8Pdejo*L;G!O51-qq*1_e$@-e488e-1xZIlfG9rYITIftDF^3|#=V+>t>+xxUss-;t z{S2eY;$!^Pu*o-%=m;nA)Fl2l-OR7_-^N$i(1C9S0n+P40VIAaqlU?j@c*@colIdD z#2v-j6ra*!kUX-9>GQy^bF|BH99f3G_$4pKaZ;NJY{H+h+o|7#^}pzUtg_Dc;~F~q z)|dj)?j{(XA;-9|{h~h9FioIGc{OaY0>FHyjhBNd05!rl7Heq*0A~ezi)>?&l_3*EPQ&>tD{Y_&DV9`*PI|esp4q{0qL+AE%u&A)thm3VX_}5rN z>aXoAAvVE9AG8nrsMlWh2pk;!CNR$VI#5XLc{G&97YUsAAAUBcsM>=Az*39CPeKAT zOOcN0`;zOi)~I1UauZgB!=-Rk<-{oP+kSNjZP`DSvsb*u3BYv75891y&v6sRyK`j= zo;iNi?kCK{=;ycLFPL;0H$#IYODhur4#qAuFg#i8yg@pfl={pI8&RJMK7dh36N($k5t0S zp6qx>Yt%cn)&6yrNs&z$q&in3`)q@cQijtpaR^m~QItGX12j0Wsd*Jpcd4A}+~0{k zmUL?c(XwzKfRysb%sF!af@n`GpQFP?vu-%cDwaIvsWnkmL~XxR##xsSuU04eIH1}QB)9~=++W%N%by*Xbb_Kf*J+8jwA`=} z&l>T*d>PH*M*(O(ZDflNeQ*k^djNjty+Bl-YDM?=Un-%Gl0YIs{-%zE79D6wBKUI&&7M`YvVP{Zr&JJMkYO0e&nyIRlE=)t}4V4CvD<{vtj9*PrQK-aQ%cNt!i_}*{=I2AHQO^eb zq_RK5!RS8NoK!DR>xyT_W;);K9Mo%xE)0?O(&49&XGu$j4Ntyc#(h9a5=Rr(U=5%> zl^_^90a@LYq%j;_y`WGXe2Uj=F?NFd zhm~o0-Y;FM>YB>Fs7H3G7gUXJ!Ih+8qg%eF&*89eLe}}=J12c~fy{!#{*4q|SR-Vk zw*W7kl~cEj>BVQpxPXJD3@js02&nk@j6_RBmA$}(NUlOfuef~(+TEF-Yn|G!S}mU? zMGpL3^V7G?>Ke`gMATU?0mTwRHR4B6o1Ef;*)u#nJqzNgO$rJBxuM-bq6Vj z!QQ2}C;8M9^?ZF(F57WdGg&d?pPJ&;EYJW6O&bh4t3OjA#e5Sl8fPOMkXnQ;frD@dz#p zXCnCWw*^nv7U?}v*Kmx}KUfNim}km1svv*0t_swg^^b@Y;PL)MEIiJ(>0e}2o`@uu zI_d|Wjnj-$NSEuANILt$VHUFItNB)&0Mtb85#Z8w_XE&WsE?TZ-Be6|H26ky@US=d z_?E-3u|DnnxiJaUVBbyc9X~LoBJL9%dZaK)0s^8rfX`(=&i8AcB%A$kbd-(1CeCwH ztqseLFr^lr)QMHcl0o1sHD#2#3V@MlON$`}jse!hOw zLvyLw^49Zeq3YExT{D{kU^=Fk97ycbljaj?XtI#3+B9Wwn~0y3DlUv54K++7A{geS<;kr{Xzx40Oi9vcHjN%*yNK$Y(Sk1Y2bkr zEm+VP;r->t{_0-72DiR+7)hOo-EY*vrg}!(`fRm>k;o-Te!)}@c}ML=et}i?J{*;#6sy? zCUX5nNL@*7*(X3?2%%g`P~bKskSgKFgf4i1pefpYRIG)a-4qtwthFtu>nGN!>g7TM zra~07T{!_umzBc3$I_ zrmUsIDff56iNm#oeBLneiN12arION&2#h+e06-v)7O`=yh{l?0n01CiRhujm4g*DoK#Vq>lb=>P6H`Dyl zuIl7ip1iA7Msi%^SIr%Mm3?|pym;5$$fzWhEYZWPx}REQl88Q@whg#PXfMK4dCjbK z%>?F^DNGm$BQYhex+`6DiPtVK&Yn_yG77!R4eDN@D6`Bf3||pK%=ZrPs8BN8s7Xbz z5?FQUpGtNM=Rj_}C#AfwgHjlWIYHCP1VSM!oWZBPY=63pnB|^+*U+qg(q6==;4X_# z4``L%W7<*Y*v@`a8lW8s)QLzz4xD?hnCNfs(MV13O%{EXJeeASqV7DE%tXn~9Jf6o zY`3B909{b%bvch5-dYv{{ZsBcKyeaFd3kN`@V3t3AMD>KhM-*@mS|OYnUN|=4E1LV z`SPFdBzwtf-M-|n+6Gma#iZy`*Hfam0!1kCWuJL5S!^pG-$-> z;;<@zC0SqOL^I)gIU*0bE0mLN5`CLld0p!LUdIlqa_m5netGDlo#aka zl6|=}{#`BoO09bo(03-^Myvr<|(%$(YVb_k%so5WPRgUxwtzusk(DIKy5A8lDEal;yh9 zsEH;FusiM@#nDgzb~)2%OV4DhTb~ry8pW*n?8IKk78)S_E)%YHCfjg{ZT_0m?HAV1 z1KJ}iHPWYTzqdbB%2h7<9Pw52S&F;@S~7%`W>IBRS@1+j|irY}(-dj(w?P4>2oG;OQT zZ=c9}V0%^CNmwO$6lfz3-(|g)=6(}SmscHnjy{EX+Md)j06i#wkJ8|4zW?DDjI=@^f~ryVNI z{Bs>R@^gwK!xDo+uTSfGeutt@74Hc7YKk9J6*HbFf0Oqh>H>*OvKN1pU26d5?EEfZYT#2&&6K}MVGzV-;4*3c_^)5eQ0oJ+)* zQmZ9rI9DC|gDf1DTBJXnIXUg`F=Vf>28+QQ zj5wQ1PJ#{GTDM~3qlq7>S85~t10Ng!r&E^{3}SxqNZuh=vh0nF7a!xOrYI@dg^M9gy{kfDGW))*Z0a{iDzj_(wVR!eqmJOw z4?XqID($g<*f`8+(wi&x}rUe|j0B{E)#*jF0<*TgN1xN)MN%}12fs>YW@NJjL*isNzby9Qi@BIq*qCfrV?3Fo z(tL|2CwO6abs!`I7wPBmwKxP58mJtz=eeWpmXF~Mq$g(aS;W9Fz($1;G8*7~509gc z3h&-==dTdYp2J+c`Aatd(*W;8{!%AN@btZ?YMZ@ak+LohtDlrWKPSJdADl!{6qGHc zAW4v-dgBo1K+Au*^Uq|-aRHsv5N@Rga8xbN*a{(js+=9`3O%=w{j5edK6ohqm1(fG!(jzi+=E2Q8XRUB1bxN;y5y z3ls3Ssp5aI%%R?VIHWMULqwL!&PpaP;1DQH%8|`w+k5yXBemLJHH0CqyQ1(lR4Rn@ z%_~_=PS(N=*(!05UM@Mf@PvK{48J{~-Olc=XjHQ|jnx#hXxxZ?s z1v}r)h=>?-0y#Bz-`0-8&o(TvPikkTk9y?OuupnM{kuFq7Q!1jYyG=DzW1IUm)?qK zWC|u+j=+OKoc!*p0A_dc=SO_(qD8pn$8Rb9P2994yt8?kCt9%Ay2&M8!eVu?U4-M43oT;L!~+ zW?_8mX9%|3YV&XB%r1_}DiFQmA&)ep@swtWm}MDMx!mt!*%MNGRozo!JWu2&8+u;% z;s?dISUo7q?mQEpE;PpDgMrsu`9hhnphV9np+?J&8red~!j^&$;ZRGk$3aEiF{mKi z6e$VZK_18v*{Vz837#dL<(FAVuitova1xQ){`hh-CJiCjArAg+rQn zTx6$?qU&qm&D(FspAv<`j3I-Gs=@o{3W{V`-N~C^QSp%|VJ*-Z6Kst<=)&@uck>fG z!Fdzg3UD_$;itk)BW+Z=CKPNU;5h9oXS|zbFvUBz{_nP+XYmMlZ-g@ehs2>4+EK-! z4$uTJ!R->c@YW=wwY$(Iq}Qrc%+OMB`p!0*jDo4o`@pKu{5C9)Lwb@%?`@LW%*pe# zW{fPGKq$Y+I&HJBF5C3nPV4TuF3IccX&$^tkwF@|)|d6ndqiKU2T9^Kr@N97P2i(X zp!wzTvaeh^dMF|fI)jP3FC`@hO1OP~skaV3sEqb_bP-00K-@4KgN8ZMi#p!PCGz7I zw(|hJHK<3qfOhiX){U>!t}{>a0{v=;WRBT@oJ&j+>mZ$en6ERNeu(y60dI}to)bEt zL|4=h33#Dz8x}Pm>OisnNXV?PnaEiFSQNBf@QlBuKZyDeTK?V3F|Rfj;+c&%PglFb zkr3=x93qnrZM`>fwA3vy}tE0PZ=w8<(6}OKvi;Fptr463Qd=hjEX705A zr=pVYn#a2%@Ay?b6Q7+ZhVz=W%%OTFulwm1Jn{8m>muDr)6cW!=5g#y&yFu@Y(M2CHb)T6UT%a~|)!xpB zl6}c{SHkbt>G~X}+5`Y$IMdIL&E62+TpNGoKjx?ojqRdeq$V6NilZIetVq2^lHRjXvNjQvcG-9qXV*1KpCSmy40t9#hB@OoVPjjJT2dqr z^@7WDW13jMu)2KsGrZ+*X?X(oBUW1_Y;3Xk92H|;Z=l(cB+sv?@t)3L?c!>QG|3|Nh>McPD*r;E=Gtvmp z>fUtT;+Yx7T0_@{NP$upR1$PN7hGYMQoED|?;i}z zMy>eUCWMBWr(G0(p7+$;HI?4p$wi=indF}bwvWJBzinr(dZt#>Vq`GhDd^501N-DX z9MQkMq-IXs(XY`e)rk4N`3*h-_!)DzyxFQp1Tf0A41`|leB}v1bB3)eKASpp*f*Bo z=}EaB7MRo)9N@yP?~#5YSfcLdN@h@V=~|(T&=u~ewdxANzRGgRnokPY)okzkxhp>4 zc|~_}c@lDJ??7D{OI5IzA#W%e@9r5)2}-IEOXXlxwF65>p`URFzRZtYewE>+{q7e~Zdl-G@RKaXQe-OktYOmpRCpd3wj^Zu)| zC&i>2R>@h{G1+>5GR{F?92X4O6W0%jKw*^D9zJ{t@EhlP2YUI$wLc94mH(P)7X|vBc!Ce=Y%&^|-2W%U`|C>-X&QA+;IAky~Z!kjs6bjSGKB z2G`v&z~K=W%79l}W#0+WeLAwuM*aG&;WGN;aC^x$F+1707m@q!w(`bqWL2Pgb(fx* z3*M)G zD-eGfsnx8rGKk$ZHTU^L4=C>Uy)&HO0b$e-ma68I$u8Rr0|sBG9^tCY8%-Qf+by7- zcGTy(yyehsgM*l^AMwj0Nu^0iWMe~d-IaDm=N*dEC0HQ>bZOU#up$oR1WjSEBgQiP z^LA>q3HnN7fk>bfbHfl9CiM_x@`aKWzd5H`XT*?7c-owDL6nlIULcE|TgT>{V9(4Q zaMi3YxA!nN)OwTiXwy$Og{hXDtvXwKpuh~%o$*uU0j?83)W&Td0>`tu>v7%GwlMm4w)U|}@ zI&UiEb+ZQune@-IC5!LK1GK$oGU|M?^itN#Q8(vh_AT6X9ZDasDgSn?LAN)qf%1eF z>|ou_#2+E9@qT#P%gd79_0o6Aqu9C!Ks8a$J`s|d>j2Vg@gBdH#q9@mAcxye`~1Uw zB6~<7U-kbiHRclkz!}kGNP!-oy%10*wz*}qNtwqUA-0FtpwI$sIF=NKd4mExaitQw{=(6=#zaZ5+Kb5klWo z)c6@CZ)_hU1_7dw10Z-xOFF8&0a!>Yp)B1yra-99+T@T-$x0&2Z*ZopKQ?7QW#&5F zQo6FQq*3I(`Q=!-l(pC=M1e3OMvSR19J^9597mbaze7u@?_VoW6C_P}JSVr{9v|js;+ysZ zR^Qaq`dG4X$yQ7$T&SI` z-r$pdJ}EE}A-QOW1|fN{(z8byq-Wa|eel~!lP266Z4btrm}#aMMruNXZ`dC5-k+KnO?n>ylqbvFm7iJZUfH>V&zz5pkuo#$$p>W zaF6;g&v?#{8z3L7K={;TvrFO|sE=?_E)o+TT3AS4ZsX(Aof6|`^iC1#y zj4z%{CVH?gBu@BH)Xrrq+AVCL@J}w%$@yN4+4uJ0mmzOev@AGH!Vv`KDY96c)9V)o zBsz|;PlbVl(FssPVsKxlwRBRA7==ocDpjuJ`c?rsDfh!mv#}u11J`Xv)-5Z|M)GQ` z9Av~6aRFxBdv(sIvt3}-_e1iUq+mQzB*n1T{M)E@?qDq_@-l!AouMQKqw>Yt#&VbbNQH@(09)T6cj5yH5Q?5v`Z#V#Aw3KgN{ic9nT7CT?^J-Q~;%6qDRTD9YTd`$0$q_#Q1Sd1OY*o>zzsHAi2rYcA$(2_PvbkHl z4Z@h8=S9%s(g)9ef9}OKC}FO+#ZWNi$}_$AX|Am?QlmChCMvVDB0q`vwfhh48)O^K z|80Izgu|qrtQrz@0$n}8h1jimcujJgQ~;}^xXCUb*IiKF;WK*_{?d>phfChu zW+Z;z#doM*#{SSi>u^Uc{@j~%wYNA!aC%e7P+6qp0Kh(c*foxXb6rOHQzJ3+80`_40}B$HDsS1`k;D141@ z{uAVll8*}%T~dbT<;>zU(^jqn5L@r%bk6>+`;F1cmU74sSyG(XWym=T)P~x;7X53| z@65`kHTY=-C*_p~uj&GtU|G+aq69Qv~#R#9Kh8otsuvyo}Q%O*obDGi-r=8UmIo7&c$O-8Loi4d&Sg>)iT)+9pfN`@gM zzsN{OYCB1-O``fYgvv=SBY}$67^d@ImNY_nF*%4+4x-%B@+62SV0E8*-YlZ08* zJ$qbdxt*Lk^oAPmADTUO<=UtU!d&JD6js`~SM&_^NGwIWmxm%}&?YQbl_ENlWm_A4 zX>qW>F#HFF3)w5lH0ZGwl9kuRJX(7E%so()p;u~~JX&*9&9LOQv--g<)c^Re*s+l2 zr*0H+nY(Zwx#nz}Od!j<4Oj5J_soWLrJZyuk5U(TICg-FwXT<>FGx9Up^5KVK^B)( z93*0D{+5DeK|cKa z_u9;IG`n1KMD7teX49b=KuHK**7#9!UrJ}PUTsHJPRS*lyGt84OP*J`&?6uqKzP+1 zI;nh1i&W=#$$RF09FQShO4WRi!&7Au2GXyx2VM9yCKX9_F;XbalT#^E!HEd+SC-DS z&Y|M8{0E!7CveHQn#!GF!4^~Ode%<*tXi|A{H5J~q`w|s3}qlNDrN3*z^CyzNkC^V zCM=d#8R;_DYSpho*LC#k^aTntIIQR3d^0#~r%o+6?uIWoPVn?KIPMo=^>27{w3UPij>n78wZ?NtKk%;jPDfH{R8@3 zMao_pX?C?RvHpmXwJ$r*+E7-N49@$SSPUxablD-yB(;Ep#NOK~pEPwl)6rO|yd?O_ zWfh4GszEC@y*v7;T9XJ|XjtVz>&RZPT2yP^G{XARZ_aE#Y_DRv`bUje=1sa91 zJk&axmf+G60^)3~*>j2p#~Frd@oDXnRAjN&uy!i~KZ8eJH?_99(v(D5xTCGmDpY8f z9gqnN#!SEndvO%O-rNd)vyw<<3HZJr_7&>8>%52^ll(C?X&`QEwh00oe74u3C=s@BHN*Ar6WMrIbFx5GJ--m3DtGgP>xTnT}c&Cdvq6 z;i(hRcxdSjS5y|-G^zn6JlQlBST-X`e6Y&CQB(!izrkdh*1DYoe@rG{RgnehZ8Dcd z?WmBtp1mg-xd)nUnwFQWT-;tsdQz~bi4UxFE?SIcZi@4Tl^L8Wh_V@G{6(6T2ponQ zUSIjEDQPfRK8~ZhP`_{4HKrJ7bOsr`_;u7D#2w@DZu+wke^j_xSm5<59-JTZ#Y;v7 z%}t`v?8xE6$&C(-l8!tNLy{6yboSbiad*6HN41kkE;l#Lg7n4dsrN^nwA=Of#SYQ; zh}iS{#*Tz0OjzbzAu#9kO7nfu)v1lPr59_5?h5>i|Ec@GMG*su{}M%*WlhEYi6A#Q zde{cZ=jPO?y(H!VBnO&h1PUqM)bHddWX$uL7wP9kP0j!=#+YzQ+eW7r=lKPZBdr2G zO+i}QdCWg}<*aEwWpVv|I6almloPyzW4M;zdNgkw+eV3gbaqAic0B6*R6NiL>0~S{ z0<8GA6!I=;@$4olCJ3iN)TZHt3@K@g>~TtRs(Ui}8U(b+3N&b}MOo-*`!RWc97mk) z*jxqlj9A*f=yHv`FlLu%Nwn5L-R zeUj8)^}TeW6@33bUScJoAx<2;LoTD<&bn~A188LIvrhgwB(X_}as^wh)@FDNqRnC$ zlmOTI@L1KVWz!g8MNgjd1_Zv0>bl(;&3A|_VN!AZ^3lQkiWBOkmj4(exQA9ZWvMtv zrjY{vX1e2=Vjf*W=!LzyS&_;KyoeD&R2Tax=)n9BCjx6S6Pp`(j={zu1O<)f&9xoik9+>{Nb)w9wBtjV@AX zSqLDk+Z1fRDVP1Y(tFXUC^O)wS*9)LNq}OZwla@Ze?rm?B? z1EEAqFmjPZOOk76&s!n?@R-ZYL4OYqR5yoAGabBxA`JE);6{kAcp?lIe3}jJqy0SH z`w&oy2{iH8v&zHSAsxE4JI5>CKe$+1$+i)maj-eB@QiHPR zS`zPOb~b=AHzup(zjgiqW?~|DC&8UcTX2vJd`otRo|khqoTru}!&gBn37%URx()80 zKoA;t`gh0;vHl{;pD!X0%(ehWjC$+keqjj>1bDIEklU6TO!8;@HHt#PF)Ib|%Ngf^ z7)*K3%otKoxSVMavlteuApDN`pj&+LfKSNZ9BXGMp z=+e=;=e!i()OOHN-_$bPdHs6iUlj z(rv?=Eu=XE6uw-aIEj+H)OTC2r}aCY<;?D~2it1x6^I>7jlxIIVB4m`MYKHQzvJbw zIT_~ChAE{@7s6Y&B)MV}lv`QU>Fmhh z5RN_GP0vBbTkuA(imgJ^2cJmc9%l_h=o|tPvd2}jhm)Ba|EFg469e?u;ly$1|HHa# zV{I(A=;@9*#)j~#5~ZjOltmgBu_w|6An^W_`5-9lvQv!aPx6;X`?-gxN0}}!(3l)K zk3RYVV8xYl|7FdI2Wcg%de;9z-R_Kva~tCOInio9_LqBp@O&Qs6XcdkpYfOr_PYob z8Uk#)NmW(-|A3pu&{x zOuT>mihxo4gyjpN{6#&o_;{K`GrygoV<}wo1DnD^vftKJ%xsN;5o%vXu3!EiH&ter zH1`B!u4oh>-?&)cnn*C00q$2x{}z}`uVKG7t?9Ed;0f!c!~4(9tnT5^*hA~sJBJmr zs7ki5@2VdhIP#0lH+SOstJv3d_S>5GPb4D>vPJc0M7qtj>z(4%{f)}BA@~?D)dYFU z-HgV-Ec#8r(A2brL&aXu+zpdP!yPx}8BwnN?Sxa~BV)2&ip~W0{o?0D3R%r) zk%phpTo|OGm0?ezPLO%4|0ewi?~47!_$;Ce3@p~w+BM}%-+_K>Ba{sO{G>|F?fQdj zs)|j7W~!FrOtE}lbQB`9tR`TrQqhp%geW4fq~t|l>pOP&_R(-Ov0)a5BV3TvaOC*- z52s77hqT3c1^ZD1St7X*0TwK=cu_B~cc4PY)^>FA%*vL zp~odf_z;rEM$Zigk9=uE5pZ8_3zgz(=!04Z(QyGyG*3U(0f+pQw zC)4X@1H^zs&7mfHF<9@}T9?G%?zs4sVXx7qm zU4P!!${(#Q`~JeT@?*wYq#vxZ03oZq`W0^2&>|5@)pNUb7=+r{dRynftOd%4m*kyN zvYVnPjj^cxvW?agnL3ig;n)BZr}hz#f;rooa8BvW_ryBNKB{0Gx1xNutr1R5wpzLl zA|d#9n3oIn%L?_D5m_#7(@AVyyzBoV?VZ9a`Sy10I33$F9ou%twr$()*tTukw(X8> zo1N_Z*V3=JHj#i)c;h}pb8bO6=muBe_#$tZl zpod2d_p;wz!|BGQ^(OCcF#3vX+>|q_e*(v!QtVY?$s0DE(0FX>#J2uRV=3zmi$o~? z!Lo_Q1JSN|V(QBGAg6#wGni*-0EXEik+nPHcsh+qAz(dPWnI=n#dNk4a?10a0ye!x z_5M?Ewmejf7kQ`)ql?k;mFqAn1={_P-DErlW-`}ETbgMQT}vrsn;3{R`EdBjIf7=cwCO_oXkyk zLEe%|(Me#~7L4_Uc>>d#1lAZba4cQqruj_UiDib{%{xkP{nIGsmWQvFOa+euzIt5Y zp9$sZ4?-~jT4%(pP7N#?Xy~`X0o)=BY#TiW!VuPU6|KQ*>Q!Z1#FTlc{+<44qqL;( z+0Gy4_E?t!f+ft94dD;O5o(<`j6P&l*^=B`8q`w*dH1c@_m094+EeNx*}3~jV(7qw z-<<|kKJkGDRlc#<>M%IA^Nd->YC4@ruXHGcs4D5qBxMmROU44}X@?sx*_*ODYKF@EEjwLyXsY#5RVS1iS{kjz3;3OhVSRvZ6RIkWV9mRdUuoY2 zz_{2RcD`9J$e)BbwFbTcis3OiP{mg@!npGaP*ViLb)gs@rte8hv@gpb+8(cxds=nH7lCa0B_mKmVwisx)@ zo|11=;|gHg^ebmsNPCdV9kL$%0-(0tH~*!rGY)2w}S zq-L|RLGE<(x2w6?neNphSgREPEvLEUzYqo-pvymCRfGd>lqcVrRJDcOiEi5&9y4`; zCp;&P+B9geo~3M)rzn79g7FWzWi*UuoTHILibYLOo%PYOv$tOE?JryiY~IDh8jYx>W>vBwp^zM*PdXpQ{bZ zJZy3aZEQq@XOSHBnRaR%PA58kDzAIRR7k+ML$UK<&pKal{~N%lA_bf%V8A;qm9(pt zo@)$LF^Of_rh0eXrr(&qAbWBwYg|f-Z+06?BVVoG+DMW}=}7CmI$aw@ zqErOexl?ggYwKam8C%ui{N3>5m)1LM_{TSaM0Kw@%GqBVd7rpcM!8T_<>?!dkRv!X z$4=(({__YWnXyKlU4sI9BG!Pa+IcZv$<%o{-cTWbI@Zoa-;D_Wi^lCIhBHQ2xN35P zV|L{%nA9U36SeZRKa8BsN~!=lpiE8^Wlow1pvUWV)1B63|4N>hZyp^#pc)JtFFY-M zAYkDej-$s`ugRJhHoG@S@KOrDCW@n>&suvX4OJh=eCb`v)QtpYLaCa?@f+Dmq~J$g zCWBrq9>lBu_dakJpz3%{HADj|=d4sgMwM8|sLaZnqKB)}OdqHy2^CLxWEFdvSmz<7 zeMd&(Ar26Q2UDS&@REoJ0ecoW_23@n!I=x*??hmBW|4Qto_H!WE_A3X2~pR@7Dw%G zO`yGF46I8*kGC5nmWi<%7ef@B1Q?D@m@Z6oZ7aJp!$<^Plp85cO$v6!=3MF4wW=Pc zI8|N#O%MnQceS-2yfQTOBJId2sw^K}2k=RE;KEo%glGOmEY5V5Cp@qlmP@?v2cDig^k|$Nuo~p@E$6&aRds22oev?E@)uB=+ z`($Q4*JwWsvRpp{Mk$Aw)^K#P60U^ za6?>`Fpe9_XjWf>KGtRYswYPwb1U|kLax;BYMf}k$khBhV!q@cdGH!;s?c8bKmkEH z4){6Zs&r>pS2lUtDTV@MV-%^F?`RqkPIwJ!EK%^@C^^w~A9=nj-XwrQ{kNLoi+>ZRoeAdPnKL_?fU%(xR!ts|MZSwV zlDeVD78JFOd z6c4A0^>4kx-wjzz`q+Hh>y$|<+J1{R#$4hoeNb;;s`i1Tz}i`#kGHqCX3sQ6(f+C~ zRgZ?-;yp}|lTw^Kbx9+$`YcRfi0G1oy=GpF3VUO3w`TFt$CTM;(x7~o^W&J6vTNV8 z;?>(h<+eCpwaS0=U(F$pSdOaPVmI+|mD1yL zi2!oJx5r#>_7)OX*Rs%)Vtpw*d?Wj%1Hx$K^@NMhmeP_WSk+oJB>N2ZI^OTE1PfcF z2=o4UiqzLQ#jSnSOv%rk#6oOJK|+0@g$wWI&Xs}t21nDo1TR5BEs@~fqX{|}j$mnJ5 z74+qjiwLeWx0Q4PW#zn15XkPjMEd>bdu9`l}yQc4yY1hIwDR*5cW*Uz+6&) zF-Co(j6a8680vz|b4zC>DHvtOc-%q>Ltxy#4B23JmqG=$0K1U_A;o8da9NvUNv@pGu1>F2qc>W`@*Q^am(SbDWU1$|> z!C!CV36Z=Xmg0B#d!O1gwi0s81K$xe?R|J`!<*!Mc;(-6=sy)baA!^()ZJIjWkWvs#y(fvG5zCWw;-1}zXP zsivn1qA6h}n&k{ zU7O$vpO`Qkb{MDpaViSHn#{ka&e7rTNUt4Z2+A?qzjcS7ZdIU-Nb_}mbx+P!Wa$`D zRqg9lr)swiTL&e4wTrsq^D1q%ZQJo_hXwG}a94fTK7dl5>3`$e|Uixh9CKzRvaZGTh+5MI7ZM#K?G2tbJb-Ac!bo;JoEM!0GP zM*7`vFl?OjC_S@EW-)4HWqL7xmfB9qB?(ioBbPfe(s(sK7M`oL-l|8eAmJ4kLk)}|N6Wg8%wyx zi6;E@^;xFj7 z(b2CLBODFKMF5hM2rB3hBe2UN3fj*kszY|0F-xqxD`b4s1xE>MhME~nqGHZL-H3U> zE^Q$pmEUna%!6V4ZT^RfC_|O`=EdKDIzh_nt;Waa1rKHQ+SUh0n*h^BB<9mIzbGUe z3a!<8==r-9s}Kay!Ab_9_LrZ0XJGQ>jb4}vy&zK*QksrVr>BFqn|v(Yea8sIz^P1G zSZQlk#8TrK7~gRr+gK|CajyD<7UgF86v>b#Tnvi_y%C5?Q&!eAFtLb1YU&RXS~mB5 zlu8L&NKEK&7?sfclV&L#%@A3G_Zxgw_k~dExPY_y5}-lHBbo*_ky@#zo|t2wwe|MT z*ZEefK=m0o7K$NfllU`wqvo@ON3-ih62K&@de2WlyT8*?q^+CG`1ADMzyh748uJm&k1X6lD=6|10_AUbJ~4XdJivfoFikLY2%1 z`yY`RUMRG+Ub1*ir`rp|xCZ(PnSCbLmjftsbO#IB^+%jRvzMT#&0KH=N+OI@JlmV*?g~|qdL-$(%u4!cRr3jnj+Z#=q-t0uD?=pRu$qEM zGux)kuw)$4H9;}gpkFk}egU(_R6V@Hx7q-ZNAyoeBFD)oVwy$(l$;99&>2~?pHk1~ zc~3@XXD7Xe=}l#hPqqz?nNFJi_sDZ(X@qiu)$B>HUHsjSjYb=TmHD!hu21LV04Q_^ z_B;ICsWY`vwF*`!+MI;;aUg}sd@QM@zqkEoyM?WmqHqmqi>4uL{BOT|-=MDC~$9==r z;_Er>te9jqzoz9xCHh+WbIM&)Rv5HO4$sZIa*fKxPX6lG@J@aw9>(FKuT~45!&>yh za(ZwQmTI|yxr@QMWD+(n{z|Y6YBBAPVOnDi%#hUZn)eIAc%iuZl~!7#!_^gaS2kk4 zyvJTEx%hjLt#X|m^t@EiK0Q0jDuTP&G+VX5EjWRHUt(wU2f5he5Tc`bxHxSdyO0@b znS$Ds&e3uj;1ijMA-=}?eCE9vUS#1v6RJj#VLuZBz6!m%dA`OF!I=k*=InpfP?)Qf zWRMy5yp=eBH=)+PPPc+6Ibvm2e39(<wi5FsnpK)3*%>!sbMEYN&eg&bS@H`Skz3sf0fG;4r1G%QpD^14S{ z2z2eYliqwtbe`Iid8XPEQgj~M8=orvh>LiWA6fjbR3sL7pyNP-lxl$Qe5jz7us=d$ z`@@`Vuu6;W%@IuCKN|m(;Ra1cLiFqQ3{xXt4)=$RhMwi+wU_y&JKBbdZF}Zrtb(Gk z#yk~pT5WeDLG0+fj`ft;<6~yv(pZ1JDUK@9*JYt zm!VC-9t@N@Djzn%tN3X#*c%ToiL}3J^Eu+a2_-GloJ?=olXBvXJ+gXsJr$Z9%mCuf z+HTt(`cp>0aQ4LEnzho&v<#x2tmF^+buIz2O=utv;cl>4>-W#=IJ+s~;*lu1if3w_ zWudZDh#ExAMW#Ia1du--zXVY8;Hm6_zy_UbkEGYz{47)WytYlD9#UGgLBXX_;MeUo z*l!8Y9YTWu1_YMHal-=3Bxg?$);}l#{?H-0{o|%TD5-e`<&^JRx0U3U5S7$Cg8y!F zU$HfPeLe6830~|Va!`B7q+Fb0&`5aAD@G^1dJeya18G5ULk2h0(E~tDRpYD%r z^SW!4emQ>Z@8bo(@jl=F)_nBI+M-*%8r5wq0e(*a@cJQ&*FWb8FX|f6(mb4RtJ}0>*P*g>#!s-!59!79 zZ*^1!$D2Gkbt4-@3LVydtbgfihH$j)05T>K)Fcn(sJOSa_iRnc-Ify=O-9ifjymQ5 zAS!UxdT+VH54dLzEgWadmA{O{Dc@^ZbgQZ(xn{J%HdQ>pZrk8Y7XQofSS3@4AM5N4|ZXUU<)Ai7O zD$V~r68Vm2_iCb>YZP*59kEVD>H4z-kBV@ab_9i~oe8d{hUETmBuu95P7;ROh!L43d=MsUa3$74|(0z0%Rm)YR}w%}gJB*?6~cf7x+#rFV0J zFMR#>bpG6(lKpzIyM^s+1NqjGc%`;~b+G$*xzK)Dw|BCC34fjst>^s8>~jBmKXi{! zQ&KZc=1j}0T|$M22^M-EVVbp>VyoclTZTmlx0-dVTE5oW2s4|zz9jpPG&;OOK=@}k zm>>PO@=v=Pc}5nZ<1|OY5_aGfry$rNctqJDhRxYo&ixa=e!*8@%H()8Lw#M%j*1vI zwm(Nt<6mzjAfnRIQcT2QS@F?nd!U|K{;cuVOfi$mCZzN9pmufmN}o~3Dp#r$WrgJE zW<48q3TOS|yW1{ViWKRIycHwsDCwM=glC{W3l0U!VbkWth+}1(^JCv#)b6<_brHAmu%$TujiR z(&0HihHl~T-POezKeNnJ!>ThgNkoVB<6#n>jbIZ84T$bZaPXLC~#G9B?0G1*I0?`7(rU>X@xu>NL#Qnx() zlkAv%MumXhS=xvIaT@EF|F1b1T;++{#R|35$fbDG^Ayp!+0N@MhblCpk5wR9u>pV$a)I-07 z_eUh1GL62*QLq`J*%pT5L{*|R&^;Eto7y~2vo_DPcjFhVo2KUf!yC2krQGfR@sfIC z76>B8W_f!>9(93n8tpUKZ*PNdVN0Lp!Lh~tytU)W9-nR z!y*Na)-Qac;C{A`s0K}-)lid=I?Mv$+}i;~MCUMRsnqUiq(x+h5%d_>iJ zp=)#s+uTxmw-iC~E~Zk7aM+~ETAS|Zbp*?hdAV!#{Nr09waj=bkR2WP-k#!)3TY%T zHm~N1xyvxE@6?a0r(v;e=S-@;{dqysVjX^!)1Ch+kS4i7M*J&O!v`ioaX_CW3hyX% z69>?WSlE3=0P#y-8Svi?-feZ&tbb7f%+_<8|O%~nXU~@ z6}_AQO4o4z<3>36&G4yw}MUdRLi-6^E+`qy`USYJ)YPunIJ zk|HF~f8)`d-+FgQxm%%pcnXj2JD^;{lqnH^e*LWR>iwBAbH$UsY={@0xSme|*WlHc zMM*inz%HGX~K9Yy&hxt zO^TFTsbH(LGwtx1?>qQ$@>@tV71@yc4-zDQ?swP)pZ*8Pb4tj7*^V~ta#WEx zqo!ir^+xhYY!8Jf)|iD=yIcc1sRChFK|MU{6SdE()H7=ufkMZdA3{9VoDXqjqf^mkVIINr51k=cX$nF$QbKvSm! zUSJL%ReA5*9__}qcw1EfsVe2G@lqj=I$-g6(YUByBm)GhkhIGq$#iTDJ1CQ{t|zLs zMmkdm{`aU0@a(^$E*uN;P`wRhSA}D@unrFc3k-z7aPmKtSBCTxhot4B9vaN$_mnPwNUjbOKG?83ojew?X?Ck6dav?bq^OUpz7BEUl= zO>i1#sd0g%_;lWKyxG%^#GxDT&KUgSb|-Td0a3)cn& zC({N{Curdwnw=cH37mS@r~9?^WT8Djb&m}E&@--?Z4ht^{6~a zFR>%)amg-UF0P(in_W{>ZGNtc7n@F{VtMNP^K4%Zidn{YG6BMjl%@bmP8DEyxPWQ2rRnzcz50eHh!%-Saah>GC8hW* zUQl?iKhpgX{^CJ%c2J)hX@q9({HD5+Nh*71FQK#KV-Wk|Tz#Yxg>aQUD@1FXY{0`{k=?kRf)?+j$#E(YtM+i?F_lZP@9Y$V5kyZ80 z&F7#z(XQsR`!7F7Fvia=Opn@XN4-gFm#9mF@U1h|ro4}C#bd-hq~s}TTY2wy=(gqMfERhGI1#lhTBg}G2ww;%~-zaY?E)wgA1u$zm+(Sr^Zb3eq24`Rmz&$5 zFj9LzSTz2o8C>)*gy{g9*AS%$7ZbUb6O;UL1Ii2AppfHSC-)8sBN7E+t`F^!sI1lq zk!IuJD5&lBKcn@Ua9Tku71Y)Zjb&B9)Vc7)W`_}w8^3|ks&rF7TOHL+ zFVExk`qy}=|Jq|%AIX~+k&zVLB2&e68aK65?b5C&x?7*Fjv7#+0PD=ExU08y(C6H( z$}s-sf!X4HfAIe}lRN=;FF8ZFz1Zh=aD3oaEYCk_GlP;~=Ls567(i&?_1={`pK@gJ z2d1%4E)2)84s(WXaW;41AjcJV<&3fo^v63UBs(z1Xo^S)u6O)N7YiA^L}y4XG@S`0 zceHro8?1FGxe_hK+>3HSieQuR$1))Xg#2uoocwoLQRkR`2B!eOFkJ2BEOGfPyTZjF zZEGCH<**v+87Q@;qCYI5bow==s;Yl)>M-#b7IpEo86tkNJjBC?;0$kNcVM=UUHR2V zVXI9u4UDC8VGy!fvDC3CQRU`5Hnu$_Ex+DiBz!#>FEce93ZmTzT$;)DcrlP34lc)|ht50w6XKvj00Zzs+{qb})6 zb0SHNObi|Cr^>1Z9XU{ab*y|dBjl|B>}Ho+yA5x+1{tQSB*WrQqGW5}bXr66_CdGFVMGp3xJtWd zL$r%Iy>$}US~~iws>ha1eC`|8FuYJj3QUIF20U}w?ep^pD*I<5&OATJ>LFujf&ds) ziZ74K7U|j$eISN7>v&TCBw{ndfkfZ?vUwbQw6~b;`yVtw1xBXL9M(!xvDNIWz<9h?d=kf!$NmCrxPkRcMp=la z&wgZC$0x{k4wikSn;_DhkZDc`^sDx6F(Tfz47r6UQ>h9rkZC?Lq=|NuL`rzUv^d_? zf6#@Rt0zn?XdH8i^PH8x{DLV^G`rn{f(7w-majodSREk=#yfU^i+X1)X6CEqbP8D*nmNnZQe&3@ zCl%+sRrnQ7g(S(lErS$kL<|e3)`Vb69Iqc_bRvQY+$Psh6-xbmEcJ6*M*bN>U>w&f z&mg$t3=ncFEtq!G2orw!>;?|U%(;_-Uz*olYiumRASBxQ(`Ojs1T1!y{n$FZJ&u?IWXOI zK?u|B*0gbH#@<*qm#5rT%}=~^MQ%^Lw3CN5K3xfJk4Q+h+6|&b=ZJZ$I#VQ7L+qcqQ-*2p!#s2X}#g5k=GuL6}ygG$?MnD=3J zS(!E(S=Bq{a-+Ag?%%a3Z-5ibLL=So7dxKg{aF)#IWa^&xMVXlcC^GIqhE1+`Su3~ zNe3UhLvhPe*+<)BJJ!4+aUoP4Ke`_bqVI@>DV(6J46&hHo%w2-hxpeU>g?EMs<(r} z%m=Mzrvi-zg}IWXUb+hs7Wbg3#`#rekOlgdIG>?zu>ymK=MCtc$xk zB63bn?%z)jBhIoK?|etU(hc$p@K@U{{7FL{N5dJ8c3`17lR#Y{zmNRi5-Qo9ZvH!( z^o-LFi>0W-iHsT7?ToafUuSh5B+AL=&TqT zT1BnLR5@mLO8?`)e?~m;53&lhCR|A_ME;T{M`b)CrRP3Uc)j$F@be#1aTfR*KX(D-(*D1tODMg03D3H6;RIZe_WLRubg{?maxW64jP^u2i z0GsY%P^^{-O94mNRP0Jx1iO=b_gqzbsSl&&MJ^O5+#`UBSM?Vz2SEv=v6S#Ye9jU9 zF9?$4Pt3pzI#zTk{oVw;{kl9p^jemizT+A$3yP=v#9rcyK#1G^Urz%M%3y9VsQi7= zZYaUI96j9rFz;Tn^WJtJ>+1R>dw>+zO46gE=PTW>3D zdG*XmE_o-4ftY@N9*QGM!Ubq0{q@=#OS|b!SxIOq=n@gg<_#W1OJXTz<*QRti+Plb z5P~1w8U={3n zOoUB<@|HmP_-nSf!enHXyCe(MbQ@@$R08uPM3f3h^&n032+^>mU0Bhr%bASdHOv!)<7%95u|h>20LvA)}U5 z)L2#YWx6^CuaNdO??g#H3jjCWOm%gYni0sw+Aks=Gm2X9T>7+e44%p={Uo3Q7Wer< z`9s5Nt(+)nP^3XU{H z>CD$uL}|vBK2fc2-3_II6SP23gXOMG7OtRzaMX-Td3bR9`@$C|YP>&A-&JlhnS$A$ zAHHNkM%xg5y%Pzws^gebkOV9Ole}`H9ZZNlE6Pzn2a_`Q@R7-`E}$dxQh$=1O`fuB zr>J)?BY)P4G|cxttv`vc*~B1AUgjH{>sTN*_~uX#Ajp~6xAzv7NuX##+0J?Sx{+um zDc7HjSN%ka7vZGVhK%{qKC=A_5c0BlNaYIGl{RjRcOcl$g-)(jh=M$+xDQ@L8|U&d znv%3E<3?qqNOq`V=%IATAAlKYX6~TWFiorEFue@HkzQ3|XGMn%=;40dDTksu@7Ca6n8WORux{b<#ZUtzVyM@u3q;X};t3;QMsow^JYyI2mOcWZXGGw`{78;t&@;Lx?y)30T=OqKmYi}(^o_Z%B%Q|5FWUT^(9R7~H znUlkb1Br%Cf_P!SCQq08oal%mc|uX5_^8t9V$i)Ycez4VI0D22@uwHr19nHL>%RBH zSW6(ogah@Syv-xFvvdXSXWJyY)_S12K(NOb4_QMK(OOVI z-eUMb3CdL;*Hs_*M*^~~00z<3Hhf7UoT*dehN=nx1a?3@(kl@Ra?ptN;IoP?2E7lZ z$fyIOM;)cqlf}v9Z%;*+75EQ4RX^!0zj^B2tZ>D1Qz6PydMd`thgCo9lzB%OxWnBB zP!3gX`SrWq@}Od2IaCfjKS3Wb^*|+077JuC@Nm z=Rk2|Jp;fwk>Ig>ZZ51E@~M#T9Q0tURIj5(@^huFrA#eoR=OUKI{IyEi*&B z2E;GY(J;21ZQqgF2}z6MNH#uc-ldl+W%mPgwVHvj(oqTeG}UQ~+#g*S9m;fJL(j}H zOn_xQm(bnOEA@c&9@48ZtbATH!wt+z;(3mAGE0T4Wv}a3UHi$ki%$zz=J!HX;X))E zI`QY=I_`jO%n{>qYu;$kc|b%Gg6!F;xX&%(Dn*|R<`}Ti$q(2*eBLzR z7k=4?5uzKJR(0 z+8{r%xiIjgfDW~Q2gb&N+OT0QpoW4U-Q-G4%gK|78U?VBtnx&eAwYVa;fQ&7IM%^I zh~-h%Fp2h50dwVj*@>FPLu|v&HDpMHus(IMBg6`e?A#5vQr>5-!p#OKd@Cm;N94u1 z%?Ev5hVqR+>a;|XS1*HYKIOd5k7t-rQ$A!2YDsFL$k6|mP(9i5mK()B28l3t!|)G@ zsM7kdEt2YA2wYB?ul*7LbN>1X_$S>XEA&ooE;d;_MUO@r6a%<-hfSHjo;NR=N;8no z=tx_K`Qa#CXCztD0y<;#CN`(GWEiS7nx{fO1y1PrmpD#Hy=D@tdwEs2%gn>?mk0Tv z5M!&AMOjE1U+f>4EcCfP03ChYn5ah`B&iJg@{WnH-`a!(aNVk$Z~Dg{epg>n`?tD9 z*Dy()e`wU?D}X(icDJNnZgaCk*idZtD-s2{uV}Vfr99O-J2aK5LTE85GEi0<>8<;+ zCKsnsc%`%Moq1RU8r=K}fTA}KVAPwkeDl&fLtqE&eP0HCpl}$%ung@AOt6t0q032q zpaICZO)xlBO>B&Cs|bJzXBna%`z zAW5h^iJBVm-8GLdR&msA?e1}2y@OEy(mg-Lb0Bs3R#5h2n+#1i2sNHB7$CQcVomO^ zrYsZrMW5VQK`Qy*bzKJ2CTQ&zn^zoz?B^Da1i^IV} zRLbDGVQ9SOcz2loZ_>v-3J$qTc zZy673f6ZgGDc+snyifb47-k6Kzv0pD3*3tYqpseal5mF1Jfj%YHURU*gK+ANK za)d;irFJ#6IUiLP-CK9v5MAFXvs1%xn}G+*gu>0C5{1h7Q!rWw0FRU=hxfX3phh_g zmfhm`obsJgasV5Cj#AE=xdhW$G_tAIT3nENFi#-xDV^~PIW=oL#`Sn7;vW)`m7uy9 zz|S1;x<2?Ll{fLwcpmsJ_e8wMZu~rA^qe!F`of_gjKv-&ddmXaGlh@KdEdzz1x!C4 z=x+ItpzXd_!7#Ui^+03dp+BQk#FIjC4NTJ4h1;geIV|}q!YD-+F0yFkq3fA8M=35p z1bwJ*vbY9b-n#6@2pJpTm;R`ynpwl|&x-$;PgJ)&KR-$2-?`Rzh zl3QH|U2n)9W9wxe&$MR7Vud)-2It~6-zq%6`(=TEiktMhk(mk$8Zsnb3>aa1H6tHH zW!X^hrC@5QA48a&*W`f#H(oZ(gO#9u8o(zSKVuTSU9rgsJr44)r!s-srM@^>V`$t8 z5RhPTDVA&+tE|aj+b&+xm)f#JtS7q;daUS+uJ6=CHN-viX?`3_47*#MpWv#t66#J% zpoaaep>i~YO!#LqdLn`Qhd|C{&bhHd0+6DKIw$E{wHe(MrA$JhN}Jdg-*OtO>Z%&s znV8RC_Ovd>6>hd$N?D_~C}DtjV2VZe z%sC^Y8lxzV`Xt1=^Upt$w*BFl*plwSUJjsHYZ|zd86EqP}OpEcj*mFiHu%dD5@i(fg(_c3e-)>=;LbQhTdRH-ybzD*DNJn zI?wmie)yhTJI{UJF)P9_ghz7HyRISOHg8j`=6dVHcJln1;nc&A6;yg-Xn@G;qB&o7 zA_vCX>}%YQz`DRlwO6p91Q;&dtLh9$^C?%2@k1->m(1UgJm8qb^4#J zmLs8m@2e9L6v_Jyx{;-)d3m+Y)vzc)UXMf0=hp8R5~3P3%GR}5FD5LIShj-4X#t5& zCP8s@_H_*dM^Y3_tD0qFJTSSzaUW)l`fT;blFEk=0NqSm0YFGyOcBPCgesk%_#_2Mh22#WBE_|HUz`X8uo( z;g*NDW!$s9^>lUZ2Tp1qi0$<$#W06J|1XRYYoZ#{SeZBjrzCVG9T(bZh)##0UI@@| zqYJ6f(qBLi^G&uA)Az%f`GQdC@T00F4Qk?1c`_1?ZJL0BTssH?wLUC+!a7?!va{qV zTBeio*=Vb5cybn}*T2L|{%nn4y)SK?Pl%s)3riB)s@K>|vP(HH>ui3oGGa&y1EMpl z=&an(PL+MNBE#y=2EWen^5FM=Bzf@fRD29^d$Rx8#$oACp}dNs)d)tCncc5nwi~jF z*JD@uc+`g7=bOwfsW24LJjfZe-pWuUzK|j-l{JUYM_mAW@*0xu zqvi$0^R=V2`6_M?4z7f@d?p4%QY)1K5_)nMt zg#{%LnpQ;Z?(Ffq?zUxo*@GWA);TD$K8Yg~!{Fo!JfaG_huG74D~Fi8u*Wlizy$*m z^Z~Y3@DyvO`TQk9Zc+udmjAk{Zk%gVu8X_yh5u6}oY)~%-x2{4haR3y?mYarXjT7_ z37u)NIghw$XoRgo5@E=N_J$|M^O^ZG?NyR;#~AT`{4Oz$)a3V$c;0`o3(^mY zVxcp%!Y!aOSj>-XQ8Qr7BCKl^czi1LFf;qVXX9735Gv6Wnwc)^G|G~@%cNx_u*aD; z2{Np^jq)sl|E;mJ3W@{R8Z?6rFt`V2f(O^&4grE&aCi6M?k>UI2^JuD7zpmcHMkSp zZDzT*{;J)5+1mH+emY%U=X|HX)3I%i;`c&zq?3D|jlRF;j`mF})#G=@hT;mrL0NmL z-m-2rK`S@&B%{{a?00uEg4$$Xd&P=vDU?`d_v}wr3DEG4Zdz# zOX~A|5cMI}mPXQnU0>h#{eNd(wPGV;|U_L{maVDcO0&67r@6vv0Iy*ZPvY8>{WD?sq5x6=EON?Xg z#ut+Tg$EPL4#U0@%tT1>z-%m2?I3L)O+t%a>b82t4`Ph%kB=dNuiv5iElFN8 zHOs{I`yPeD`!fl=v!a=urH}&edk32}+_Z70G*68RRkx1Z&jS6p2898z^lXvk^&X?( zL-N|*E_Qo~(= zOi%jRcWfB1vUrpT+w_U->>!*=f(-ghHkQY?k(Ij>dZ67}3{98YzzU9MsxyBUg%ot4 z2_Hg~^H)WVis1JPNO8ZX;nCW)zH%s3(*$jsi4-Zk&s1fhf;&MQ^J^~FgU8Mt$IzXT zomWCCO<>Wb!5vWHY(U%le1`XWmQBBW?CS7un$vDaePZuqCzo>;u6j)iwbhoZbe|zd zdKAg$5Kli0a7E|ta&z6gzHa;qF0Btycdy+GJ}KTfK`$rDTU(m3vrf+DfCtLD_4gRJ zizfXWxia3QeO(Tzcv0gjvUIcnetZ5*aQv++FH^INbx#uvc8wf#lXuG7R`XZ!(336? zF^&M5deaEo;Y0tJX@QPtB;%`$n8hs0Uzu-noIFbU+esXPkfrOfblkjJ3b87et5N2F z3=v#WWeEh&sCe1QrMIr0>4i*uO11^@g$McMjoKJ(-;sStAQ_kGQXR?72J!y~$3Q;( zAC7VKUyec3VDx`;48@bCB9#9>j&Z#HKOEyDiTVGVV@!G6d#Sb_Kz<|WUPai1-0J<8 zW7uh-e=q(I#~>ki;~0(QJZ~JMXK1Y}?{CB^>Vh&R&T&cgo+Tw~ud_@h70xdFwvu;V_lf z;y7tVDLZ?^!i>3l*N!}~GyYh{!&J5ku{?*BCO+~(@nkQL@5{un`tori+a%tE$OGUj z7nV>s^&Q>z_llka9DH(e3K8F@oenFPoaoc(Q`RDb&h_Qv9vyUg`GpS^*JDh6RPjNHf@1vnLi_huap5!OYlgD3fnF02Tv+?=h<0 zxz49B_IZb8nfb{1Z*_RuOl-!C5w>q7x0pALuoj6`1vW}qv_;OvCVW7L#$OGGasu_2 zFX^A{+(0hvn;ixS6Orxv#u{&$p7aKdWzs}re4-xJt0Y$PmOAs(xMuO86uRfp1mRdz zg}km~-cMwoY>+fM#Cf!^Ch*yVggPvfoR?udb&~h{_&}%`@m6-L97HvCeJC3l53 z|L{r86nO{3#-WzMoZcpn=btdSNS}oC0%n%!Txa{`RmcC$NB*TFIuelPJhD)b3fT7Q z_EGpjJHS)kgE>L6AQ@}V0-^O$BRAwDn-YcQ{zE0bCp|MAttSETy#BXJg9bYX=~K!P zpg&-l4*tNA5u|f?&q-o-m@&2wbtM22sfxjvD#_V5GCa z;CiE4t(j@mxVgsSys3gnI2@z7$5k-HSB7}pzKZ0;-$Cnah>~ttM7_sm|4vU&fAI^Y z!8TS`$6f;ioz6FxH}0^o0(@Fv{~mjrULmKau$z|(t3U)_1J>s1exZsfx$UY%E_L`@s_yTs$&P~iiiYdfS?~k|l z$MafQ?}AHEExZiR4crr}E+^hcr^rU;iL1d^QyJNA7sHtr~rnaz+*W zB&d(5X<&y>Wo|t!Ra?b*ky3Z?qZWtR;ru=m5s0DzAgZTlO zrr77^m|xyci@9uY;_YiT)#kZO)ZAmmfGx}O?T>MYb-Z+;?;Uyf_>2Eh_`UjQJdpL0 zJfz4~_@ob+Sm#ne0?2n|abyodkiA_C#Hp(x?2kIR-1%t14=A%@NJ#v6_lH9_+GSgE ze`;@O&D}%z`2cKT_!^J5ij;yL{k67u@^l<#QA?u5$~d7z_Je`rO79wL%=1cA7T_CC zBaHbqs8J)TCsj<*6-U?zwe{LF+D%-s88Oq7YK&@5D;+fXKD-aGpkLl#3ltWq()TX@ zf&0vDE(b?}67|X%4v@WdaFr{m`>Fq_$L&OcP|_OFc5>>esXnNz53y0U*@ce9Sgi^2 z*@|UeArCR>db?%@?ae-8LfO7Dm{4*u_C=rp`i-=)S$fYJltKazV+?(TRa_B#d_uy< z79VgtCJZ0(hkocpG=jv8q|a9iR4p&q*_&<+B{g;_OtP1`5~WF#+>mp2h)t6Xphn-B z-z2V;ifym@BGxIhx^CdN>la3Cq1J;_aQ|4DXPr_cTX^CBV7Dxm7+zqW{@~8h>`}94 zx-m;-ShyH+H1gTl#(zYelq+;JUw8ZiA9I0o*|?nDdr=-mu*317o@QSkd^o(MOjo6$ zuoH*tJ~sAo2{YcZNR=h5gOKP8F_uzA=H_t6+J1)PP_%WBjv;qrfW9xc9ZVvk%G?y` z+mvg6xbjCb_2obBu9K^3pJr66-s}`}_Z(I~`~*pcprG1HV9g@z^px_S=+rR`^RD2W z^6m-ZkS6r42x`-K0I3${b6IyC70a4E7STj8#Lua#AR>!^K2W=d%Jl8PWh2oIDMTRv zsCc?x6$mi|d8SkV8K^zV=fLsL)Yp2~@8vs&70qVlN8B4CyFr1yYN4=YVx;cuKPe2}{oAa1*1hELTT^In+rBmEs(V4T(3`V(p zZ7yDSBwaxHT<2x*qbhT+JS408cE~ShW=f%d@FvM@lk|pLtdaJ)P1DY@7_YQTGpo3gTRbOoUwD!<**M(>mTMJ zsP8XT7NAH!sz9F>QFD5hra2+Le92p7=@A^_|MpcGyPC zwR-{88ke~vY3aDE%qQ7cg7KQ;$Neu`;&$>w0ls>5ZeuIb&4NB?DmVf^Qb4Z($YU`()=z7UkUK#xG zL-V^giBeKY_b)q|!MVoM;58{uEq1tY&e?q|2eC!tp77t6~3joeOQbN-6zj7^~szJFg7 zgpB&ih2hHCZka~KB(r~8{vAI4VS*}Ba{`gvJ&9w@K8J5LWEqO+$C^HA7S(G_59#htI<|)pOueWP#Yp)t=}6Xvut?u2^gGxy zity`({0Oyk(`@$T)DY}~C^J@+B-@1KUxgwqROBO{0NS7Eyz5_K+70=Zk#t&?2}0>Y zIEi%bIBFqs?rFOP^RNS1!F^3TV}Y9*)vg$#gPB%hxE_dnK_!gE>f7%~%HTkZZ_j4< zz5cJs5z)+$Dy-@!>!#daZ#*OuvcEP#TV)^`&J5Kc-2U^} zBcf2+J$TxmSkBFkI<(|uBlZ&OMcG&!xL4_lG98G<>zhZcl$@s+~3z|Aoz)+_d+tc4`)Cl5iW#2txv);`ekZ6tx(RuRX- zpLGuXSloRMj0u~$SBDreXAGSz!a)o7rJq-qLKoHX0D;-ULq(X%-SmWsz?U9SU=Is2P`!0bhdO1dd?`C)$Z@ z^zANmQPPr>O^sR6P8FF1^n6(*Bd32V=f7v?N9i7X{OjujCoktIcEpkkKFnyvm893S zjdVJ+9$;`Pzn6V?T5Dn4y6fl2*^{$_uSHhxjYAS5LKk(7E;H1A@p#RE7AP5><~|a7 z6aWoPiIdK_547wy2?2vb)$x0trQC&=#^z9Q`cgiWhSV~qM?F#(Z_m6kGxON_B9WK^@hb;~urGp``l+w5 ztzHu)*tI?EQM?|uEW>zdIDQl)zx%_9UP|9phL414Qrds+LJdJJpqi#jpWIt7EX!{3 zvq4+?O8!cCp53Zu^3TkkVvxk@1+LxIyd-yuC`o>)&xR)OK~v**11GSqm*rlDUQ*dc7*4H0^sO$ya(*$tgL4W9 zx)<~;e(Q8{Y_$35CMWg#hQ&ecbz-FE?@L-qa<5g`b*tOelMkt0xaB1=mxQ zD-&e|P2Y;lmx@Z0|e_L6BDkdX5eVb@n*j7g=b`d^f9D|d_*Q!*lpZc1^Z z4mLzHr8fu0ciiIOjk?R?ibN&Bs)5#;l%||z_5gyh$3Sm)%0i=ZKwQL2pxN-B#7$9X zy7QLry4(3d1i*c-sAhHPc~YrS7%jjUfSOwrz6q#E3(mMC<_Mwb_wvja0xZ@;3uedi za&mGwcCX3BM*))5NPq6p&-Hu8-8dsZY+EF3Rih|+BZd|(iSJm(W(|MM`_78^F?t^; z2T94Z%XVi8NSjre6MIQzbkuBY+oapLo>28nKbg8j<&XXB+p0x8#48GK1p!2W@yuO}mqP4cY=U;@S&I1+g3_en9bmq^Uw!TADd%@68>&G!%X%Xd41VIuZ&Zfs33Az1a1NeWMBAPU`(F&m6rXdhbv@1)~9SyP)FDSmh8 zYf+N!n@wnC8yG+LH=%MbL2tq9*mI}ls$Xv#9mt-M4!b>H8U1+jqE);0j`+*#DK1BZ z{|`1H)9pq-0AHxMW>W}?Zu#=#4MzOibcZM@3zyZ7eqtrv6e_G4-RTz-h}yU@(o?y4@CDhhO|VomNy zME&>YOWTFa6G2iOK5`k$)?7fv-FJcO&tH`lcYmJsd{AAGA7VK23P;FvX?E@m_$X?t zP3A2A8nb@o*}BY%;!}?->l*8c(o%J?X>EkbD4VNbMZ}`?_`-%=$vkrCkb;*cFA@ zaR_Kj)M6GD{)xz+g(XTv(8ch}5t_wue_BZMoO&KvT}^ML{mz^^4h-4TGqS->>qN~V zS)Zi;Cm~IOk!1e9pVBN{o7N*0!B1(Y$Emm^s||HT;{JO}P7y3#H4fKlaokeWNW<{N zUDt7l>73+jrTQv(k$@Sfn6>HrH?dEpHnTt5tI8|!7GRJwMPZnbW(>-s{ z(T-isr2uTJh33X4KSG`-nUR5X4a8*~AI9>O+GoYS@^}5YN z9-}HgLGa}AWzcA4K;U3}4ggn|lvKnr{83yiR|7fr7zxjRvi-`Sx*2lpp@{xi4%2OO zU5vN@F_(}{+-N6$F{BZqLS<`Q^evDK+~^lyOLt-QPZ#p61CJUM?rECR&eHVd{85NI zEP}ksv6$`eVwa?NZm69Zu^~H>y`n)9WMsVLqbDyNp+U&>N`w z8>RGOPaofC`OEo;;WKY+V%^m?6TImGu1Ow{Au8nsv)@ZHq`#g{4Sm0bhAw(>yUc)3 zKK7ed^$VIa{wbXgoA}MP^I&0py(G64cm721oWRJ}%3jTs)+VvM5cv2{bz0)dbjb=) zOKFYaXGod)F2-@if?-kFm^eZ@QUDquOCWZ2KMtdST1bjKyc=PJs&-8jfndfrN*$py zMGDV$RaV^dY??E0x(#W#j#rh30|(E4DlA@QKbQ11U7B%6OY5qRQ4g_yeHGXb=K6sE zN4nSUW0|mTt;(Cq#JOe)&y?2G*Pw?pj$Y!Ri?5B~DEr`;(n310YwBaA4aMU`WI#Ow zaOu~Zj}2S`NeoOWAFIOCc)pGI=r_*Ro3TEA&m@&Q*xx765!VC^Qy?wzM@1KEsCjB^ z%#Z+=w!`6w5<+}~z2E0gk?Gd$j$Fl-sEa`H6JlUWq+hS+%Y(0Dvzsv-f ziXd0D31G3Q&movq#Xs6dyJd88iIh(_L|xjQRcWmJkCg#Whtro^3+ERzLQWlX zNJhBCsSbWU=t9poA_nJUEZ*2wj457pB?}(X*Z%j_jWWmj#|~E+>nS&SCrMo~__>JF zb}j|_rQ0!LD-LBl*-;9K;_|E!e;tuCO6GE-36f%%eNm?F$6vKpg=JCIQYA9m3PL^> zE;}v|{I&~0us=&~V-)KtH;hd6uGC9bRMumoi;vLr&K6cX0e#@jWub>5Nm`@LEtBg!8bC_^Ax#WN!*RQ81 a3Pw*e84!s_2mlTa1_RN#9>Xaj!~HKK%^wf| diff --git a/golang-external-secrets/charts/external-secrets-0.6.0.tgz b/golang-external-secrets/charts/external-secrets-0.6.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..b5a5db2611a56499e8462c543b21d2980e1d773f GIT binary patch literal 43570 zcmb4}LzE>2gqFqulpbI5sfvzxGLFqx^cTkEKDb0}(PaL8-g*%&*Rd8;Wq@=KZ9 z*#oZnw_W~nWl(P|e8A$bT{ZDbN=tXltVuT8TCBEg*T2YYUVFtNR=$xkt#SM9lD^DivM_STNiZumGe^oXY zl>Rp?6$@*CL<9utz`&=g%EBMFucxV_QYt}yUyrAi!i0pvAFrol#eFL_Xr6*NxXB;4 zw#Uwz)K3}0ti#tRXSpdDEy3MTYn`u0YA;OuYw@T^_)3xQm)~|+MV)X`#KE#@#H2$Z zEO)fIWD-e2evaa3ZlD?>Wnag4PSInuxlknXh_<{9A0vc_CzW=Zkd9zG{6|sYY0zR} zds2}wqsPcD4F)beu@@=xCX*S|Qd2IskoFEVv2Rnm@PChs7Hj}>LEo|+(RqPzBD2H!XP~Ki7avwy_LB@`V&XgCw>5Q+S?MN!^_j44H0H8 zdOjelf*G+`FSx(`yUQRie|<2bg*}dSRFVR)a|XOmr4szn;s*-yzgzPFU+l`1*1M%eu8d0Yqs|w3IryL1xq^LFM@8835Lp6Ig8H2dCh9jJqn%!^o0iS0Hy_ zS#v|ruprMU#QUhwuq+9n&$eNhOtBC{_)!?3#m8brMN+}I2R`B0ph+ai2Vh@>muq1W zoUKogC=Bu-)HF^oH)SBQJW8lbuMh|^GMDgh!Ot$-dpBBB5VK@C`vMdQg%uW8hLl_p zv}P9lYXJ3uDhDcE`-D*rOAZVDx)6()#1S^Lk<_cf{fag`ccXpx$JvQ<3HdG{LPSOc zzZBYtbg=yuObSQ;2wFVG{2o2RDlw8xd`b7y@eIz9g<%N4`d1@JLmPrkuM{UBGmx7f z?D9A$ZgMS!mHjq`5m=M}qd>m)n{;>bkus47dJgj#SFhs6S5Fp5tApm%faBTpmYRzSVV9?*xfyL(+@gYs*4yIP%SP(eS0$oRAg zABANGNW1GmAfw2b_9%z!;%BGFhVXw+@UGOD`*Gu?P`HjBgQVcmo5na~DuXawpB}BL zLM~FFTYMx<{W40>W==EF$8TmwT(ZWdb+D+`7!eD$hAnb{dyBDfR?EpLH!w%_l7YsM zL04r1lSuw5+^RVtIRP|T`GE!iM-uK2Q;m@OB3#jZ;X}-9cQy#bSmeCv$z||-bjE9P z3K_{IRV5Ac;rwsNbJcqhE`yn%WW~^#V}-_HAwQ>)>rr~c&zg{N?{@7__qQ+0A9PAuS0cBD>LX+tGNR>Q+b>2@ZY@e8u26=c1P z1W3`KQSC2C1`14Hlv=MlTb&3mvVn%wKI~(b%Ur>gC!xT4mod8N1%tyxh#C}HDu|Xs z&*VdzBG>++ls5m#?foYQ<)i>^Vv8+1={0E-lho8iY)RV zWF-b0v7`gXE!!s{#+>Fy-c5Q*XOV<<_a-^eX*teqt=EKQ)9~Pmms4#X97bzLiR&Iy zpn_}LygaPQOeKvFo1>Pm^&@ly3FGVM*p)3GJFut|PNh zLV;>j@sg7^B^(Am%B)G7+m3`0fmdBD!FC*}!XQ+=AK969J%{Fqe!H}V`kW!<(TVVw68NAskd5>AnRj3|mvqr1Pp+IC+vI3`RS3UvJ zFTRtIj!OwAu2?-~k8>uU5SGiEf;@ZQ+yF9fI^wWYJV7=f$32)u=M-Q%~SIZ>xPDgDIi0=y8oFX5DuQA5ii4}??)AV!_XliHcTFb=Vm zAm`KXdZBW5*RdcNAJ(L-%>*eDuVY;9F=4vhkDJ11X=dNsH*mY_&3tXA2)1Qmb3=nW zYV2&48_Kl77OR|i_pMx7MYv%xB4e1bQc{-rW&INp34vbZuj6klCf9YV;XwT{;1*Sf zGDuEZQ2lBUrMOWOtrO>TP$?on$r1~&iVX=c&q!7*Pqzmw{^rU{cpc|C{&6rPM7S!A z4rXgb<`&o7$CpR71FIt+jr(k``BKK>ztwxYLN3e!U-Up(m4&uP7M(y5%N17!;2XDj zQ{0LW+mBZdD(PU!enYH4E~!NmF#QI14j4ZFn?9jf8b#7Dx8hCa{rhyXp~KIyD&xXU z8&`fo2$2{@JWfozNnn??wl6>UH}cb8F=cr6Njc*P8h8x#q^&rN3ezdQrKQd}GiBUy zcAcj?*SII#23*uf@g`)6ph|OSRdzc^gO2D^ zW4QR}&$f>E)1WUsS%q?{`>6McjufD3OX?~TmNyz=tT}z7aQX$c-2HeUQbB%FodF*y zqS2&mXhuoCx0;T`^H)hCt&yV3138tVi%Vf1AIx-W(SK7Y_z9zu+_%p0s`TR51Na=Q z@E119kHTYKBI?l?9KTe;+!NkM@ zLd`?LLIT0uL&4jV>hRWjVkpjDFvm()i0>W zJFqeNbTctz6*i@qW$l`>bUsw4#wbF!fM4KQkt!j=g+?jk=YUyGo#W)a(EVXti)OEV z#8>K#ZC^5|Av0m^l5_f^Q89}xFXd)wl+3=Fi)ztvji*0M4G;zvlo>XRVPdc(a5*Rx zL5p$0nY85J;9ozy&``b#N#ToTgi zam4>N5F!T+{JQVvJb(Iou_6ovHj6mNU?Yk3Y^w(megIdLuYVFhtV@8ecV9oB2ZJ~L zZ)q|rq_i(6Xf8jEk%3>$ub;OnC;CPqLjmp!;4Lcyq&5}VF0yznZyE_!*2T6@pujX1 zGMM33s!PQ@J@Z(IAMh8;&!M<->m>5(NMMhrMZ5kd#8}D@(2OY~#0cCd2bXmcs%%2z zI{YgnFYefmL=CsQY;DY*g8v#q!nrK8*fh^5I4&Q?_0DNPncDm3MW#bNjaB@h)acev z`7dr#D0X@%o@XBiYIL4lZI{&~h}saI)hDyvwKPP=J*lM4T8>rn`o}KRJ@dlEI~IF% zYT>tL$A6M0l90TMjb8e>-6g`E^-=osS@*uMeB+v-UV!6D zB+F2(qtmMBMrY1?uWhi_r?q{Vr|0%>P(BLkt&+ui1UdK{=t*=1G!1+!E|C_AX1{$3 zt--U=(xg@Z9pF*hr&ESbWtuNZpn=4VN{IN2EpC{L1TpWx(keJ-s&~wrRUy17l5Lnm z7sgsSBRC8+Q!9eyxGX*IjbE}GSOVM3|F3Ovx9c#qw*WQ2!S{WS9FV4uh>F%eVCeC+ z(-3@nHhI1?e68yo2G(g-^4f-p^Rn}xG-zus@?rV**6^Wa3UiTnzlQCA%z9k$hW0z= z#xqAwb9&^;4kOmGn6@g;**10uOm*6~D0FKZ;S*y_JsR^$zzWlPt#VL1S6)Todc65R zP7JIAb`D3i-dtY>u?TZ>JNH|kZ9n~w5G2bNgkHmzKboIJ?787hbm27x25z~(oWhW> z8xvj^>IW1S5)>o9D_g)U!*$J#JfZ|X7gDJdXI=^?5(9l)_%;J?jxH+nA(~wtM~B!> zBL5xr7MCR9T#`^~Mxw0e&6M_-@O4>7F>2$R}%EgMnyfF4dI&V{$W;s`l};JpH- zeW4tvW{&1{U1UM+a>=6z^1=EoF3UQLm9K9kxE}tyMO80RE8c`ju>J6EL7WpR@guT} z?VY*u)^E)=hcaUB;+VB~EA8s#?%jr+vKhMrv%U*1;a`dJ>*i}peaHipZ<20wmXk4^ zq~sOL0$0>7HZ%-4;xDyKdRzzX{v%k$T_;mQ9mfnd7B<|tnx>Eztx4jnXkvV=%w~d~D4QyM z&hINOe69YLE>vH9VHesfBA-q%E8l--fw=oghB!!Zer%4^#~P+d^eAtccx~JOU`fkz zP-@|}Q|?4~`UJC^k18LLW~;*TiHEKmq2e-A1lqVjBzA@Z7;fcK_=>nqDCosxjbVU zj#`6iUkS1Mz5WRijh-p4uoT;&K}kw_RB`C2R3#UkxsJLu;PVKOV^xYcLoIB^PY7}a zRt#Ow=uQW^DG^?#58DXAkipxylQfPw_1kkF#L0K$D#-SbP5)pFPvmB*%edWk>eh0I~40)(z^B+)*0Lf0{x&moG0CtA#S zWr7N~WI#y5Qk03XXK+PiXs8X@=>-P2GN(Q)A!6a{i z5z_b{Zm6Rk8Khxm?RsxT3~A;Y3i~l!#*V+??*fyVZ_%=4WCf_!IT&Xn%?=63HRESb zPPwwG+W)Juieqgevd~)`{b}G^ywo9DC-@UJ7dtLX{&Ky?d^#rRK=D2UOfN@4rv_Tu z7;^l^rO1*M!qG8d$m->>XTqZCiEE$SO$mC1@y2tD5~CRtWBaZA(E+!5tx>2`nsIv` z+O#b;TY*oI-@0iRx$=&c@33FXRmLWfX+<*8rgfWjO$cyTVH^MJ#R0?!|D6>3b05(y z76H@lk)T;lLZ$ER134M^M~Pnx{3;npNVI-DI58o}pMN~~=@s`SiR0DPk5J^He7}TP ziW4YtgEbXdCzZ*a$s%TNwk>?1ac`mQ?@X>wxx9t3bc&2xNm3EH#oZccWwhgHRpKg> z7vw+8vbsvVv#hFq$Y?I8JBO*SufkrT~r{{f8{LHi5S%n`=KS z_&Wye;OgG(>Ov9CS0uR0mYg4-T;_C>uBhwXWNnYbkP2M;u4FC;(I|fP3r-)7D&cXG zKq5i@pK2N4|EiI5pg$kkQediA)7(v zGO_j@NGQnd!*>z*`oQ%CEan$bh(C}VYjoiSCnyiGh1{YH>^jm_BfOJ6`td999g>x+ ztVrfMc8`vdF=AS9!Y9#~u5T8AO-KwO%jF_eB~9eTVv<$_OdWDj-Nk?ALb2;4S}SB{ zZk2AVjv_0A(V<&OXQz6)L(=X!>$%2rso$E;j@72#dsQBQgp2FtCoGgK{T_YJ{RsSc zjRc}h1_%QC0;7%EHUppbKtz*NmzhX5-w_8UYQeU%e_O45bMH9$^GSkk$rAJF6@z2e zN~($}FwXA@cGvU)h(-9!w89_SuT*s_Q@U0+cQ z51y1d=SJwl% zN#ncM7)zm5WR|STbwtxEzvXbe7Wf^efztd4M_EU&#@JdcUs`;t^ zTI49woOuzF3AtlHr~iI!bO{1$?+D%*j@kFAe+k0L73Fek@PMU@@55w@hxqq=(bWwdz6`mYFrG1RP|S@-HF;rksiB>lbCSF`pJy^RR0}>c-t% zG(s$7s?NK$Smss6w#cc{k4*H|t5AB7+8@6lfpYlj+5o>p7&JxVUm2Z<0AP&tC>}>n zbF;fH^-xhg#Ol3niDI?li~(2wICc=xle}r2IVZG9m+-6&=l%gGYeP!W zL{h9|KI`R@_Y>=}+h^gmf>{mm86;#S69D@V)!MAuOiR~Gnl#gbbS~`S6h|pUR$t(Z zEdg%!<+iby4FPa{@?g6?M;w-8;(p_yGK?dyg8wh3Q(&P8tcFLZqmBysuLq?MoAd*? zi3R3%!xZMqefskgE9=bprq;q-u9S~+dT?6(j@}Ws)*e$jcC5!yVsc`Frod_O%tMt1 zRd`QV>nfyE$PoUoDuyNNzb7#-%;02zOyty%nx1lTSN=QxIOj6SX=u!zSAm01C?mn6 z&wUbeBOvgHXaIN)xT3nWd@dCGv1~`$9f~KX$EUIRz%yBGHc6huw44A{Le)r{I#S<` zxY-#x3BqBt-Bz!by>af*KX6k2Esq$uqq%v(`&b+y9}HHHT#F4_`pmu_?jC4gNAg(} zc9%Gxn@FJKwUj78(!-YCnLyNIP1_5yqB!Js6FtB4M;N51x}X0~JyGX@CrAHdukNyq zar(&Eq`iMM_~@u*WajH-y9I3nRrd`GgCvq(BeJMQ$$#LQTDRszAu9N05$f{|KMYoZ z=QX)FXUD2G!Ygfjp}EKfOB!YC4;Mqt2|8-(aJ;VzVO650sK#HYI;&uwSFnmikx=SzL0-iBTW*udfMLG*Bw9b6un62OJ&+Dr zj63z2p2Lc(*8C~csFN|x3`w(S7#kS3dI4^39l@860jMf_f0(VPm- zI{#L0E2##2`Ei}xLmAmoH(2A$J&Qf+6ExF<3uCZxvCviED8e^vP~A~4E7R6sAUw5AJE zL$`x%$|-#Yt?h6kNh;r7w02**S~lo=t; z!xYWVR~?V1h?3)Iu4BMZu%?xntZ+pLgPT+ExxrgTsCbZnOYC{Z8%L2Mo5NbUd!oU% zjTR?lK+?u~ar*R|5jtvIju?N19QQ&bX(!BkJpQ=}SdIW^ShLK^xe1=&nd7i_M5mB5 zqV>GEP1&!Ge~3&VM;fZ0hCnV|=0$_NLL`$M!k=W<8o|C;{zw9#+lI(uu`4(O95mBx zKeF+dUmX`MfJGgxuM1N@!U2(uZJDk;^?dA~mpeZkv*niNz6OCgJI4;?+Mp}hn-C$C zTy^c74G*?g-mmK370k>II<5rEW`j+h3plVeFwSuROr)!CYk5o@SWW6ano(4H-IWIk zGyxtDwNLI`cD-18E49@47e17FG+aC9a_wij&5esZvJS#$4cFxOVry~UbK^n1B;xsl z7EzBEV!+%a9-CO_*DkLRQE)-9&Unm&?R)rmrU6xFHX$Q%g0dR)pZ?1G#+p#yl%i-} z=KvMdEJB^7VYc}4IA|Znd{4X7AOC^{k>6R&Y8maA7(>F!4F*fl;%sgSDHgDsgNm_# zEdoe=l9}KiSzu+c`}9db!MCfRr${zd6E%;+@;aziGi#c`-#yx`AIj9RcBMs%C_OVJ zt3Jti@zF78%hAfP%`E^ln2^p0J?wm(wggTv_1M4aw5MQ@>07PN zLRi$5cfNn+a;Cv?xZ!;LOceO$i*WA0EQ7r5qn~h4WL{y5SIV}HpTGFiprI*ko7jJ& z^UzIaKd^8Ko!5CWKt`VN)vY5C>10U=R?N0Zmp|%T9{27|umTIc>8SB~e`fw`6$<&i zRqBBcW;H-h!3BnQpv-WFq4R^FrEB2$Y#<`X+KkSYV5P9`yr3s#PBt6G*U>K+VBW50 z(lukEWKF$HJXrpKpYqWFireqy8%{Jsa&epP3iAL**3)4w{rGg2MttHcsR#UUm7WDx zqs=cNnaVSsN>}Gr?Ob>{HabzbQDhipaAGJLvuT#cWIWCZNi=WP^6lK^Yx#>835CwH zyt5y2UxlM!3F;9{U-ed~^xBuLs>!Uc&$U>(^`SSVI;L)JkSuzfLltTzpkOS?SxPaV z3dghz)od^-Vbl<m&r>&+vtyt-NqUkV()>@JvrUbkl~^THzzTR5Aijn_+rA|nqy zhaHJ-ecaX!h?nIp$gyWc%GbQ&$OfUaIbpJyKP8q|C>)?`sB&W0McF( z)1XDJbav7C6QM=(B-P#nKM_`C`1?uZ3Odl`eT!Ft3+m(q%E`wWF~GRFLOfi#FAqUQ z-%upkX7xWw<|6tZ6nHAWl77{U;6|gv+M`X{d$yl6rYMI^=he5tYZOc zhm6KZSlr>@b$M15hTya;7k8X>!c>kiQvGaNGnI;T8}F)wS@#7d zN>P41t2GF((&4WW*$VitLmBk9Xs??G1H1lgMq{2ll7FTnp>r7mtQLp!^$c4u=ECCN z-l{yVC2i9Y?oG;?YXF;kqbImweU1&D#IoO0x8p3cR7LVpeCTD?La$c3Yz zJDMxoTm*=m?v!IhB}AFfHRPf7l-mc%8<9{JAiQ@Louos>qJPN&FzBXEo0}TzjX&od zYLKGu|IPC0^(V7Rx)jp%$I!qWDpW2W>0e|?8oRNhU1Wi98KTNJV`-S8du-!<4#{PM zmlQqRj?d1dFI?W=K+A`k;LnKPd64VbP#e*j{GrM6ZH43U>lT@N=qk7jVi@MJvKzkE z*O+plP-II9{MhcZM>i*vaG)mh9?dK!&W{G1bW1gwrhUDk$agn=`J@oPrJ^$q_mtiA@P7mf3;O!1e4aT^@Fa$O2 zU;<0#L^#EcGM#RXuE=&c9_#nPe=TRMb9M1!o0DR;E#QyF z*FK~@AvI}v%ZB>P3qFa{)3ZCA;nA-&jJ3yTJEXA>al5ZjW*RHhp~8b=oWDQ~0!ZPP3LOq?iv^HH?!!`wES=Elo_OYtDP zwp1+8E!r)4_yRsB>pCT->)@|Xqq1OcD}M5a_3&wAjS1ppB%Q**Q7uN-<5p)Hth}(R zcr%NhT2_vnASDu_MSd>0A_m^6xFQIPM+p=rnr5gJ?|OWl`6(f#Xhf5f%@mN#Zjn~C zy@6j$iYQP*g0%k&$p|#ClpNaHWwJDPGo)oRg|H#f9luX(#4h^)nm{Mcv?cHUoKh>A z{6`pXoa2ELDyRW|X#oFceS4SBw>@b?qV0lz5!;=)hNsLZG_;Ti{B{^L2Zae`3d_e~X%1;KfTk8ue+H^8Av89~0(4qb&tLM}>D?PEFED5Omx#|?`HL#L@s3Ns|)r$z0Pad%;MlKoTP z>LknAgY!f?#%Gj1P?50)Cv*t|ge}pRFTv+#Bzn}B@&ev#N*uf)`RnV6=1RNQCE8nW zGB9S$J+O$UOp3k45$043>r6iYc2*TBs63k!YU$T`EQw_dAKl)$=o2NCsk9YKtUVzm zHHtNPC-U!@HUHQn+-|SYehmDF3k3Fu)B5(#{*4UZPE7pc=XSjLR(Ew!bbEtrdVaI( z=ju6e+zq?b@$@q2$kRG77+k|4R7b$3B0GbwfMNuvb>7Fq^i`DBb-iJ2FHCHoRWQ}H z-2mNfE2xlhPsTfLpk$L^8sq6ID=toJL3B6JR4=uLiq$kEa71dkXgv0=_5!1B5*dCY z^GP?@?TC3H{$&$XXmYKI7ytkaFw=cg=tEub>BFy`6-^WLUz6xZ&^V@_$W}g`Y>utv z3ZGtPrZJeBIqd3>D*ab|0-kmM5{euC`(!1O^!v#YBEr2C0zMm;xF9J9zcC-*w#06K zExBRcCv;-i`^k}-ExvB4Sz_x8FdcnT!tYvK!AVfSelL3>%wq6C90Wo|V13F7zoEUp z?S|e(>YdizAp!{rtEuzP3qrnjOOI%ujcM0tQ+gt=ZKuT_zDRp%gWGBsiVQ_ZNh4eZ zF_hT}D#$JwZb?R*2+4lDpN3_qQz4x*urb<6vS4I}3`e{hDB}*6?*42<>>YJw(7ScD zDL-8+0V?H;Qz5&)(J2XU*8JoU7T5caQZWzavki+Gh9o4a;OOu)x?2BH;_bJ;pFFs) zL8%+VwnzgCc=0A_o-jwcd)5Px3i1+Wq5J&2feWH1F-~NHU*L6^6x2Z@gKABPc1A zM{7LIdzD}WaOGHybxP4e9v{2-yN8-lmE${!D)2ef!*`h46?!_yyk;HpKUn^^P~T5l zeSF;$?#hQJO7^rx;7Go3L{%WmKl4eF@3?~FnCSYK0;5s}f`h`o%Y7UZNq2a=z$WByQ( zDSOg(;Rab&_M}oyxK1v$Riy81b=w5)VB&`J?k8Bp^{>@qQII4>QNDVDKT%EBu1i#~~l$ub588lQtw6 z3p=8&B=qhPNWTzOnhHh)P~4B3u@0F)ys*dbYR30jkNaEnEUk`83<0PwQ$IsocJwmO zyTYDI+TgRmt}v9jzBDd4ifFUWe+yHsxmaN~0FK{ZxRRz%f3iyq9F2NzSwny5X8t*eg%?#X`G{p!WShlu}~mf=iG-Oq!!XXIr@mtjtd^b1`um)5d93Zj4HZ zKmDGCVtXJ%LeO2h6XGmpF!rOI=`7f6KQya^r{W-oLDRa$#CH4mw_2uJgD4LMEcy2Vkq{$W5bU_&m`j~My zhTR&?OJI)_jZ0t+<|GHjE}eW6|3I|k%ryo>lN;aCG*+^~xz?CR0B+~!3v+uJWK`%I z{1P%=6cTu$R}JBbld^Kdai?wG>hJ&Tx(e_Va+#NV*nq#brF3oXWbjt~gjf3;zXadd zc?8Wf|LcVKYF+gfec25#&|PWK;$xC^WVPBRC<3wXuJuF8%=d}qtK)t8-W9(d)d3ri zI3ErS^$Q;)g?RHXc=5{fRbrB7^p$B{EnfG}5nA21UZpK(lo#H`B&XIB4)JzTl8dVD zByaiD_?59dCVPL1F>E5Y#u>gtr@23*)nb$*QxzFAjl@u-l`X0>B2NMu$_?*`9DV#> z#8P;DLn(_NK&@2}UI1I&CnUtYjVAs`l87 z>4uFT=TP<1v9?;NpTOC*WTQZ6R0O&wA_G1embqli(OVC#23OsL$v|WHPYJw~G3y~M zd`X5d9~y=B;kT%cGV>CmyhwMy95nlXr+TJY6_TgjC=0O{43~y@v=QimrOeKfWR>6J z$Nh+Y7LA#N?@uVdHCz*q`?s!nnScN0%CtwV+x?g9LnRCk*=c_(@7bNRb9#`Rm%Ve0)e($-bC zh?qDK=;Z%)^Yy-&AFCstrTqRg>){EzTi){lYK2|rGE;+^a=crpqhtXWr=&{=ArwmX zysR@KP?OB)o|?G-boe#XYo2jb)-`D!bq*WzrI5*P&$S{hErVm0%bIZJWm86pe=7h)7zscqcH>h8Vek-bly@`gIP2O|2i|J2(#8AwF&t-OAC~ zsDAku)qJF(BZOpKcUXzt`%QeixOxIAlx@TL?O*)@A8&qyN(bQ$llR zv*~Q=^x~bEY&wKqZI;_9yz_I|ZheUuxyvS(Ks@6w&h4(f; z+WNI|;mt8#OTfrgr)qpG$T6-P$t>_Z*cRZb5|GdkPs*&h(#<9wzJ9xC69 zO4557EzkP6hJ zQU&c`R>GFI+9K4mE?IJwQiSg#$tevjOR3Ng#dSj zx@YE7m?744KkzG}ROzqpw8I;_$|gt^4(m+4;>((gMYEq0(rSR z0p|@wIiPQb)%Q>YP69q_+2s`R=lOXoo$oUwf(>z~ollO&j7eC;a$v|}v&<`N92sqm z>XuYoIz%L02s)3k?E>neEYEKQLIzOU=Ef+O*;GF#_RH)5#}F4sUq!$uPoTV zOwd2P-?f34>&a-hPt&3O)du}u;~_1&lO?XGD!-Yrs(6LXxS7rR8Ih?Ix=F-Bwhll< znR151&AK|_;mp8hyt%oEC6pW>Oj|OyLyySgIfF=sIsV8N%_1*fm9aYnu>1dXAF9i08{R%r@2X-KH(F6isD8TUhs z$mq0^@xpZmkyv6?VTQETwCFEU`nr-KNwg8=tu0eG~GZo|J5tFEB;H!}3Ws{)ofz%io{@*wSzcAlYQQ_3m zLA*Ksl7fkQ(B$l$ktpv(hPHE=yK%b{kt2yE5y5(l*YJG+rCDEu3qBP{N>lvCt|5^R zY;sK$Ovl`9(Zfr@W#rW@;g$5nNhyBwmD!Sy$?7JqbN{aSh_CU4Lx$JPpwF(qBtK%` z#;iv{!BHcvru06b?A*BNO3f$ntRMul72{)a>D`2Y%Y{@UnF$xkG zCXZJ}UOqlv_%Fcz;<+bsO6{gNp>?^Q=uh>%i_gS*@iodpC>psoiz1{&`(+g9-`6;r z`h4_FOthd*v6hP%^iiIUqve?{tAWr{!KVV-m7sRUfjyXh@eE7aX*E#tG)@Qg7i!1D zNd1O_H0nTU$7JPc#IN-vRX}GcPa>aI8Rlwmu?nd6dYR-|x`(GGhx?OI>xzUMFrgyP zGw>+K^HX-D$TQ&GCb8xHVvKHFPpRj%46CS?_AHFMQU6+o%`@{{#zlI|~ zK6P7e{B2}OR&+v+u+UtIbGI@G+ z``~`EVQBtKXZX6nfi)30Cv+I}r;vSBBKm`}{$+i#~6|qzvpZ~}4aH+oM)*UJsijI*< zLwr|*7<(d`J^!zDNEB`hmhs9H2lrkl55UBk;fC0}7 zRK-u|!&Ii@uHsS~TDC2eFR5$2`~lGO>%$Hkrr1eNhs?(!%E?SS^a7E4?H%e5!ac#@mfAErNYap$a<5#IW z8$;2g&AC;RpT4yGnM^xF^o3hPmUbDK;xxI~b9F&fgE%bBlEvpZ#3hsJOKSqQi}PF& zt*m_XYWcQe$PPq`^Fmq9Zo|kc(TSK1!%RY>clBJ7XLzg^{8HayXvDck_A}z5WUvL~ z_@DLq1~SIORkJwji;cV1-D4`DroY3)?t$G+Mj)mIz^u7nP%cUKNk{=eQ$@uwcSUKL z-}6>^6gM4(7zQZFFbUo=>`>lvuPsYkSs9KUwg#Efy;MQQwpt+xo8doOcc? zWRf6eWMaX!PnUDSM{he?Hj2GWp*)VrufxDd!h z--XuYl>$Y?&5og(`C3O-X$eiK0{bmUMIi-{KSw0V_VfNFsMH z2O_(A3YT&o`PHTo`k`~Ni9pGYt1XNqQTad}v(2!r}f_hCUSGr*Z z(Lzzeq)k0R$RXUUlgJFok8x22S8-=oU^Ki_^#sVPZljS&mL^lTR<)n(M||^G#+Zd9uJjO5Raqp%?~m$=@?(Hhv>KW2R*ir3oHj zVcP1L(MHt8U_;uoD<6DOtHgUWNc~b*W5m;R{5J7VhGeF;GJQ7TT~Izme)Kb|T(`e;mW6X}`##v#u>WwF<4|0dx zA+XHTQd*~KR?Y1X1Ksf{fBv9M2|IBw1co4ss!a^P zQmt6EW|2nyTr5peVr4y@cC`kHP@W*Wzp|aPZdb(Z#6^rI<~KAA?NQKyuto+3@gsxOV_cM=vgKTJS3&b z!M)t5$vGdTiE<#Ui8Bb!Q&%*fa$K~D*MU_D_8#e2*ICtSOl3=L^*;hT>vcGcxO@D0 zTNJ1l8dZTEk1WRZrzK60aii zf`Oi599Pv~ummL(D59k#sJjd(rr<$k)G#*gVeN%doQ>VsNY8w0-=0dZ>4O)^Pl&*_5DjN_wnQEqe}GJI~wQ$GxWJ zJhjrQKeb_znvI>Ny6kE4jqf@V6Br;6#pbiOC;j?m zdTw!TNttGE*iU3Zo8}*6R(hl_H#ms5i~0u8J5{zw+uZRPW`c2sHC=1$eGa%uce1w=;$q9d_gY9-Re3?(Ds}vBDo8tc{JH(H8=Qz9ZkDuW!z$|*L0Y1^*@DY z&cD8}ON4%L>(I^p&!dpuV+#`C0U6BVNyT6RH@#C*7r{JR`u_s$J$+>XF+rRU=8x0xK03OB zIg+l8@t0gB{hsUM9Er|VzhnA2|HM9c(jrRJc`Vb@c8}Y8L3L)&t1mWHE780nMCY{P zTSb2|;=#3*?T$&=e6Nwuaw;kop6xA%mt(OdT-4(?1%do0!Eq0<=S|%JMzb_YyjJ1Z zwB{|k=B_7Qnnw5x7w{G;od<_bge&jHpv>H}fhsjC#kzQWTFXQRM8LNV@h6bClS>MH zvWlFn!g-A>p~qL*QBfmsbFi;!(_Q#KHbGf2 zF3E6TbOwG9!kpTcJk=l}#bh$S1WVK#1PfcXL`;DKOPRzT5H_Y}?ARk*>PWj*rHuJW z^R_DTmVIlOb=tV8ZkcGU$u)RmsRX-^s#8CEqfwsYS!_azLZjbIgKpRxt;TlezICdt(ikhIOaJ)4(sTRNyfE~OK&|q!a_a>58oC@mq-=K@mJa_&jvHOc*O|a62`XUEYnW;?MJSCDZ$Qe@e&#nVlFOuz_ika~GlZ9l&7dhxSX^-jG#Ms-H z_(?gZn~(1Auy?EnRm@2*8I-$4;KT%PJv;L8{x-N!)*t zm*VwemACuL{qre8yHB#>3Cwg+77QlTgb<(W%50wPOCQf4cg5CsDrs*;;_rKw%7wjR zZ0nt8c0+6&HV?de zUHAuEboTK6^6^qLaHeWO-}AtWJXxo_F(%KWPpv81qjl&JrEHusuAu#Nha)^+L97qo z+f)!!l?yuy@0jp5DLXU>*3k&!l!=O5-(3wszFl$5sf02K=J2$!9auoOJz3|Yd;8(( zMgiwI4`|K#Jvi|F%P0qW^Wybw0C<%gOz0`=wAW|-{ZgzXR;qnP=HwCJ_0Hkx;Pmi7 zA{mX3>H9$<=jx4S88~YcE>pi0CJ+oO6kzUHf(OsSv7LqNy(84>47AVFsqKyg2WkuUASryyUgYn1SHSSUv3i(E#M5!y+BRo5Zg z5u*vA0zG(3OVph3iN4Eq&$9c$x6tF-DU|B`6VnV+Yd(z1aX1{z^lhNb zCmptCDUM}OM^#hC;%NtGeDHX71w0X~KD?Msq>6<&GB7=;DJlAdH$lnZJ%N8pgU%qI z@(;Jo(EcE1rJFe?(F2DDedq{^k;;E z+^O;hKv%HNMQ&p$H2p@9K&k$NZZ!np3B70{Amu?z*UQtO|JC0TCgitDG^euTa=7SU zR$kpwYS6SO$+aRLm5||F8y%2Agsv59N$J2$?gPvy4HNHV${>(VbCXO1rA&$)FF1u{ zx^WChD{7VUvKgKu?e-m+&$JN#2!?)Kwg|ab4LU|JSlBOq3l5qs;k}_AnXW-XRvyN7 z)aXPe+)HDA65M5t#N01b6++lEU`Bp=cb5U=WS`4t;LZ?9%;ZcXLX{_7Ov@+psmyy) zzHt^yQ38%Lw@bF)*@H2LCHX9s5u}UJ<|qO%1MVg-gT8Nvo|JA`Ir)=xFK@$!!ABV7 zHEuaMd724bi#0%B(jc|MzDBQ9^$C(g9Ao+d%}FcjsK@)#*CQPa-^@xFmPH4}nSm&! z!5F;~J?!BA{1AS0i|~<=WYCp7vieO5u!sXl??>w_$dak5o|hq~O|!-_nt6hm9Vn=Vw6caaQIv*5A|W0*&_fgnE4mUe|v51YNyP<_V++``Ts|SRCL+F=*E+a?!A# zS)NNM9O65s+w8f(zP-}6!K8ry2NumK2P%)g(wSA~QW~A&31O6c?jE|2c+SjV;c&&riDuWq{A0HCS-M|#3voYouQb$1<$IOdJBi?uf?I_BL?xBb5ID&eMo<@t5sELEG@v>4-3m4)R z+Q@tHxUX9?u06iPQP%kPH#-U-5%Ofg2ziejspdJ&r2Shv7j|dUFsBXh?OSr{{VhZ6 zRI!chc()(T)JY)g{z6f~_!pJDVt2{Mb!d9;8NV9Fnk9uz3(XPATfwtCR3=S@js?`i z{m0+UwyLBe&7x+k>giZqcf_(@OhH1XH5JyRrA#sMnCl#v+dr0zr zhEm=t)7zIITWgm9J@$DAm_IhV+i~5kxvBo;H3*aQ8wG7YAS?xS?wPhk#sjxIt}ftP z->z*7RO75oI?Bm0^zh#h{TFVy6jcvM-%5sTHfFn9W^m}H9&|t526y=tm;cOcZ zephdTLyqCB&vH>^s}xEUP9KTVC8LDU0R|bhhOnQPi;{eBQ3qKINS`hxqQj*ih5FJ_ zmWn*&v%J(_2FB0&YKLORxn1ojU|>pa7ZbCt4y z4BNRxI-y98RnuF@B|0apH zg|pi)kd@aOmFK>MYo*U9Q!Xgv%5B`>m!$5KDoM_wDlYm$&!#GW?>0Qj z;9tKD$}lVBJxGIgvXdKv8o8RNK>v@=fv3IBhZ75TdTX?m<8dp4mWi@E`X#5mJfk{k z;=FM*9*&`zK<2_smZlM4{XY`evu)kIjLuM&;s*6bDmV}qC#7PX|dTJ9w z{^s15KL~9rQfJ-xG2PI*W$2L=WVQmpBC)6Cf4GKxvNwtW2DEZwS8V5{jX z%UC2%j#&|dP`k|4lyhVl2hKzJv->ICqSQrs{C40op?5p7^G?eCD1H%Kg$t!Pqe(}` z<;axd*BPHD9$q$S>z5>!44(S60}S;dD+7vRD>#QI90kwpvr5{fp#GFV*vjnt#3!5b z<$^lR(8%Z_;h7b-`XJoeQ-pQqI&F>vEE=X{BdSIh=6DrM*VTAqrqbD12Ml8mBD|kA zlx}TBkrx^Roy7^Sj3Pk(iNQsdSU;MO<_cS-C6vTXWrGwnj|raXT#kqp6NrS-8-+<# zL~I3@EX<+HNjjyNve2b`mB!wGYY@-?H5H6b0pBO#Hp{=B$z={wxt&BPBl&O*!EHn+ zWLTq&moY#gWWgGPM1vJV{%Vv!@J$~{1w7bZo+TZOG;9mCNw6?BE*<_=KaC(uQ;HiRf5eK0Jst<4^r z+_&Qk(kj{U?f9<6Jw&i181ahadtwaFizUr*nIaZMGFj1!5LrgHpWa^2hO#ASVA-#v zgD)#6*rz5%M3Qpx4LG`1Zd-k-=>$~D6tNh*YK)`mnfi(bJa`k~SzPgVphj?u1~l*a z(<(lUxvfu>ga{TLqD2dYv971WTp?COXqShLcZ?PHFYotg-LA~fV4Ys&wFVX`N=Y$P z5`O)t`+k1kI+v3jsT`DL$=?m(>?W~fTo_BFtk)Ptkw{tTjYNARQ3GxyyO`8dzR{hm znRMT)-q*#&FB;EhFgPn*e;d!V`y!DF>?kdED*O_rLD8qN&Q;LSG-NYuw{g5W5RDwM%r5nfT1}O!#p5BfszY7cL@JJhW<|W*$Ko;k z$3KmzruY0d{7DZrzZl_wmw)N|F@>adl^&}&o^40cy`pK~(>EL%7+M{6g?G=JI0%t3 zz&GYyqBopo1t&R9f}KzA=uL0v-N1lGn%Iw~3r9-Q z>Bsz|W{sa!bO+8zx;p%Zy66`}B?osB?+WqXshw(0@Mr_$H?^|>`?+7dsvMFDhCVyg+GemAW9*gzw z3oy(s7Ing!&Z1&o35AHt7F0830sl7lx;F0E}yU=K|OS~ z+JQ9-5!*U2Y>b|U$MV1EU)XB3+b7S0qQq*wTi8I&?e+#1@nqzXWpL6` z@m-EziWMC1Z!Ekzw6_#wqGjW`++U_je;OtKNr89hcT*9vZ>ii)3@K zb=6VF{{1;0qe5C<6ztRqB-*xKlD449bb&3lM;V}8yW|Orsu3?4!H95%EoHPw z3EB6J?1-B?!a)B!vO@=1_z$uJuKE|*-TW7_yJ+EORPOmkc8qX&HTJ18l{JFa|9fQT zakQc%@SOeV?b0_{^nkMA0gI>3A^|X}7lys8jeSY>Xz=SII6P>NEcIFdQtWMF2Dv!s z0oPOM$*pqhsyx-JC3OCEOe5{k#k}A&zdM>ckjeWAE2?9$^?%nqftb6v4DSnGW2cmNvPH(8+r^FDJ$!JQS4p3KJ`b~Aw zsP_1AF(6+8^mC!_UzWElc~j(Qz(6hSz3+e>r*~55>%XnKEDh`MrW=-X4ojdMw5QC* z5hd<=FrF&=HBNQcj;AEg|C?%zO*K?>IJ{)-z}B-ighe@>=tPXL(2CA1NlZLSd;o1`{)0Ls%_$B^EK41d_6|F~$K?#^;q(UM$>nPa z*Rma9A?3nRae-$Y;IQmpRuEbYA`5}Gw&Ze7{tVAS+c(0~b%Nu_fB^sJ7U8lzwl0Ux zp8#?GVW~3%f_RiCc#va!h(>ssqxOu@!oxkPwGXNwUErj9`O5w1MPwXG^B;m&(FJ?Z zqWYPW`%;-bxM4$brO9XrA{ETfrClObabs5gs<5;+#`r#LdPrd0F&EV4X&r!P9;FRJ z_Wsi2bqkYeOR#xw3&R zGY`=TUW?C$Ojmepj2lrP5R|4@66-IVSnaI&L>+tLu0BFxn}>|tY>rYXVoQkyw9!T2 zr%e(KzYgREUz(T<>#MzOp>ta~uHHK>1@lTwYQ&`PfvLR&LPaSq{=di(4zb7J0)Int zv*m)+%>6f4Op;w3pVYGN%%C&x9dmt_L)MXO!V{^Mf~h^R%MNFHr_NZ%1Dgm;=%4c} z$id~hIT*$6+bVfodRrW(ji^HA`H6t6tiM1W@kPcUlpqr8A$ICjL}QuwEmrv*DBG2l zC>kBimLwtP>4VbYs&IjBQ)k~PEEB6Ue(tnBg;cM!mLb=MPXVi6R#!9Ewd9-4)_Ff! z+-;+t@wjYMQnV!wfFr9yW56XkI_lhtwNsnmxHWjW=AAChq*p!0DsAEqQ&g1%qhS`2 zo@Qr&Oh$*;f`TjWYMn6JTRR2E30MMneB$v@sp6qm8xCyr^*Kj73ugF2E%i@@L|i3y z|Dk}A7XMPfcuRfOma*22`T9MJ?lA>_L(i^+qH1R&nO-xO2?;!1oXwz}uT>TAF4s(d z7;rFcd*_qtw`Sxxeb)|An<#s^qTUBM3hAAsGS$ZgAYlp=m-^aZ{M+{%z;|MY81(r+ z=V49L!0#Ml!x}&2ch6+V{ld}5$ z`GO@#nqc?Wl|aEQBh2$~-e{EA&vGY|c^oHgFsRqb)z$U!@=WFF>$~hZ{*}w;_i=C_ zfG6O4|L1f}VI~Lf^RcvWWJH00;1ddWYb=UJVHnKxC-?fR&eE7KS?s2R2Tafi zAy6?kse(>C)g11Ma8U*w5|n)dSB3>=45CWVhC*#@;6dT8WCCnx-jTL$A1NOYsY*QF z8)#}P4T5zk<^o@N+V~6u*3^8#uZ=-mbC4oZ>BZz^^QD`z9X4Y9G!FSLm#B7+rQ7>v zxvR8ctRN>iFF90=q=;59*7J*Fw11lm>rM!OVi(Se=p>{SGHEWFAuJltKT*S4$|Xgf z9yE}o>%jVwEUA`Ass)LU*k!G3_{v6MPx&;&es-%eBpn>Aw|@fnd4!16K394LK4Jx* z-(OEYWk3t3{uX}0H#yER=0mgC|It5O-luNM!#DJWJ#Yo1)7amGCaV)eJezN-l`VC08wT z@9dkpf=2e~8tywMT5yNdmMb6VO0l|G?trs|+K(ADDs10r3Y%1-a1Vu+(d z3K?on1Ifg>tltap6)+hnUC z6My};qCk&R;clr60hem2mwj*5C9R~%jfk{AZktXMbo4CZ+E3nQv&$O>oC0v9ho6aW z6AM?ppu*&K4r15M&YSNhdz~}Nuz>jS0~nrH^+O04_2UMo>9tj2YLg~xW6q#DF zw7b*7I`v1ePYhY#<5ctXMXcYC3*ndch6?41G2Yc1QJ?7Xd z@3Of+b)q6K`pB!DVi9wP?l*!UuoNhmSz({WvKeF?x*Km|Qs*3fF+yXg4Lq5Wz7uq$>;bJ=5s4MU9K!q}N? z=hDb;rW!j5m$+W$@4Q;$u$ZZcZ?5hQAbA0_kMl=aQVMpKoD#Xm2~6C##AH?FYr3k7 zRIAEH$wc^Xdw_FZ>>g!2BS^o+LcCMy($AVNSlL_U?2fW&WbUG9(p@N-&+xo-I5z^> zyKh@2;3obpMsRYS@)}-rd}72RX51+;vEF&Dl{I54U$HbSVRZls*rh3tJfL711r5&_ zQkLY;X}Y%1bg|l^YyJv;Q4=wepdZW9Q~?# z?#H>B&^TkG`~$~yj;M=l<$?jM36TD64R)i}`BiUbHL;wjZ|MSl!^3eko5a1VuLLgI5&(WI5kw z;-bZJ4;JmTIxg>Q{cxSq1iqDW?5B>+k;^t=JP1T)DCUqXVnASnVnYl@*3i`E=)Mo~ zwAYfP*X@n?y=33*;*(N>dUo01hS)I0(l#tr&F$XEjopIM)v#_dq8Qu~)C&;zqB*OK zkC*o=Dy(2+Fr+8aib2+VP~45t%Qx-JmW(?Az0G)Rw#rh#4fM>)PckBQ)~V%VLIZ3} z!t#rUvg;)JUej_#Xrv`fgRB^r(#w%32(2X7Rm-++*3+mBT+XXK2vr1U4SVo0FdK%j zN;yTr&wck>kMmAMP_5y}1+|({D-mo#%Kbi9e!7DNcp5cL?XK~EF>kTYb}K8V8!cV=+6$G1410&itNSq=+0=@X@rrCHKm36IF!Y7Eme_a zymPoDc*9wTRr!P@Ncm$b2-G9g{1&>b0X$$$8(>%Q4omk8?gi&22k=B62FmfIi5wAt zn;@Ko?H`kM0Ip5Z54i`=la+B<_&bwp_2nM>E_e0C#IpBzX#5CAzC}&nL-WyYdtqgn zhjz|46UzoMC66>YFp@@`c0(S>#X;D56_(I)(9~RFWHy{!p_$XQI;?y&j4;}qk0Ry? z8CWlqj!sTkvUkD%9%t%Qb;)KAspb1@x4Z=hrDQ6?$dh!~5Z_vk(I!NBB*=)lt!Wd^ z>w1i%{)Gp=$4PVygZTi&bgYthFd7h}<6A2Zdns!~h2lMMVhcFJjiB;#j?8s6kiAP( z`7phy2UYnvL=~{gbztyg7AA^j)Pm2N(UI<0I=dcQt@YE$&r1RR@g9XE$Tq#J?8yj} z`l09z%?dImA|18r1W+F5${A@f&55CVt;F3GtTy1nE^-$u^Q5uYR&pZ5-0M%pBtC3l zv`tfa15RMQ(jQ?uuO5Dx8roa9IjtGwi!4LlU- zy+3pE$=(i;+!sJ}9DX{KHkZ=0)#AGGVG$RXKsFa?05cnzSdkFM7Z^eSE1Y_BhUO(4OwhoKU^mZDPC!sXrIKP z!7<1Ph(v2~x)k>aSB<`lMn*Nv7$%2B4aBrp)w3mg4GM#qY>WIAON4QymE>J$>7!7x z(|#$MLru8eXX>i^Z4TP!yrno*9!HS3S0TfiDh#2tMAoM>YHcQMp27c-gu zclE$aQ(rf?V4f`KBw%b!R73X{_172zBc;cTKVwrMUfs3@M>KbZff(wr@^9xV;|mlW#8BtikpVI8;_W&dUL( zlflx?)P=2yvum>n*89#>JI{e`p-g%v`BcnOnW*R)*NFPRvZ-wyBcCPoPI9T3HZx6M zo!?)czubRDICWHVZmDHmQ_H)f^=@`Xs&Hl1R^v{)zdSB|F$q-Oo9Uo^omGB)cis+4 z1^6l#zqsDc&Kk@A&iL}~Xnqn{bo#~Y3&d4#b=qDW|Jx>LGrptv5T;@Cm$Uo2 zWLLpHwU(%Rm`dKDr_4!;ikB7P<9okKA`hgsJY>*UUQ$y z>_mfQ%QPy-t0pKYymtIlOkhd`H> zJ5P+0wD;A=@mvoj;}FJp`b89tY3!7Pm*9zg(>fxUQXfCS_L^ZbFCpC+$T;@!v*WHu zo84QKz%96)@t{sL!BL#kf9C4!b+YG;p0;k}U;zaS-fCqX#fS9_UG$R-0$C-0QYTin zb5?M_?Ww-AA+A3mLmB8+@EXB#E+`a+(I^db(`)ud9CyTGp8%HhY75n-YFUIjb50icd?;)JK^EZXP4BW9Qts|rF6h;ul! zy%`h=o$jgwvrrQdp+x&1AFsOrb0|Ij`tc77C}bM!^nQ!B_mosW@K?}pQErA(tLy2J zv#Dwcwu49hmttg>ae{)~GcvU6#nBfgE6BAAFZSNgRO{+~Y%A{t?_tMmeMi#c4fboBfNQ;6rh?X!}gGCB~}8x(T4<1s`U}M@B?~IYzWW zmY;Dj0qVZDmgl3FeAV4XPv`}e9Jft+8uUz)H``Ttb-bhF4Z!=FcAy&N4;sAvzGucO z?Nx5pp`$32YFb)_7rq$(v;1})@nR!-VzrkuZ%W&WCeS972@xq-t_q&~=?s{L+ARig z0q}3>1e>cyaox`_=f5kbo&T;YmX6 z?D0o?Dny6V|5B9m3t=b6war>EP^HND$CVq-`7H|6m9Z1QQ9w%ud!|mrbRfC!hmwXC zxNfh^H)@jL-ep^LpKL7Mfrk-|{91f|s+hZ(ze(cc9e7;aZdyQPRp@JX($ZYue1YZk zwr8Cdc)vG}T+2Z{g3{?ucN8$MusLfh`)HssC~ORU zdn(>}=LXwdNP2p^1UH2#Xc~=077>{*Q5GH)YdH$yF#(d+w?hdfoPm3PYTlA(+fo#E zVS)$2FvqB4&M6#*e(*obbaYSY4Aw^v7j_PTQQDHWB>&%Zy6h2sMUnppoz8BCKp*5R zVOVlxq^wt-VTW78;i;rH!>$P!f@_9bOq)Ryr$N#!(59(SnhvhAJp&dQy}q~s+kQRy zxRi&3SJAj@7?OkX%&KZXH%_oh@@oDg%9Loav%WB(e_@&Y_YQNY{j#6A@Y?gzyHSbo zngkjE@5cD;bL%#x4`P=&mbq~9dKo5EDqID#(hqvZ1XTF+68EH=+EUL<$)jb9F68gm z+WYfyi=EHh=SD8D1Q&~xJMGwy&87oyuW2_shvki;yH$B^9v&VzQDJ0j=v^R5F6)H% zwVPeBKnKNwLr$VEOFSe%0D=F`yAi!onK_Q?V49nb%0zs~EA(orI_Olg-5USIyx014 z)p((WN=v+uf!;VyDe4m1%iAP&LGQ-r*0tmM=T?pFdNeN)y@^VioH8ZZj9wE!7^3QO$iJSAPBKtsp zmRqy6vP)=eZ5*Bq5tq^+YjwRD8gm;Nx&GgarF8yUqrb!?Z-UUG&6I^{-qUEoQVow| z2E{m79MX5wo|}}1lC;H77{@0PUtC0`ALtHWDieVPYuNW3me4OPLF}W)GVOZkQGGbr z^-qSlRBj~X+VT4L&ij@P8{Mi1icd3H3OdyArW-R*1~w~OsRu~z8nL)|6!|!T6dOPs zw5$U5=E=#SEPzE0Bldj4>G=t%B(BP(tMoQ-x?0>o9u(GjjLI`S(oCK@Z@Jv1z03xCk( zbZ`A-q8<@+Xf-gwi;aHLN0HsQb?=&yI?y8~g6q-f;1)Ug2*BTAGrHwAF<_z=JWc7T zG^^R&hWf%$>`E(j|9w+F(s~`dL!QPW!L#YjSya%}<*oeKDKemTDy+&Z!Ac}j!qApW zV(u!u4;V+5%v^`fAQQy}bKEB<9xu5v6QGsTn#9wZRQpKNC?r-H}O7Nv7C4>aDd z-6cgr>8P{hl(D@!;Is#WvYW9h^0fi&<5$sS0IKN?&I#pVyHN&qWxHuB5?kfIlNkcj zl!h!03n2=@$+X``SI=x+sl8_}!&O3*6W`tB7F5q~1<0G~zQfaUv85wnzaS`VE)Jv$ zw5q`WdPYb}w4;k;XwjHls}knevijY^*3?gQt=7*+q6k-*gLH}&uti2akL-c716gf_ zJgA;Z-pPojIpi=8M@NTpm_9Ymxu zT6Rke+?`S`gVl~+X&oGU{|?axqna^{TO!!`iBE(!fr@xh01PdZd?~o-rb1h-^at+4 z)|4Hpuit&n+G%=pZOR&eO8wkP>>$!xcMrS}U&AN5tw2nkenWjF(mmlf(8}o`k#%u1 zzR73TukK!no11vv??-F|5BS2syEi9Cv@Y#@I$snQ^Bf_xNYc{7C8C=GqG@=w71zz{ zdQL4}h|a zBsVuuqMC`yiOWSlO?8h$b6IKn;rppbFlIo%Vr~#p$-z9^2J4GIStdZ zY*E@8^BMx8B7gm}hu<{6ZA&{-Gsj_sSRE^@^Hn|LR!Md#P=U!(z00rXfQX|QF)@J5 z3}T7s)*o=(Z3sN3r9Nh=(Ts^sv~q1`MskFpeEJ z)dnYfSLqD$YG6~ywFgCHtqm-J$X#HIq6WwP@-=16nF5jsK7eFk$zRYr9?CRk7LgUK z?*9GgU}bQ?U<_wrd3?51yqZiFMfz)YVy+vv`!BK=*Lrz-A)S`Ngd65xnkBXOGHlTI zq=^f^APdnx)gH3l^Zn+OzUTM06nq-j-e7zhPPQA~@Wp~>_-Yl~g>*d-B+0H}9er7= zV=_${1fcCKy_^e8j4`SbqTKA_Zj^(TjB_16BuQ7({0y`0Pjwv|_pI3IAqB0-Z0J6? zYEWDDm7|u5pZPWhr!INi7btnolO1?KFSKscEZ!FUwM|w|hP=dzUlt!zE>U8s1rCrG?(`u6%clK|sEzg$)B@ds zbGioez_!Zhn6i29Vc`t+x59+3U~o`tzW(BkD2%f#n5^aRLOSR|QR}*0g>|g;usl3v z4u1|y*v2o1aK)JIoN%eR)q&vQf~L`yraLnB8yf$DyGu_>;ldUjWJ*YZGVN(Oo(|Yk z1Ec9uM}^AVu8gAzO~7>UnpHF*Ym7()i%abhk(F6GgfW+Ff}#ksQDKIjqgO~h5ox_m1ilRnb%Y}TGPmiDc2#e>N2AelXR1Cg~rk{y}n+9@W{Dc zEE0R*jceesY$3pM{G0eay-&R{4?VES$(ZdueT0IK{ESJeRpyi_Lgm4}5b(FnVI=(X zpeG4Ih4Qy?53<~}fM4g*955B|>xe_^0Y~bIg^oqDGDo59$D|h%n1isIFHjO}%CCm; zcG9&w6_s(>03_@2LO_`jsR6d#syEktv-LfGmcE{!r;<68?7Sd-wCD+=v~fq2^?)|L zc5sqVO9hA;bYg>G6KUT72E>goQTyq;?J)!7;A`SK1s>*${&~UVwj?D#EPDCRq2(TdH;m`Y)ms5~Qi!B@1@<9^#xs3CHG z$^QDW7R?c7Sp{_tA(H0^tv-E&Bv;d|KDpbNr;-_*VRacZ;pvHnkvP?{jQ z{wbSetDcxNni!bXT+_eT@o84t$w${iS_0Yks`IMTHu+~;?9T6&RjlneKMuCdvYW+4 z`gK}cG5$Ic*o~;Sr4%btF%L->jVe|T$p(i`v8MAhfNM7h7o;N5CU#YA>PaL{zh!AwvCNO+_SONT|^3Vo%C`@CXp!RsCe{A3X7#shKRF z`<}xm1c-3+M}w~p*c<;z5A=sF>0d0R@y_+qU*;rLpY!P0PSo^5hJ#Vo?N}@+vE>QO6p{?^1AQ3*@e|Cs$Yp^3YQqlx zY=hkCT7{FBGRFIX(gzz7O7ene5uLC!uoEqCepsD|#9ALrZm0w%3E4hZTEDi^NeH6B z$$9r~Zh`Lg4h$iir5eC!iL>$AI4-H^-S_R%Kz2tlnYZ0(I%o)NpDkqk>As=AeyEd& z7gos?cMnqJnsg}BX*Zj?XC)8yfl?&Fp+m7GaTwMQbCGr7)zAQKjYFMCh-tL*hE$e? z=A0sy!6BY33S_`Ux$-Ara#CWgM=-j-Qbq)f3Yjeo4HBQd!PY9s za^E;pcGZG?XK_5;<~pfV$zIli-Mke`HhaZ!Vh!gS5P^Vgh31LONuY3RHh#;JX*Qz8 z3zbp!$!P-g4+|^9E#*G$(w}Y$pX*Nh+2Ve6OiPt0Q)$XS5Ym52@k(R!7LTj5b#6XPvV$=i*C2nC>2QIgV zq=aLmyCVcj^MDnK5e!co+!z!IrBvcFa5_Z_NCdoMi$S}xN;|vm?Civv{_&@e;Q#G< zsS+D2w+=(49EAl9LZ{CgAS8J6bH!%R^!XimVCTdcF8ZB0pH;AqyF6xLXy!cAuh}t4 z8Y>BwS^AZ-MwHerAeR_(43`bN&YFN;k`%>ntf+z!*g4-^2T99Vd<^(3L6H|;WSrQk z&^Wa3f;0S9@gxY_iVS@(EC|U%4hz^(*u=)%`kfq7wzyr}dIGx@lhIM zQN2#Ivo!i%*`4AY=MRnI=RN)0Es>t_O4(BquzjAhKW(h2iXF3vX($x%y}xpTc>s1) zNFTFe?%thiq=00LpgIC;iB%J~#8ZI5YduAHFg3`J{|!dcGFf8IhK4a~?#C$s5gz=&g*f?Z7u% zav5$2HSyRog>^->@W>?XU1?}1XI0{>(w%!G$U;u_2(!&Vke4FPXGR^0qRiTwig#b< zoJ!^bxe<49LIP7EYBNq?bkrrp%t2pf3ABUcQqV(jv*|CCH7Ov&rngEd&>$L&3QCha zWR)-sO$}$=l7V0lcb6&66sk2umZ?f0Cujo87@pH!E!F-5m28V=I4(DOnWL6g>eTjw zo@W}K#~`4;tr;e)vvQgq*KIk?2ZkbB!hu)66h8NtN_gc?Wa3Pv4fve;@s(HY6(j>< z^cK}kwRJC5w?kQg#<dh}SM=G{RI!+-ZG7HDBNPRraETlb9_2nPB-CtN z0)d}zTO}0Lz7aqKa!hkZBrP==gb3dv4bwYGmXJ`XbKkW;1)4!V>{rBx!K}613QIPw zJ{-xJvlv!+sFF8FV?(JsOJ(M}$ssEKP# zbxw&qbURKR0zK4jx=nBGg@9zH5Bq)3OUc!o?o*RDtHxRr3~TGw{Y-OhaqU7hb%u&p z^Fsj2eGgex%Oh7@km2Cawj2?>V)}L?Q~>R6m<^jO8fsN==1gVO>=YFHQ6Pg<@G7!8 zv<_Tp9(ci$4p&tIGquD4Qq(>$r!=bphwyxRd^L+LcebSQG0jLKEp)u^Jx76kBCmAP zdXH<6^_73hZ86`*9l*YJo8mnH(T)x?HPf=H;BqoXE}84nXAZFN`P3C}?bsTd*2 z;V!!Tbs4La-R@s+xfWmUq+Xwo3%l4tKiU3V?~u5CK<9RhIcS_AX&!ioxa9Rv?5zBh zXVb-(R|JhtMS3~51X3BDM~q*noN%9UaAv5u!#9-VL8VquIm3wL7Y#?kq|EFX6C;uj zIqGbyMJcxtprJ8gO~NOew|MGSCDAJ^H|Jo9D6CM#fxYW==4hy*$kMbvA5Gr1y8cU7i0FYKxuP8a%3aVTGt?p`*A$DL4qtRGn?n|tT6Z9pq9dy+O13uW zu!q)9BRWe)M6m%~3)aE^MG>*G2P;w7N$?7}P%nY;D{a@3EhRngmNbU)z=VKKiSgKj zJ$N$n0Mb)wauzexgOWn#upEc(gJO(r%KJf$xPeC&dXxs{!k`Y?H>{(ReVmMt4Jc;? zgQHcQn+xd2Rc<_f6Y46kVw!BR7=n$1CRv}QT#l=gYO=Biuhwq(#`P(QBl$zIBoz6h zS%ex}7jIuBS308Sw2y3f*QDJoQmV7{@4>2>zSAnx zXrF79fzt|7!F7dXrn?sg42~axjTXXhJt({8q~ z?EUZik4?5mb83d4iJ`%*w2Ez#snF@A(=gI{#GilS8F1`$Beh3MN~l1Gfn<3tgKl&?^*%CQ#~W|iojAFIy2Ogt=)<1%%c8nr(mBqt`}`-Jl- zd`Ce^1PV>xF1WFqTx&!ub2;t011lIKwAOFQqGr2Q_-kv4c`Tjt-$3&Xf`qXj$J~}R zM@&>zYk^Amg)ApP^RW(BPav;cf}gpBy^A)P((pB66iN6wS)0Fm%*)d*Zsr9pN=R5Q zP*X*tsvsHQSo-XNoV!O*#Co6#bu?O+?0e1fPl(jK&UjzVEs5ZK3Rm;_C*{3xk6V$Y z>KK2Qi8|`PdBp$>?BnR~y|Z|+4Y$Y9foB&N=yK~G-n+!&GK5DUu6kW`Ne{$#@T2y zn<1|*d`9aTRvB&H@*o0G@Cs!y;|IyMp&OSQRRB`edyJ+IUU&FfN?K{cp#|4G{j}@y zE%uaI6`s=L>OTjhfTqHYS_R2iATQXu5VA+izVg?UNpvmxe7RQK$g@xsce%}@gpc26 ziK9dU0VdiNSS3ZJU~aSI6+U-q2Eww#3rNBQ?b7>1bB#+QYp-Q47jL@zhu50*&0N|E)+f2J}7^@xp5 z4y|6AQ>y`c3)FSxEQ;rDbLE`P?JEjGSDJfavz@(<*9>F|H-GQ#n0yd!{x+E1BrfdO zYg0~usu<4oK}8>9ed|_!!w&}-O~Aj>B(%i`L_?4jOHg*HVX>>2LEBe)*Ay6hIrzID zKkWz)QsDQoSew?aW)7P0K^`#;VZP6={wfc68=29DdPjN}zO+w8+yXo&B@N$RXA(LSj)` zzSVi`4-mfNLlvb8?SCLwlde_hGkB_h*iU);n#0ZQT>Z6o=luTAV80U2K}@Zunk1*% zPd}kwPZu#;V;T^b0jIF|Tr}(wsWelbM)kG?-R=iuMp*-xU4cUl<8mhfybCCw*PvZX zVB{{Akl?%Nv;nw!Z|_h74Y@*rI}!b8omV)O^LW zZ_Ceb&X7}RYhQVqS3GXgm+vQrCmw@CLxeguYcnONvZP(G*M5DX6hgn5ix(c8iqDs2 zOL%TdAWB~U`%MUJWUo)cleU|A#m|NP+@8JusCVPxcH?S4P*3T@;b??jP5$clbh=my z^R?|NC*kC-s6HQT#D<^7QrfD3KhfIC+e28?Nghc0vzFC}N4b&(pv2m4rL)eU@;#zY z)aW=3n);>#FK?`{G!4_}gpM`qE%PtXfOJ2eSlvp+yPk5TyY&aY?v6`;Q0~s-rn$~j zyH2!4dpfa!b~eFiF>g?N#PPSLa|M|mPA4Sn0YrhZA9CUgtXkwQ>b$QEmSBcO%)*7R z`;ROO8RjVL&v%>MzP{f__h+}#TM9U8aI0bjpGlaKP^%jwT-hyjT zIav}17UYY_h^D~NsXNXUbv%^2x3;T4hFL z{wBYH@s&NF@n6^uiGoI*eT%v9>uf*Vs2&3EL)}(W9;LnacON~j%1;3zg_$D3G*WPs zFi{&O4NgPMF+1YD91#lJpGN0q3Z|p11QmKA=?Tpb0_jqttANwljC8W>XQ!RG zfVX1=deOdiFVn|c9v{>w)pKbt`Y-HK|Km8NhqtrCb4yzLr_>p(Aotv)8h}Jd7@xbz zWngqV)k4Fd0(Gae{KdElC(4WI%?7nFJ9GO)*5yB_1=HwX)Y8m9{f%0v$T#V9+$QEZ zeJ5Wz3`5uy2wb*RVjcL7yQ1wm{v|D{`gMU?%8wgxLT=XR9f--3NOGf!1!x_)Bq^t- zj2dV^Iot@XQxKg{`yw*KDKlFVk<_6;c^)|S zDCXQ1Aa9bGGn0#YNlplpKmJ=$=M-H@1FhTGww-kBj&0jX$L!d)ZQHgw9ox2@j_qV; z-~8v^ac;fTYdsW3t-0p+k*zSwjuM=2RnQIr5E`YzIII09=Ae^})fY_FOS@Hr>)x(U zMkaa~4Q5Fm5e76TNdhqFp8ga$4)fPJ1S`kW^+@Q=i^Ln@U+LZ{Seav=rc4&KUKgek zw#B|IwO?{C4_N=MJv=*MSXQ2CMd~hj-?IJ{P!=n((vLrZ8!OI;gp(N?!OhyQ$aBPx zokXHlgoL^TA>3w%6p-hA(b4XN^R%_eIn~W=K4dTl12}IIm97#7gjfH*)A1Vsv((OW z&or@6MTYG+X^mK(;g-^>lKY9m_2e7o@k4!%|35Tj{$XbxWKmxAo$QZ3HXnURN=Ag~ zv%OxqWC6#ATudcXi4GSqUe#Fn(DAgFs-e=Zv$QB@CH_b%64Pf`G20Kbcz18;!pB1Z zPs`z_vayE(wNF44H={6(alSwoL`Wr~aV>bjW%eRzPMSl)T*`9iToT3VKox&rjpLVj z7LAFt{RfMD z=AA;Pv>V}g(FO7NUIOIxRI?8&GG~Hp6WI5VyrE%iHYH?X;Yk0zq^SFmffsT@{5pHC zP2MG>;wPTtbpV`tdY@U)8Z@gVEtyZA@m2Kv=G>~*EZa0(Zn#{SRs7MMw(x2mr(v-o z^;2I%#zwGaEm!~kn)2p*bS%50Fx?qnPA^Kf?SO%t$gWs(e3Q}{o{&@O%gSDz1vid@ z43HJ$REHX~`P;hI;44Gj|JH9CVlt@1H<1*cDJrUw3a#jDF;;!+#~hKNYaS>kY=;M;|dyJa2Ra~t22XUQa` zDp=X6zy1$!!C8L;u9g1*T%~)xH%hjEP**GvFRYnT(b<6~dcFPmRVPA*o7D3vQFDYK z--%<_-hMVRL)u+52iH5D>g|`0HNgL8TzU=772Tp27@IDo@@bHI|1qwb{e%ByT-M2{ zM;MDO9J$MHsQ=5jo-uw-`j{p@7jHU(5@AQ`=pbVR;OoGy+0bUS(_7U~u3!9L#x)`F z&A8lQw>-^nrv42NnXZ<5UC1lzIfE^U(cQCLk(ddaY(JaDu9rEga+7_>gVUR*Mm9Mc zt1a}T%Gvn-GeX2AHFXFi>xVdEz|L4(ycAM#{np{HA&iyYKO3?b90IhW&gbgoSmJ%nO+eV zOZ-r*TC?>`3D*Ra6!?9%RQ`ub7=?#rZTSR?0i8pA^ooG{}R z+sdOwI-F^S-Z=yt`tKmQ$7__kt8X8kdh8mUJu5dLFt5L0d>ymp7<8y&TF%kP${5$} z*V<5Ht5xj6bW*VtR!y8J<(y`K=wN|!&*-|Px}IB_L9gojPKdB3Ww%-!YaV2{z-3q* z23S-~rJ_S-+^% z;p%ta_KhAk!~asbGfrLla@PsMRFm>CI`2@a^T)gz(Un23+Ia%)66aV5r++x-QuVvquL&@k*=;n_; z58HKf>Bz-NfUj3>pC|X9HWxJ-cpT~SA5Hoav7CkX@X(aO$7rC(BA1sKf~pfOHblyi z_$cupOUy}3u^gA(1aCS&J&_ga8GF0XoD|UuO2)(3v2q8g$9YX%@K?O%$x0BK)Cp@; z3Q{O#W#~(KxIAy*OG|bv=x~KdkP6fU&yM78N?G5!`<4&he|TZcGnqk>+JXbht~1Ko_UZAz}wcRgZtBjlWprsPfv&7=**c_)#r>T zPbod45E2DIQaOgaj=2tgKd^E|*rT;=4csLZzAIHGu(W7ACvxwGRQSBGV3_Eg|_l*T)W99v{uK% zZY+&K*bd)4FY|1!G~4i(u0Tx)UjJIrw$8!e^YQLHB7T{7vq`enrbr!7s}YN$THaU? z<64(9`Ac41OqopaPTX%YJ~^@NDqNXyQUX?$zUNhKR1dA=58F*NR{18Xg_zsBrAI!@ zr{1cfmm)`*lQ&>gxtp*~-0M*Bbkgyz6qv>(t1?{OHpCOUS;pQ;kP=tIn>&In(y!wB?LXB?~Q;d zq}(ncye~_xFiwAH=0XGEEsfZaizCEd=vcBA>3gfM-||AmDoJd({vBzJVzuCFK#4#B zBWGGA>iagdgo_9^#41ym=YRSZ{{0nsKvtN}WmvF2#jI}7kenE%1!TuIJj?1@4pshv z2t;N3dul$VB~V6tn@ZBng#d01CBZo9BZH6F!xucmS%D9@r)MQJK8SlN;Fs8e%Gmgg zOZb$li-v(dKT=pLY7_E64^A9bJ2b8-fRGC@oSCkb@*%Q3@EN`wX`=33i7jo^h6dx)?aI+IB< z{So7Guw0Q(z1`h^QQZZLh!7?AH1x_S&Jt>%ireKI!i_*!5`B{xsX$rF#Hzwd&Y>&u zxO|4mTR>tK2dntb1(bfZP`3Y}LPHI)w5$p@@q&I;|)>Di7O61k( zB-C+18n=^6d>*J!bbU%g8Y5*Sv(7?R6*TdEy#Gt??!U=h$baNc6Xk!%U7^!uv9b#1 z&DWP7g$bUB1mu`RMx5{YU8D2}DP#hRs8K;^;tyt+Q^1W^Y{rzIf${5yUR|N1L6!;= z_kMJZ+qouuYh_H_I7xBJ#eodT;`2LAP~uchF=+MkevlX|arKlZGC2wM?>5XtO7j%@ z1V)LOL*^2CG)`J`n69{B7-ZMKGl?3WsD4~HfvYK`AV1jK%tx9{MHkXW7=*_^N6v&? z@qU(1u9t|?&qMx3?^${r|AP90Q|s-LeGH6vZSw0>2BUS|7BZvKCWWhL{QKM2NrD5Vs{wM+WL5P`fb z@taFnT@T>DkO82&_GOSwl7%&K2aj;@lw&Cmq*#HB8JGuy>(Dr6Nl+6986Mp#mZiv0 zW*`B%fSZh3Vnu=_Ih{t-S&&?2ger?;&>=$`R$^^{JNB#=>+Rf^ZVTJO@ouTvd{=XR zd*>>jVh^bL`)i?W~^{JbZL+Nn+b%2Y8eW%nsC)B zup+1p{aGEVsXV>{T9y+vb*=4+jvsFQZ}0GO=K(V#U6t-u0wc6IFChX z)9oX`$!LJTB5`TmiBH_-j{zZTS6r+YVXv2&lcF@2AZ&43-HqtHy|CN_vuyPU)ld7znV8$ zmutbP*9PS_tIf&JKkgVJtLW6@l(gu)!f&x%+(9($du(afnd5q89ae^L_eL3sqC22M zCnT_^z4IqzeZ{7YNYcCn8(fA5Kq3E}O6-k=o(AQ)Y@5WDv;bf6%>uiKvSKly*L1&8ag@F2h`;ykC z5{q0gV$Gm9fwTGH;`vW0;HcoSb;xzdA5(7LeHe@4H;zhlisNJ<#95A>jkK-`_v~&`UrhD9GdS zH5iQz{oNS1cN>lkE$I&m{fn{XJkk?l-2jQ>OT*7G{s=W3SF!(;5W#pz^1qpny~vQE zSSHs2^U(X(1lXfb7vEOr;E*8bzwx&<-2xamVVSMfl^2)#t8Ron2uQr_u{TXWsaA~q zHwYN29u&y9Ws;&|mkLf<#rO}aB!;BwHmi53i)vKB4?PHVQELcN3Y&TH(K*Lf0f0E* zFf%4Bg0`e=e9vA{YU6~P{+o}W2SMG7eP0g-R|SJfji?BCX%kQlEB=VT<&F{xC0rpHEA z$*tcf$_K+Sq{hGq0WpNUCR`vRuhm$SlY5+qHvHH#e$c)WB|T0R$G1JnOev_%XMe1J zB9I&`b*BnD{yx4Dx1sNvA39V|K=JYMG+SXZ(kCiK+bINE z$=@u^TQxodsa52*iJwc%u`Sj3_Ueep@EC9ixOYWfPhG?5Y~cY)#M>DQkkV`)tFeE^ z2441DqG01{CT;!Bo_SFbQ6{4$R^svfr#E^NjA%2p0*{*s9QW0cM%CO}dfwD&THis7fpx4d?(CzN15#ebznIEWq)iDm1 zt_n3#WLB+4=%If3H&fpM=RG-m4iOJA9^4WuBZrX`2_j(W<;tMRa_zgQ4DOE{h@Xe7 zp&eZ2Zq^TFRxxwDO%0hcV(-(*TSS4Mule!??S;pZ{D{x_#fLY@&d>SSv+>_mDZSA@ z!cC6y=*UrUPEdM$EckLMgL(gcB*rsivK!j+r;!n@vI}{)HPrd7=wv_E4oGJwrmC4V zwI_ZtNM_J_SyZS()w>B*X%SHG+rk(|7_j1nd-WNOGiT7YIw`yoWnE^e+>Ah*BI%#SVj`j17$OKo=rqdts&h#OVtvk?-v%3qvuW2)_u^aFd3-A5tO~Eeo4f>%S*0)L_ z`xbIOa^_Djq>B~Ul}%)rhzUqFY!uQD{zjXjKqO)|w9;(qLj(j)ovA zof;k5d8wKmL;RhRd>%^L-Scy{K)5{qi@HtR7x%OEE~;6o52NBieeEnI- zhzyr02h3rDT&yN6BTgM+-LNK|mD*~6A4)VOvDL}s4-Ycd`e8^ly$AELJBAzfy9yq3 zaS-NDU2o6U^t%j~kk%LiyEEy`7jOn#QsgKR_f78VDPp#p@XXfidV31N9V*J-BS4+b zPEcISKH&E?5g&j0BgMrVJihv4D+*ka4sxq&2R*>-G*~d)`58dvf{bSMQ4VS1Yqe?9pg z2&6rmbc(b&w}j-xUS(Ol-Bc>$8z>L^x+n?Dm~tUYb=3}ldiVFa_3Z}&Z#@-(?)OJ1f7Y$0U;jP$ zzrs%q=Oauv$U&-=q4}rh;OCN`PSwE}4A5zntrw1$WRfdVQXPCR(U4L`i2aau)dBeX zHCfc?fV1t5y-!pO9O(Uy1_k-7UJ?V?b2noMd_sI$H}K^Yz!06Mv&)I}4^dmJVwbmL z2m3O6c)QjCd3rru9lZ-T%U2NJmSd*hckuu!{e=}42_$|7a=jTcH0ctRzVF5I$&6=9 z(&p&*pvyiHXG*~~mbDsV0w*=cPEyi1@O|qECu@?}i zle9R|&u zO}$3272$?l2sD!=B(lho0=j9ISLlon4}caZOuSU|yxDw7pR?nhLsBe6->C_6s-0XD zjhvkCpP22riskCRd663F+}qwfV+8#qSwQZP9)ow+u!4BlgU_gWSiDGcDXMU~&3C;+ zGC8`8kbnhVF3Gw2Jd1@Aw|+$M+Dy%vm@lb^$yPba^sNZnvJ~f>fvjCReZb}T6qpH- z_`a+fb)x3&g_FQB3Feyk=5l_fd&VBj;NG{5l`6F)37yG7YOJMFt+vCVNZIWXQQ|`* z<(8ULPrI!zV+^@4si~?J@vP}2tH(k=SAOz656Lu!F2IZbSpr8d9i_S545{bT@lqvR z2%~{zbIuvQG~4=KX&!J(Ke~hv-kJ=N)##Q>Kj-7FDGfgT;+DzBOo?Poh3bM+ph)$K zLktbr9q|Oo{`T|Z{JC^O8=NbC4Ruh=5Ki5L6Z}*_B|wmckL!coLl2 zG0z|``Zmd?4Lgm_7Z6wCmKd)LHw$^>1W)T8J~$U|Jd5f0&wj%H(w6&IgBi7cKlt#nlK9YmV_z;j}JQL3NX(>J9V<{hUiU1b z0*@KDe5!)j?B~I9_=+{EmljY=@W(T7w|k6Jpk-VTWFG2-lIE^u3y|P5VG`)^IVfxo z+#P>_(Ce2X07W8$Tj~k^;3hLfJM0@Zg%Ua4?#DeS?*6go7RSBkNDWK2=Zpbih&2~E zz?Cue3@Qp5bBiWzAamg^_1XO+pdd3vqOUojkmf7EhjR6E)Sdh`41!K5@{*eR%LCFM#c@mJ--b?luT9TMuW}z9l`dx~!Ko>vMeMzV{^j&4&KVU?dmMnsdBjgI{@AnVhB61%#f-a~AN44GAhTnN!65R*{r*5~{V5QO9mQZz zjL=V0KJ#1st#(hoNN!^yN>sAMBQE%VmA8Xn#;jpElj%7~;5@~HAWgV z9bcVxc6S3`S8I=sji|}bAP(MM*qULwGGbo|iu=+>QeYn~Ea^9pYfnVw%2F5#jJbi~ zlGPvpc0@z=BOtzLSPwYxu08}_5T_f5klja!lW3wXw)p0XaR|kg3K?e7g zGViaTcgGv}1%SAJ%Og4d4&nkk{ea}fW`1})Nf7U%X9>UqMJMVf zp+=hfvu;e~q@Vs9Mp&VU8;rHi@+OT+G@AIB9pfho&@@Unm7dWfpZEdZhWM? zS_UfyOg!H0p?d)D9=va1BaBLz0NN_ghD%Ky8i77w=rZ8*?8Yo;vGyWI`!o`Iw=(kt zEFfkEe3%}C8_wpov3v+lV*YB+t@l!g2Xa^KiGMdyTL&5^M-+k!wlwlWN`1gUPnzy} zzkY3UVMEPVrRzeISNCd@jWjSlhf2!nr77+K43(B^Q35m0+_(X$5}F8(Vnt!3H8>Mf zD9s(C5JC20KM@bB_hkQ3uHW@D?p3p0rV})Y%^HjN373gJyh^ZT?BvlP{OJD`m$C&*9E#u>#U+Jh62vaOlJac$ICyGk zu#;9(C&x+-lV{?gGDKv-@$n&k+^SH1>{fH1399Ft)8OAro>@rlQ@ZQE)Ek5A{lkF+%TXQmU9hW0oA{SrfOpM z>NPkBV(JzRHPaligpF~gFYb}v zVu||Bcti>GS1B(^9AYFk`q0pL3l9z;-~gikC)$le09+E25OD%F{62yN$b@$M)asKQ zWj|d9M!Kr!oU#UHKW6TC)+Sm^w#MV{pJ)jiI(ZWj%m}!MxHHm{vStt}=tcsoT9Wt% z&e3#5MU$014)0oUnL2qw{6hMj>QAIk8Z(}`^=sA~`6vv#V+0XN7tTUO6v#%oqKPYT ziMcYd0{gUq#^WGoF}oijy~;To-YgOMgeA%=HQ0=VrQ_yz^XmF~l0uhyWHsoZkjhCn zfgbi!39rkh=lxq6LlkdbrsQED7+c(V3W;L4zbmXj(%H=kDEi&MJGI@5NU__|9T}x{ z`TEHnN?ja-n*Ae-dq3*Rl+CH4G6ztgu}t(?kMB-I3`hN~Vk-IDDY z_;ub&kEu5l*s1j?M!7_ol+QF-_2tz?G=Zl4t8h?xR zUKoZ|_(8By*QjK_dBhxIQ6VF4GhlIOL1o}NQwmBF`<_B)sY{fG4s$18km={#srozj zK(KLrMGHR@W%gr@+dFf*8x09r&q-%~b%5K{RdMOSGN`Yr#X`8KobeqK2EsRjgW$XN zvc^a{w@_zGV;;^KgVDbI8MJUOlA-po-JAe_`Yv^Y&w2!p4f1Uy>7W~>hhd@YhhrWr zmlYNvh)9=9^_(YR}A)0g$Kf2RMJO66{I3Lw@RR`@?tH}$)tKChRMKpKDFmyQc20BGSLGB zQ>=KVFyXoCKtbb2Dg9#dIpJN)h{%lWW#lP!Wg&_RN-|jVRFuK%2)Frq;^;gX!DNbM z@IlGOmhpC_LR#JITUVLfuPM=WkaMrQKa4Lqx({J#nP4F6-LLpVfm@LJ0rol!69{S| z{+N*y%OohlU=w|%#%`>tjwySxXjrR<@O<)dqEfzW%Kdq3qDoW^rq7l&#q7XR(o{qFjM3D!4bJt>ZQ<^A5+8N(MZ4oLa*i}@ z9P{-`4x*NBY)cQb2$bW6Wf-ZV?4i<&mvW@-zl$@WC3nATrfC{F{PXJG3`bcrOM|cvA%ljb}O2Md<|YAt#Ej zK|}1EtVBUB!W#zAje|#7M2GH@zXn=D)FAw z=09=!W*q>LRfin0WdQP0c&qx&rgcLM@66y{45X|1Oy1Y?&u^2 IS%3ohKOM~@6aWAK literal 0 HcmV?d00001 diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 7d9fa628..e4e517e2 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,12 +45,12 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm - external-secrets.io/component : webhook + external-secrets.io/component: webhook --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml apiVersion: v1 @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: clusterexternalsecrets.external-secrets.io spec: @@ -251,6 +251,9 @@ spec: - name type: object target: + default: + creationPolicy: Owner + deletionPolicy: Retain description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. properties: creationPolicy: @@ -443,7 +446,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: clustersecretstores.external-secrets.io spec: @@ -500,8 +503,51 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Akeyless. properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + description: Reference to a Secret that contains the details to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication @@ -543,8 +589,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object required: - akeylessGWApiURL @@ -613,6 +657,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -717,6 +766,11 @@ spec: serviceAccountRef: description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -791,6 +845,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -919,6 +978,11 @@ spec: serviceAccount: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -1130,6 +1194,11 @@ spec: serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -1191,6 +1260,11 @@ spec: serviceAccountRef: description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -1506,8 +1580,51 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Akeyless. properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + description: Reference to a Secret that contains the details to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication @@ -1549,8 +1666,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object required: - akeylessGWApiURL @@ -1619,6 +1734,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -1717,12 +1837,26 @@ spec: - ManagedIdentity - WorkloadIdentity type: string + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string identityId: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -1741,6 +1875,60 @@ spec: required: - vaultUrl type: object + doppler: + description: Doppler configures this store to sync secrets using the Doppler provider + properties: + auth: + description: Auth configures how the Operator authenticates with the Doppler API + properties: + secretRef: + properties: + dopplerToken: + description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - dopplerToken + type: object + required: + - secretRef + type: object + config: + description: Doppler config (required if not using a Service Token) + type: string + format: + description: Format enables the downloading of secrets as a file (string) + enum: + - json + - dotnet-json + - env + - yaml + - docker + type: string + nameTransformer: + description: Environment variable compatible name transforms that change secret names to a different format + enum: + - upper-camel + - camel + - lower-snake + - tf-var + - dotnet-env + type: string + project: + description: Doppler project (required if not using a Service Token) + type: string + required: + - auth + type: object fake: description: Fake configures a store with static key/value pairs properties: @@ -1797,6 +1985,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -1841,6 +2034,9 @@ spec: required: - SecretRef type: object + environment: + description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) + type: string projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -1936,6 +2132,11 @@ spec: serviceAccount: description: points to a service account that should be used for authentication properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -2216,17 +2417,22 @@ spec: description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -2288,6 +2494,11 @@ spec: serviceAccountRef: description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -2622,7 +2833,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: externalsecrets.external-secrets.io spec: @@ -3016,6 +3227,9 @@ spec: - name type: object target: + default: + creationPolicy: Owner + deletionPolicy: Retain description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. properties: creationPolicy: @@ -3162,7 +3376,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: secretstores.external-secrets.io spec: @@ -3219,8 +3433,51 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Akeyless. properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + description: Reference to a Secret that contains the details to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication @@ -3262,8 +3519,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object required: - akeylessGWApiURL @@ -3332,6 +3587,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -3436,6 +3696,11 @@ spec: serviceAccountRef: description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -3510,6 +3775,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -3638,6 +3908,11 @@ spec: serviceAccount: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -3849,6 +4124,11 @@ spec: serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -3910,6 +4190,11 @@ spec: serviceAccountRef: description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -4225,8 +4510,51 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Akeyless. properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + description: Reference to a Secret that contains the details to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication @@ -4268,8 +4596,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object required: - akeylessGWApiURL @@ -4338,6 +4664,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -4436,12 +4767,26 @@ spec: - ManagedIdentity - WorkloadIdentity type: string + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string identityId: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -4460,6 +4805,60 @@ spec: required: - vaultUrl type: object + doppler: + description: Doppler configures this store to sync secrets using the Doppler provider + properties: + auth: + description: Auth configures how the Operator authenticates with the Doppler API + properties: + secretRef: + properties: + dopplerToken: + description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - dopplerToken + type: object + required: + - secretRef + type: object + config: + description: Doppler config (required if not using a Service Token) + type: string + format: + description: Format enables the downloading of secrets as a file (string) + enum: + - json + - dotnet-json + - env + - yaml + - docker + type: string + nameTransformer: + description: Environment variable compatible name transforms that change secret names to a different format + enum: + - upper-camel + - camel + - lower-snake + - tf-var + - dotnet-env + type: string + project: + description: Doppler project (required if not using a Service Token) + type: string + required: + - auth + type: object fake: description: Fake configures a store with static key/value pairs properties: @@ -4516,6 +4915,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -4560,6 +4964,9 @@ spec: required: - SecretRef type: object + environment: + description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) + type: string projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -4655,6 +5062,11 @@ spec: serviceAccount: description: points to a service account that should be used for authentication properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -4935,17 +5347,22 @@ spec: description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -5007,6 +5424,11 @@ spec: serviceAccountRef: description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -5342,10 +5764,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5400,10 +5822,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5491,10 +5913,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -5517,10 +5939,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -5544,10 +5966,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5564,10 +5986,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5600,10 +6022,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5639,10 +6061,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5660,12 +6082,12 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm - external-secrets.io/component : webhook + external-secrets.io/component: webhook spec: type: ClusterIP ports: @@ -5684,10 +6106,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5704,7 +6126,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.5.9" + image: "ghcr.io/external-secrets/external-secrets:v0.6.0" imagePullPolicy: IfNotPresent args: - certcontroller @@ -5731,10 +6153,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5751,7 +6173,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.5.9" + image: "ghcr.io/external-secrets/external-secrets:v0.6.0" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -5767,10 +6189,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5788,7 +6210,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.5.9" + image: "ghcr.io/external-secrets/external-secrets:v0.6.0" imagePullPolicy: IfNotPresent args: - webhook @@ -5796,6 +6218,7 @@ spec: - --dns-name=golang-external-secrets-webhook.default.svc - --cert-dir=/tmp/certs - --check-interval=5m + - --healthz-addr=:8081 ports: - containerPort: 8080 protocol: TCP diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 872b07de..d11ff78f 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,12 +45,12 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm - external-secrets.io/component : webhook + external-secrets.io/component: webhook --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml apiVersion: v1 @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: clusterexternalsecrets.external-secrets.io spec: @@ -251,6 +251,9 @@ spec: - name type: object target: + default: + creationPolicy: Owner + deletionPolicy: Retain description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. properties: creationPolicy: @@ -443,7 +446,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: clustersecretstores.external-secrets.io spec: @@ -500,8 +503,51 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Akeyless. properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + description: Reference to a Secret that contains the details to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication @@ -543,8 +589,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object required: - akeylessGWApiURL @@ -613,6 +657,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -717,6 +766,11 @@ spec: serviceAccountRef: description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -791,6 +845,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -919,6 +978,11 @@ spec: serviceAccount: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -1130,6 +1194,11 @@ spec: serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -1191,6 +1260,11 @@ spec: serviceAccountRef: description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -1506,8 +1580,51 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Akeyless. properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + description: Reference to a Secret that contains the details to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication @@ -1549,8 +1666,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object required: - akeylessGWApiURL @@ -1619,6 +1734,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -1717,12 +1837,26 @@ spec: - ManagedIdentity - WorkloadIdentity type: string + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string identityId: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -1741,6 +1875,60 @@ spec: required: - vaultUrl type: object + doppler: + description: Doppler configures this store to sync secrets using the Doppler provider + properties: + auth: + description: Auth configures how the Operator authenticates with the Doppler API + properties: + secretRef: + properties: + dopplerToken: + description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - dopplerToken + type: object + required: + - secretRef + type: object + config: + description: Doppler config (required if not using a Service Token) + type: string + format: + description: Format enables the downloading of secrets as a file (string) + enum: + - json + - dotnet-json + - env + - yaml + - docker + type: string + nameTransformer: + description: Environment variable compatible name transforms that change secret names to a different format + enum: + - upper-camel + - camel + - lower-snake + - tf-var + - dotnet-env + type: string + project: + description: Doppler project (required if not using a Service Token) + type: string + required: + - auth + type: object fake: description: Fake configures a store with static key/value pairs properties: @@ -1797,6 +1985,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -1841,6 +2034,9 @@ spec: required: - SecretRef type: object + environment: + description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) + type: string projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -1936,6 +2132,11 @@ spec: serviceAccount: description: points to a service account that should be used for authentication properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -2216,17 +2417,22 @@ spec: description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -2288,6 +2494,11 @@ spec: serviceAccountRef: description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -2622,7 +2833,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: externalsecrets.external-secrets.io spec: @@ -3016,6 +3227,9 @@ spec: - name type: object target: + default: + creationPolicy: Owner + deletionPolicy: Retain description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. properties: creationPolicy: @@ -3162,7 +3376,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: secretstores.external-secrets.io spec: @@ -3219,8 +3433,51 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Akeyless. properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + description: Reference to a Secret that contains the details to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication @@ -3262,8 +3519,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object required: - akeylessGWApiURL @@ -3332,6 +3587,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -3436,6 +3696,11 @@ spec: serviceAccountRef: description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -3510,6 +3775,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -3638,6 +3908,11 @@ spec: serviceAccount: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -3849,6 +4124,11 @@ spec: serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -3910,6 +4190,11 @@ spec: serviceAccountRef: description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -4225,8 +4510,51 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Akeyless. properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' + description: Reference to a Secret that contains the details to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication @@ -4268,8 +4596,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object required: - akeylessGWApiURL @@ -4338,6 +4664,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -4436,12 +4767,26 @@ spec: - ManagedIdentity - WorkloadIdentity type: string + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string identityId: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -4460,6 +4805,60 @@ spec: required: - vaultUrl type: object + doppler: + description: Doppler configures this store to sync secrets using the Doppler provider + properties: + auth: + description: Auth configures how the Operator authenticates with the Doppler API + properties: + secretRef: + properties: + dopplerToken: + description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - dopplerToken + type: object + required: + - secretRef + type: object + config: + description: Doppler config (required if not using a Service Token) + type: string + format: + description: Format enables the downloading of secrets as a file (string) + enum: + - json + - dotnet-json + - env + - yaml + - docker + type: string + nameTransformer: + description: Environment variable compatible name transforms that change secret names to a different format + enum: + - upper-camel + - camel + - lower-snake + - tf-var + - dotnet-env + type: string + project: + description: Doppler project (required if not using a Service Token) + type: string + required: + - auth + type: object fake: description: Fake configures a store with static key/value pairs properties: @@ -4516,6 +4915,11 @@ spec: serviceAccountRef: description: A reference to a ServiceAccount resource. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -4560,6 +4964,9 @@ spec: required: - SecretRef type: object + environment: + description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) + type: string projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -4655,6 +5062,11 @@ spec: serviceAccount: description: points to a service account that should be used for authentication properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -4935,17 +5347,22 @@ spec: description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -5007,6 +5424,11 @@ spec: serviceAccountRef: description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array name: description: The name of the ServiceAccount resource being referred to. type: string @@ -5342,10 +5764,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5400,10 +5822,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5491,10 +5913,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -5517,10 +5939,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -5544,10 +5966,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5564,10 +5986,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5600,10 +6022,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5639,10 +6061,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5660,12 +6082,12 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm - external-secrets.io/component : webhook + external-secrets.io/component: webhook spec: type: ClusterIP ports: @@ -5684,10 +6106,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5704,7 +6126,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.5.9" + image: "ghcr.io/external-secrets/external-secrets:v0.6.0" imagePullPolicy: IfNotPresent args: - certcontroller @@ -5731,10 +6153,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5751,7 +6173,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.5.9" + image: "ghcr.io/external-secrets/external-secrets:v0.6.0" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -5767,10 +6189,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.9 + helm.sh/chart: external-secrets-0.6.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" + app.kubernetes.io/version: "v0.6.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5788,7 +6210,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.5.9" + image: "ghcr.io/external-secrets/external-secrets:v0.6.0" imagePullPolicy: IfNotPresent args: - webhook @@ -5796,6 +6218,7 @@ spec: - --dns-name=golang-external-secrets-webhook.default.svc - --cert-dir=/tmp/certs - --check-interval=5m + - --healthz-addr=:8081 ports: - containerPort: 8080 protocol: TCP diff --git a/tests/golang-external-secrets.expected.diff b/tests/golang-external-secrets.expected.diff index 9925606f..f11d01ae 100644 --- a/tests/golang-external-secrets.expected.diff +++ b/tests/golang-external-secrets.expected.diff @@ -1,6 +1,6 @@ --- tests/golang-external-secrets-naked.expected.yaml +++ tests/golang-external-secrets-normal.expected.yaml -@@ -5827,7 +5827,7 @@ +@@ -6250,7 +6250,7 @@ spec: provider: vault: From ef5e1369c807c1d02db41a2234712ad299775107 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 25 Oct 2022 15:29:09 +0200 Subject: [PATCH 0600/1263] Use UBI based images for ESO Thsi changes switches to use UBI based images as reccomended by Matthias. Tested on MCG on hub and also checked the regional cluster is still working correctly: oc get -n golang-external-secrets pods -o yaml |grep -i image: image: ghcr.io/external-secrets/external-secrets:v0.6.0-ubi image: ghcr.io/external-secrets/external-secrets:v0.6.0-ubi image: ghcr.io/external-secrets/external-secrets:v0.6.0-ubi image: ghcr.io/external-secrets/external-secrets:v0.6.0-ubi image: ghcr.io/external-secrets/external-secrets:v0.6.0-ubi image: ghcr.io/external-secrets/external-secrets:v0.6.0-ubi Note that after this change, when we update the ESO subchart we also need to tweak the version. That is why I added a README with the reminder. --- golang-external-secrets/README.md | 5 +++++ golang-external-secrets/values.yaml | 10 ++++++++++ tests/golang-external-secrets-naked.expected.yaml | 6 +++--- tests/golang-external-secrets-normal.expected.yaml | 6 +++--- 4 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 golang-external-secrets/README.md diff --git a/golang-external-secrets/README.md b/golang-external-secrets/README.md new file mode 100644 index 00000000..6db62db7 --- /dev/null +++ b/golang-external-secrets/README.md @@ -0,0 +1,5 @@ +# Subchart Update + +When updating this sub-chart, please remember to tweak the image tag in values.yaml. +That is because we want to use -ubi images if possible and there is no suffix option, so +we just override the tag with the version + "-ubi" diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index b4656f3b..f9518666 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -4,3 +4,13 @@ mountRole: "hub-role" global: hubClusterDomain: hub.example.com + +external-secrets: + image: + tag: v0.6.0-ubi + webhook: + image: + tag: v0.6.0-ubi + certController: + image: + tag: v0.6.0-ubi diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index e4e517e2..4baa3da1 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6126,7 +6126,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.6.0" + image: "ghcr.io/external-secrets/external-secrets:v0.6.0-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -6173,7 +6173,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.6.0" + image: "ghcr.io/external-secrets/external-secrets:v0.6.0-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -6210,7 +6210,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.6.0" + image: "ghcr.io/external-secrets/external-secrets:v0.6.0-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index d11ff78f..2945247c 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6126,7 +6126,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.6.0" + image: "ghcr.io/external-secrets/external-secrets:v0.6.0-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -6173,7 +6173,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.6.0" + image: "ghcr.io/external-secrets/external-secrets:v0.6.0-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -6210,7 +6210,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.6.0" + image: "ghcr.io/external-secrets/external-secrets:v0.6.0-ubi" imagePullPolicy: IfNotPresent args: - webhook From e51f70b23909691246c41f460bd2d481e45dec66 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 25 Oct 2022 17:11:38 +0200 Subject: [PATCH 0601/1263] Use no_log: true in any sensitive place Let's lower the risk of leaking any secrets by adding no_log: true to any task that might be managing secrets/tokens or the likes. Tested via MCG and correctly deployed it. --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 1 + ansible/roles/vault_utils/tasks/vault_init.yaml | 4 ++++ ansible/roles/vault_utils/tasks/vault_secrets_init.yaml | 3 +++ 3 files changed, 8 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 8d1bba5e..6c325da8 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -39,6 +39,7 @@ secret_template: "{{ pattern_dir }}/values-secret.yaml.template" - name: Loads secrets file into the vault of a cluster + no_log: true vault_load_secrets: values_secrets: ~/values-secret.yaml check_missing_secrets: false diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index e3ba9cb6..6d65740f 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -37,6 +37,7 @@ # We need to retry here because the vault service might be starting # and can return a 500 internal server until its state is fully ready - name: Init vault operator + no_log: true kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" @@ -48,6 +49,7 @@ when: not vault_initialized - name: Set vault init output json fact + no_log: true ansible.builtin.set_fact: vault_init_json: "{{ vault_init_json_out.stdout | from_json }}" when: not vault_initialized @@ -56,6 +58,7 @@ # the vault was not already initialized *and* when unseal_from_cluster # is set to false - name: Save vault operator output (local file) + no_log: true ansible.builtin.copy: follow: true dest: "{{ output_file_abs }}" @@ -69,6 +72,7 @@ # the cluster when the vault was not already initialized *and* when # unseal_from_cluster is set to true - name: Save vault operator output (into a secret inside the cluster) + no_log: true kubernetes.core.k8s: state: present definition: diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 9fdb9024..bfd38e9b 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -34,6 +34,7 @@ when: kubernetes_enabled.rc != 0 - name: Get token from service account secret {{ external_secrets_ns }}/{{ external_secrets_secret }} + no_log: true kubernetes.core.k8s_info: kind: Secret namespace: "{{ external_secrets_ns }}" @@ -43,10 +44,12 @@ failed_when: token_data.resources | length == 0 - name: Set sa_token fact + no_log: true ansible.builtin.set_fact: sa_token: "{{ token_data.resources[0].data.token | b64decode }}" - name: Configure hub kubernetes backend + no_log: true kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" From 8c87ec3e68be1545935f11e11ad9ef9c9eff9a09 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 26 Oct 2022 09:29:58 +0200 Subject: [PATCH 0602/1263] Disable VALIDATE_DOCKERFILE_HADOLINT VALIDATE_TEKTON by default in common/Makefile By adding the following: -e VALIDATE_DOCKERFILE_HADOLINT=false \ -e VALIDATE_TEKTON=false \ we can drop the custom super-linter target in most patterns. If there is a pattern that needs to enable those it can update the super-linter target in its top-level Makefile. Also remove the .mypy_cache folder as it gets generated by super-linter and then ends up being scanned as well which is inefficient. --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 4b8b3383..56261bba 100644 --- a/Makefile +++ b/Makefile @@ -93,12 +93,15 @@ load-secrets: ## loads the secrets into the vault common/scripts/vault-utils.sh push_secrets common/pattern-vault.init super-linter: ## Runs super linter locally + rm -rf .mypy_cache podman run -e RUN_LOCAL=true -e USE_FIND_ALGORITHM=true \ -e VALIDATE_BASH=false \ -e VALIDATE_JSCPD=false \ -e VALIDATE_KUBERNETES_KUBEVAL=false \ -e VALIDATE_YAML=false \ -e VALIDATE_ANSIBLE=false \ + -e VALIDATE_DOCKERFILE_HADOLINT=false \ + -e VALIDATE_TEKTON=false \ $(DISABLE_LINTERS) \ -v $(PWD):/tmp/lint:rw,z docker.io/github/super-linter:slim-v4 From ac7624de6e5f5059a142148c88a79d283e632537 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 26 Oct 2022 12:31:23 -0500 Subject: [PATCH 0603/1263] Remove .Release.name from namespace labels --- clustergroup/templates/core/namespaces.yaml | 1 - tests/clustergroup-normal.expected.yaml | 2 -- tests/clustergroup.expected.diff | 14 ++++++-------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/clustergroup/templates/core/namespaces.yaml b/clustergroup/templates/core/namespaces.yaml index 6d2ad164..bf0bfc7d 100644 --- a/clustergroup/templates/core/namespaces.yaml +++ b/clustergroup/templates/core/namespaces.yaml @@ -4,7 +4,6 @@ apiVersion: v1 kind: Namespace metadata: labels: - name: {{ default "pattern" $.Release.name }} argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} name: {{ . }} spec: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index e3b3371b..c059c129 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -4,7 +4,6 @@ apiVersion: v1 kind: Namespace metadata: labels: - name: pattern argocd.argoproj.io/managed-by: mypattern-example name: open-cluster-management spec: @@ -14,7 +13,6 @@ apiVersion: v1 kind: Namespace metadata: labels: - name: pattern argocd.argoproj.io/managed-by: mypattern-example name: application-ci spec: diff --git a/tests/clustergroup.expected.diff b/tests/clustergroup.expected.diff index 9bcfe9b3..0bd2da15 100644 --- a/tests/clustergroup.expected.diff +++ b/tests/clustergroup.expected.diff @@ -1,13 +1,12 @@ --- tests/clustergroup-naked.expected.yaml +++ tests/clustergroup-normal.expected.yaml -@@ -1,17 +1,229 @@ +@@ -1,17 +1,227 @@ --- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: -+ name: pattern + argocd.argoproj.io/managed-by: mypattern-example + name: open-cluster-management +spec: @@ -17,7 +16,6 @@ +kind: Namespace +metadata: + labels: -+ name: pattern + argocd.argoproj.io/managed-by: mypattern-example + name: application-ci +spec: @@ -232,7 +230,7 @@ # Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 -@@ -36,7 +248,7 @@ +@@ -36,7 +246,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -241,7 +239,7 @@ roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole -@@ -45,16 +257,587 @@ +@@ -45,16 +255,587 @@ - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-application-controller name: example-gitops-argocd-application-controller @@ -832,7 +830,7 @@ --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 -@@ -65,7 +848,7 @@ +@@ -65,7 +846,7 @@ # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: example-gitops @@ -841,7 +839,7 @@ annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: -@@ -94,10 +877,10 @@ +@@ -94,10 +875,10 @@ --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE @@ -856,7 +854,7 @@ --set clusterGroup.name=example --post-renderer ./kustomize"] applicationSet: -@@ -174,11 +957,59 @@ +@@ -174,11 +955,59 @@ kind: ConsoleLink metadata: name: example-gitops-link From 490c3b9130480cc56508cd8ddc26456efe7eda53 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 28 Oct 2022 09:40:05 +0200 Subject: [PATCH 0604/1263] Upgrade to vault-0.22.1 helm chart --- hashicorp-vault/Chart.yaml | 2 +- hashicorp-vault/charts/vault-0.22.0.tgz | Bin 51863 -> 0 bytes hashicorp-vault/charts/vault-0.22.1.tgz | Bin 0 -> 51939 bytes hashicorp-vault/values.yaml | 2 +- tests/hashicorp-vault-naked.expected.yaml | 21 +++++++++++---------- tests/hashicorp-vault-normal.expected.yaml | 21 +++++++++++---------- tests/hashicorp-vault.expected.diff | 2 +- 7 files changed, 25 insertions(+), 23 deletions(-) delete mode 100644 hashicorp-vault/charts/vault-0.22.0.tgz create mode 100644 hashicorp-vault/charts/vault-0.22.1.tgz diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index 51ae676f..eea57ea0 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -6,5 +6,5 @@ name: hashicorp-vault version: 0.0.1 dependencies: - name: vault - version: "0.22.0" + version: "0.22.1" repository: "https://helm.releases.hashicorp.com" diff --git a/hashicorp-vault/charts/vault-0.22.0.tgz b/hashicorp-vault/charts/vault-0.22.0.tgz deleted file mode 100644 index e6111e1f7482f02b422e1341eca5d369e2f3cc98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51863 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 Ly%<5xTYojN( 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 bYunSyd(&rT3oiTbU%q^$4TourIX}iX0ap0H diff --git a/hashicorp-vault/charts/vault-0.22.1.tgz b/hashicorp-vault/charts/vault-0.22.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..28a41700cf3f76a119b26dde90bfd5c0b237eddc GIT binary patch literal 51939 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%0D4{oncBp$cuPkAdg66` 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 Date: Fri, 28 Oct 2022 10:41:36 +0200 Subject: [PATCH 0605/1263] Clean up how we patch the subchart for vault --- hashicorp-vault/README.md | 8 ++++-- .../0001-patch-server-route.patch | 28 +++++++++++++++++++ hashicorp-vault/update-helm-dependency.sh | 4 ++- 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 hashicorp-vault/local-patches/0001-patch-server-route.patch diff --git a/hashicorp-vault/README.md b/hashicorp-vault/README.md index 153b516a..a3d4249f 100644 --- a/hashicorp-vault/README.md +++ b/hashicorp-vault/README.md @@ -1,9 +1,11 @@ # VP hashicorp-vault -**IMPORTANT**: Due to the fact that 'null' values do not work in helm charts ([GH#9136](https://github.com/helm/helm/issues/9136)), -we need to patch the chart to skip setting the host. +**IMPORTANT**: Due to the fact that 'null' values do not work in helm charts +([GH#9136](https://github.com/helm/helm/issues/9136)), we need to patch the +chart to skip setting the host. -Make sure to run "./update-helm-dependency.sh" +Make sure to run "./update-helm-dependency.sh" after you updated the subchart +(by calling helm dependency update .) We can drop this local patch when any one the two conditions is true: diff --git a/hashicorp-vault/local-patches/0001-patch-server-route.patch b/hashicorp-vault/local-patches/0001-patch-server-route.patch new file mode 100644 index 00000000..edc22c57 --- /dev/null +++ b/hashicorp-vault/local-patches/0001-patch-server-route.patch @@ -0,0 +1,28 @@ +diff -up vault/values.yaml.orig vault/values.yaml +--- vault/values.yaml.orig 2022-09-05 20:42:02.468428184 +0200 ++++ vault/values.yaml 2022-09-05 20:42:05.218435871 +0200 +@@ -406,7 +406,8 @@ server: + + labels: {} + annotations: {} +- host: chart-example.local ++ #host: chart-example.local ++ host: null + # tls will be passed directly to the route's TLS config, which + # can be used to configure other termination methods that terminate + # TLS at the router +diff -up vault/values.schema.json.orig vault/values.schema.json +--- vault/values.schema.json.orig 2022-09-11 21:00:34.834334961 +0200 ++++ vault/values.schema.json 2022-09-11 21:00:57.190368032 +0200 +@@ -838,7 +838,10 @@ + "type": "boolean" + }, + "host": { +- "type": "string" ++ "type": [ ++ "null", ++ "string" ++ ] + }, + "labels": { + "type": "object" diff --git a/hashicorp-vault/update-helm-dependency.sh b/hashicorp-vault/update-helm-dependency.sh index afd6d522..64adef27 100755 --- a/hashicorp-vault/update-helm-dependency.sh +++ b/hashicorp-vault/update-helm-dependency.sh @@ -19,7 +19,9 @@ pushd "${CHARTDIR}" rm -rf "${NAME}" tar xfz "${TAR}" pushd "${NAME}" -patch -p1 < ../../patch-server-route.diff +for i in ../../local-patches/*.patch; do + patch -p1 < "${i}" +done popd tar cvfz "${TAR}" "${NAME}" rm -rf "${NAME}" From c72d4fdb414950e7228874a5aec036c3844ddbd0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 28 Oct 2022 10:41:49 +0200 Subject: [PATCH 0606/1263] Remove old patch --- hashicorp-vault/patch-server-route.diff | 28 ------------------------- 1 file changed, 28 deletions(-) delete mode 100644 hashicorp-vault/patch-server-route.diff diff --git a/hashicorp-vault/patch-server-route.diff b/hashicorp-vault/patch-server-route.diff deleted file mode 100644 index edc22c57..00000000 --- a/hashicorp-vault/patch-server-route.diff +++ /dev/null @@ -1,28 +0,0 @@ -diff -up vault/values.yaml.orig vault/values.yaml ---- vault/values.yaml.orig 2022-09-05 20:42:02.468428184 +0200 -+++ vault/values.yaml 2022-09-05 20:42:05.218435871 +0200 -@@ -406,7 +406,8 @@ server: - - labels: {} - annotations: {} -- host: chart-example.local -+ #host: chart-example.local -+ host: null - # tls will be passed directly to the route's TLS config, which - # can be used to configure other termination methods that terminate - # TLS at the router -diff -up vault/values.schema.json.orig vault/values.schema.json ---- vault/values.schema.json.orig 2022-09-11 21:00:34.834334961 +0200 -+++ vault/values.schema.json 2022-09-11 21:00:57.190368032 +0200 -@@ -838,7 +838,10 @@ - "type": "boolean" - }, - "host": { -- "type": "string" -+ "type": [ -+ "null", -+ "string" -+ ] - }, - "labels": { - "type": "object" From 0bc01fe48deb1f33ca6eb587f3b147ff57cad210 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 28 Oct 2022 11:36:56 +0200 Subject: [PATCH 0607/1263] Write down some new features in Changes.md --- Changes.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Changes.md b/Changes.md index 8f105e52..bfe6b380 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,14 @@ # Changes +## October 28, 2022 + +* Updated vault helm chart to v0.22.1 and vault containers to 1.12.0 + +## October 25, 2022 + +* Updated External Secrets Operator to v0.6.0 +* Moved to -UBI based ESO containers + ## October 13, 2022 * Added global.clusterVersion as a new helm variable which represents the OCP From 495dd3720ca35baa717d27706e7fd377a5700f33 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 4 Nov 2022 08:48:48 +0100 Subject: [PATCH 0608/1263] Upgrade ESO to 0.6.1 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.6.0.tgz | Bin 43570 -> 0 bytes .../charts/external-secrets-0.6.1.tgz | Bin 0 -> 44651 bytes golang-external-secrets/values.yaml | 6 +- ...olang-external-secrets-naked.expected.yaml | 157 ++++++++++++++---- ...lang-external-secrets-normal.expected.yaml | 157 ++++++++++++++---- tests/golang-external-secrets.expected.diff | 2 +- 7 files changed, 249 insertions(+), 75 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.6.0.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.6.1.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index ebc6a138..71b867f8 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.6.0" + version: "0.6.1" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.6.0.tgz b/golang-external-secrets/charts/external-secrets-0.6.0.tgz deleted file mode 100644 index b5a5db2611a56499e8462c543b21d2980e1d773f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43570 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 IS%3ohKOM~@6aWAK diff --git a/golang-external-secrets/charts/external-secrets-0.6.1.tgz b/golang-external-secrets/charts/external-secrets-0.6.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..82520667c03fb93b475298065117f428c124f335 GIT binary patch literal 44651 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 LZ~%1-5B0wQ0{Ubg literal 0 HcmV?d00001 diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index f9518666..1851d855 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -7,10 +7,10 @@ global: external-secrets: image: - tag: v0.6.0-ubi + tag: v0.6.1-ubi webhook: image: - tag: v0.6.0-ubi + tag: v0.6.1-ubi certController: image: - tag: v0.6.0-ubi + tag: v0.6.1-ubi diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 4baa3da1..50c62b84 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -1563,6 +1563,49 @@ spec: spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: + conditions: + description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore + items: + description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + properties: + namespaceSelector: + description: Choose namespace using a labelSelector + 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 + required: + - key + - operator + type: object + type: array + 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 + namespaces: + description: Choose namespaces by name + items: + type: string + type: array + type: object + type: array controller: description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' type: string @@ -4493,6 +4536,49 @@ spec: spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: + conditions: + description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore + items: + description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + properties: + namespaceSelector: + description: Choose namespace using a labelSelector + 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 + required: + - key + - operator + type: object + type: array + 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 + namespaces: + description: Choose namespaces by name + items: + type: string + type: array + type: object + type: array controller: description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' type: string @@ -5764,10 +5850,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5822,10 +5908,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5906,6 +5992,7 @@ rules: verbs: - "create" - "update" + - "delete" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -5913,10 +6000,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -5939,10 +6026,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -5966,10 +6053,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5986,10 +6073,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6022,10 +6109,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6061,10 +6148,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6082,10 +6169,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -6106,10 +6193,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6126,7 +6213,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.6.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -6153,10 +6240,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6173,7 +6260,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.6.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -6189,10 +6276,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6210,7 +6297,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.6.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 2945247c..fc9597d5 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -1563,6 +1563,49 @@ spec: spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: + conditions: + description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore + items: + description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + properties: + namespaceSelector: + description: Choose namespace using a labelSelector + 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 + required: + - key + - operator + type: object + type: array + 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 + namespaces: + description: Choose namespaces by name + items: + type: string + type: array + type: object + type: array controller: description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' type: string @@ -4493,6 +4536,49 @@ spec: spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: + conditions: + description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore + items: + description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + properties: + namespaceSelector: + description: Choose namespace using a labelSelector + 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 + required: + - key + - operator + type: object + type: array + 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 + namespaces: + description: Choose namespaces by name + items: + type: string + type: array + type: object + type: array controller: description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' type: string @@ -5764,10 +5850,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5822,10 +5908,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5906,6 +5992,7 @@ rules: verbs: - "create" - "update" + - "delete" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -5913,10 +6000,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -5939,10 +6026,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -5966,10 +6053,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5986,10 +6073,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6022,10 +6109,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6061,10 +6148,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6082,10 +6169,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -6106,10 +6193,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6126,7 +6213,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.6.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -6153,10 +6240,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6173,7 +6260,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.6.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -6189,10 +6276,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.0 + helm.sh/chart: external-secrets-0.6.1 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.0" + app.kubernetes.io/version: "v0.6.1" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6210,7 +6297,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.6.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets.expected.diff b/tests/golang-external-secrets.expected.diff index f11d01ae..19d26594 100644 --- a/tests/golang-external-secrets.expected.diff +++ b/tests/golang-external-secrets.expected.diff @@ -1,6 +1,6 @@ --- tests/golang-external-secrets-naked.expected.yaml +++ tests/golang-external-secrets-normal.expected.yaml -@@ -6250,7 +6250,7 @@ +@@ -6337,7 +6337,7 @@ spec: provider: vault: From 3c609305c2b67d9afeb97a165a6794c3524ba93a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 4 Nov 2022 09:20:19 +0100 Subject: [PATCH 0609/1263] Make sure we can parse FQDNs after a secrets tag in values-secrets.yaml If we have a values-secrets.yaml like the following: secrets.region-one.blueprints.rhecoeng.com: foo: secret1: bar This currently breaks as we split all dots. Only split the first dot after secrets. So this example can work. Also add a test case to cover for this. --- ansible/plugins/modules/vault_load_secrets.py | 2 +- ansible/tests/unit/test_vault_load_secrets.py | 34 +++++++++++++++++++ ansible/tests/unit/values-secret-fqdn.yaml | 11 ++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 ansible/tests/unit/values-secret-fqdn.yaml diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index bea59e7b..82c64768 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -332,7 +332,7 @@ def get_secrets_vault_paths(module, syaml, keyname): continue # We are in the presence of either 'secrets.region-one' or 'files.cluster1' top-level keys - tmp = key.split(".") + tmp = key.split(".", 1) if len(tmp) != 2: module.fail_json(f"values-secrets.yaml key is non-conformant: {key}") diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 108153c1..e018adfa 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -286,6 +286,40 @@ def test_ensure_bad_template_checking(self): ) assert mock_run_command.call_count == 0 + def test_ensure_fqdn_secrets(self): + set_module_args( + {"values_secrets": os.path.join(self.testdir, "values-secret-fqdn.yaml")} + ) + + with patch.object(vault_load_secrets, "run_command") as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 3 + + calls = [ + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/test' secret1='foo'\"", # noqa: E501 + attempts=3, + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/region-one.blueprints.rhecoeng.com/config-demo' secret='region123'\"", # noqa: E501 + attempts=3, + ), + call( + "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/region-one/ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, + ), + ] + mock_run_command.assert_has_calls(calls) + if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/values-secret-fqdn.yaml b/ansible/tests/unit/values-secret-fqdn.yaml new file mode 100644 index 00000000..f33dd00f --- /dev/null +++ b/ansible/tests/unit/values-secret-fqdn.yaml @@ -0,0 +1,11 @@ +--- +secrets: + test: + secret1: foo + +secrets.region-one.blueprints.rhecoeng.com: + config-demo: + secret: region123 + +files.region-one: + ca: /home/michele/ca.crt From 1980aa7b44b1c15b375b870e28015278efe24079 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 4 Nov 2022 15:12:56 +0100 Subject: [PATCH 0610/1263] Reorganize tests in preparation for a V2 secrets format --- ansible/tests/unit/test_vault_load_secrets.py | 26 +++++++++++-------- .../unit/{ => v1}/mcg-values-secret.yaml | 0 .../unit/{ => v1}/template-mcg-missing.yaml | 0 .../unit/{ => v1}/template-mcg-working.yaml | 0 .../unit/{ => v1}/values-secret-broken1.yaml | 0 .../unit/{ => v1}/values-secret-broken2.yaml | 0 .../unit/{ => v1}/values-secret-broken3.yaml | 0 .../{ => v1}/values-secret-empty-files.yaml | 0 .../{ => v1}/values-secret-empty-secrets.yaml | 0 .../unit/{ => v1}/values-secret-fqdn.yaml | 0 10 files changed, 15 insertions(+), 11 deletions(-) rename ansible/tests/unit/{ => v1}/mcg-values-secret.yaml (100%) rename ansible/tests/unit/{ => v1}/template-mcg-missing.yaml (100%) rename ansible/tests/unit/{ => v1}/template-mcg-working.yaml (100%) rename ansible/tests/unit/{ => v1}/values-secret-broken1.yaml (100%) rename ansible/tests/unit/{ => v1}/values-secret-broken2.yaml (100%) rename ansible/tests/unit/{ => v1}/values-secret-broken3.yaml (100%) rename ansible/tests/unit/{ => v1}/values-secret-empty-files.yaml (100%) rename ansible/tests/unit/{ => v1}/values-secret-empty-secrets.yaml (100%) rename ansible/tests/unit/{ => v1}/values-secret-fqdn.yaml (100%) diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index e018adfa..08f77f35 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -69,7 +69,7 @@ def setUp(self): ) self.mock_module_helper.start() self.addCleanup(self.mock_module_helper.stop) - self.testdir = os.path.dirname(os.path.abspath(__file__)) + self.testdir_v1 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v1") def test_module_fail_when_required_args_missing(self): with self.assertRaises(AnsibleFailJson): @@ -96,7 +96,7 @@ def test_ensure_empty_files_but_not_secrets_is_ok(self): set_module_args( { "values_secrets": os.path.join( - self.testdir, + self.testdir_v1, "values-secret-empty-files.yaml", ) } @@ -134,7 +134,7 @@ def test_ensure_broken_files_fail(self): "values-secret-broken3.yaml", ): with self.assertRaises(AnsibleFailJson) as ansible_err: - set_module_args({"values_secrets": os.path.join(self.testdir, i)}) + set_module_args({"values_secrets": os.path.join(self.testdir_v1, i)}) vault_load_secrets.main() ret = ansible_err.exception.args[0] @@ -144,7 +144,7 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): set_module_args( { "values_secrets": os.path.join( - self.testdir, + self.testdir_v1, "values-secret-empty-secrets.yaml", ) } @@ -173,7 +173,7 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): def test_ensure_command_called(self): set_module_args( - {"values_secrets": os.path.join(self.testdir, "values-secret.yaml")} + {"values_secrets": os.path.join(self.testdir_v1, "values-secret.yaml")} ) with patch.object(vault_load_secrets, "run_command") as mock_run_command: @@ -195,7 +195,7 @@ def test_ensure_command_called(self): attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/googleapi' key='lskdjflskjdflsdjflsdkjfldsjkfldsj'\"", # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put 'secret/hub/googleapi' key='test123'\"", # noqa: E501 attempts=3, ), call( @@ -232,10 +232,12 @@ def test_ensure_command_called(self): def test_ensure_good_template_checking(self): set_module_args( { - "values_secrets": os.path.join(self.testdir, "mcg-values-secret.yaml"), + "values_secrets": os.path.join( + self.testdir_v1, "mcg-values-secret.yaml" + ), "check_missing_secrets": True, "values_secret_template": os.path.join( - self.testdir, "template-mcg-working.yaml" + self.testdir_v1, "template-mcg-working.yaml" ), } ) @@ -263,10 +265,12 @@ def test_ensure_good_template_checking(self): def test_ensure_bad_template_checking(self): set_module_args( { - "values_secrets": os.path.join(self.testdir, "mcg-values-secret.yaml"), + "values_secrets": os.path.join( + self.testdir_v1, "mcg-values-secret.yaml" + ), "check_missing_secrets": True, "values_secret_template": os.path.join( - self.testdir, "template-mcg-missing.yaml" + self.testdir_v1, "template-mcg-missing.yaml" ), } ) @@ -288,7 +292,7 @@ def test_ensure_bad_template_checking(self): def test_ensure_fqdn_secrets(self): set_module_args( - {"values_secrets": os.path.join(self.testdir, "values-secret-fqdn.yaml")} + {"values_secrets": os.path.join(self.testdir_v1, "values-secret-fqdn.yaml")} ) with patch.object(vault_load_secrets, "run_command") as mock_run_command: diff --git a/ansible/tests/unit/mcg-values-secret.yaml b/ansible/tests/unit/v1/mcg-values-secret.yaml similarity index 100% rename from ansible/tests/unit/mcg-values-secret.yaml rename to ansible/tests/unit/v1/mcg-values-secret.yaml diff --git a/ansible/tests/unit/template-mcg-missing.yaml b/ansible/tests/unit/v1/template-mcg-missing.yaml similarity index 100% rename from ansible/tests/unit/template-mcg-missing.yaml rename to ansible/tests/unit/v1/template-mcg-missing.yaml diff --git a/ansible/tests/unit/template-mcg-working.yaml b/ansible/tests/unit/v1/template-mcg-working.yaml similarity index 100% rename from ansible/tests/unit/template-mcg-working.yaml rename to ansible/tests/unit/v1/template-mcg-working.yaml diff --git a/ansible/tests/unit/values-secret-broken1.yaml b/ansible/tests/unit/v1/values-secret-broken1.yaml similarity index 100% rename from ansible/tests/unit/values-secret-broken1.yaml rename to ansible/tests/unit/v1/values-secret-broken1.yaml diff --git a/ansible/tests/unit/values-secret-broken2.yaml b/ansible/tests/unit/v1/values-secret-broken2.yaml similarity index 100% rename from ansible/tests/unit/values-secret-broken2.yaml rename to ansible/tests/unit/v1/values-secret-broken2.yaml diff --git a/ansible/tests/unit/values-secret-broken3.yaml b/ansible/tests/unit/v1/values-secret-broken3.yaml similarity index 100% rename from ansible/tests/unit/values-secret-broken3.yaml rename to ansible/tests/unit/v1/values-secret-broken3.yaml diff --git a/ansible/tests/unit/values-secret-empty-files.yaml b/ansible/tests/unit/v1/values-secret-empty-files.yaml similarity index 100% rename from ansible/tests/unit/values-secret-empty-files.yaml rename to ansible/tests/unit/v1/values-secret-empty-files.yaml diff --git a/ansible/tests/unit/values-secret-empty-secrets.yaml b/ansible/tests/unit/v1/values-secret-empty-secrets.yaml similarity index 100% rename from ansible/tests/unit/values-secret-empty-secrets.yaml rename to ansible/tests/unit/v1/values-secret-empty-secrets.yaml diff --git a/ansible/tests/unit/values-secret-fqdn.yaml b/ansible/tests/unit/v1/values-secret-fqdn.yaml similarity index 100% rename from ansible/tests/unit/values-secret-fqdn.yaml rename to ansible/tests/unit/v1/values-secret-fqdn.yaml From 0896a3dfe2f485c2ac5d84f3dd56dc6efbdb2ce4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 4 Nov 2022 15:25:14 +0100 Subject: [PATCH 0611/1263] Add ansible unittest --- .github/workflows/ansible-unittest.yml | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/ansible-unittest.yml diff --git a/.github/workflows/ansible-unittest.yml b/.github/workflows/ansible-unittest.yml new file mode 100644 index 00000000..9affd9f6 --- /dev/null +++ b/.github/workflows/ansible-unittest.yml @@ -0,0 +1,47 @@ +--- +name: Ansible unit tests + +# +# Documentation: +# https://help.github.com/en/articles/workflow-syntax-for-github-actions +# + +############################# +# Start the job on all push # +############################# +on: [push, pull_request] + +############### +# Set the Job # +############### +jobs: + ansible_unittests: + # Name the Job + name: Ansible unit tests + strategy: + matrix: + python-version: [3.9.7] + # Set the agent to run on + runs-on: ubuntu-latest + + ################## + # Load all steps # + ################## + steps: + ########################## + # Checkout the code base # + ########################## + - name: Checkout Code + uses: actions/checkout@v2 + with: + # Full git history is needed to get a proper list of changed files within `super-linter` + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + + - name: Run make ansible-unittest + run: | + make ansible-unittest From 61f5e9429e983508b1e445a8a4c396cb26da4814 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 4 Nov 2022 15:26:25 +0100 Subject: [PATCH 0612/1263] Use proper 3.9 version that exists in ubuntu --- .github/workflows/ansible-unittest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ansible-unittest.yml b/.github/workflows/ansible-unittest.yml index 9affd9f6..619b12d8 100644 --- a/.github/workflows/ansible-unittest.yml +++ b/.github/workflows/ansible-unittest.yml @@ -20,7 +20,7 @@ jobs: name: Ansible unit tests strategy: matrix: - python-version: [3.9.7] + python-version: [3.9.15] # Set the agent to run on runs-on: ubuntu-latest From 7a679f6f03a58006a214bf2684d9f5365f557db2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 4 Nov 2022 15:27:44 +0100 Subject: [PATCH 0613/1263] Install pytest via pip --- .github/workflows/ansible-unittest.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ansible-unittest.yml b/.github/workflows/ansible-unittest.yml index 619b12d8..267f1227 100644 --- a/.github/workflows/ansible-unittest.yml +++ b/.github/workflows/ansible-unittest.yml @@ -42,6 +42,11 @@ jobs: with: python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest + - name: Run make ansible-unittest run: | make ansible-unittest From 3d79550ef537a959040803dbf4f059b984419ee2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 4 Nov 2022 15:29:06 +0100 Subject: [PATCH 0614/1263] Add ansible in the pip modules --- .github/workflows/ansible-unittest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ansible-unittest.yml b/.github/workflows/ansible-unittest.yml index 267f1227..df28b96f 100644 --- a/.github/workflows/ansible-unittest.yml +++ b/.github/workflows/ansible-unittest.yml @@ -45,7 +45,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install pytest + pip install pytest ansible - name: Run make ansible-unittest run: | From 0a51b585fa920d6d4c5165c3fa97c23a18aa8936 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 4 Nov 2022 16:28:15 +0100 Subject: [PATCH 0615/1263] Add support for creating temporary files and referncing them in tests This will allow us to run tests in an environment (CI) where there are no existing known files that we can use to test the vault commands. --- ansible/tests/unit/test_vault_load_secrets.py | 25 +++++++++++++++---- .../unit/v1/values-secret-empty-files.yaml | 1 - .../unit/v1/values-secret-empty-secrets.yaml | 2 +- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 08f77f35..fbd23113 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -20,6 +20,7 @@ import json import os import sys +import tempfile import unittest from unittest.mock import call, patch @@ -62,6 +63,14 @@ def fail_json(*args, **kwargs): raise AnsibleFailJson(kwargs) +def template_values_secret(fname, find, replacewith): + """creates a temp file from a yaml file and replaces some text""" + with open(fname, "r") as fh, tempfile.NamedTemporaryFile(delete=False) as tmp: + content = fh.read() + tmp.write(content.replace(find, replacewith).encode()) + return tmp.name + + class TestMyModule(unittest.TestCase): def setUp(self): self.mock_module_helper = patch.multiple( @@ -141,12 +150,18 @@ def test_ensure_broken_files_fail(self): self.assertEqual(ret["failed"], True) def test_ensure_empty_secrets_but_not_files_is_ok(self): + tmp_testfile = tempfile.NamedTemporaryFile(delete=False) + tmp_valuesfile = template_values_secret( + os.path.join( + self.testdir_v1, + "values-secret-empty-secrets.yaml", + ), + "__PLACEHOLDER__", + tmp_testfile.name, + ) set_module_args( { - "values_secrets": os.path.join( - self.testdir_v1, - "values-secret-empty-secrets.yaml", - ) + "values_secrets": tmp_valuesfile, } ) @@ -165,7 +180,7 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): calls = [ call( - "cat '/home/michele/.ssh/id_rsa.pub' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/publickey b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + f"cat '{tmp_testfile.name}' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/publickey b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), ] diff --git a/ansible/tests/unit/v1/values-secret-empty-files.yaml b/ansible/tests/unit/v1/values-secret-empty-files.yaml index b609c1fe..078166a0 100644 --- a/ansible/tests/unit/v1/values-secret-empty-files.yaml +++ b/ansible/tests/unit/v1/values-secret-empty-files.yaml @@ -13,4 +13,3 @@ secrets: # Required for automated spoke deployment files: # # ssh-rsa AAA... - # publickey: ~/.ssh/id_rsa.pub diff --git a/ansible/tests/unit/v1/values-secret-empty-secrets.yaml b/ansible/tests/unit/v1/values-secret-empty-secrets.yaml index 0624ecb5..fb36fc65 100644 --- a/ansible/tests/unit/v1/values-secret-empty-secrets.yaml +++ b/ansible/tests/unit/v1/values-secret-empty-secrets.yaml @@ -13,4 +13,4 @@ secrets: # Required for automated spoke deployment files: # # ssh-rsa AAA... - publickey: ~/.ssh/id_rsa.pub + publickey: __PLACEHOLDER__ From 6ac30e5a831c486a0a1f2ccfe288f43e2b189bf5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 4 Nov 2022 16:49:49 +0100 Subject: [PATCH 0616/1263] Revert to a simpler way to test file upload --- ansible/tests/unit/test_vault_load_secrets.py | 31 ++++++------------- .../unit/v1/values-secret-empty-secrets.yaml | 2 +- ansible/tests/unit/v1/values-secret-fqdn.yaml | 2 +- 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index fbd23113..03dd1737 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -63,14 +63,6 @@ def fail_json(*args, **kwargs): raise AnsibleFailJson(kwargs) -def template_values_secret(fname, find, replacewith): - """creates a temp file from a yaml file and replaces some text""" - with open(fname, "r") as fh, tempfile.NamedTemporaryFile(delete=False) as tmp: - content = fh.read() - tmp.write(content.replace(find, replacewith).encode()) - return tmp.name - - class TestMyModule(unittest.TestCase): def setUp(self): self.mock_module_helper = patch.multiple( @@ -79,6 +71,7 @@ def setUp(self): self.mock_module_helper.start() self.addCleanup(self.mock_module_helper.stop) self.testdir_v1 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v1") + self.testfile = open('/tmp/ca.crt', 'w') def test_module_fail_when_required_args_missing(self): with self.assertRaises(AnsibleFailJson): @@ -150,18 +143,12 @@ def test_ensure_broken_files_fail(self): self.assertEqual(ret["failed"], True) def test_ensure_empty_secrets_but_not_files_is_ok(self): - tmp_testfile = tempfile.NamedTemporaryFile(delete=False) - tmp_valuesfile = template_values_secret( - os.path.join( - self.testdir_v1, - "values-secret-empty-secrets.yaml", - ), - "__PLACEHOLDER__", - tmp_testfile.name, - ) set_module_args( { - "values_secrets": tmp_valuesfile, + "values_secrets": os.path.join( + self.testdir_v1, + "values-secret-empty-secrets.yaml", + ), } ) @@ -180,7 +167,7 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): calls = [ call( - f"cat '{tmp_testfile.name}' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/publickey b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + f"cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/publickey b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), ] @@ -234,11 +221,11 @@ def test_ensure_command_called(self): attempts=3, ), call( - "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/cluster_alejandro_ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/cluster_alejandro_ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), call( - "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/region-one/ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/region-one/ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), ] @@ -333,7 +320,7 @@ def test_ensure_fqdn_secrets(self): attempts=3, ), call( - "cat '/home/michele/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/region-one/ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/region-one/ca b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), ] diff --git a/ansible/tests/unit/v1/values-secret-empty-secrets.yaml b/ansible/tests/unit/v1/values-secret-empty-secrets.yaml index fb36fc65..13739a27 100644 --- a/ansible/tests/unit/v1/values-secret-empty-secrets.yaml +++ b/ansible/tests/unit/v1/values-secret-empty-secrets.yaml @@ -13,4 +13,4 @@ secrets: # Required for automated spoke deployment files: # # ssh-rsa AAA... - publickey: __PLACEHOLDER__ + publickey: /tmp/ca.crt diff --git a/ansible/tests/unit/v1/values-secret-fqdn.yaml b/ansible/tests/unit/v1/values-secret-fqdn.yaml index f33dd00f..c77496c1 100644 --- a/ansible/tests/unit/v1/values-secret-fqdn.yaml +++ b/ansible/tests/unit/v1/values-secret-fqdn.yaml @@ -8,4 +8,4 @@ secrets.region-one.blueprints.rhecoeng.com: secret: region123 files.region-one: - ca: /home/michele/ca.crt + ca: /tmp/ca.crt From cab87d4882be0da56f894188b7777eebf53ea67f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 4 Nov 2022 16:52:13 +0100 Subject: [PATCH 0617/1263] Add tearDown() in unit tests --- ansible/tests/unit/test_vault_load_secrets.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 03dd1737..060fd977 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -73,6 +73,13 @@ def setUp(self): self.testdir_v1 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v1") self.testfile = open('/tmp/ca.crt', 'w') + def tearDown(self): + self.testfile.close() + try: + os.remove('/tmp/ca.crt') + except OSError: + pass + def test_module_fail_when_required_args_missing(self): with self.assertRaises(AnsibleFailJson): set_module_args({}) From 0ee72d12a007227dea90e49f76a24def3fc1cac4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 4 Nov 2022 17:20:51 +0100 Subject: [PATCH 0618/1263] Add ansible/tests/unit/v1/values-secret-good.yaml --- ansible/tests/unit/test_vault_load_secrets.py | 2 +- ansible/tests/unit/v1/values-secret-good.yaml | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 ansible/tests/unit/v1/values-secret-good.yaml diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 060fd977..294c5f36 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -182,7 +182,7 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): def test_ensure_command_called(self): set_module_args( - {"values_secrets": os.path.join(self.testdir_v1, "values-secret.yaml")} + {"values_secrets": os.path.join(self.testdir_v1, "values-secret-good.yaml")} ) with patch.object(vault_load_secrets, "run_command") as mock_run_command: diff --git a/ansible/tests/unit/v1/values-secret-good.yaml b/ansible/tests/unit/v1/values-secret-good.yaml new file mode 100644 index 00000000..6db47285 --- /dev/null +++ b/ansible/tests/unit/v1/values-secret-good.yaml @@ -0,0 +1,36 @@ +--- +secrets: + # NEVER COMMIT THESE VALUES TO GIT + config-demo: + # Secret used for demonstrating vault storage, external secrets, and ACM distribution + secret: demo123 + googleapi: + key: test123 + + cluster_alejandro: + name: alejandro + bearerToken: sha256~bumxi-012345678901233455675678678098-abcdef + + test: + s3.accessKey: "1234" + s3.secretKey: "4321" + + test2: + s3.accessKey: accessKey + s3.secretKey: secretKey + s3Secret: fooo + + test3: + s3.accessKey: "aaaaa" + s3.secretKey: "bbbbbbbb" + +files: + # oc extract -n openshift-config cm/kube-root-ca.crt --to=/home/michele/ --keys=ca.crt + cluster_alejandro_ca: /tmp/ca.crt + +secrets.region-one: + config-demo: + secret: region123 + +files.region-one: + ca: /tmp/ca.crt From e3b58f432e89fd04497c542073102a1190c4be8e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 4 Nov 2022 17:21:32 +0100 Subject: [PATCH 0619/1263] Fix super linter warnings --- ansible/tests/unit/test_vault_load_secrets.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 294c5f36..55e18bdf 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -20,7 +20,6 @@ import json import os import sys -import tempfile import unittest from unittest.mock import call, patch @@ -71,12 +70,12 @@ def setUp(self): self.mock_module_helper.start() self.addCleanup(self.mock_module_helper.stop) self.testdir_v1 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v1") - self.testfile = open('/tmp/ca.crt', 'w') + self.testfile = open("/tmp/ca.crt", "w") def tearDown(self): self.testfile.close() try: - os.remove('/tmp/ca.crt') + os.remove("/tmp/ca.crt") except OSError: pass @@ -174,7 +173,7 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): calls = [ call( - f"cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/publickey b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put secret/hub/publickey b64content=- content=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), ] From b8409d439e28794c2cf8f24431cda138631b1be0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 6 Nov 2022 20:57:59 +0100 Subject: [PATCH 0620/1263] Add support for /values--.yaml In the same vein as /values--.yaml support, we allow for clusterplatform overrides. Values files now look like the following: /values-global.yaml, /values-group-one.yaml, /values-AWS-group-one.yaml, /values-4.11-group-one.yaml Tested with https://github.com/hybrid-cloud-patterns/patterns-operator/pull/42 and could get the correct /values file inclusion in cluster-wide argo and on the namespaced-argo on both the hub and the spoke clusters. --- Changes.md | 4 ++++ acm/templates/policies/application-policies.yaml | 3 +++ clustergroup/templates/plumbing/applications.yaml | 7 +++++++ tests/acm-normal.expected.yaml | 6 ++++++ tests/acm.expected.diff | 8 +++++++- tests/clustergroup-normal.expected.yaml | 4 ++++ tests/clustergroup.expected.diff | 12 ++++++++---- 7 files changed, 39 insertions(+), 5 deletions(-) diff --git a/Changes.md b/Changes.md index bfe6b380..4c4daf37 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## November 6, 2022 + +* Add support for /values--.yaml (e.g. /values-AWS-group-one.yaml) + ## October 28, 2022 * Updated vault helm chart to v0.22.1 and vault containers to 1.12.0 diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 5fc45d69..c62fd68e 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -45,6 +45,7 @@ spec: valueFiles: - "/values-global.yaml" - "/values-{{ .name }}.yaml" + - '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}-{{ .name }}.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - '/values-{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}-{{ .name }}.yaml' @@ -70,6 +71,8 @@ spec: # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}' + - name: global.clusterPlatform + value: {{ $.Values.global.clusterPlatform }} - name: clusterGroup.name value: {{ $group.name }} {{- range .helmOverrides }} diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index f7e165d8..57c1cad8 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -71,6 +71,8 @@ spec: value: {{ $.Values.global.clusterDomain }} - name: global.clusterVersion value: "{{ $.Values.global.clusterVersion }}" + - name: global.clusterPlatform + value: "{{ $.Values.global.clusterPlatform }}" - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain @@ -149,6 +151,9 @@ spec: valueFiles: - "/values-global.yaml" - "/values-{{ $.Values.clusterGroup.name }}.yaml" + {{- if $.Values.global.clusterPlatform }} + - "/values-{{ $.Values.global.clusterPlatform }}-{{ $.Values.clusterGroup.name }}.yaml" + {{- end }} {{- if $.Values.global.clusterVersion }} - "/values-{{ $.Values.global.clusterVersion }}-{{ $.Values.clusterGroup.name }}.yaml" {{- end }} @@ -169,6 +174,8 @@ spec: value: {{ $.Values.global.clusterDomain }} - name: global.clusterVersion value: "{{ $.Values.global.clusterVersion }}" + - name: global.clusterPlatform + value: "{{ $.Values.global.clusterPlatform }}" - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index bec7d031..bf52bfc1 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -527,6 +527,7 @@ spec: valueFiles: - "/values-global.yaml" - "/values-acm-edge.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-edge.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-edge.yaml' @@ -549,6 +550,8 @@ spec: # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + - name: global.clusterPlatform + value: - name: clusterGroup.name value: acm-edge - name: clusterGroup.isHubCluster @@ -614,6 +617,7 @@ spec: valueFiles: - "/values-global.yaml" - "/values-acm-provision-edge.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-edge.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-provision-edge.yaml' @@ -636,6 +640,8 @@ spec: # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + - name: global.clusterPlatform + value: - name: clusterGroup.name value: acm-provision-edge - name: clusterGroup.isHubCluster diff --git a/tests/acm.expected.diff b/tests/acm.expected.diff index dd994b75..25b35645 100644 --- a/tests/acm.expected.diff +++ b/tests/acm.expected.diff @@ -461,7 +461,7 @@ # Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule -@@ -44,6 +482,181 @@ +@@ -44,6 +482,187 @@ values: - OpenShift --- @@ -510,6 +510,7 @@ + valueFiles: + - "/values-global.yaml" + - "/values-acm-edge.yaml" ++ - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-edge.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-edge.yaml' @@ -532,6 +533,8 @@ + # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) + - name: global.clusterVersion + value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' ++ - name: global.clusterPlatform ++ value: + - name: clusterGroup.name + value: acm-edge + - name: clusterGroup.isHubCluster @@ -597,6 +600,7 @@ + valueFiles: + - "/values-global.yaml" + - "/values-acm-provision-edge.yaml" ++ - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-edge.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-provision-edge.yaml' @@ -619,6 +623,8 @@ + # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) + - name: global.clusterVersion + value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' ++ - name: global.clusterPlatform ++ value: + - name: clusterGroup.name + value: acm-provision-edge + - name: clusterGroup.isHubCluster diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index c059c129..94ecfeca 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -528,6 +528,8 @@ spec: value: region.example.com - name: global.clusterVersion value: "" + - name: global.clusterPlatform + value: "" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -581,6 +583,8 @@ spec: value: region.example.com - name: global.clusterVersion value: "" + - name: global.clusterPlatform + value: "" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain diff --git a/tests/clustergroup.expected.diff b/tests/clustergroup.expected.diff index 0bd2da15..7f04814c 100644 --- a/tests/clustergroup.expected.diff +++ b/tests/clustergroup.expected.diff @@ -239,7 +239,7 @@ roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole -@@ -45,16 +255,587 @@ +@@ -45,16 +255,591 @@ - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-application-controller name: example-gitops-argocd-application-controller @@ -519,6 +519,8 @@ + value: region.example.com + - name: global.clusterVersion + value: "" ++ - name: global.clusterPlatform ++ value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain @@ -572,6 +574,8 @@ + value: region.example.com + - name: global.clusterVersion + value: "" ++ - name: global.clusterPlatform ++ value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain @@ -830,7 +834,7 @@ --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 -@@ -65,7 +846,7 @@ +@@ -65,7 +850,7 @@ # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: example-gitops @@ -839,7 +843,7 @@ annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: -@@ -94,10 +875,10 @@ +@@ -94,10 +879,10 @@ --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE @@ -854,7 +858,7 @@ --set clusterGroup.name=example --post-renderer ./kustomize"] applicationSet: -@@ -174,11 +955,59 @@ +@@ -174,11 +959,59 @@ kind: ConsoleLink metadata: name: example-gitops-link From cdaa2de2f5b709caba370129dfdaa9fe604565f1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 7 Nov 2022 11:45:51 +0100 Subject: [PATCH 0621/1263] Fix gitleaks file after last unit test change --- .github/linters/.gitleaks.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/linters/.gitleaks.toml b/.github/linters/.gitleaks.toml index 7f59b235..b80cdc04 100644 --- a/.github/linters/.gitleaks.toml +++ b/.github/linters/.gitleaks.toml @@ -4,5 +4,5 @@ files = [ "ansible/plugins/modules/*.py", "ansible/tests/unit/test_*.py", - "ansible/tests/unit/*.yaml", + "ansible/tests/unit/v1/*.yaml", ] From 1d9736c558b0e18302c79a7d0fb2b0dded022440 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 18 Nov 2022 02:55:01 +0000 Subject: [PATCH 0622/1263] Allow multiple test inputs It is unrealistic to try and test every permutation of every common chart with a single input. Tweak the scripts to look for mutliple inputs and run the tests for each, and bring in some actual inputs from existing patterns --- Makefile | 6 +- examples/industrial-edge-factory.yaml | 81 + examples/industrial-edge-hub.yaml | 213 + examples/medical-diagnosis-hub.yaml | 231 + scripts/lint.sh | 18 + scripts/test.sh | 11 +- .../acm-industrial-edge-factory.expected.yaml | 93 + tests/acm-industrial-edge-hub.expected.yaml | 221 + tests/acm-medical-diagnosis-hub.expected.yaml | 212 + ...roup-industrial-edge-factory.expected.yaml | 660 ++ ...tergroup-industrial-edge-hub.expected.yaml | 1416 ++++ ...rgroup-medical-diagnosis-hub.expected.yaml | 1532 ++++ tests/examples-blank-naked.expected.yaml | 6 - tests/examples-blank-normal.expected.yaml | 6 - tests/examples-blank.expected.diff | 0 ...les-kustomize-renderer-naked.expected.yaml | 36 - ...es-kustomize-renderer-normal.expected.yaml | 36 - .../examples-kustomize-renderer.expected.diff | 19 - ...rets-industrial-edge-factory.expected.yaml | 6421 +++++++++++++++++ ...-secrets-industrial-edge-hub.expected.yaml | 6421 +++++++++++++++++ ...ecrets-medical-diagnosis-hub.expected.yaml | 6421 +++++++++++++++++ ...ault-industrial-edge-factory.expected.yaml | 410 ++ ...rp-vault-industrial-edge-hub.expected.yaml | 410 ++ ...-vault-medical-diagnosis-hub.expected.yaml | 410 ++ ...tall-industrial-edge-factory.expected.yaml | 66 + .../install-industrial-edge-hub.expected.yaml | 66 + ...nstall-medical-diagnosis-hub.expected.yaml | 66 + ...tall-industrial-edge-factory.expected.yaml | 29 + ...-install-industrial-edge-hub.expected.yaml | 29 + ...nstall-medical-diagnosis-hub.expected.yaml | 29 + 30 files changed, 25467 insertions(+), 108 deletions(-) create mode 100644 examples/industrial-edge-factory.yaml create mode 100644 examples/industrial-edge-hub.yaml create mode 100644 examples/medical-diagnosis-hub.yaml create mode 100755 scripts/lint.sh create mode 100644 tests/acm-industrial-edge-factory.expected.yaml create mode 100644 tests/acm-industrial-edge-hub.expected.yaml create mode 100644 tests/acm-medical-diagnosis-hub.expected.yaml create mode 100644 tests/clustergroup-industrial-edge-factory.expected.yaml create mode 100644 tests/clustergroup-industrial-edge-hub.expected.yaml create mode 100644 tests/clustergroup-medical-diagnosis-hub.expected.yaml delete mode 100644 tests/examples-blank-naked.expected.yaml delete mode 100644 tests/examples-blank-normal.expected.yaml delete mode 100644 tests/examples-blank.expected.diff delete mode 100644 tests/examples-kustomize-renderer-naked.expected.yaml delete mode 100644 tests/examples-kustomize-renderer-normal.expected.yaml delete mode 100644 tests/examples-kustomize-renderer.expected.diff create mode 100644 tests/golang-external-secrets-industrial-edge-factory.expected.yaml create mode 100644 tests/golang-external-secrets-industrial-edge-hub.expected.yaml create mode 100644 tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml create mode 100644 tests/hashicorp-vault-industrial-edge-factory.expected.yaml create mode 100644 tests/hashicorp-vault-industrial-edge-hub.expected.yaml create mode 100644 tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml create mode 100644 tests/install-industrial-edge-factory.expected.yaml create mode 100644 tests/install-industrial-edge-hub.expected.yaml create mode 100644 tests/install-medical-diagnosis-hub.expected.yaml create mode 100644 tests/operator-install-industrial-edge-factory.expected.yaml create mode 100644 tests/operator-install-industrial-edge-hub.expected.yaml create mode 100644 tests/operator-install-medical-diagnosis-hub.expected.yaml diff --git a/Makefile b/Makefile index 56261bba..a0b94bd9 100644 --- a/Makefile +++ b/Makefile @@ -32,12 +32,12 @@ help: ## This help message show: ## show the starting template without installing it helm template common/install/ --name-template $(NAME) $(HELM_OPTS) -CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | sed -e 's/.\///') +CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | grep -v examples | sed -e 's/.\///') test: ## run helm tests - @for t in $(CHARTS); do common/scripts/test.sh $$t all "$(TEST_OPTS) $(PATTERN_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done + @for t in $(CHARTS); do common/scripts/test.sh $$t all "$(TEST_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done helmlint: ## run helm lint - @for t in $(CHARTS); do helm lint $(TEST_OPTS) $(PATTERN_OPTS) $$t; if [ $$? != 0 ]; then exit 1; fi; done + @for t in $(CHARTS); do common/scripts/lint.sh $$t $(TEST_OPTS); if [ $$? != 0 ]; then exit 1; fi; done API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.10/ KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition' diff --git a/examples/industrial-edge-factory.yaml b/examples/industrial-edge-factory.yaml new file mode 100644 index 00000000..9ed1e8d3 --- /dev/null +++ b/examples/industrial-edge-factory.yaml @@ -0,0 +1,81 @@ +clusterGroup: + name: factory + isHubCluster: false + + namespaces: + - manuela-stormshift-line-dashboard + - manuela-stormshift-machine-sensor + - manuela-stormshift-messaging + - manuela-factory-ml-workspace + + operatorgroupExcludes: + - manuela-factory-ml-workspace + + subscriptions: + - name: opendatahub-operator + channel: stable + source: community-operators + + - name: seldon-operator + namespace: manuela-stormshift-messaging + channel: stable + source: community-operators + + - name: amq-streams + namespace: manuela-stormshift-messaging + channel: stable + + - name: amq-broker-rhel8 + namespace: manuela-stormshift-messaging + channel: 7.x + + - name: red-hat-camel-k + namespace: manuela-stormshift-messaging + channel: stable + + projects: + - factory + + applications: + - name: stormshift + project: factory + path: charts/factory/manuela-stormshift + plugin: + name: helm-with-kustomize + + - name: odh + namespace: manuela-factory-ml-workspace + project: factory + path: charts/datacenter/opendatahub + +# +# To have apps in multiple flavors, use namespaces and use helm overrides as appropriate +# +# - name: pipelines +# namespace: production +# project: datacenter +# path: applications/pipeline +# repoURL: https://github.com/you/applications.git +# targetRevision: stable +# overrides: +# - name: myparam +# value: myparam +# +# - name: pipelines +# namespace: staging +# project: datacenter +# path: applications/pipeline +# repoURL: https://github.com/you/applications.git +# targetRevision: main +# +# Additional applications +# Be sure to include additional resources your apps will require +# +X machines +# +Y RAM +# +Z CPU +# - name: vendor-app +# namespace: default +# project: vendor +# path: path/to/myapp +# repoURL: https://github.com/vendor/applications.git +# targetRevision: main diff --git a/examples/industrial-edge-hub.yaml b/examples/industrial-edge-hub.yaml new file mode 100644 index 00000000..662ac85d --- /dev/null +++ b/examples/industrial-edge-hub.yaml @@ -0,0 +1,213 @@ +clusterGroup: + name: datacenter + isHubCluster: true + # Note: setting this to true stores the vault unseal keys inside a cluster secret and + # is fundamentally insecure + insecureUnsealVaultInsideCluster: true + + namespaces: + - golang-external-secrets + - external-secrets + - open-cluster-management + - manuela-ml-workspace + - manuela-tst-all + - manuela-ci + - manuela-data-lake + - staging + - vault + + operatorgroupExcludes: + - manuela-ml-workspace + + subscriptions: + acm: + name: advanced-cluster-management + namespace: open-cluster-management + channel: release-2.6 + + amqbroker-prod: + name: amq-broker-rhel8 + namespace: manuela-tst-all + channel: 7.x + + amqstreams-prod-dev: + name: amq-streams + namespaces: + - manuela-data-lake + - manuela-tst-all + channel: stable + + camelk-prod-dev: + name: red-hat-camel-k + namespaces: + - manuela-data-lake + - manuela-tst-all + channel: stable + + seldon-prod-dev: + name: seldon-operator + namespaces: + - manuela-ml-workspace + - manuela-tst-all + channel: stable + source: community-operators + + pipelines: + name: openshift-pipelines-operator-rh + channel: latest + source: redhat-operators + + odh: + name: opendatahub-operator + channel: stable + source: community-operators + + projects: + - datacenter + - production-datalake + - golang-external-secrets + - vault + + applications: + acm: + name: acm + namespace: open-cluster-management + project: datacenter + path: common/acm + ignoreDifferences: + - group: internal.open-cluster-management.io + kind: ManagedClusterInfo + jsonPointers: + - /spec/loggingCA + + odh: + name: odh + namespace: manuela-ml-workspace + project: datacenter + path: charts/datacenter/opendatahub + + pipelines: + name: pipelines + namespace: manuela-ci + project: datacenter + path: charts/datacenter/pipelines + + production-data-lake: + name: production-data-lake + namespace: manuela-data-lake + project: production-datalake + path: charts/datacenter/manuela-data-lake + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status + - group: image.openshift.io + kind: ImageStream + jsonPointers: + - /spec/tags + - group: apps.openshift.io + kind: DeploymentConfig + jsonPointers: + - /spec/template/spec/containers/0/image + + test: + name: manuela-test + namespace: manuela-tst-all + project: datacenter + path: charts/datacenter/manuela-tst + plugin: + name: helm-with-kustomize + + vault: + name: vault + namespace: vault + project: datacenter + chart: vault + repoURL: https://helm.releases.hashicorp.com + targetRevision: v0.20.1 + overrides: + - name: global.openshift + value: "true" + - name: injector.enabled + value: "false" + - name: ui.enabled + value: "true" + - name: ui.serviceType + value: LoadBalancer + - name: server.route.enabled + value: "true" + - name: server.route.host + value: null + - name: server.route.tls.termination + value: edge + - name: server.image.repository + value: "registry.connect.redhat.com/hashicorp/vault" + - name: server.image.tag + value: "1.10.3-ubi" + + secrets-operator: + name: golang-external-secrets + namespace: golang-external-secrets + project: golang-external-secrets + path: common/golang-external-secrets + + secrets: + name: external-secrets + namespace: external-secrets + project: golang-external-secrets + path: charts/datacenter/external-secrets + +# To have apps in multiple flavors, use namespaces and use helm overrides as appropriate +# +# - name: pipelines +# namespace: production +# project: datacenter +# path: applications/pipeline +# repoURL: https://github.com/you/applications.git +# targetRevision: stable +# overrides: +# - name: myparam +# value: myparam +# +# - name: pipelines +# namespace: staging +# project: datacenter +# path: applications/pipeline +# repoURL: https://github.com/you/applications.git +# targetRevision: main +# +# Additional applications +# Be sure to include additional resources your apps will require +# +X machines +# +Y RAM +# +Z CPU +# - name: vendor-app +# namespace: default +# project: vendor +# path: path/to/myapp +# repoURL: https://github.com/vendor/applications.git +# targetRevision: main + + managedClusterGroups: + factory: + name: factory + # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git + # targetRevision: main + helmOverrides: + # Values must be strings! + - name: clusterGroup.isHubCluster + value: "false" + clusterSelector: + matchLabels: + clusterGroup: factory + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift + diff --git a/examples/medical-diagnosis-hub.yaml b/examples/medical-diagnosis-hub.yaml new file mode 100644 index 00000000..e303b93f --- /dev/null +++ b/examples/medical-diagnosis-hub.yaml @@ -0,0 +1,231 @@ +clusterGroup: + name: hub + isHubCluster: true + # Note: setting this to true stores the vault unseal keys inside a cluster secret and + # is fundamentally insecure + insecureUnsealVaultInsideCluster: true + + namespaces: + - open-cluster-management + - openshift-serverless + - opendatahub + - openshift-storage + - xraylab-1 + - knative-serving + - staging + - vault + - golang-external-secrets + + subscriptions: + amq-streams: + name: amq-streams + namespace: xraylab-1 + channel: stable + + grafana: + name: grafana-operator + namespace: xraylab-1 + channel: v4 + source: community-operators + + odf: + name: odf-operator + namespace: openshift-storage + channel: stable-4.11 + + severless: + name: serverless-operator + channel: stable + + opendatahub: + name: opendatahub-operator + source: community-operators + + projects: + - hub + - medical-diagnosis + + applications: + vault: + name: vault + namespace: vault + project: hub + chart: vault + repoURL: https://helm.releases.hashicorp.com + targetRevision: v0.20.1 + overrides: + - name: global.openshift + value: "true" + - name: injector.enabled + value: "false" + - name: ui.enabled + value: "true" + - name: ui.serviceType + value: LoadBalancer + - name: server.route.enabled + value: "true" + - name: server.route.host + value: null + - name: server.route.tls.termination + value: edge + - name: server.image.repository + value: "registry.connect.redhat.com/hashicorp/vault" + - name: server.image.tag + value: "1.10.3-ubi" + + golang-external-secrets: + name: golang-external-secrets + namespace: golang-external-secrets + project: hub + path: common/golang-external-secrets + + opendatahub: + name: odh + namespace: opendatahub + project: medical-diagnosis + path: charts/all/opendatahub + + openshift-data-foundations: + name: odf + namespace: openshift-storage + project: medical-diagnosis + path: charts/all/openshift-data-foundations + + openshift-serverless: + name: serverless + namespace: xraylab-1 + project: medical-diagnosis + path: charts/all/openshift-serverless + + kafka: + name: kafka + namespace: xraylab-1 + project: medical-diagnosis + path: charts/all/kafka + + kafdrop: + name: kafdrop + namespace: xraylab-1 + project: medical-diagnosis + path: charts/all/kafdrop + + service-account: + name: xraylab-service-account + namespace: xraylab-1 + project: medical-diagnosis + path: charts/all/medical-diagnosis/service-account + + xraylab-init: + name: xraylab-init + namespace: xraylab-1 + project: medical-diagnosis + path: charts/all/medical-diagnosis/xray-init + + xraylab-database: + name: xraylab-database + namespace: xraylab-1 + project: medical-diagnosis + path: charts/all/medical-diagnosis/database + + xraylab-grafana-dashboards: + name: xraylab-grafana-dashboards + namespace: xraylab-1 + project: medical-diagnosis + path: charts/all/medical-diagnosis/grafana + + xraylab-image-server: + name: xraylab-image-server + namespace: xraylab-1 + project: medical-diagnosis + path: charts/all/medical-diagnosis/image-server + ignoreDifferences: + - group: apps.openshift.io + kind: DeploymentConfig + jqPathExpressions: + - '.spec.template.spec.containers[].image' + + xraylab-image-generator: + name: xraylab-image-generator + namespace: xraylab-1 + project: medical-diagnosis + path: charts/all/medical-diagnosis/image-generator + ignoreDifferences: + - group: apps.openshift.io + kind: DeploymentConfig + jqPathExpressions: + - '.spec.template.spec.containers[].image' + + imperative: + # NOTE: We *must* use lists and not hashes. As hashes lose ordering once parsed by helm + # The default schedule is every 10 minutes: imperative.schedule + # Total timeout of all jobs is 1h: imperative.activeDeadlineSeconds + # imagePullPolicy is set to always: imperative.imagePullPolicy + # For additional overrides that apply to the jobs, please refer to + # https://hybrid-cloud-patterns.io/imperative-actions/#additional-job-customizations + jobs: + - name: regional-ca + # ansible playbook to be run + playbook: ansible/playbooks/on-hub-get-regional-ca.yml + # per playbook timeout in seconds + timeout: 234 + # verbosity: "-v" + + managedClusterGroups: + region-one: + name: region-one + helmOverrides: + - name: clusterGroup.isHubCluster + value: false + clusterSelector: + matchLabels: + clusterGroup: region-one + +# To have apps in multiple flavors, use namespaces and use helm overrides as appropriate +# +# pipelines: +# name: pipelines +# namespace: production +# project: datacenter +# path: applications/pipeline +# repoURL: https://github.com/you/applications.git +# targetRevision: stable +# overrides: +# - name: myparam +# value: myparam +# +# pipelines_staging: +# - name: pipelines +# namespace: staging +# project: datacenter +# path: applications/pipeline +# repoURL: https://github.com/you/applications.git +# targetRevision: main +# +# Additional applications +# Be sure to include additional resources your apps will require +# +X machines +# +Y RAM +# +Z CPU +# vendor-app: +# name: vendor-app +# namespace: default +# project: vendor +# path: path/to/myapp +# repoURL: https://github.com/vendor/applications.git +# targetRevision: main + +# managedSites: +# factory: +# name: factory +# # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git +# targetRevision: main +# path: applications/factory +# helmOverrides: +# - name: site.isHubCluster +# value: false +# clusterSelector: +# matchExpressions: +# - key: vendor +# operator: In +# values: +# - OpenShift diff --git a/scripts/lint.sh b/scripts/lint.sh new file mode 100755 index 00000000..3a3d8e6f --- /dev/null +++ b/scripts/lint.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# helm template (even with --dry-run) can interact with the cluster +# This won't protect us if a user has ~/.kube +# Also call helm template with a non existing --kubeconfig while we're at it +unset KUBECONFIG +target=$1 +shift +name=$(echo $target | sed -e s@/@-@g -e s@charts-@@) + +# Test the charts as the pattern would drive them +INPUTS=$(ls -1 common/examples/*.yaml | grep -v secret) +for input in $INPUTS; do + helm lint $* -f $input $target + if [ $? != 0 ]; then exit 1; fi +done + +exit 0 diff --git a/scripts/test.sh b/scripts/test.sh index 07fab6a4..a2aae9f4 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -111,10 +111,17 @@ if [ $2 = "all" ]; then doTest naked # Test the charts as the pattern would drive them - doTest normal "$3" + INPUTS=$(ls -1 common/examples/*.yaml | grep -v secret) + for input in $INPUTS; do + variant=normal + if [ "$input" != "common/examples/values-example.yaml" ]; then + variant=$(echo $input | sed -e 's@.*/@@' -e 's@\.yaml@@') + fi + doTest $variant "$3 -f $input" + done # Ensure the differences between the two results are also stable - doTestCompare + #doTestCompare else doTest $2 "$3" fi diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml new file mode 100644 index 00000000..e94c6a51 --- /dev/null +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -0,0 +1,93 @@ +--- +# Source: acm/templates/policies/application-policies.yaml +# TODO: Also create a GitOpsCluster.apps.open-cluster-management.io +--- +# Source: acm/templates/multiclusterhub.yaml +apiVersion: operator.open-cluster-management.io/v1 +kind: MultiClusterHub +metadata: + name: multiclusterhub + namespace: open-cluster-management + annotations: + argocd.argoproj.io/sync-wave: "-1" +spec: {} +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: openshift-gitops-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: openshift-gitops-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: openshift-gitops-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: openshift-gitops-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: openshift-gitops-policy + annotations: + policy.open-cluster-management.io/standards: NIST-CSF + policy.open-cluster-management.io/categories: PR.DS Data Security + policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: openshift-gitops-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + # This is an auto-generated file. DO NOT EDIT + apiVersion: operators.coreos.com/v1alpha1 + kind: Subscription + metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: '' + spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: "*" diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml new file mode 100644 index 00000000..008e0a39 --- /dev/null +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -0,0 +1,221 @@ +--- +# Source: acm/templates/multiclusterhub.yaml +apiVersion: operator.open-cluster-management.io/v1 +kind: MultiClusterHub +metadata: + name: multiclusterhub + namespace: open-cluster-management + annotations: + argocd.argoproj.io/sync-wave: "-1" +spec: {} +--- +# Source: acm/templates/policies/application-policies.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: factory-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: factory-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: factory-clustergroup-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: openshift-gitops-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: openshift-gitops-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: openshift-gitops-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/application-policies.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: factory-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: { + "matchExpressions": [ + { + "key": "vendor", + "operator": "In", + "values": [ + "OpenShift" + ] + } + ], + "matchLabels": { + "clusterGroup": "factory" + } +} +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: openshift-gitops-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift +--- +# Source: acm/templates/policies/application-policies.yaml +# TODO: Also create a GitOpsCluster.apps.open-cluster-management.io +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: factory-clustergroup-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: factory-clustergroup-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + apiVersion: argoproj.io/v1alpha1 + kind: Application + metadata: + name: mypattern-factory + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground + spec: + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-factory.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-factory.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-factory.yaml' + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + # Requires ACM 2.6 or higher + - name: global.clusterDomain + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' + # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) + - name: global.clusterVersion + value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + - name: global.clusterPlatform + value: + - name: clusterGroup.name + value: factory + - name: clusterGroup.isHubCluster + value: "false" + destination: + server: https://kubernetes.default.svc + namespace: mypattern-factory + syncPolicy: + automated: + prune: false + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: openshift-gitops-policy + annotations: + policy.open-cluster-management.io/standards: NIST-CSF + policy.open-cluster-management.io/categories: PR.DS Data Security + policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: openshift-gitops-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + # This is an auto-generated file. DO NOT EDIT + apiVersion: operators.coreos.com/v1alpha1 + kind: Subscription + metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: '' + spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: "*" diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml new file mode 100644 index 00000000..7f4791e8 --- /dev/null +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -0,0 +1,212 @@ +--- +# Source: acm/templates/multiclusterhub.yaml +apiVersion: operator.open-cluster-management.io/v1 +kind: MultiClusterHub +metadata: + name: multiclusterhub + namespace: open-cluster-management + annotations: + argocd.argoproj.io/sync-wave: "-1" +spec: {} +--- +# Source: acm/templates/policies/application-policies.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: region-one-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: region-one-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: region-one-clustergroup-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: openshift-gitops-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: openshift-gitops-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: openshift-gitops-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/application-policies.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: region-one-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: { + "matchLabels": { + "clusterGroup": "region-one" + } +} +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: openshift-gitops-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift +--- +# Source: acm/templates/policies/application-policies.yaml +# TODO: Also create a GitOpsCluster.apps.open-cluster-management.io +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: region-one-clustergroup-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: region-one-clustergroup-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + apiVersion: argoproj.io/v1alpha1 + kind: Application + metadata: + name: mypattern-region-one + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground + spec: + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-region-one.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-region-one.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-region-one.yaml' + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + # Requires ACM 2.6 or higher + - name: global.clusterDomain + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' + # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) + - name: global.clusterVersion + value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + - name: global.clusterPlatform + value: + - name: clusterGroup.name + value: region-one + - name: clusterGroup.isHubCluster + value: "false" + destination: + server: https://kubernetes.default.svc + namespace: mypattern-region-one + syncPolicy: + automated: + prune: false + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: openshift-gitops-policy + annotations: + policy.open-cluster-management.io/standards: NIST-CSF + policy.open-cluster-management.io/categories: PR.DS Data Security + policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: openshift-gitops-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + # This is an auto-generated file. DO NOT EDIT + apiVersion: operators.coreos.com/v1alpha1 + kind: Subscription + metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: '' + spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: "*" diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml new file mode 100644 index 00000000..31178ed1 --- /dev/null +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -0,0 +1,660 @@ +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-factory + name: manuela-stormshift-line-dashboard +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-factory + name: manuela-stormshift-machine-sensor +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-factory + name: manuela-stormshift-messaging +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-factory + name: manuela-factory-ml-workspace +spec: +--- +# Source: pattern-clustergroup/templates/imperative/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: imperative + argocd.argoproj.io/managed-by: mypattern-factory + name: imperative +--- +# Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: mypattern-factory + # The name here needs to be consistent with + # - acm/templates/policies/application-policies.yaml + # - clustergroup/templates/applications.yaml + # - any references to secrets and route URLs in documentation + name: mypattern-factory +spec: {} +--- +# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/imperative/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: helm-values-configmap-factory + namespace: imperative +data: + values.yaml: | + clusterGroup: + applications: + - name: stormshift + path: charts/factory/manuela-stormshift + plugin: + name: helm-with-kustomize + project: factory + - name: odh + namespace: manuela-factory-ml-workspace + path: charts/datacenter/opendatahub + project: factory + imperative: + activeDeadlineSeconds: 3600 + clusterRoleName: imperative-cluster-role + clusterRoleYaml: "" + cronJobName: imperative-cronjob + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' + jobName: imperative-job + jobs: + - name: test + playbook: ansible/test.yml + namespace: imperative + roleName: imperative-role + roleYaml: "" + schedule: '*/10 * * * *' + serviceAccountCreate: true + serviceAccountName: imperative-sa + valuesConfigMap: helm-values-configmap + verbosity: "" + insecureUnsealVaultInsideCluster: true + isHubCluster: false + name: factory + namespaces: + - manuela-stormshift-line-dashboard + - manuela-stormshift-machine-sensor + - manuela-stormshift-messaging + - manuela-factory-ml-workspace + operatorgroupExcludes: + - manuela-factory-ml-workspace + projects: + - factory + subscriptions: + - channel: stable + name: opendatahub-operator + source: community-operators + - channel: stable + name: seldon-operator + namespace: manuela-stormshift-messaging + source: community-operators + - channel: stable + name: amq-streams + namespace: manuela-stormshift-messaging + - channel: 7.x + name: amq-broker-rhel8 + namespace: manuela-stormshift-messaging + - channel: stable + name: red-hat-camel-k + namespace: manuela-stormshift-messaging + targetCluster: in-cluster + enabled: all + global: + clusterDomain: region.example.com + git: + account: hybrid-cloud-patterns + dev_revision: main + email: someone@somewhere.com + hostname: github.com + hubClusterDomain: apps.hub.example.com + localClusterDomain: apps.region.example.com + namespace: pattern-namespace + options: + installPlanApproval: Automatic + syncPolicy: Manual + useCSV: true + pattern: mypattern + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + main: + clusterGroupName: example + git: + repoURL: https://github.com/pattern-clone/mypattern + revision: main + secretStore: + kind: ClusterSecretStore + name: vault-backend + secretsBase: + key: secret/data/hub +--- +# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: imperative-cluster-role +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - get + - list + - watch +--- +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: imperative-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: imperative-cluster-role +subjects: + - kind: ServiceAccount + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: openshift-gitops-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: openshift-gitops-argocd-application-controller + namespace: openshift-gitops + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + name: openshift-gitops-argocd-server + namespace: openshift-gitops +--- +# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: mypattern-factory-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + # This is the {ArgoCD.name}-argocd-application-controller + name: factory-gitops-argocd-application-controller + namespace: mypattern-factory + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + # This is the {ArgoCD.name}-argocd-server + name: factory-gitops-argocd-server + namespace: mypattern-factory + # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) + - kind: ServiceAccount + name: factory-gitops-argocd-dex-server + namespace: mypattern-factory +--- +# Source: pattern-clustergroup/templates/imperative/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: imperative-role + namespace: imperative +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: imperative-admin-rolebinding + namespace: imperative +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: imperative-role +subjects: + - kind: ServiceAccount + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/imperative/job.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: imperative-cronjob + namespace: imperative +spec: + schedule: "*/10 * * * *" + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: 3600 + template: + metadata: + name: imperative-job + spec: + serviceAccountName: imperative-sa + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" + - name: test + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - "600" + - ansible-playbook + - -e + - "@/values/values.yaml" + - ansible/test.yml + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: "done" + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + command: + - 'sh' + - '-c' + - 'echo' + - 'done' + - '\n' + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-factory + restartPolicy: Never +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +--- +--- +# Source: pattern-clustergroup/templates/plumbing/projects.yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: factory + namespace: mypattern-factory +spec: + description: "Pattern factory" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: stormshift + namespace: mypattern-factory + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: mypattern-factory + project: factory + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/factory/manuela-stormshift + plugin: { + "name": "helm-with-kustomize" +} + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: odh + namespace: mypattern-factory + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: manuela-factory-ml-workspace + project: factory + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/datacenter/opendatahub + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-factory.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + finalizers: + - argoproj.io/finalizer + # Changing the name affects the ClusterRoleBinding, the generated secret, + # route URL, and argocd.argoproj.io/managed-by annotations + name: factory-gitops + namespace: mypattern-factory + annotations: + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + applicationInstanceLabelKey: argocd.argoproj.io/instance + # Not the greatest way to pass git/quay info to sub-applications, but it will do until + # we can support helmChart with kustomize + # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION + configManagementPlugins: | + - name: kustomize-version + generate: + command: ["sh", "-c"] + args: ["kustomize version 1>&2 && exit 1"] + - name: kustomize-with-helm + generate: + command: ["kustomize"] + args: ["build", "--enable-helm"] + - name: helm-with-kustomize + init: + command: ["/bin/sh", "-c"] + args: ["helm dependency build"] + generate: + command: ["/bin/bash", "-c"] + args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f $(git rev-parse --show-toplevel)/values-global.yaml + -f $(git rev-parse --show-toplevel)/values-factory.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern=mypattern + --set global.clusterDomain=region.example.com + --set global.hubClusterDomain=apps.hub.example.com + --set global.localClusterDomain=apps.region.example.com + --set clusterGroup.name=factory + --post-renderer ./kustomize"] + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + controller: + processors: {} + resources: + limits: + cpu: "4" + memory: 4Gi + requests: + cpu: 500m + memory: 2Gi + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + rbac: + defaultPolicy: role:admin + repo: + resources: + limits: + cpu: "1" + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + tls: + insecureEdgeTerminationPolicy: Redirect + termination: reencrypt + service: + type: "" + tls: + ca: {} +status: +--- +# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: factory-gitops-link + namespace: mypattern-factory +spec: + applicationMenu: + section: OpenShift GitOps + imageURL: data:image/png;base64,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 + href: 'https://factory-gitops-server-mypattern-factory.apps.region.example.com' + location: ApplicationMenu + text: 'Factory ArgoCD' +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: manuela-stormshift-line-dashboard-operator-group + namespace: manuela-stormshift-line-dashboard +spec: + targetNamespaces: + - manuela-stormshift-line-dashboard +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: manuela-stormshift-machine-sensor-operator-group + namespace: manuela-stormshift-machine-sensor +spec: + targetNamespaces: + - manuela-stormshift-machine-sensor +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: manuela-stormshift-messaging-operator-group + namespace: manuela-stormshift-messaging +spec: + targetNamespaces: + - manuela-stormshift-messaging +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: opendatahub-operator + namespace: openshift-operators +spec: + name: opendatahub-operator + source: community-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: seldon-operator + namespace: manuela-stormshift-messaging +spec: + name: seldon-operator + source: community-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: amq-streams + namespace: manuela-stormshift-messaging +spec: + name: amq-streams + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: amq-broker-rhel8 + namespace: manuela-stormshift-messaging +spec: + name: amq-broker-rhel8 + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: 7.x + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: red-hat-camel-k + namespace: manuela-stormshift-messaging +spec: + name: red-hat-camel-k + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml new file mode 100644 index 00000000..d6f0f520 --- /dev/null +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -0,0 +1,1416 @@ +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-datacenter + name: golang-external-secrets +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-datacenter + name: external-secrets +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-datacenter + name: open-cluster-management +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-datacenter + name: manuela-ml-workspace +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-datacenter + name: manuela-tst-all +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-datacenter + name: manuela-ci +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-datacenter + name: manuela-data-lake +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-datacenter + name: staging +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-datacenter + name: vault +spec: +--- +# Source: pattern-clustergroup/templates/imperative/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: imperative + argocd.argoproj.io/managed-by: mypattern-datacenter + name: imperative +--- +# Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: mypattern-datacenter + # The name here needs to be consistent with + # - acm/templates/policies/application-policies.yaml + # - clustergroup/templates/applications.yaml + # - any references to secrets and route URLs in documentation + name: mypattern-datacenter +spec: {} +--- +# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/imperative/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: helm-values-configmap-datacenter + namespace: imperative +data: + values.yaml: | + clusterGroup: + applications: + acm: + ignoreDifferences: + - group: internal.open-cluster-management.io + jsonPointers: + - /spec/loggingCA + kind: ManagedClusterInfo + name: acm + namespace: open-cluster-management + path: common/acm + project: datacenter + odh: + name: odh + namespace: manuela-ml-workspace + path: charts/datacenter/opendatahub + project: datacenter + pipelines: + name: pipelines + namespace: manuela-ci + path: charts/datacenter/pipelines + project: datacenter + production-data-lake: + ignoreDifferences: + - group: apps + jsonPointers: + - /spec/replicas + kind: Deployment + - group: route.openshift.io + jsonPointers: + - /status + kind: Route + - group: image.openshift.io + jsonPointers: + - /spec/tags + kind: ImageStream + - group: apps.openshift.io + jsonPointers: + - /spec/template/spec/containers/0/image + kind: DeploymentConfig + name: production-data-lake + namespace: manuela-data-lake + path: charts/datacenter/manuela-data-lake + project: production-datalake + secrets: + name: external-secrets + namespace: external-secrets + path: charts/datacenter/external-secrets + project: golang-external-secrets + secrets-operator: + name: golang-external-secrets + namespace: golang-external-secrets + path: common/golang-external-secrets + project: golang-external-secrets + test: + name: manuela-test + namespace: manuela-tst-all + path: charts/datacenter/manuela-tst + plugin: + name: helm-with-kustomize + project: datacenter + vault: + chart: vault + name: vault + namespace: vault + overrides: + - name: global.openshift + value: "true" + - name: injector.enabled + value: "false" + - name: ui.enabled + value: "true" + - name: ui.serviceType + value: LoadBalancer + - name: server.route.enabled + value: "true" + - name: server.route.host + value: null + - name: server.route.tls.termination + value: edge + - name: server.image.repository + value: registry.connect.redhat.com/hashicorp/vault + - name: server.image.tag + value: 1.10.3-ubi + project: datacenter + repoURL: https://helm.releases.hashicorp.com + targetRevision: v0.20.1 + imperative: + activeDeadlineSeconds: 3600 + clusterRoleName: imperative-cluster-role + clusterRoleYaml: "" + cronJobName: imperative-cronjob + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' + jobName: imperative-job + jobs: + - name: test + playbook: ansible/test.yml + namespace: imperative + roleName: imperative-role + roleYaml: "" + schedule: '*/10 * * * *' + serviceAccountCreate: true + serviceAccountName: imperative-sa + valuesConfigMap: helm-values-configmap + verbosity: "" + insecureUnsealVaultInsideCluster: true + isHubCluster: true + managedClusterGroups: + factory: + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift + matchLabels: + clusterGroup: factory + helmOverrides: + - name: clusterGroup.isHubCluster + value: "false" + name: factory + name: datacenter + namespaces: + - golang-external-secrets + - external-secrets + - open-cluster-management + - manuela-ml-workspace + - manuela-tst-all + - manuela-ci + - manuela-data-lake + - staging + - vault + operatorgroupExcludes: + - manuela-ml-workspace + projects: + - datacenter + - production-datalake + - golang-external-secrets + - vault + subscriptions: + acm: + channel: release-2.6 + name: advanced-cluster-management + namespace: open-cluster-management + amqbroker-prod: + channel: 7.x + name: amq-broker-rhel8 + namespace: manuela-tst-all + amqstreams-prod-dev: + channel: stable + name: amq-streams + namespaces: + - manuela-data-lake + - manuela-tst-all + camelk-prod-dev: + channel: stable + name: red-hat-camel-k + namespaces: + - manuela-data-lake + - manuela-tst-all + odh: + channel: stable + name: opendatahub-operator + source: community-operators + pipelines: + channel: latest + name: openshift-pipelines-operator-rh + source: redhat-operators + seldon-prod-dev: + channel: stable + name: seldon-operator + namespaces: + - manuela-ml-workspace + - manuela-tst-all + source: community-operators + targetCluster: in-cluster + enabled: all + global: + clusterDomain: region.example.com + git: + account: hybrid-cloud-patterns + dev_revision: main + email: someone@somewhere.com + hostname: github.com + hubClusterDomain: apps.hub.example.com + localClusterDomain: apps.region.example.com + namespace: pattern-namespace + options: + installPlanApproval: Automatic + syncPolicy: Manual + useCSV: true + pattern: mypattern + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + main: + clusterGroupName: example + git: + repoURL: https://github.com/pattern-clone/mypattern + revision: main + secretStore: + kind: ClusterSecretStore + name: vault-backend + secretsBase: + key: secret/data/hub +--- +# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: imperative-cluster-role +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - get + - list + - watch +--- +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: imperative-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: imperative-cluster-role +subjects: + - kind: ServiceAccount + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: openshift-gitops-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: openshift-gitops-argocd-application-controller + namespace: openshift-gitops + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + name: openshift-gitops-argocd-server + namespace: openshift-gitops +--- +# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: mypattern-datacenter-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + # This is the {ArgoCD.name}-argocd-application-controller + name: datacenter-gitops-argocd-application-controller + namespace: mypattern-datacenter + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + # This is the {ArgoCD.name}-argocd-server + name: datacenter-gitops-argocd-server + namespace: mypattern-datacenter + # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) + - kind: ServiceAccount + name: datacenter-gitops-argocd-dex-server + namespace: mypattern-datacenter +--- +# Source: pattern-clustergroup/templates/imperative/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: imperative-role + namespace: imperative +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: imperative-admin-rolebinding + namespace: imperative +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: imperative-role +subjects: + - kind: ServiceAccount + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/imperative/job.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: imperative-cronjob + namespace: imperative +spec: + schedule: "*/10 * * * *" + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: 3600 + template: + metadata: + name: imperative-job + spec: + serviceAccountName: imperative-sa + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" + - name: test + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - "600" + - ansible-playbook + - -e + - "@/values/values.yaml" + - ansible/test.yml + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: "done" + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + command: + - 'sh' + - '-c' + - 'echo' + - 'done' + - '\n' + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-datacenter + restartPolicy: Never +--- +# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: unsealvault-cronjob + namespace: imperative +spec: + schedule: "*/5 * * * *" + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: 3600 + template: + metadata: + name: unsealvault-job + spec: + serviceAccountName: imperative-sa + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" + - name: unseal-playbook + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - "600" + - ansible-playbook + - -e + - "@/values/values.yaml" + - -e + - '{"file_unseal": false}' + - -t + - 'vault_init,vault_unseal,vault_secrets_init' + - "common/ansible/playbooks/vault/vault.yaml" + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: "done" + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + command: + - 'sh' + - '-c' + - 'echo' + - 'done' + - '\n' + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-datacenter + restartPolicy: Never +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +--- +--- +# Source: pattern-clustergroup/templates/plumbing/projects.yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: datacenter + namespace: mypattern-datacenter +spec: + description: "Pattern datacenter" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- +# Source: pattern-clustergroup/templates/plumbing/projects.yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: production-datalake + namespace: mypattern-datacenter +spec: + description: "Pattern production-datalake" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- +# Source: pattern-clustergroup/templates/plumbing/projects.yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: golang-external-secrets + namespace: mypattern-datacenter +spec: + description: "Pattern golang-external-secrets" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- +# Source: pattern-clustergroup/templates/plumbing/projects.yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: vault + namespace: mypattern-datacenter +spec: + description: "Pattern vault" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: acm + namespace: mypattern-datacenter + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: open-cluster-management + project: datacenter + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/acm + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-datacenter.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + ignoreDifferences: [ + { + "group": "internal.open-cluster-management.io", + "jsonPointers": [ + "/spec/loggingCA" + ], + "kind": "ManagedClusterInfo" + } +] + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: odh + namespace: mypattern-datacenter + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: manuela-ml-workspace + project: datacenter + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/datacenter/opendatahub + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-datacenter.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: pipelines + namespace: mypattern-datacenter + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: manuela-ci + project: datacenter + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/datacenter/pipelines + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-datacenter.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: production-data-lake + namespace: mypattern-datacenter + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: manuela-data-lake + project: production-datalake + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/datacenter/manuela-data-lake + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-datacenter.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + ignoreDifferences: [ + { + "group": "apps", + "jsonPointers": [ + "/spec/replicas" + ], + "kind": "Deployment" + }, + { + "group": "route.openshift.io", + "jsonPointers": [ + "/status" + ], + "kind": "Route" + }, + { + "group": "image.openshift.io", + "jsonPointers": [ + "/spec/tags" + ], + "kind": "ImageStream" + }, + { + "group": "apps.openshift.io", + "jsonPointers": [ + "/spec/template/spec/containers/0/image" + ], + "kind": "DeploymentConfig" + } +] + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: external-secrets + namespace: mypattern-datacenter + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: external-secrets + project: golang-external-secrets + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/datacenter/external-secrets + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-datacenter.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: golang-external-secrets + namespace: mypattern-datacenter + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: golang-external-secrets + project: golang-external-secrets + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/golang-external-secrets + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-datacenter.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: manuela-test + namespace: mypattern-datacenter + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: manuela-tst-all + project: datacenter + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/datacenter/manuela-tst + plugin: { + "name": "helm-with-kustomize" +} + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: vault + namespace: mypattern-datacenter + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: vault + project: datacenter + source: + repoURL: https://helm.releases.hashicorp.com + targetRevision: v0.20.1 + chart: vault + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-datacenter.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + - name: global.openshift + value: "true" + - name: injector.enabled + value: "false" + - name: ui.enabled + value: "true" + - name: ui.serviceType + value: "LoadBalancer" + - name: server.route.enabled + value: "true" + - name: server.route.host + value: + - name: server.route.tls.termination + value: "edge" + - name: server.image.repository + value: "registry.connect.redhat.com/hashicorp/vault" + - name: server.image.tag + value: "1.10.3-ubi" + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + finalizers: + - argoproj.io/finalizer + # Changing the name affects the ClusterRoleBinding, the generated secret, + # route URL, and argocd.argoproj.io/managed-by annotations + name: datacenter-gitops + namespace: mypattern-datacenter + annotations: + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + applicationInstanceLabelKey: argocd.argoproj.io/instance + # Not the greatest way to pass git/quay info to sub-applications, but it will do until + # we can support helmChart with kustomize + # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION + configManagementPlugins: | + - name: kustomize-version + generate: + command: ["sh", "-c"] + args: ["kustomize version 1>&2 && exit 1"] + - name: kustomize-with-helm + generate: + command: ["kustomize"] + args: ["build", "--enable-helm"] + - name: helm-with-kustomize + init: + command: ["/bin/sh", "-c"] + args: ["helm dependency build"] + generate: + command: ["/bin/bash", "-c"] + args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f $(git rev-parse --show-toplevel)/values-global.yaml + -f $(git rev-parse --show-toplevel)/values-datacenter.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern=mypattern + --set global.clusterDomain=region.example.com + --set global.hubClusterDomain=apps.hub.example.com + --set global.localClusterDomain=apps.region.example.com + --set clusterGroup.name=datacenter + --post-renderer ./kustomize"] + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + controller: + processors: {} + resources: + limits: + cpu: "4" + memory: 4Gi + requests: + cpu: 500m + memory: 2Gi + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + rbac: + defaultPolicy: role:admin + repo: + resources: + limits: + cpu: "1" + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + tls: + insecureEdgeTerminationPolicy: Redirect + termination: reencrypt + service: + type: "" + tls: + ca: {} +status: +--- +# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: datacenter-gitops-link + namespace: mypattern-datacenter +spec: + applicationMenu: + section: OpenShift GitOps + imageURL: data:image/png;base64,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 + href: 'https://datacenter-gitops-server-mypattern-datacenter.apps.region.example.com' + location: ApplicationMenu + text: 'Datacenter ArgoCD' +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: golang-external-secrets-operator-group + namespace: golang-external-secrets +spec: + targetNamespaces: + - golang-external-secrets +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: external-secrets-operator-group + namespace: external-secrets +spec: + targetNamespaces: + - external-secrets +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: open-cluster-management-operator-group + namespace: open-cluster-management +spec: + targetNamespaces: + - open-cluster-management +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: manuela-tst-all-operator-group + namespace: manuela-tst-all +spec: + targetNamespaces: + - manuela-tst-all +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: manuela-ci-operator-group + namespace: manuela-ci +spec: + targetNamespaces: + - manuela-ci +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: manuela-data-lake-operator-group + namespace: manuela-data-lake +spec: + targetNamespaces: + - manuela-data-lake +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: staging-operator-group + namespace: staging +spec: + targetNamespaces: + - staging +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: vault-operator-group + namespace: vault +spec: + targetNamespaces: + - vault +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: advanced-cluster-management + namespace: open-cluster-management +spec: + name: advanced-cluster-management + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: release-2.6 + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: amq-broker-rhel8 + namespace: manuela-tst-all +spec: + name: amq-broker-rhel8 + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: 7.x + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: amq-streams + namespace: manuela-data-lake +spec: + name: amq-streams + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: amq-streams + namespace: manuela-tst-all +spec: + name: amq-streams + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: red-hat-camel-k + namespace: manuela-data-lake +spec: + name: red-hat-camel-k + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: red-hat-camel-k + namespace: manuela-tst-all +spec: + name: red-hat-camel-k + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: opendatahub-operator + namespace: openshift-operators +spec: + name: opendatahub-operator + source: community-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-pipelines-operator-rh + namespace: openshift-operators +spec: + name: openshift-pipelines-operator-rh + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: latest + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: seldon-operator + namespace: manuela-ml-workspace +spec: + name: seldon-operator + source: community-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: seldon-operator + namespace: manuela-tst-all +spec: + name: seldon-operator + source: community-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml new file mode 100644 index 00000000..5f0fa1e8 --- /dev/null +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -0,0 +1,1532 @@ +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-hub + name: open-cluster-management +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-hub + name: openshift-serverless +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-hub + name: opendatahub +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-hub + name: openshift-storage +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-hub + name: xraylab-1 +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-hub + name: knative-serving +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-hub + name: staging +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-hub + name: vault +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-hub + name: golang-external-secrets +spec: +--- +# Source: pattern-clustergroup/templates/imperative/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: imperative + argocd.argoproj.io/managed-by: mypattern-hub + name: imperative +--- +# Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: mypattern-hub + # The name here needs to be consistent with + # - acm/templates/policies/application-policies.yaml + # - clustergroup/templates/applications.yaml + # - any references to secrets and route URLs in documentation + name: mypattern-hub +spec: {} +--- +# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/imperative/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: helm-values-configmap-hub + namespace: imperative +data: + values.yaml: | + clusterGroup: + applications: + golang-external-secrets: + name: golang-external-secrets + namespace: golang-external-secrets + path: common/golang-external-secrets + project: hub + kafdrop: + name: kafdrop + namespace: xraylab-1 + path: charts/all/kafdrop + project: medical-diagnosis + kafka: + name: kafka + namespace: xraylab-1 + path: charts/all/kafka + project: medical-diagnosis + opendatahub: + name: odh + namespace: opendatahub + path: charts/all/opendatahub + project: medical-diagnosis + openshift-data-foundations: + name: odf + namespace: openshift-storage + path: charts/all/openshift-data-foundations + project: medical-diagnosis + openshift-serverless: + name: serverless + namespace: xraylab-1 + path: charts/all/openshift-serverless + project: medical-diagnosis + service-account: + name: xraylab-service-account + namespace: xraylab-1 + path: charts/all/medical-diagnosis/service-account + project: medical-diagnosis + vault: + chart: vault + name: vault + namespace: vault + overrides: + - name: global.openshift + value: "true" + - name: injector.enabled + value: "false" + - name: ui.enabled + value: "true" + - name: ui.serviceType + value: LoadBalancer + - name: server.route.enabled + value: "true" + - name: server.route.host + value: null + - name: server.route.tls.termination + value: edge + - name: server.image.repository + value: registry.connect.redhat.com/hashicorp/vault + - name: server.image.tag + value: 1.10.3-ubi + project: hub + repoURL: https://helm.releases.hashicorp.com + targetRevision: v0.20.1 + xraylab-database: + name: xraylab-database + namespace: xraylab-1 + path: charts/all/medical-diagnosis/database + project: medical-diagnosis + xraylab-grafana-dashboards: + name: xraylab-grafana-dashboards + namespace: xraylab-1 + path: charts/all/medical-diagnosis/grafana + project: medical-diagnosis + xraylab-image-generator: + ignoreDifferences: + - group: apps.openshift.io + jqPathExpressions: + - .spec.template.spec.containers[].image + kind: DeploymentConfig + name: xraylab-image-generator + namespace: xraylab-1 + path: charts/all/medical-diagnosis/image-generator + project: medical-diagnosis + xraylab-image-server: + ignoreDifferences: + - group: apps.openshift.io + jqPathExpressions: + - .spec.template.spec.containers[].image + kind: DeploymentConfig + name: xraylab-image-server + namespace: xraylab-1 + path: charts/all/medical-diagnosis/image-server + project: medical-diagnosis + xraylab-init: + name: xraylab-init + namespace: xraylab-1 + path: charts/all/medical-diagnosis/xray-init + project: medical-diagnosis + imperative: + activeDeadlineSeconds: 3600 + clusterRoleName: imperative-cluster-role + clusterRoleYaml: "" + cronJobName: imperative-cronjob + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' + jobName: imperative-job + jobs: + - name: test + playbook: ansible/test.yml + timeout: 234 + namespace: imperative + roleName: imperative-role + roleYaml: "" + schedule: '*/10 * * * *' + serviceAccountCreate: true + serviceAccountName: imperative-sa + valuesConfigMap: helm-values-configmap + verbosity: "" + insecureUnsealVaultInsideCluster: true + isHubCluster: true + managedClusterGroups: + region-one: + clusterSelector: + matchLabels: + clusterGroup: region-one + helmOverrides: + - name: clusterGroup.isHubCluster + value: false + name: region-one + name: hub + namespaces: + - open-cluster-management + - openshift-serverless + - opendatahub + - openshift-storage + - xraylab-1 + - knative-serving + - staging + - vault + - golang-external-secrets + projects: + - hub + - medical-diagnosis + subscriptions: + amq-streams: + channel: stable + name: amq-streams + namespace: xraylab-1 + grafana: + channel: v4 + name: grafana-operator + namespace: xraylab-1 + source: community-operators + odf: + channel: stable-4.11 + name: odf-operator + namespace: openshift-storage + opendatahub: + name: opendatahub-operator + source: community-operators + severless: + channel: stable + name: serverless-operator + targetCluster: in-cluster + enabled: all + global: + clusterDomain: region.example.com + git: + account: hybrid-cloud-patterns + dev_revision: main + email: someone@somewhere.com + hostname: github.com + hubClusterDomain: apps.hub.example.com + localClusterDomain: apps.region.example.com + namespace: pattern-namespace + options: + installPlanApproval: Automatic + syncPolicy: Manual + useCSV: true + pattern: mypattern + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + main: + clusterGroupName: example + git: + repoURL: https://github.com/pattern-clone/mypattern + revision: main + secretStore: + kind: ClusterSecretStore + name: vault-backend + secretsBase: + key: secret/data/hub +--- +# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: imperative-cluster-role +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - get + - list + - watch +--- +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: imperative-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: imperative-cluster-role +subjects: + - kind: ServiceAccount + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: openshift-gitops-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: openshift-gitops-argocd-application-controller + namespace: openshift-gitops + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + name: openshift-gitops-argocd-server + namespace: openshift-gitops +--- +# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: mypattern-hub-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + # This is the {ArgoCD.name}-argocd-application-controller + name: hub-gitops-argocd-application-controller + namespace: mypattern-hub + # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP + - kind: ServiceAccount + # This is the {ArgoCD.name}-argocd-server + name: hub-gitops-argocd-server + namespace: mypattern-hub + # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) + - kind: ServiceAccount + name: hub-gitops-argocd-dex-server + namespace: mypattern-hub +--- +# Source: pattern-clustergroup/templates/imperative/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: imperative-role + namespace: imperative +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: imperative-admin-rolebinding + namespace: imperative +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: imperative-role +subjects: + - kind: ServiceAccount + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/imperative/job.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: imperative-cronjob + namespace: imperative +spec: + schedule: "*/10 * * * *" + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: 3600 + template: + metadata: + name: imperative-job + spec: + serviceAccountName: imperative-sa + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" + - name: test + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - "234" + - ansible-playbook + - -e + - "@/values/values.yaml" + - ansible/test.yml + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: "done" + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + command: + - 'sh' + - '-c' + - 'echo' + - 'done' + - '\n' + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-hub + restartPolicy: Never +--- +# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: unsealvault-cronjob + namespace: imperative +spec: + schedule: "*/5 * * * *" + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: 3600 + template: + metadata: + name: unsealvault-job + spec: + serviceAccountName: imperative-sa + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" + - name: unseal-playbook + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - "600" + - ansible-playbook + - -e + - "@/values/values.yaml" + - -e + - '{"file_unseal": false}' + - -t + - 'vault_init,vault_unseal,vault_secrets_init' + - "common/ansible/playbooks/vault/vault.yaml" + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: "done" + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + command: + - 'sh' + - '-c' + - 'echo' + - 'done' + - '\n' + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-hub + restartPolicy: Never +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +--- +--- +# Source: pattern-clustergroup/templates/plumbing/projects.yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: hub + namespace: mypattern-hub +spec: + description: "Pattern hub" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- +# Source: pattern-clustergroup/templates/plumbing/projects.yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: medical-diagnosis + namespace: mypattern-hub +spec: + description: "Pattern medical-diagnosis" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: golang-external-secrets + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: golang-external-secrets + project: hub + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/golang-external-secrets + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: kafdrop + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: xraylab-1 + project: medical-diagnosis + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/all/kafdrop + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: kafka + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: xraylab-1 + project: medical-diagnosis + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/all/kafka + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: odh + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: opendatahub + project: medical-diagnosis + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/all/opendatahub + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: odf + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: openshift-storage + project: medical-diagnosis + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/all/openshift-data-foundations + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: serverless + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: xraylab-1 + project: medical-diagnosis + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/all/openshift-serverless + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: xraylab-service-account + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: xraylab-1 + project: medical-diagnosis + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/all/medical-diagnosis/service-account + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: vault + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: vault + project: hub + source: + repoURL: https://helm.releases.hashicorp.com + targetRevision: v0.20.1 + chart: vault + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + - name: global.openshift + value: "true" + - name: injector.enabled + value: "false" + - name: ui.enabled + value: "true" + - name: ui.serviceType + value: "LoadBalancer" + - name: server.route.enabled + value: "true" + - name: server.route.host + value: + - name: server.route.tls.termination + value: "edge" + - name: server.image.repository + value: "registry.connect.redhat.com/hashicorp/vault" + - name: server.image.tag + value: "1.10.3-ubi" + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: xraylab-database + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: xraylab-1 + project: medical-diagnosis + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/all/medical-diagnosis/database + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: xraylab-grafana-dashboards + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: xraylab-1 + project: medical-diagnosis + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/all/medical-diagnosis/grafana + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: xraylab-image-generator + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: xraylab-1 + project: medical-diagnosis + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/all/medical-diagnosis/image-generator + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + ignoreDifferences: [ + { + "group": "apps.openshift.io", + "jqPathExpressions": [ + ".spec.template.spec.containers[].image" + ], + "kind": "DeploymentConfig" + } +] + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: xraylab-image-server + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: xraylab-1 + project: medical-diagnosis + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/all/medical-diagnosis/image-server + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + ignoreDifferences: [ + { + "group": "apps.openshift.io", + "jqPathExpressions": [ + ".spec.template.spec.containers[].image" + ], + "kind": "DeploymentConfig" + } +] + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: xraylab-init + namespace: mypattern-hub + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: xraylab-1 + project: medical-diagnosis + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: charts/all/medical-diagnosis/xray-init + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-hub.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.clusterDomain + value: region.example.com + - name: global.clusterVersion + value: "" + - name: global.clusterPlatform + value: "" + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + finalizers: + - argoproj.io/finalizer + # Changing the name affects the ClusterRoleBinding, the generated secret, + # route URL, and argocd.argoproj.io/managed-by annotations + name: hub-gitops + namespace: mypattern-hub + annotations: + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + applicationInstanceLabelKey: argocd.argoproj.io/instance + # Not the greatest way to pass git/quay info to sub-applications, but it will do until + # we can support helmChart with kustomize + # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION + configManagementPlugins: | + - name: kustomize-version + generate: + command: ["sh", "-c"] + args: ["kustomize version 1>&2 && exit 1"] + - name: kustomize-with-helm + generate: + command: ["kustomize"] + args: ["build", "--enable-helm"] + - name: helm-with-kustomize + init: + command: ["/bin/sh", "-c"] + args: ["helm dependency build"] + generate: + command: ["/bin/bash", "-c"] + args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f $(git rev-parse --show-toplevel)/values-global.yaml + -f $(git rev-parse --show-toplevel)/values-hub.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern=mypattern + --set global.clusterDomain=region.example.com + --set global.hubClusterDomain=apps.hub.example.com + --set global.localClusterDomain=apps.region.example.com + --set clusterGroup.name=hub + --post-renderer ./kustomize"] + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + controller: + processors: {} + resources: + limits: + cpu: "4" + memory: 4Gi + requests: + cpu: 500m + memory: 2Gi + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + rbac: + defaultPolicy: role:admin + repo: + resources: + limits: + cpu: "1" + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + tls: + insecureEdgeTerminationPolicy: Redirect + termination: reencrypt + service: + type: "" + tls: + ca: {} +status: +--- +# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: hub-gitops-link + namespace: mypattern-hub +spec: + applicationMenu: + section: OpenShift GitOps + imageURL: data:image/png;base64,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 + href: 'https://hub-gitops-server-mypattern-hub.apps.region.example.com' + location: ApplicationMenu + text: 'Hub ArgoCD' +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: open-cluster-management-operator-group + namespace: open-cluster-management +spec: + targetNamespaces: + - open-cluster-management +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: openshift-serverless-operator-group + namespace: openshift-serverless +spec: + targetNamespaces: + - openshift-serverless +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: opendatahub-operator-group + namespace: opendatahub +spec: + targetNamespaces: + - opendatahub +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: openshift-storage-operator-group + namespace: openshift-storage +spec: + targetNamespaces: + - openshift-storage +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: xraylab-1-operator-group + namespace: xraylab-1 +spec: + targetNamespaces: + - xraylab-1 +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: knative-serving-operator-group + namespace: knative-serving +spec: + targetNamespaces: + - knative-serving +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: staging-operator-group + namespace: staging +spec: + targetNamespaces: + - staging +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: vault-operator-group + namespace: vault +spec: + targetNamespaces: + - vault +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: golang-external-secrets-operator-group + namespace: golang-external-secrets +spec: + targetNamespaces: + - golang-external-secrets +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: amq-streams + namespace: xraylab-1 +spec: + name: amq-streams + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: grafana-operator + namespace: xraylab-1 +spec: + name: grafana-operator + source: community-operators + sourceNamespace: openshift-marketplace + channel: v4 + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: odf-operator + namespace: openshift-storage +spec: + name: odf-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: stable-4.11 + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: opendatahub-operator + namespace: openshift-operators +spec: + name: opendatahub-operator + source: community-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: serverless-operator + namespace: openshift-operators +spec: + name: serverless-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: diff --git a/tests/examples-blank-naked.expected.yaml b/tests/examples-blank-naked.expected.yaml deleted file mode 100644 index 51a92e5d..00000000 --- a/tests/examples-blank-naked.expected.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# Source: blank/templates/manifest.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: example diff --git a/tests/examples-blank-normal.expected.yaml b/tests/examples-blank-normal.expected.yaml deleted file mode 100644 index 51a92e5d..00000000 --- a/tests/examples-blank-normal.expected.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# Source: blank/templates/manifest.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: example diff --git a/tests/examples-blank.expected.diff b/tests/examples-blank.expected.diff deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/examples-kustomize-renderer-naked.expected.yaml b/tests/examples-kustomize-renderer-naked.expected.yaml deleted file mode 100644 index 0aa7ee5d..00000000 --- a/tests/examples-kustomize-renderer-naked.expected.yaml +++ /dev/null @@ -1,36 +0,0 @@ ---- -# Source: example/templates/environment.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: environment -data: - IMAGE_PROVIDER: - IMAGE_ACCOUNT: PLAINTEXT - GIT_EMAIL: SOMEWHERE@EXAMPLE.COM - GIT_DEV_REPO_URL: https:///PLAINTEXT/manuela-dev.git - GIT_DEV_REPO_REVISION: main - GIT_OPS_REPO_TEST_URL: - GIT_OPS_REPO_TEST_REVISION: - GIT_OPS_REPO_PROD_URL: - GIT_OPS_REPO_PROD_REVISION: - IOT_CONSUMER_IMAGE: iot-consumer - IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag - IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml - IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml - IOT_FRONTEND_IMAGE: iot-frontend - IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag - IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml - IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml - IOT_SWSENSOR_IMAGE: iot-software-sensor - IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag - IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml - IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml - IOT_ANOMALY_IMAGE: iot-anomaly-detection - IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag - IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml - IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/tests/examples-kustomize-renderer-normal.expected.yaml b/tests/examples-kustomize-renderer-normal.expected.yaml deleted file mode 100644 index 09e04b22..00000000 --- a/tests/examples-kustomize-renderer-normal.expected.yaml +++ /dev/null @@ -1,36 +0,0 @@ ---- -# Source: example/templates/environment.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: environment -data: - IMAGE_PROVIDER: - IMAGE_ACCOUNT: PLAINTEXT - GIT_EMAIL: someone@somewhere.com - GIT_DEV_REPO_URL: https://github.com/hybrid-cloud-patterns/manuela-dev.git - GIT_DEV_REPO_REVISION: main - GIT_OPS_REPO_TEST_URL: https://github.com/pattern-clone/mypattern - GIT_OPS_REPO_TEST_REVISION: - GIT_OPS_REPO_PROD_URL: https://github.com/pattern-clone/mypattern - GIT_OPS_REPO_PROD_REVISION: - IOT_CONSUMER_IMAGE: iot-consumer - IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag - IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml - IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml - IOT_FRONTEND_IMAGE: iot-frontend - IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag - IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml - IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml - IOT_SWSENSOR_IMAGE: iot-software-sensor - IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag - IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml - IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml - IOT_ANOMALY_IMAGE: iot-anomaly-detection - IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag - IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml - IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/tests/examples-kustomize-renderer.expected.diff b/tests/examples-kustomize-renderer.expected.diff deleted file mode 100644 index cbb72a99..00000000 --- a/tests/examples-kustomize-renderer.expected.diff +++ /dev/null @@ -1,19 +0,0 @@ ---- tests/examples-kustomize-renderer-naked.expected.yaml -+++ tests/examples-kustomize-renderer-normal.expected.yaml -@@ -7,12 +7,12 @@ - data: - IMAGE_PROVIDER: - IMAGE_ACCOUNT: PLAINTEXT -- GIT_EMAIL: SOMEWHERE@EXAMPLE.COM -- GIT_DEV_REPO_URL: https:///PLAINTEXT/manuela-dev.git -+ GIT_EMAIL: someone@somewhere.com -+ GIT_DEV_REPO_URL: https://github.com/hybrid-cloud-patterns/manuela-dev.git - GIT_DEV_REPO_REVISION: main -- GIT_OPS_REPO_TEST_URL: -+ GIT_OPS_REPO_TEST_URL: https://github.com/pattern-clone/mypattern - GIT_OPS_REPO_TEST_REVISION: -- GIT_OPS_REPO_PROD_URL: -+ GIT_OPS_REPO_PROD_URL: https://github.com/pattern-clone/mypattern - GIT_OPS_REPO_PROD_REVISION: - IOT_CONSUMER_IMAGE: iot-consumer - IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml new file mode 100644 index 00000000..fc9597d5 --- /dev/null +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -0,0 +1,6421 @@ +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-secrets-cert-controller + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: golang-external-secrets + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml +apiVersion: v1 +kind: Secret +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm + external-secrets.io/component: webhook +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml +apiVersion: v1 +kind: Secret +metadata: + name: golang-external-secrets + namespace: golang-external-secrets + annotations: + kubernetes.io/service-account.name: golang-external-secrets +type: kubernetes.io/service-account-token +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: clusterexternalsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ClusterExternalSecret + listKind: ClusterExternalSecretList + plural: clusterexternalsecrets + shortNames: + - ces + singular: clusterexternalsecret + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ClusterExternalSecret is the Schema for the clusterexternalsecrets 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: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret. + properties: + externalSecretName: + description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret + type: string + externalSecretSpec: + description: The spec for the ExternalSecrets to be created + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + properties: + extract: + description: Used to extract multiple key/value pairs from one secret + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + find: + description: Used to find secrets based on tags or regular expressions + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + name: + description: Finds secrets based on the name. + properties: + regexp: + description: Finds secrets base + type: string + type: object + path: + description: A root path to start the find operations. + type: string + tags: + additionalProperties: + type: string + description: Find secrets based on tags. + type: object + type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + default: + creationPolicy: Owner + deletionPolicy: Retain + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Orphan + - Merge + - None + type: string + deletionPolicy: + default: Retain + description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + enum: + - Delete + - Merge + - Retain + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v2 + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + type: object + namespaceSelector: + description: The labels to select by to find the Namespaces to create the ExternalSecrets in. + 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 + required: + - key + - operator + type: object + type: array + 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 + refreshTime: + description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. + type: string + required: + - externalSecretSpec + - namespaceSelector + type: object + status: + description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret. + properties: + conditions: + items: + properties: + message: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + failedNamespaces: + description: Failed namespaces are the namespaces that failed to apply an ExternalSecret + items: + description: ClusterExternalSecretNamespaceFailure represents a failed namespace deployment and it's reason. + properties: + namespace: + description: Namespace is the namespace that failed when trying to apply an ExternalSecret + type: string + reason: + description: Reason is why the ExternalSecret failed to apply to the namespace + type: string + required: + - namespace + type: object + type: array + provisionedNamespaces: + description: ProvisionedNamespaces are the namespaces where the ClusterExternalSecret has secrets + items: + type: string + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/clustersecretstore.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: clustersecretstores.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ClusterSecretStore + listKind: ClusterSecretStoreList + plural: clustersecretstores + shortNames: + - css + singular: clustersecretstore + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object + secretRef: + description: Reference to a Secret that contains the details to authenticate with Akeyless. + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: + type: string + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + conditions: + description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore + items: + description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + properties: + namespaceSelector: + description: Choose namespace using a labelSelector + 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 + required: + - key + - operator + type: object + type: array + 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 + namespaces: + description: Choose namespaces by name + items: + type: string + type: array + type: object + type: array + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object + secretRef: + description: Reference to a Secret that contains the details to authenticate with Akeyless. + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + doppler: + description: Doppler configures this store to sync secrets using the Doppler provider + properties: + auth: + description: Auth configures how the Operator authenticates with the Doppler API + properties: + secretRef: + properties: + dopplerToken: + description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - dopplerToken + type: object + required: + - secretRef + type: object + config: + description: Doppler config (required if not using a Service Token) + type: string + format: + description: Format enables the downloading of secrets as a file (string) + enum: + - json + - dotnet-json + - env + - yaml + - docker + type: string + nameTransformer: + description: Environment variable compatible name transforms that change secret names to a different format + enum: + - upper-camel + - camel + - lower-snake + - tf-var + - dotnet-env + type: string + project: + description: Doppler project (required if not using a Service Token) + type: string + required: + - auth + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + environment: + description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) + type: string + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 + properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + onepassword: + description: OnePassword configures this store to sync secrets using the 1Password Cloud provider + properties: + auth: + description: Auth defines the information necessary to authenticate against OnePassword Connect Server + properties: + secretRef: + description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. + properties: + connectTokenSecretRef: + description: The ConnectToken is used for authentication to a 1Password Connect Server. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - connectTokenSecretRef + type: object + required: + - secretRef + type: object + connectHost: + description: ConnectHost defines the OnePassword Connect Server to connect to + type: string + vaults: + additionalProperties: + type: integer + description: Vaults defines which OnePassword vaults to search in which order + type: object + required: + - auth + - connectHost + - vaults + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + senhasegura: + description: Senhasegura configures this store to sync secrets using senhasegura provider + properties: + auth: + description: Auth defines parameters to authenticate in senhasegura + properties: + clientId: + type: string + clientSecretSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecretSecretRef + type: object + ignoreSslCertificate: + default: false + description: IgnoreSslCertificate defines if SSL certificate must be ignored + type: boolean + module: + description: Module defines which senhasegura module should be used to get secrets + type: string + url: + description: URL of senhasegura + type: string + required: + - auth + - module + - url + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + items: + type: string + type: array + expirationSeconds: + description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexcertificatemanager: + description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Certificate Manager + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + refreshInterval: + description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. + type: integer + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/externalsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: externalsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ExternalSecret + listKind: ExternalSecretList + plural: externalsecrets + shortNames: + - es + singular: externalsecret + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v1 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + - target + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + properties: + extract: + description: Used to extract multiple key/value pairs from one secret + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + find: + description: Used to find secrets based on tags or regular expressions + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + name: + description: Finds secrets based on the name. + properties: + regexp: + description: Finds secrets base + type: string + type: object + path: + description: A root path to start the find operations. + type: string + tags: + additionalProperties: + type: string + description: Find secrets based on tags. + type: object + type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + default: + creationPolicy: Owner + deletionPolicy: Retain + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Orphan + - Merge + - None + type: string + deletionPolicy: + default: Retain + description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + enum: + - Delete + - Merge + - Retain + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v2 + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/secretstore.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: secretstores.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: SecretStore + listKind: SecretStoreList + plural: secretstores + shortNames: + - ss + singular: secretstore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object + secretRef: + description: Reference to a Secret that contains the details to authenticate with Akeyless. + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: + type: string + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + conditions: + description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore + items: + description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + properties: + namespaceSelector: + description: Choose namespace using a labelSelector + 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 + required: + - key + - operator + type: object + type: array + 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 + namespaces: + description: Choose namespaces by name + items: + type: string + type: array + type: object + type: array + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object + secretRef: + description: Reference to a Secret that contains the details to authenticate with Akeyless. + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + doppler: + description: Doppler configures this store to sync secrets using the Doppler provider + properties: + auth: + description: Auth configures how the Operator authenticates with the Doppler API + properties: + secretRef: + properties: + dopplerToken: + description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - dopplerToken + type: object + required: + - secretRef + type: object + config: + description: Doppler config (required if not using a Service Token) + type: string + format: + description: Format enables the downloading of secrets as a file (string) + enum: + - json + - dotnet-json + - env + - yaml + - docker + type: string + nameTransformer: + description: Environment variable compatible name transforms that change secret names to a different format + enum: + - upper-camel + - camel + - lower-snake + - tf-var + - dotnet-env + type: string + project: + description: Doppler project (required if not using a Service Token) + type: string + required: + - auth + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + environment: + description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) + type: string + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 + properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + onepassword: + description: OnePassword configures this store to sync secrets using the 1Password Cloud provider + properties: + auth: + description: Auth defines the information necessary to authenticate against OnePassword Connect Server + properties: + secretRef: + description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. + properties: + connectTokenSecretRef: + description: The ConnectToken is used for authentication to a 1Password Connect Server. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - connectTokenSecretRef + type: object + required: + - secretRef + type: object + connectHost: + description: ConnectHost defines the OnePassword Connect Server to connect to + type: string + vaults: + additionalProperties: + type: integer + description: Vaults defines which OnePassword vaults to search in which order + type: object + required: + - auth + - connectHost + - vaults + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + senhasegura: + description: Senhasegura configures this store to sync secrets using senhasegura provider + properties: + auth: + description: Auth defines parameters to authenticate in senhasegura + properties: + clientId: + type: string + clientSecretSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecretSecretRef + type: object + ignoreSslCertificate: + default: false + description: IgnoreSslCertificate defines if SSL certificate must be ignored + type: boolean + module: + description: Module defines which senhasegura module should be used to get secrets + type: string + url: + description: URL of senhasegura + type: string + required: + - auth + - module + - url + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + items: + type: string + type: array + expirationSeconds: + description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexcertificatemanager: + description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Certificate Manager + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + refreshInterval: + description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. + type: integer + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-cert-controller + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "apiextensions.k8s.io" + resources: + - "customresourcedefinitions" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" + - apiGroups: + - "admissionregistration.k8s.io" + resources: + - "validatingwebhookconfigurations" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "endpoints" + verbs: + - "list" + - "get" + - "watch" + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-controller + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "secretstores" + - "clustersecretstores" + - "externalsecrets" + - "clusterexternalsecrets" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "externalsecrets/status" + - "externalsecrets/finalizers" + - "secretstores" + - "secretstores/status" + - "secretstores/finalizers" + - "clustersecretstores" + - "clustersecretstores/status" + - "clustersecretstores/finalizers" + - "clusterexternalsecrets" + - "clusterexternalsecrets/status" + - "clusterexternalsecrets/finalizers" + verbs: + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "serviceaccounts" + - "namespaces" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "list" + - "watch" + - "create" + - "update" + - "delete" + - "patch" + - apiGroups: + - "" + resources: + - "serviceaccounts/token" + verbs: + - "create" + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + verbs: + - "create" + - "update" + - "delete" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-view + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm + rbac.authorization.k8s.io/aggregate-to-view: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "secretstores" + - "clustersecretstores" + verbs: + - "get" + - "watch" + - "list" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-edit + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "secretstores" + - "clustersecretstores" + verbs: + - "create" + - "delete" + - "deletecollection" + - "patch" + - "update" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: golang-external-secrets-cert-controller + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: golang-external-secrets-cert-controller +subjects: + - name: external-secrets-cert-controller + namespace: "default" + kind: ServiceAccount +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: golang-external-secrets-controller + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: golang-external-secrets-controller +subjects: + - name: golang-external-secrets + namespace: "default" + kind: ServiceAccount +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: role-tokenreview-binding + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: golang-external-secrets + namespace: golang-external-secrets +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: golang-external-secrets-leaderelection + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "" + resources: + - "configmaps" + resourceNames: + - "external-secrets-controller" + verbs: + - "get" + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "create" + - apiGroups: + - "coordination.k8s.io" + resources: + - "leases" + verbs: + - "get" + - "create" + - "update" + - "patch" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: golang-external-secrets-leaderelection + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: golang-external-secrets-leaderelection +subjects: + - kind: ServiceAccount + name: golang-external-secrets + namespace: "default" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm + external-secrets.io/component: webhook +spec: + type: ClusterIP + ports: + - port: 443 + targetPort: 10250 + protocol: TCP + name: webhook + selector: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets-cert-controller + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + spec: + serviceAccountName: external-secrets-cert-controller + containers: + - name: cert-controller + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + imagePullPolicy: IfNotPresent + args: + - certcontroller + - --crd-requeue-interval=5m + - --service-name=golang-external-secrets-webhook + - --service-namespace=default + - --secret-name=golang-external-secrets-webhook + - --secret-namespace=default + ports: + - containerPort: 8080 + protocol: TCP + name: metrics + readinessProbe: + httpGet: + port: 8081 + path: /readyz + initialDelaySeconds: 20 + periodSeconds: 5 +--- +# Source: golang-external-secrets/charts/external-secrets/templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + spec: + serviceAccountName: golang-external-secrets + containers: + - name: external-secrets + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + imagePullPolicy: IfNotPresent + args: + - --concurrent=1 + ports: + - containerPort: 8080 + protocol: TCP + name: metrics +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + spec: + hostNetwork: false + serviceAccountName: external-secrets-webhook + containers: + - name: webhook + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + imagePullPolicy: IfNotPresent + args: + - webhook + - --port=10250 + - --dns-name=golang-external-secrets-webhook.default.svc + - --cert-dir=/tmp/certs + - --check-interval=5m + - --healthz-addr=:8081 + ports: + - containerPort: 8080 + protocol: TCP + name: metrics + - containerPort: 10250 + protocol: TCP + name: webhook + readinessProbe: + httpGet: + port: 8081 + path: /readyz + initialDelaySeconds: 20 + periodSeconds: 5 + volumeMounts: + - name: certs + mountPath: /tmp/certs + readOnly: true + volumes: + - name: certs + secret: + secretName: golang-external-secrets-webhook +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ClusterSecretStore +metadata: + name: vault-backend + namespace: golang-external-secrets +spec: + provider: + vault: + server: https://vault-vault.apps.hub.example.com + path: secret + # Version of KV backend + version: v2 + caProvider: + type: ConfigMap + name: kube-root-ca.crt + key: ca.crt + namespace: golang-external-secrets + auth: + kubernetes: + mountPath: hub + role: hub-role + secretRef: + name: golang-external-secrets + namespace: golang-external-secrets + key: "token" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: secretstore-validate + labels: + external-secrets.io/component: webhook +webhooks: +- name: "validate.secretstore.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["secretstores"] + scope: "Namespaced" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-secretstore + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 + +- name: "validate.clustersecretstore.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["clustersecretstores"] + scope: "Cluster" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-clustersecretstore + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 +--- +# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: externalsecret-validate + labels: + external-secrets.io/component: webhook +webhooks: +- name: "validate.externalsecret.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["externalsecrets"] + scope: "Namespaced" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-externalsecret + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 + failurePolicy: Fail diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml new file mode 100644 index 00000000..fc9597d5 --- /dev/null +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -0,0 +1,6421 @@ +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-secrets-cert-controller + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: golang-external-secrets + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml +apiVersion: v1 +kind: Secret +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm + external-secrets.io/component: webhook +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml +apiVersion: v1 +kind: Secret +metadata: + name: golang-external-secrets + namespace: golang-external-secrets + annotations: + kubernetes.io/service-account.name: golang-external-secrets +type: kubernetes.io/service-account-token +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: clusterexternalsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ClusterExternalSecret + listKind: ClusterExternalSecretList + plural: clusterexternalsecrets + shortNames: + - ces + singular: clusterexternalsecret + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ClusterExternalSecret is the Schema for the clusterexternalsecrets 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: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret. + properties: + externalSecretName: + description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret + type: string + externalSecretSpec: + description: The spec for the ExternalSecrets to be created + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + properties: + extract: + description: Used to extract multiple key/value pairs from one secret + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + find: + description: Used to find secrets based on tags or regular expressions + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + name: + description: Finds secrets based on the name. + properties: + regexp: + description: Finds secrets base + type: string + type: object + path: + description: A root path to start the find operations. + type: string + tags: + additionalProperties: + type: string + description: Find secrets based on tags. + type: object + type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + default: + creationPolicy: Owner + deletionPolicy: Retain + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Orphan + - Merge + - None + type: string + deletionPolicy: + default: Retain + description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + enum: + - Delete + - Merge + - Retain + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v2 + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + type: object + namespaceSelector: + description: The labels to select by to find the Namespaces to create the ExternalSecrets in. + 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 + required: + - key + - operator + type: object + type: array + 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 + refreshTime: + description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. + type: string + required: + - externalSecretSpec + - namespaceSelector + type: object + status: + description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret. + properties: + conditions: + items: + properties: + message: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + failedNamespaces: + description: Failed namespaces are the namespaces that failed to apply an ExternalSecret + items: + description: ClusterExternalSecretNamespaceFailure represents a failed namespace deployment and it's reason. + properties: + namespace: + description: Namespace is the namespace that failed when trying to apply an ExternalSecret + type: string + reason: + description: Reason is why the ExternalSecret failed to apply to the namespace + type: string + required: + - namespace + type: object + type: array + provisionedNamespaces: + description: ProvisionedNamespaces are the namespaces where the ClusterExternalSecret has secrets + items: + type: string + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/clustersecretstore.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: clustersecretstores.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ClusterSecretStore + listKind: ClusterSecretStoreList + plural: clustersecretstores + shortNames: + - css + singular: clustersecretstore + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object + secretRef: + description: Reference to a Secret that contains the details to authenticate with Akeyless. + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: + type: string + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + conditions: + description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore + items: + description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + properties: + namespaceSelector: + description: Choose namespace using a labelSelector + 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 + required: + - key + - operator + type: object + type: array + 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 + namespaces: + description: Choose namespaces by name + items: + type: string + type: array + type: object + type: array + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object + secretRef: + description: Reference to a Secret that contains the details to authenticate with Akeyless. + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + doppler: + description: Doppler configures this store to sync secrets using the Doppler provider + properties: + auth: + description: Auth configures how the Operator authenticates with the Doppler API + properties: + secretRef: + properties: + dopplerToken: + description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - dopplerToken + type: object + required: + - secretRef + type: object + config: + description: Doppler config (required if not using a Service Token) + type: string + format: + description: Format enables the downloading of secrets as a file (string) + enum: + - json + - dotnet-json + - env + - yaml + - docker + type: string + nameTransformer: + description: Environment variable compatible name transforms that change secret names to a different format + enum: + - upper-camel + - camel + - lower-snake + - tf-var + - dotnet-env + type: string + project: + description: Doppler project (required if not using a Service Token) + type: string + required: + - auth + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + environment: + description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) + type: string + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 + properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + onepassword: + description: OnePassword configures this store to sync secrets using the 1Password Cloud provider + properties: + auth: + description: Auth defines the information necessary to authenticate against OnePassword Connect Server + properties: + secretRef: + description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. + properties: + connectTokenSecretRef: + description: The ConnectToken is used for authentication to a 1Password Connect Server. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - connectTokenSecretRef + type: object + required: + - secretRef + type: object + connectHost: + description: ConnectHost defines the OnePassword Connect Server to connect to + type: string + vaults: + additionalProperties: + type: integer + description: Vaults defines which OnePassword vaults to search in which order + type: object + required: + - auth + - connectHost + - vaults + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + senhasegura: + description: Senhasegura configures this store to sync secrets using senhasegura provider + properties: + auth: + description: Auth defines parameters to authenticate in senhasegura + properties: + clientId: + type: string + clientSecretSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecretSecretRef + type: object + ignoreSslCertificate: + default: false + description: IgnoreSslCertificate defines if SSL certificate must be ignored + type: boolean + module: + description: Module defines which senhasegura module should be used to get secrets + type: string + url: + description: URL of senhasegura + type: string + required: + - auth + - module + - url + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + items: + type: string + type: array + expirationSeconds: + description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexcertificatemanager: + description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Certificate Manager + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + refreshInterval: + description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. + type: integer + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/externalsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: externalsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ExternalSecret + listKind: ExternalSecretList + plural: externalsecrets + shortNames: + - es + singular: externalsecret + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v1 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + - target + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + properties: + extract: + description: Used to extract multiple key/value pairs from one secret + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + find: + description: Used to find secrets based on tags or regular expressions + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + name: + description: Finds secrets based on the name. + properties: + regexp: + description: Finds secrets base + type: string + type: object + path: + description: A root path to start the find operations. + type: string + tags: + additionalProperties: + type: string + description: Find secrets based on tags. + type: object + type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + default: + creationPolicy: Owner + deletionPolicy: Retain + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Orphan + - Merge + - None + type: string + deletionPolicy: + default: Retain + description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + enum: + - Delete + - Merge + - Retain + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v2 + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/secretstore.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: secretstores.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: SecretStore + listKind: SecretStoreList + plural: secretstores + shortNames: + - ss + singular: secretstore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object + secretRef: + description: Reference to a Secret that contains the details to authenticate with Akeyless. + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: + type: string + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + conditions: + description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore + items: + description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + properties: + namespaceSelector: + description: Choose namespace using a labelSelector + 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 + required: + - key + - operator + type: object + type: array + 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 + namespaces: + description: Choose namespaces by name + items: + type: string + type: array + type: object + type: array + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object + secretRef: + description: Reference to a Secret that contains the details to authenticate with Akeyless. + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + doppler: + description: Doppler configures this store to sync secrets using the Doppler provider + properties: + auth: + description: Auth configures how the Operator authenticates with the Doppler API + properties: + secretRef: + properties: + dopplerToken: + description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - dopplerToken + type: object + required: + - secretRef + type: object + config: + description: Doppler config (required if not using a Service Token) + type: string + format: + description: Format enables the downloading of secrets as a file (string) + enum: + - json + - dotnet-json + - env + - yaml + - docker + type: string + nameTransformer: + description: Environment variable compatible name transforms that change secret names to a different format + enum: + - upper-camel + - camel + - lower-snake + - tf-var + - dotnet-env + type: string + project: + description: Doppler project (required if not using a Service Token) + type: string + required: + - auth + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + environment: + description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) + type: string + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 + properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + onepassword: + description: OnePassword configures this store to sync secrets using the 1Password Cloud provider + properties: + auth: + description: Auth defines the information necessary to authenticate against OnePassword Connect Server + properties: + secretRef: + description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. + properties: + connectTokenSecretRef: + description: The ConnectToken is used for authentication to a 1Password Connect Server. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - connectTokenSecretRef + type: object + required: + - secretRef + type: object + connectHost: + description: ConnectHost defines the OnePassword Connect Server to connect to + type: string + vaults: + additionalProperties: + type: integer + description: Vaults defines which OnePassword vaults to search in which order + type: object + required: + - auth + - connectHost + - vaults + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + senhasegura: + description: Senhasegura configures this store to sync secrets using senhasegura provider + properties: + auth: + description: Auth defines parameters to authenticate in senhasegura + properties: + clientId: + type: string + clientSecretSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecretSecretRef + type: object + ignoreSslCertificate: + default: false + description: IgnoreSslCertificate defines if SSL certificate must be ignored + type: boolean + module: + description: Module defines which senhasegura module should be used to get secrets + type: string + url: + description: URL of senhasegura + type: string + required: + - auth + - module + - url + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + items: + type: string + type: array + expirationSeconds: + description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexcertificatemanager: + description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Certificate Manager + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + refreshInterval: + description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. + type: integer + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-cert-controller + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "apiextensions.k8s.io" + resources: + - "customresourcedefinitions" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" + - apiGroups: + - "admissionregistration.k8s.io" + resources: + - "validatingwebhookconfigurations" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "endpoints" + verbs: + - "list" + - "get" + - "watch" + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-controller + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "secretstores" + - "clustersecretstores" + - "externalsecrets" + - "clusterexternalsecrets" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "externalsecrets/status" + - "externalsecrets/finalizers" + - "secretstores" + - "secretstores/status" + - "secretstores/finalizers" + - "clustersecretstores" + - "clustersecretstores/status" + - "clustersecretstores/finalizers" + - "clusterexternalsecrets" + - "clusterexternalsecrets/status" + - "clusterexternalsecrets/finalizers" + verbs: + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "serviceaccounts" + - "namespaces" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "list" + - "watch" + - "create" + - "update" + - "delete" + - "patch" + - apiGroups: + - "" + resources: + - "serviceaccounts/token" + verbs: + - "create" + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + verbs: + - "create" + - "update" + - "delete" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-view + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm + rbac.authorization.k8s.io/aggregate-to-view: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "secretstores" + - "clustersecretstores" + verbs: + - "get" + - "watch" + - "list" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-edit + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "secretstores" + - "clustersecretstores" + verbs: + - "create" + - "delete" + - "deletecollection" + - "patch" + - "update" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: golang-external-secrets-cert-controller + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: golang-external-secrets-cert-controller +subjects: + - name: external-secrets-cert-controller + namespace: "default" + kind: ServiceAccount +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: golang-external-secrets-controller + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: golang-external-secrets-controller +subjects: + - name: golang-external-secrets + namespace: "default" + kind: ServiceAccount +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: role-tokenreview-binding + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: golang-external-secrets + namespace: golang-external-secrets +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: golang-external-secrets-leaderelection + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "" + resources: + - "configmaps" + resourceNames: + - "external-secrets-controller" + verbs: + - "get" + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "create" + - apiGroups: + - "coordination.k8s.io" + resources: + - "leases" + verbs: + - "get" + - "create" + - "update" + - "patch" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: golang-external-secrets-leaderelection + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: golang-external-secrets-leaderelection +subjects: + - kind: ServiceAccount + name: golang-external-secrets + namespace: "default" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm + external-secrets.io/component: webhook +spec: + type: ClusterIP + ports: + - port: 443 + targetPort: 10250 + protocol: TCP + name: webhook + selector: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets-cert-controller + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + spec: + serviceAccountName: external-secrets-cert-controller + containers: + - name: cert-controller + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + imagePullPolicy: IfNotPresent + args: + - certcontroller + - --crd-requeue-interval=5m + - --service-name=golang-external-secrets-webhook + - --service-namespace=default + - --secret-name=golang-external-secrets-webhook + - --secret-namespace=default + ports: + - containerPort: 8080 + protocol: TCP + name: metrics + readinessProbe: + httpGet: + port: 8081 + path: /readyz + initialDelaySeconds: 20 + periodSeconds: 5 +--- +# Source: golang-external-secrets/charts/external-secrets/templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + spec: + serviceAccountName: golang-external-secrets + containers: + - name: external-secrets + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + imagePullPolicy: IfNotPresent + args: + - --concurrent=1 + ports: + - containerPort: 8080 + protocol: TCP + name: metrics +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + spec: + hostNetwork: false + serviceAccountName: external-secrets-webhook + containers: + - name: webhook + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + imagePullPolicy: IfNotPresent + args: + - webhook + - --port=10250 + - --dns-name=golang-external-secrets-webhook.default.svc + - --cert-dir=/tmp/certs + - --check-interval=5m + - --healthz-addr=:8081 + ports: + - containerPort: 8080 + protocol: TCP + name: metrics + - containerPort: 10250 + protocol: TCP + name: webhook + readinessProbe: + httpGet: + port: 8081 + path: /readyz + initialDelaySeconds: 20 + periodSeconds: 5 + volumeMounts: + - name: certs + mountPath: /tmp/certs + readOnly: true + volumes: + - name: certs + secret: + secretName: golang-external-secrets-webhook +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ClusterSecretStore +metadata: + name: vault-backend + namespace: golang-external-secrets +spec: + provider: + vault: + server: https://vault-vault.apps.hub.example.com + path: secret + # Version of KV backend + version: v2 + caProvider: + type: ConfigMap + name: kube-root-ca.crt + key: ca.crt + namespace: golang-external-secrets + auth: + kubernetes: + mountPath: hub + role: hub-role + secretRef: + name: golang-external-secrets + namespace: golang-external-secrets + key: "token" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: secretstore-validate + labels: + external-secrets.io/component: webhook +webhooks: +- name: "validate.secretstore.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["secretstores"] + scope: "Namespaced" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-secretstore + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 + +- name: "validate.clustersecretstore.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["clustersecretstores"] + scope: "Cluster" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-clustersecretstore + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 +--- +# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: externalsecret-validate + labels: + external-secrets.io/component: webhook +webhooks: +- name: "validate.externalsecret.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["externalsecrets"] + scope: "Namespaced" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-externalsecret + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 + failurePolicy: Fail diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml new file mode 100644 index 00000000..fc9597d5 --- /dev/null +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -0,0 +1,6421 @@ +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-secrets-cert-controller + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: golang-external-secrets + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml +apiVersion: v1 +kind: Secret +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm + external-secrets.io/component: webhook +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml +apiVersion: v1 +kind: Secret +metadata: + name: golang-external-secrets + namespace: golang-external-secrets + annotations: + kubernetes.io/service-account.name: golang-external-secrets +type: kubernetes.io/service-account-token +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: clusterexternalsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ClusterExternalSecret + listKind: ClusterExternalSecretList + plural: clusterexternalsecrets + shortNames: + - ces + singular: clusterexternalsecret + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ClusterExternalSecret is the Schema for the clusterexternalsecrets 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: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret. + properties: + externalSecretName: + description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret + type: string + externalSecretSpec: + description: The spec for the ExternalSecrets to be created + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + properties: + extract: + description: Used to extract multiple key/value pairs from one secret + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + find: + description: Used to find secrets based on tags or regular expressions + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + name: + description: Finds secrets based on the name. + properties: + regexp: + description: Finds secrets base + type: string + type: object + path: + description: A root path to start the find operations. + type: string + tags: + additionalProperties: + type: string + description: Find secrets based on tags. + type: object + type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + default: + creationPolicy: Owner + deletionPolicy: Retain + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Orphan + - Merge + - None + type: string + deletionPolicy: + default: Retain + description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + enum: + - Delete + - Merge + - Retain + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v2 + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + type: object + namespaceSelector: + description: The labels to select by to find the Namespaces to create the ExternalSecrets in. + 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 + required: + - key + - operator + type: object + type: array + 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 + refreshTime: + description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. + type: string + required: + - externalSecretSpec + - namespaceSelector + type: object + status: + description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret. + properties: + conditions: + items: + properties: + message: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + failedNamespaces: + description: Failed namespaces are the namespaces that failed to apply an ExternalSecret + items: + description: ClusterExternalSecretNamespaceFailure represents a failed namespace deployment and it's reason. + properties: + namespace: + description: Namespace is the namespace that failed when trying to apply an ExternalSecret + type: string + reason: + description: Reason is why the ExternalSecret failed to apply to the namespace + type: string + required: + - namespace + type: object + type: array + provisionedNamespaces: + description: ProvisionedNamespaces are the namespaces where the ClusterExternalSecret has secrets + items: + type: string + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/clustersecretstore.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: clustersecretstores.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ClusterSecretStore + listKind: ClusterSecretStoreList + plural: clustersecretstores + shortNames: + - css + singular: clustersecretstore + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object + secretRef: + description: Reference to a Secret that contains the details to authenticate with Akeyless. + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: + type: string + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + conditions: + description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore + items: + description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + properties: + namespaceSelector: + description: Choose namespace using a labelSelector + 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 + required: + - key + - operator + type: object + type: array + 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 + namespaces: + description: Choose namespaces by name + items: + type: string + type: array + type: object + type: array + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object + secretRef: + description: Reference to a Secret that contains the details to authenticate with Akeyless. + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + doppler: + description: Doppler configures this store to sync secrets using the Doppler provider + properties: + auth: + description: Auth configures how the Operator authenticates with the Doppler API + properties: + secretRef: + properties: + dopplerToken: + description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - dopplerToken + type: object + required: + - secretRef + type: object + config: + description: Doppler config (required if not using a Service Token) + type: string + format: + description: Format enables the downloading of secrets as a file (string) + enum: + - json + - dotnet-json + - env + - yaml + - docker + type: string + nameTransformer: + description: Environment variable compatible name transforms that change secret names to a different format + enum: + - upper-camel + - camel + - lower-snake + - tf-var + - dotnet-env + type: string + project: + description: Doppler project (required if not using a Service Token) + type: string + required: + - auth + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + environment: + description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) + type: string + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 + properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + onepassword: + description: OnePassword configures this store to sync secrets using the 1Password Cloud provider + properties: + auth: + description: Auth defines the information necessary to authenticate against OnePassword Connect Server + properties: + secretRef: + description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. + properties: + connectTokenSecretRef: + description: The ConnectToken is used for authentication to a 1Password Connect Server. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - connectTokenSecretRef + type: object + required: + - secretRef + type: object + connectHost: + description: ConnectHost defines the OnePassword Connect Server to connect to + type: string + vaults: + additionalProperties: + type: integer + description: Vaults defines which OnePassword vaults to search in which order + type: object + required: + - auth + - connectHost + - vaults + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + senhasegura: + description: Senhasegura configures this store to sync secrets using senhasegura provider + properties: + auth: + description: Auth defines parameters to authenticate in senhasegura + properties: + clientId: + type: string + clientSecretSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecretSecretRef + type: object + ignoreSslCertificate: + default: false + description: IgnoreSslCertificate defines if SSL certificate must be ignored + type: boolean + module: + description: Module defines which senhasegura module should be used to get secrets + type: string + url: + description: URL of senhasegura + type: string + required: + - auth + - module + - url + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + items: + type: string + type: array + expirationSeconds: + description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexcertificatemanager: + description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Certificate Manager + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + refreshInterval: + description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. + type: integer + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/externalsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: externalsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: ExternalSecret + listKind: ExternalSecretList + plural: externalsecrets + shortNames: + - es + singular: externalsecret + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v1 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + - target + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. + properties: + data: + description: Data defines the connection between the Kubernetes Secret keys and the Provider data + items: + description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. + properties: + remoteRef: + description: ExternalSecretDataRemoteRef defines Provider data location. + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + secretKey: + type: string + required: + - remoteRef + - secretKey + type: object + type: array + dataFrom: + description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + items: + properties: + extract: + description: Used to extract multiple key/value pairs from one secret + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + key: + description: Key is the key used in the Provider, mandatory + type: string + metadataPolicy: + description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + type: string + property: + description: Used to select a specific property of the Provider value (if a map), if supported + type: string + version: + description: Used to select a specific version of the Provider value, if supported + type: string + required: + - key + type: object + find: + description: Used to find secrets based on tags or regular expressions + properties: + conversionStrategy: + default: Default + description: Used to define a conversion Strategy + type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string + name: + description: Finds secrets based on the name. + properties: + regexp: + description: Finds secrets base + type: string + type: object + path: + description: A root path to start the find operations. + type: string + tags: + additionalProperties: + type: string + description: Find secrets based on tags. + type: object + type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array + type: object + type: array + refreshInterval: + default: 1h + description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + type: string + secretStoreRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + target: + default: + creationPolicy: Owner + deletionPolicy: Retain + description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + properties: + creationPolicy: + default: Owner + description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Orphan + - Merge + - None + type: string + deletionPolicy: + default: Retain + description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + enum: + - Delete + - Merge + - Retain + type: string + immutable: + description: Immutable defines if the final secret will be immutable + type: boolean + name: + description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + type: string + template: + description: Template defines a blueprint for the created Secret resource. + properties: + data: + additionalProperties: + type: string + type: object + engineVersion: + default: v2 + type: string + metadata: + description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + templateFrom: + items: + maxProperties: 1 + minProperties: 1 + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + name: + type: string + required: + - items + - name + type: object + type: object + type: array + type: + type: string + type: object + type: object + required: + - secretStoreRef + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/secretstore.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: secretstores.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - externalsecrets + kind: SecretStore + listKind: SecretStoreList + plural: secretstores + shortNames: + - ss + singular: secretstore + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + deprecated: true + name: v1alpha1 + schema: + openAPIV3Schema: + description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object + secretRef: + description: Reference to a Secret that contains the details to authenticate with Akeyless. + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + properties: + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + serviceAccount: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + items: + type: string + type: array + expirationSeconds: + description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. + 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: SecretStoreSpec defines the desired state of SecretStore. + properties: + conditions: + description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore + items: + description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + properties: + namespaceSelector: + description: Choose namespace using a labelSelector + 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 + required: + - key + - operator + type: object + type: array + 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 + namespaces: + description: Choose namespaces by name + items: + type: string + type: array + type: object + type: array + controller: + description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + type: string + provider: + description: Used to configure the provider. Only one provider may be set + maxProperties: 1 + minProperties: 1 + properties: + akeyless: + description: Akeyless configures this store to sync secrets using Akeyless Vault provider + properties: + akeylessGWApiURL: + description: Akeyless GW API Url from which the secrets to be fetched from. + type: string + authSecretRef: + description: Auth configures how the operator authenticates with Akeyless. + properties: + kubernetesAuth: + description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + properties: + accessID: + description: the Akeyless Kubernetes auth-method access-id + type: string + k8sConfName: + description: Kubernetes-auth configuration name in Akeyless-Gateway + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - accessID + - k8sConfName + type: object + secretRef: + description: Reference to a Secret that contains the details to authenticate with Akeyless. + properties: + accessID: + description: The SecretAccessID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessType: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessTypeParam: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + required: + - akeylessGWApiURL + - authSecretRef + type: object + alibaba: + description: Alibaba configures this store to sync secrets using Alibaba Cloud provider + properties: + auth: + description: AlibabaAuth contains a secretRef for credentials. + properties: + secretRef: + description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + accessKeySecretSecretRef: + description: The AccessKeySecret is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - accessKeyIDSecretRef + - accessKeySecretSecretRef + type: object + required: + - secretRef + type: object + endpoint: + type: string + regionID: + description: Alibaba Region to be used for the provider + type: string + required: + - auth + - regionID + type: object + aws: + description: AWS configures this store to sync secrets using AWS Secret Manager provider + properties: + auth: + description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: AWS Region to be used for the provider + type: string + role: + description: Role is a Role ARN which the SecretManager provider will assume + type: string + service: + description: Service defines which service should be used to fetch the secrets + enum: + - SecretsManager + - ParameterStore + type: string + required: + - region + - service + type: object + azurekv: + description: AzureKV configures this store to sync secrets using Azure Key Vault provider + properties: + authSecretRef: + description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + authType: + default: ServicePrincipal + description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + enum: + - ServicePrincipal + - ManagedIdentity + - WorkloadIdentity + type: string + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + vaultUrl: + description: Vault Url from which the secrets to be fetched from. + type: string + required: + - vaultUrl + type: object + doppler: + description: Doppler configures this store to sync secrets using the Doppler provider + properties: + auth: + description: Auth configures how the Operator authenticates with the Doppler API + properties: + secretRef: + properties: + dopplerToken: + description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - dopplerToken + type: object + required: + - secretRef + type: object + config: + description: Doppler config (required if not using a Service Token) + type: string + format: + description: Format enables the downloading of secrets as a file (string) + enum: + - json + - dotnet-json + - env + - yaml + - docker + type: string + nameTransformer: + description: Environment variable compatible name transforms that change secret names to a different format + enum: + - upper-camel + - camel + - lower-snake + - tf-var + - dotnet-env + type: string + project: + description: Doppler project (required if not using a Service Token) + type: string + required: + - auth + type: object + fake: + description: Fake configures a store with static key/value pairs + properties: + data: + items: + properties: + key: + type: string + value: + type: string + valueMap: + additionalProperties: + type: string + type: object + version: + type: string + required: + - key + type: object + type: array + required: + - data + type: object + gcpsm: + description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + properties: + auth: + description: Auth defines the information necessary to authenticate against GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID project where secret is located + type: string + type: object + gitlab: + description: Gitlab configures this store to sync secrets using Gitlab Variables provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a GitLab instance. + properties: + SecretRef: + properties: + accessToken: + description: AccessToken is used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - SecretRef + type: object + environment: + description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) + type: string + projectID: + description: ProjectID specifies a project where secrets are located. + type: string + url: + description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. + type: string + required: + - auth + type: object + ibm: + description: IBM configures this store to sync secrets using IBM Cloud provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 + properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object + secretRef: + properties: + secretApiKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + serviceUrl: + description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance + type: string + required: + - auth + type: object + kubernetes: + description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + properties: + auth: + description: Auth configures how secret-manager authenticates with a Kubernetes instance. + maxProperties: 1 + minProperties: 1 + properties: + cert: + description: has both clientCert and clientKey as secretKeySelector + properties: + clientCert: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientKey: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + serviceAccount: + description: points to a service account that should be used for authentication + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + token: + description: use static token to authenticate with + properties: + bearerToken: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + remoteNamespace: + default: default + description: Remote namespace to fetch the secrets from + type: string + server: + description: configures the Kubernetes server Address. + properties: + caBundle: + description: CABundle is a base64-encoded CA certificate + format: byte + type: string + caProvider: + description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + url: + default: kubernetes.default + description: configures the Kubernetes server Address. + type: string + type: object + required: + - auth + type: object + onepassword: + description: OnePassword configures this store to sync secrets using the 1Password Cloud provider + properties: + auth: + description: Auth defines the information necessary to authenticate against OnePassword Connect Server + properties: + secretRef: + description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. + properties: + connectTokenSecretRef: + description: The ConnectToken is used for authentication to a 1Password Connect Server. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - connectTokenSecretRef + type: object + required: + - secretRef + type: object + connectHost: + description: ConnectHost defines the OnePassword Connect Server to connect to + type: string + vaults: + additionalProperties: + type: integer + description: Vaults defines which OnePassword vaults to search in which order + type: object + required: + - auth + - connectHost + - vaults + type: object + oracle: + description: Oracle configures this store to sync secrets using Oracle Vault provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + properties: + secretRef: + description: SecretRef to pass through sensitive information. + properties: + fingerprint: + description: Fingerprint is the fingerprint of the API private key. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + privatekey: + description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - fingerprint + - privatekey + type: object + tenancy: + description: Tenancy is the tenancy OCID where user is located. + type: string + user: + description: User is an access OCID specific to the account. + type: string + required: + - secretRef + - tenancy + - user + type: object + region: + description: Region is the region where vault is located. + type: string + vault: + description: Vault is the vault's OCID of the specific vault where secret is located. + type: string + required: + - region + - vault + type: object + senhasegura: + description: Senhasegura configures this store to sync secrets using senhasegura provider + properties: + auth: + description: Auth defines parameters to authenticate in senhasegura + properties: + clientId: + type: string + clientSecretSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - clientId + - clientSecretSecretRef + type: object + ignoreSslCertificate: + default: false + description: IgnoreSslCertificate defines if SSL certificate must be ignored + type: boolean + module: + description: Module defines which senhasegura module should be used to get secrets + type: string + url: + description: URL of senhasegura + type: string + required: + - auth + - module + - url + type: object + vault: + description: Vault configures this store to sync secrets using Hashi provider + properties: + auth: + description: Auth configures how secret-manager authenticates with the Vault server. + properties: + appRole: + description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + properties: + path: + default: approle + description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + type: string + roleId: + description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + type: string + secretRef: + description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + - roleId + - secretRef + type: object + cert: + description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + properties: + clientCert: + description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretRef: + description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + jwt: + description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + properties: + kubernetesServiceAccountToken: + description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + items: + type: string + type: array + expirationSeconds: + description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + format: int64 + type: integer + serviceAccountRef: + description: Service account field containing the name of a kubernetes ServiceAccount. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - serviceAccountRef + type: object + path: + default: jwt + description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + type: string + role: + description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - path + type: object + kubernetes: + description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + properties: + mountPath: + default: kubernetes + description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + type: string + role: + description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + type: string + secretRef: + description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - mountPath + - role + type: object + ldap: + description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + properties: + path: + default: ldap + description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + type: string + required: + - path + - username + type: object + tokenSecretRef: + description: TokenSecretRef authenticates with Vault by presenting a token. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caBundle: + description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate Vault server certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + forwardInconsistent: + description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + type: boolean + namespace: + description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + type: string + path: + description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + type: string + readYourWrites: + description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + type: boolean + server: + description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' + type: string + version: + default: v2 + description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + enum: + - v1 + - v2 + type: string + required: + - auth + - server + type: object + webhook: + description: Webhook configures this store to sync secrets using a generic templated webhook + properties: + body: + description: Body + type: string + caBundle: + description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + format: byte + type: string + caProvider: + description: The provider for the CA bundle to use to validate webhook server certificate. + properties: + key: + description: The key the value inside of the provider type to use, only used with "Secret" type + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object + headers: + additionalProperties: + type: string + description: Headers + type: object + method: + description: Webhook Method + type: string + result: + description: Result formatting + properties: + jsonPath: + description: Json path of return value + type: string + type: object + secrets: + description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + items: + properties: + name: + description: Name of this secret in templates + type: string + secretRef: + description: Secret ref to fill in credentials + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - name + - secretRef + type: object + type: array + timeout: + description: Timeout + type: string + url: + description: Webhook url to call + type: string + required: + - result + - url + type: object + yandexcertificatemanager: + description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Certificate Manager + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + yandexlockbox: + description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + properties: + apiEndpoint: + description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') + type: string + auth: + description: Auth defines the information necessary to authenticate against Yandex Lockbox + properties: + authorizedKeySecretRef: + description: The authorized key used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + caProvider: + description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. + properties: + certSecretRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - auth + type: object + type: object + refreshInterval: + description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. + type: integer + retrySettings: + description: Used to configure http retries if failed + properties: + maxRetries: + format: int32 + type: integer + retryInterval: + type: string + type: object + required: + - provider + type: object + status: + description: SecretStoreStatus defines the observed state of the SecretStore. + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-cert-controller + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "apiextensions.k8s.io" + resources: + - "customresourcedefinitions" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" + - apiGroups: + - "admissionregistration.k8s.io" + resources: + - "validatingwebhookconfigurations" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "endpoints" + verbs: + - "list" + - "get" + - "watch" + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "list" + - "watch" + - "update" + - "patch" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-controller + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "secretstores" + - "clustersecretstores" + - "externalsecrets" + - "clusterexternalsecrets" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "externalsecrets/status" + - "externalsecrets/finalizers" + - "secretstores" + - "secretstores/status" + - "secretstores/finalizers" + - "clustersecretstores" + - "clustersecretstores/status" + - "clustersecretstores/finalizers" + - "clusterexternalsecrets" + - "clusterexternalsecrets/status" + - "clusterexternalsecrets/finalizers" + verbs: + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "serviceaccounts" + - "namespaces" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "get" + - "list" + - "watch" + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "list" + - "watch" + - "create" + - "update" + - "delete" + - "patch" + - apiGroups: + - "" + resources: + - "serviceaccounts/token" + verbs: + - "create" + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + verbs: + - "create" + - "update" + - "delete" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-view + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm + rbac.authorization.k8s.io/aggregate-to-view: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "secretstores" + - "clustersecretstores" + verbs: + - "get" + - "watch" + - "list" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-edit + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + - "secretstores" + - "clustersecretstores" + verbs: + - "create" + - "delete" + - "deletecollection" + - "patch" + - "update" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: golang-external-secrets-cert-controller + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: golang-external-secrets-cert-controller +subjects: + - name: external-secrets-cert-controller + namespace: "default" + kind: ServiceAccount +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: golang-external-secrets-controller + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: golang-external-secrets-controller +subjects: + - name: golang-external-secrets + namespace: "default" + kind: ServiceAccount +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: role-tokenreview-binding + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: golang-external-secrets + namespace: golang-external-secrets +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: golang-external-secrets-leaderelection + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "" + resources: + - "configmaps" + resourceNames: + - "external-secrets-controller" + verbs: + - "get" + - "update" + - "patch" + - apiGroups: + - "" + resources: + - "configmaps" + verbs: + - "create" + - apiGroups: + - "coordination.k8s.io" + resources: + - "leases" + verbs: + - "get" + - "create" + - "update" + - "patch" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: golang-external-secrets-leaderelection + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: golang-external-secrets-leaderelection +subjects: + - kind: ServiceAccount + name: golang-external-secrets + namespace: "default" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm + external-secrets.io/component: webhook +spec: + type: ClusterIP + ports: + - port: 443 + targetPort: 10250 + protocol: TCP + name: webhook + selector: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets +--- +# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets-cert-controller + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets-cert-controller + app.kubernetes.io/instance: golang-external-secrets + spec: + serviceAccountName: external-secrets-cert-controller + containers: + - name: cert-controller + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + imagePullPolicy: IfNotPresent + args: + - certcontroller + - --crd-requeue-interval=5m + - --service-name=golang-external-secrets-webhook + - --service-namespace=default + - --secret-name=golang-external-secrets-webhook + - --secret-namespace=default + ports: + - containerPort: 8080 + protocol: TCP + name: metrics + readinessProbe: + httpGet: + port: 8081 + path: /readyz + initialDelaySeconds: 20 + periodSeconds: 5 +--- +# Source: golang-external-secrets/charts/external-secrets/templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + spec: + serviceAccountName: golang-external-secrets + containers: + - name: external-secrets + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + imagePullPolicy: IfNotPresent + args: + - --concurrent=1 + ports: + - containerPort: 8080 + protocol: TCP + name: metrics +--- +# Source: golang-external-secrets/charts/external-secrets/templates/webhook-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: golang-external-secrets-webhook + namespace: "default" + labels: + helm.sh/chart: external-secrets-0.6.1 + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + template: + metadata: + labels: + app.kubernetes.io/name: external-secrets-webhook + app.kubernetes.io/instance: golang-external-secrets + spec: + hostNetwork: false + serviceAccountName: external-secrets-webhook + containers: + - name: webhook + image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + imagePullPolicy: IfNotPresent + args: + - webhook + - --port=10250 + - --dns-name=golang-external-secrets-webhook.default.svc + - --cert-dir=/tmp/certs + - --check-interval=5m + - --healthz-addr=:8081 + ports: + - containerPort: 8080 + protocol: TCP + name: metrics + - containerPort: 10250 + protocol: TCP + name: webhook + readinessProbe: + httpGet: + port: 8081 + path: /readyz + initialDelaySeconds: 20 + periodSeconds: 5 + volumeMounts: + - name: certs + mountPath: /tmp/certs + readOnly: true + volumes: + - name: certs + secret: + secretName: golang-external-secrets-webhook +--- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ClusterSecretStore +metadata: + name: vault-backend + namespace: golang-external-secrets +spec: + provider: + vault: + server: https://vault-vault.apps.hub.example.com + path: secret + # Version of KV backend + version: v2 + caProvider: + type: ConfigMap + name: kube-root-ca.crt + key: ca.crt + namespace: golang-external-secrets + auth: + kubernetes: + mountPath: hub + role: hub-role + secretRef: + name: golang-external-secrets + namespace: golang-external-secrets + key: "token" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: secretstore-validate + labels: + external-secrets.io/component: webhook +webhooks: +- name: "validate.secretstore.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["secretstores"] + scope: "Namespaced" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-secretstore + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 + +- name: "validate.clustersecretstore.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["clustersecretstores"] + scope: "Cluster" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-clustersecretstore + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 +--- +# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: externalsecret-validate + labels: + external-secrets.io/component: webhook +webhooks: +- name: "validate.externalsecret.external-secrets.io" + rules: + - apiGroups: ["external-secrets.io"] + apiVersions: ["v1beta1"] + operations: ["CREATE", "UPDATE", "DELETE"] + resources: ["externalsecrets"] + scope: "Namespaced" + clientConfig: + service: + namespace: "default" + name: golang-external-secrets-webhook + path: /validate-external-secrets-io-v1beta1-externalsecret + admissionReviewVersions: ["v1", "v1beta1"] + sideEffects: None + timeoutSeconds: 5 + failurePolicy: Fail diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml new file mode 100644 index 00000000..73417bc3 --- /dev/null +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -0,0 +1,410 @@ +--- +# Source: hashicorp-vault/charts/vault/templates/server-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +--- +# Source: hashicorp-vault/charts/vault/templates/server-config-configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hashicorp-vault-config + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +data: + extraconfig-from-values.hcl: |- + disable_mlock = true + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + # Enable unauthenticated metrics access (necessary for Prometheus Operator) + #telemetry { + # unauthenticated_metrics_access = "true" + #} + } + storage "file" { + path = "/vault/data" + } + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} + + # Example configuration for enabling Prometheus metrics in your config. + #telemetry { + # prometheus_retention_time = "30s", + # disable_hostname = true + #} +--- +# Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: hashicorp-vault-server-binding + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: hashicorp-vault + namespace: default +--- +# Source: hashicorp-vault/charts/vault/templates/server-headless-service.yaml +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault-internal + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm + vault-internal: "true" + annotations: + +spec: + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: "http" + port: 8200 + targetPort: 8200 + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server +--- +# Source: hashicorp-vault/charts/vault/templates/server-service.yaml +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm + annotations: + +spec: + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: true + ports: + - name: http + port: 8200 + targetPort: 8200 + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server +--- +# Source: hashicorp-vault/charts/vault/templates/ui-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault-ui + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault-ui + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + publishNotReadyAddresses: true + ports: + - name: http + port: 8200 + targetPort: 8200 + type: LoadBalancer + externalTrafficPolicy: Cluster +--- +# Source: hashicorp-vault/charts/vault/templates/server-statefulset.yaml +# StatefulSet to run the actual vault server cluster. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: hashicorp-vault + namespace: default + labels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + serviceName: hashicorp-vault-internal + podManagementPolicy: Parallel + replicas: 1 + updateStrategy: + type: OnDelete + selector: + matchLabels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + template: + metadata: + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + spec: + + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: "hashicorp-vault" + component: server + topologyKey: kubernetes.io/hostname + + + + + terminationGracePeriodSeconds: 10 + serviceAccountName: hashicorp-vault + + + volumes: + + - name: config + configMap: + name: hashicorp-vault-config + + - name: home + emptyDir: {} + containers: + - name: vault + + image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + imagePullPolicy: IfNotPresent + command: + - "/bin/sh" + - "-ec" + args: + - | + cp /vault/config/extraconfig-from-values.hcl /tmp/storageconfig.hcl; + [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /tmp/storageconfig.hcl; + [ -n "${API_ADDR}" ] && sed -Ei "s|API_ADDR|${API_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${TRANSIT_ADDR}" ] && sed -Ei "s|TRANSIT_ADDR|${TRANSIT_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${RAFT_ADDR}" ] && sed -Ei "s|RAFT_ADDR|${RAFT_ADDR?}|g" /tmp/storageconfig.hcl; + /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl + + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: VAULT_K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: VAULT_ADDR + value: "http://127.0.0.1:8200" + - name: VAULT_API_ADDR + value: "http://$(POD_IP):8200" + - name: SKIP_CHOWN + value: "true" + - name: SKIP_SETCAP + value: "true" + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_CLUSTER_ADDR + value: "https://$(HOSTNAME).hashicorp-vault-internal:8201" + - name: HOME + value: "/home/vault" + + + + volumeMounts: + + + + - name: data + mountPath: /vault/data + + + + - name: config + mountPath: /vault/config + + - name: home + mountPath: /home/vault + ports: + - containerPort: 8200 + name: http + - containerPort: 8201 + name: https-internal + - containerPort: 8202 + name: http-rep + readinessProbe: + # Check status; unsealed vault servers return 0 + # The exit code reflects the seal status: + # 0 - unsealed + # 1 - error + # 2 - sealed + exec: + command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + lifecycle: + # Vault container doesn't receive SIGTERM from Kubernetes + # and after the grace period ends, Kube sends SIGKILL. This + # causes issues with graceful shutdowns such as deregistering itself + # from Consul (zombie services). + preStop: + exec: + command: [ + "/bin/sh", "-c", + # Adding a sleep here to give the pod eviction a + # chance to propagate, so requests will not be made + # to this pod while it's terminating + "sleep 5 && kill -SIGTERM $(pidof vault)", + ] + + + volumeClaimTemplates: + - metadata: + name: data + + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +# Source: hashicorp-vault/templates/vault-app.yaml +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: vault-link + namespace: vault +spec: + applicationMenu: + section: HashiCorp Vault + imageURL: data:image/png;base64,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 + href: 'https://vault-vault.apps.region.example.com' + location: ApplicationMenu + text: 'Vault' +--- +# Source: hashicorp-vault/charts/vault/templates/server-route.yaml +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + host: + to: + kind: Service + name: hashicorp-vault + weight: 100 + port: + targetPort: 8200 + tls: + termination: edge +--- +# Source: hashicorp-vault/charts/vault/templates/tests/server-test.yaml +apiVersion: v1 +kind: Pod +metadata: + name: "hashicorp-vault-server-test" + namespace: default + annotations: + "helm.sh/hook": test +spec: + + containers: + - name: hashicorp-vault-server-test + image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + imagePullPolicy: IfNotPresent + env: + - name: VAULT_ADDR + value: http://hashicorp-vault.default.svc:8200 + + command: + - /bin/sh + - -c + - | + echo "Checking for sealed info in 'vault status' output" + ATTEMPTS=10 + n=0 + until [ "$n" -ge $ATTEMPTS ] + do + echo "Attempt" $n... + vault status -format yaml | grep -E '^sealed: (true|false)' && break + n=$((n+1)) + sleep 5 + done + if [ $n -ge $ATTEMPTS ]; then + echo "timed out looking for sealed info in 'vault status' output" + exit 1 + fi + + exit 0 + volumeMounts: + volumes: + restartPolicy: Never diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml new file mode 100644 index 00000000..73417bc3 --- /dev/null +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -0,0 +1,410 @@ +--- +# Source: hashicorp-vault/charts/vault/templates/server-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +--- +# Source: hashicorp-vault/charts/vault/templates/server-config-configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hashicorp-vault-config + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +data: + extraconfig-from-values.hcl: |- + disable_mlock = true + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + # Enable unauthenticated metrics access (necessary for Prometheus Operator) + #telemetry { + # unauthenticated_metrics_access = "true" + #} + } + storage "file" { + path = "/vault/data" + } + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} + + # Example configuration for enabling Prometheus metrics in your config. + #telemetry { + # prometheus_retention_time = "30s", + # disable_hostname = true + #} +--- +# Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: hashicorp-vault-server-binding + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: hashicorp-vault + namespace: default +--- +# Source: hashicorp-vault/charts/vault/templates/server-headless-service.yaml +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault-internal + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm + vault-internal: "true" + annotations: + +spec: + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: "http" + port: 8200 + targetPort: 8200 + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server +--- +# Source: hashicorp-vault/charts/vault/templates/server-service.yaml +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm + annotations: + +spec: + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: true + ports: + - name: http + port: 8200 + targetPort: 8200 + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server +--- +# Source: hashicorp-vault/charts/vault/templates/ui-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault-ui + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault-ui + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + publishNotReadyAddresses: true + ports: + - name: http + port: 8200 + targetPort: 8200 + type: LoadBalancer + externalTrafficPolicy: Cluster +--- +# Source: hashicorp-vault/charts/vault/templates/server-statefulset.yaml +# StatefulSet to run the actual vault server cluster. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: hashicorp-vault + namespace: default + labels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + serviceName: hashicorp-vault-internal + podManagementPolicy: Parallel + replicas: 1 + updateStrategy: + type: OnDelete + selector: + matchLabels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + template: + metadata: + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + spec: + + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: "hashicorp-vault" + component: server + topologyKey: kubernetes.io/hostname + + + + + terminationGracePeriodSeconds: 10 + serviceAccountName: hashicorp-vault + + + volumes: + + - name: config + configMap: + name: hashicorp-vault-config + + - name: home + emptyDir: {} + containers: + - name: vault + + image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + imagePullPolicy: IfNotPresent + command: + - "/bin/sh" + - "-ec" + args: + - | + cp /vault/config/extraconfig-from-values.hcl /tmp/storageconfig.hcl; + [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /tmp/storageconfig.hcl; + [ -n "${API_ADDR}" ] && sed -Ei "s|API_ADDR|${API_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${TRANSIT_ADDR}" ] && sed -Ei "s|TRANSIT_ADDR|${TRANSIT_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${RAFT_ADDR}" ] && sed -Ei "s|RAFT_ADDR|${RAFT_ADDR?}|g" /tmp/storageconfig.hcl; + /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl + + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: VAULT_K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: VAULT_ADDR + value: "http://127.0.0.1:8200" + - name: VAULT_API_ADDR + value: "http://$(POD_IP):8200" + - name: SKIP_CHOWN + value: "true" + - name: SKIP_SETCAP + value: "true" + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_CLUSTER_ADDR + value: "https://$(HOSTNAME).hashicorp-vault-internal:8201" + - name: HOME + value: "/home/vault" + + + + volumeMounts: + + + + - name: data + mountPath: /vault/data + + + + - name: config + mountPath: /vault/config + + - name: home + mountPath: /home/vault + ports: + - containerPort: 8200 + name: http + - containerPort: 8201 + name: https-internal + - containerPort: 8202 + name: http-rep + readinessProbe: + # Check status; unsealed vault servers return 0 + # The exit code reflects the seal status: + # 0 - unsealed + # 1 - error + # 2 - sealed + exec: + command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + lifecycle: + # Vault container doesn't receive SIGTERM from Kubernetes + # and after the grace period ends, Kube sends SIGKILL. This + # causes issues with graceful shutdowns such as deregistering itself + # from Consul (zombie services). + preStop: + exec: + command: [ + "/bin/sh", "-c", + # Adding a sleep here to give the pod eviction a + # chance to propagate, so requests will not be made + # to this pod while it's terminating + "sleep 5 && kill -SIGTERM $(pidof vault)", + ] + + + volumeClaimTemplates: + - metadata: + name: data + + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +# Source: hashicorp-vault/templates/vault-app.yaml +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: vault-link + namespace: vault +spec: + applicationMenu: + section: HashiCorp Vault + imageURL: data:image/png;base64,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 + href: 'https://vault-vault.apps.region.example.com' + location: ApplicationMenu + text: 'Vault' +--- +# Source: hashicorp-vault/charts/vault/templates/server-route.yaml +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + host: + to: + kind: Service + name: hashicorp-vault + weight: 100 + port: + targetPort: 8200 + tls: + termination: edge +--- +# Source: hashicorp-vault/charts/vault/templates/tests/server-test.yaml +apiVersion: v1 +kind: Pod +metadata: + name: "hashicorp-vault-server-test" + namespace: default + annotations: + "helm.sh/hook": test +spec: + + containers: + - name: hashicorp-vault-server-test + image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + imagePullPolicy: IfNotPresent + env: + - name: VAULT_ADDR + value: http://hashicorp-vault.default.svc:8200 + + command: + - /bin/sh + - -c + - | + echo "Checking for sealed info in 'vault status' output" + ATTEMPTS=10 + n=0 + until [ "$n" -ge $ATTEMPTS ] + do + echo "Attempt" $n... + vault status -format yaml | grep -E '^sealed: (true|false)' && break + n=$((n+1)) + sleep 5 + done + if [ $n -ge $ATTEMPTS ]; then + echo "timed out looking for sealed info in 'vault status' output" + exit 1 + fi + + exit 0 + volumeMounts: + volumes: + restartPolicy: Never diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml new file mode 100644 index 00000000..73417bc3 --- /dev/null +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -0,0 +1,410 @@ +--- +# Source: hashicorp-vault/charts/vault/templates/server-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +--- +# Source: hashicorp-vault/charts/vault/templates/server-config-configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hashicorp-vault-config + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +data: + extraconfig-from-values.hcl: |- + disable_mlock = true + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + # Enable unauthenticated metrics access (necessary for Prometheus Operator) + #telemetry { + # unauthenticated_metrics_access = "true" + #} + } + storage "file" { + path = "/vault/data" + } + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} + + # Example configuration for enabling Prometheus metrics in your config. + #telemetry { + # prometheus_retention_time = "30s", + # disable_hostname = true + #} +--- +# Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: hashicorp-vault-server-binding + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: hashicorp-vault + namespace: default +--- +# Source: hashicorp-vault/charts/vault/templates/server-headless-service.yaml +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault-internal + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm + vault-internal: "true" + annotations: + +spec: + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: "http" + port: 8200 + targetPort: 8200 + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server +--- +# Source: hashicorp-vault/charts/vault/templates/server-service.yaml +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm + annotations: + +spec: + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: true + ports: + - name: http + port: 8200 + targetPort: 8200 + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server +--- +# Source: hashicorp-vault/charts/vault/templates/ui-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: hashicorp-vault-ui + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault-ui + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + publishNotReadyAddresses: true + ports: + - name: http + port: 8200 + targetPort: 8200 + type: LoadBalancer + externalTrafficPolicy: Cluster +--- +# Source: hashicorp-vault/charts/vault/templates/server-statefulset.yaml +# StatefulSet to run the actual vault server cluster. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: hashicorp-vault + namespace: default + labels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + serviceName: hashicorp-vault-internal + podManagementPolicy: Parallel + replicas: 1 + updateStrategy: + type: OnDelete + selector: + matchLabels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + template: + metadata: + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + component: server + spec: + + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: "hashicorp-vault" + component: server + topologyKey: kubernetes.io/hostname + + + + + terminationGracePeriodSeconds: 10 + serviceAccountName: hashicorp-vault + + + volumes: + + - name: config + configMap: + name: hashicorp-vault-config + + - name: home + emptyDir: {} + containers: + - name: vault + + image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + imagePullPolicy: IfNotPresent + command: + - "/bin/sh" + - "-ec" + args: + - | + cp /vault/config/extraconfig-from-values.hcl /tmp/storageconfig.hcl; + [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /tmp/storageconfig.hcl; + [ -n "${API_ADDR}" ] && sed -Ei "s|API_ADDR|${API_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${TRANSIT_ADDR}" ] && sed -Ei "s|TRANSIT_ADDR|${TRANSIT_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${RAFT_ADDR}" ] && sed -Ei "s|RAFT_ADDR|${RAFT_ADDR?}|g" /tmp/storageconfig.hcl; + /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl + + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: VAULT_K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: VAULT_ADDR + value: "http://127.0.0.1:8200" + - name: VAULT_API_ADDR + value: "http://$(POD_IP):8200" + - name: SKIP_CHOWN + value: "true" + - name: SKIP_SETCAP + value: "true" + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_CLUSTER_ADDR + value: "https://$(HOSTNAME).hashicorp-vault-internal:8201" + - name: HOME + value: "/home/vault" + + + + volumeMounts: + + + + - name: data + mountPath: /vault/data + + + + - name: config + mountPath: /vault/config + + - name: home + mountPath: /home/vault + ports: + - containerPort: 8200 + name: http + - containerPort: 8201 + name: https-internal + - containerPort: 8202 + name: http-rep + readinessProbe: + # Check status; unsealed vault servers return 0 + # The exit code reflects the seal status: + # 0 - unsealed + # 1 - error + # 2 - sealed + exec: + command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + lifecycle: + # Vault container doesn't receive SIGTERM from Kubernetes + # and after the grace period ends, Kube sends SIGKILL. This + # causes issues with graceful shutdowns such as deregistering itself + # from Consul (zombie services). + preStop: + exec: + command: [ + "/bin/sh", "-c", + # Adding a sleep here to give the pod eviction a + # chance to propagate, so requests will not be made + # to this pod while it's terminating + "sleep 5 && kill -SIGTERM $(pidof vault)", + ] + + + volumeClaimTemplates: + - metadata: + name: data + + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +# Source: hashicorp-vault/templates/vault-app.yaml +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: vault-link + namespace: vault +spec: + applicationMenu: + section: HashiCorp Vault + imageURL: data:image/png;base64,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 + href: 'https://vault-vault.apps.region.example.com' + location: ApplicationMenu + text: 'Vault' +--- +# Source: hashicorp-vault/charts/vault/templates/server-route.yaml +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: hashicorp-vault + namespace: default + labels: + helm.sh/chart: vault-0.22.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: hashicorp-vault + app.kubernetes.io/managed-by: Helm +spec: + host: + to: + kind: Service + name: hashicorp-vault + weight: 100 + port: + targetPort: 8200 + tls: + termination: edge +--- +# Source: hashicorp-vault/charts/vault/templates/tests/server-test.yaml +apiVersion: v1 +kind: Pod +metadata: + name: "hashicorp-vault-server-test" + namespace: default + annotations: + "helm.sh/hook": test +spec: + + containers: + - name: hashicorp-vault-server-test + image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + imagePullPolicy: IfNotPresent + env: + - name: VAULT_ADDR + value: http://hashicorp-vault.default.svc:8200 + + command: + - /bin/sh + - -c + - | + echo "Checking for sealed info in 'vault status' output" + ATTEMPTS=10 + n=0 + until [ "$n" -ge $ATTEMPTS ] + do + echo "Attempt" $n... + vault status -format yaml | grep -E '^sealed: (true|false)' && break + n=$((n+1)) + sleep 5 + done + if [ $n -ge $ATTEMPTS ]; then + echo "timed out looking for sealed info in 'vault status' output" + exit 1 + fi + + exit 0 + volumeMounts: + volumes: + restartPolicy: Never diff --git a/tests/install-industrial-edge-factory.expected.yaml b/tests/install-industrial-edge-factory.expected.yaml new file mode 100644 index 00000000..0dfd0d84 --- /dev/null +++ b/tests/install-industrial-edge-factory.expected.yaml @@ -0,0 +1,66 @@ +--- +# Source: pattern-install/templates/argocd/namespace.yaml +# Pre-create so we can create our argo app for keeping subscriptions in sync +# Do it here so that we don't try to sync it in the future +apiVersion: v1 +kind: Namespace +metadata: + name: openshift-gitops +--- +# Source: pattern-install/templates/argocd/application.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: install-example + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: install-example + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-example.yaml" + # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: install + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.clusterVersion + value: "" + syncPolicy: + automated: {} +--- +# Source: pattern-install/templates/argocd/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: "" +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: install-example,openshift-gitops diff --git a/tests/install-industrial-edge-hub.expected.yaml b/tests/install-industrial-edge-hub.expected.yaml new file mode 100644 index 00000000..0dfd0d84 --- /dev/null +++ b/tests/install-industrial-edge-hub.expected.yaml @@ -0,0 +1,66 @@ +--- +# Source: pattern-install/templates/argocd/namespace.yaml +# Pre-create so we can create our argo app for keeping subscriptions in sync +# Do it here so that we don't try to sync it in the future +apiVersion: v1 +kind: Namespace +metadata: + name: openshift-gitops +--- +# Source: pattern-install/templates/argocd/application.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: install-example + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: install-example + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-example.yaml" + # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: install + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.clusterVersion + value: "" + syncPolicy: + automated: {} +--- +# Source: pattern-install/templates/argocd/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: "" +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: install-example,openshift-gitops diff --git a/tests/install-medical-diagnosis-hub.expected.yaml b/tests/install-medical-diagnosis-hub.expected.yaml new file mode 100644 index 00000000..0dfd0d84 --- /dev/null +++ b/tests/install-medical-diagnosis-hub.expected.yaml @@ -0,0 +1,66 @@ +--- +# Source: pattern-install/templates/argocd/namespace.yaml +# Pre-create so we can create our argo app for keeping subscriptions in sync +# Do it here so that we don't try to sync it in the future +apiVersion: v1 +kind: Namespace +metadata: + name: openshift-gitops +--- +# Source: pattern-install/templates/argocd/application.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: install-example + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: in-cluster + namespace: install-example + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-example.yaml" + # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: install + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.clusterVersion + value: "" + syncPolicy: + automated: {} +--- +# Source: pattern-install/templates/argocd/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-gitops-operator + namespace: openshift-operators + labels: + operators.coreos.com/openshift-gitops-operator.openshift-operators: "" +spec: + channel: stable + installPlanApproval: Automatic + name: openshift-gitops-operator + source: redhat-operators + sourceNamespace: openshift-marketplace + config: + env: + - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES + value: install-example,openshift-gitops diff --git a/tests/operator-install-industrial-edge-factory.expected.yaml b/tests/operator-install-industrial-edge-factory.expected.yaml new file mode 100644 index 00000000..dc07be36 --- /dev/null +++ b/tests/operator-install-industrial-edge-factory.expected.yaml @@ -0,0 +1,29 @@ +--- +# Source: pattern-install/templates/pattern.yaml +apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 +kind: Pattern +metadata: + name: operator-install + namespace: openshift-operators +spec: + clusterGroupName: example + gitSpec: + targetRepo: https://github.com/pattern-clone/mypattern + targetRevision: main + gitOpsSpec: + operatorChannel: stable +--- +# Source: pattern-install/templates/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: patterns-operator + namespace: openshift-operators + labels: + operators.coreos.com/patterns-operator.openshift-operators: "" +spec: + channel: fast + installPlanApproval: Automatic + name: patterns-operator + source: community-operators + sourceNamespace: openshift-marketplace diff --git a/tests/operator-install-industrial-edge-hub.expected.yaml b/tests/operator-install-industrial-edge-hub.expected.yaml new file mode 100644 index 00000000..dc07be36 --- /dev/null +++ b/tests/operator-install-industrial-edge-hub.expected.yaml @@ -0,0 +1,29 @@ +--- +# Source: pattern-install/templates/pattern.yaml +apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 +kind: Pattern +metadata: + name: operator-install + namespace: openshift-operators +spec: + clusterGroupName: example + gitSpec: + targetRepo: https://github.com/pattern-clone/mypattern + targetRevision: main + gitOpsSpec: + operatorChannel: stable +--- +# Source: pattern-install/templates/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: patterns-operator + namespace: openshift-operators + labels: + operators.coreos.com/patterns-operator.openshift-operators: "" +spec: + channel: fast + installPlanApproval: Automatic + name: patterns-operator + source: community-operators + sourceNamespace: openshift-marketplace diff --git a/tests/operator-install-medical-diagnosis-hub.expected.yaml b/tests/operator-install-medical-diagnosis-hub.expected.yaml new file mode 100644 index 00000000..dc07be36 --- /dev/null +++ b/tests/operator-install-medical-diagnosis-hub.expected.yaml @@ -0,0 +1,29 @@ +--- +# Source: pattern-install/templates/pattern.yaml +apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 +kind: Pattern +metadata: + name: operator-install + namespace: openshift-operators +spec: + clusterGroupName: example + gitSpec: + targetRepo: https://github.com/pattern-clone/mypattern + targetRevision: main + gitOpsSpec: + operatorChannel: stable +--- +# Source: pattern-install/templates/subscription.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: patterns-operator + namespace: openshift-operators + labels: + operators.coreos.com/patterns-operator.openshift-operators: "" +spec: + channel: fast + installPlanApproval: Automatic + name: patterns-operator + source: community-operators + sourceNamespace: openshift-marketplace From 7f9ea40f7233bac9ea1b25ef7424aa7a15b1c124 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 8 Nov 2022 11:46:53 +0100 Subject: [PATCH 0623/1263] Initial v2 plumbing --- ansible/plugins/modules/load_secrets_v1.py | 145 ++++++++++++++++++ ansible/plugins/modules/vault_load_secrets.py | 132 +++------------- .../roles/vault_utils/tasks/push_secrets.yaml | 2 +- .../tests/unit/test_vault_load_secrets_v2.py | 104 +++++++++++++ .../tests/unit/v2/values-secret-v2-base.yaml | 45 ++++++ 5 files changed, 316 insertions(+), 112 deletions(-) create mode 100644 ansible/plugins/modules/load_secrets_v1.py create mode 100644 ansible/tests/unit/test_vault_load_secrets_v2.py create mode 100644 ansible/tests/unit/v2/values-secret-v2-base.yaml diff --git a/ansible/plugins/modules/load_secrets_v1.py b/ansible/plugins/modules/load_secrets_v1.py new file mode 100644 index 00000000..7e6a2a06 --- /dev/null +++ b/ansible/plugins/modules/load_secrets_v1.py @@ -0,0 +1,145 @@ +# Copyright 2022 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Module that implements V1 of the values-secret.yaml spec +""" + +import base64 +import os + +def sanitize_values(module, syaml): + """ + Sanitizes the secrets YAML object. If a specific secret key has + s3.accessKey and s3.secretKey but not s3Secret, the latter will be + generated as the base64 encoding of both s3.accessKey and s3.secretKey. + + secrets: + test: + s3.accessKey: "1234" + s3.secretKey: "4321" + + will push three secrets at 'secret/hub/test': + + s3.accessKey: 1234 + s3.secretKey: 4321 + s3Secret: czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ== + + Parameters: + module(AnsibleModule): The current AnsibleModule being used + + syaml(obj): The parsed yaml object representing the secrets + + Returns: + syaml(obj): The parsed yaml object sanitized + """ + if not ("secrets" in syaml or "files" in syaml): + module.fail_json( + f"Values secrets file does not contain 'secrets' or" + f"'files' keys: {syaml}" + ) + + secrets = syaml.get("secrets", {}) + # We need to explicitely check for None because the file might contain the + # top-level 'secrets:' or 'files:' key but have nothing else under it which will + # return None and not {} + if secrets is None: + secrets = {} + files = syaml.get("files", {}) + if files is None: + files = {} + if len(secrets) == 0 and len(files) == 0: + module.fail_json( + f"Neither 'secrets' nor 'files have any secrets to be parsed: {syaml}" + ) + + if isinstance(secrets, list) or isinstance(files, list): + module.fail_json(f"Neither 'secrets' nor 'files can be lists: {syaml}") + + for secret in secrets: + if not isinstance(secrets[secret], dict): + module.fail_json( + f"Each key under 'secrets' needs to point to " + f"a dictionary of key value pairs: {syaml}" + ) + + for file in files: + path = files[file] + if not os.path.isfile(os.path.expanduser(path)): + module.fail_json(f"File {path} does not exist") + + # If s3Secret key does not exist but s3.accessKey and s3.secretKey do exist + # generate s3Secret so a user does not need to do it manually which tends to be error-prone + for secret in secrets: + tmp = secrets[secret] + if "s3.accessKey" in tmp and "s3.secretKey" in tmp and "s3Secret" not in tmp: + s3secret = ( + f"s3.accessKey: {tmp['s3.accessKey']}\n" + f"s3.secretKey: {tmp['s3.secretKey']}" + ) + s3secretb64 = base64.b64encode(s3secret.encode()) + secrets[secret]["s3Secret"] = s3secretb64.decode("utf-8") + + return syaml + + +def get_secrets_vault_paths(module, syaml, keyname): + """ + Walks a secrets yaml object to look for all top-level keys that start with + 'keyname' and returns a list of tuples [(keyname1, path1), (keyname2, path2)...] + where the path is the relative vault path + For example, given a yaml with the following: + secrets: + foo: bar + secrets.region1: + foo: baz + secrets.region2: + foo: barbaz + + a call with keyname set to 'secrets' will return the following: + [('secrets', 'hub'), ('secrets', 'region1'), ('secrets', 'region2')] + + Parameters: + module(AnsibleModule): The current AnsibleModule being used + + syaml(obj): The parsed yaml object representing the secrets + + keyname(str): The keytypes to look for either usually 'secrets' or 'files' + + Returns: + keys_paths(list): List of tuples containing (keyname, relative-vault-path) + """ + all_keys = syaml.keys() + keys_paths = [] + for key in all_keys: + # We skip any key that does not start with 'secrets' or 'files' + # (We should probably bail out in the presence of unexpected top-level keys) + if not key.startswith(keyname): + continue + + # If there is no '.' after secrets or files, assume the secrets need to + # go to the hub vault path + if key == keyname: + keys_paths.append((key, "hub")) + continue + + # We are in the presence of either 'secrets.region-one' or 'files.cluster1' top-level keys + tmp = key.split(".", 1) + if len(tmp) != 2: + module.fail_json(f"values-secrets.yaml key is non-conformant: {key}") + + keys_paths.append((key, tmp[1])) + + return keys_paths diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 82c64768..b13e50e8 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -50,7 +50,6 @@ testbar: ~/ca.crt """ -import base64 import os import subprocess import time @@ -59,6 +58,9 @@ import yaml from ansible.module_utils.basic import AnsibleModule +from .load_secrets_v1 import sanitize_values, get_secrets_vault_paths + + ANSIBLE_METADATA = { "metadata_version": "1.1", "status": ["preview"], @@ -216,22 +218,10 @@ def flatten(dictionary, parent_key=False, separator="."): return dict(items) -def sanitize_values(module, syaml): +def sanitize_values_v2(module, syaml): """ - Sanitizes the secrets YAML object. If a specific secret key has - s3.accessKey and s3.secretKey but not s3Secret, the latter will be - generated as the base64 encoding of both s3.accessKey and s3.secretKey. - - secrets: - test: - s3.accessKey: "1234" - s3.secretKey: "4321" - - will push three secrets at 'secret/hub/test': - - s3.accessKey: 1234 - s3.secretKey: 4321 - s3Secret: czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ== + Sanitizes the secrets YAML object version 2.0 + ..TODO.. Parameters: module(AnsibleModule): The current AnsibleModule being used @@ -241,105 +231,14 @@ def sanitize_values(module, syaml): Returns: syaml(obj): The parsed yaml object sanitized """ - if not ("secrets" in syaml or "files" in syaml): - module.fail_json( - f"Values secrets file does not contain 'secrets' or" - f"'files' keys: {syaml}" - ) - - secrets = syaml.get("secrets", {}) - # We need to explicitely check for None because the file might contain the - # top-level 'secrets:' or 'files:' key but have nothing else under it which will - # return None and not {} - if secrets is None: - secrets = {} - files = syaml.get("files", {}) - if files is None: - files = {} - if len(secrets) == 0 and len(files) == 0: - module.fail_json( - f"Neither 'secrets' nor 'files have any secrets to be parsed: {syaml}" - ) - - if isinstance(secrets, list) or isinstance(files, list): - module.fail_json(f"Neither 'secrets' nor 'files can be lists: {syaml}") - - for secret in secrets: - if not isinstance(secrets[secret], dict): - module.fail_json( - f"Each key under 'secrets' needs to point to " - f"a dictionary of key value pairs: {syaml}" - ) + version = get_version(syaml) + if version != "2.0": + module.fail_json(f"Version expected is 2.0 but got: {version}") - for file in files: - path = files[file] - if not os.path.isfile(os.path.expanduser(path)): - module.fail_json(f"File {path} does not exist") - - # If s3Secret key does not exist but s3.accessKey and s3.secretKey do exist - # generate s3Secret so a user does not need to do it manually which tends to be error-prone - for secret in secrets: - tmp = secrets[secret] - if "s3.accessKey" in tmp and "s3.secretKey" in tmp and "s3Secret" not in tmp: - s3secret = ( - f"s3.accessKey: {tmp['s3.accessKey']}\n" - f"s3.secretKey: {tmp['s3.secretKey']}" - ) - s3secretb64 = base64.b64encode(s3secret.encode()) - secrets[secret]["s3Secret"] = s3secretb64.decode("utf-8") return syaml -def get_secrets_vault_paths(module, syaml, keyname): - """ - Walks a secrets yaml object to look for all top-level keys that start with - 'keyname' and returns a list of tuples [(keyname1, path1), (keyname2, path2)...] - where the path is the relative vault path - For example, given a yaml with the following: - secrets: - foo: bar - secrets.region1: - foo: baz - secrets.region2: - foo: barbaz - - a call with keyname set to 'secrets' will return the following: - [('secrets', 'hub'), ('secrets', 'region1'), ('secrets', 'region2')] - - Parameters: - module(AnsibleModule): The current AnsibleModule being used - - syaml(obj): The parsed yaml object representing the secrets - - keyname(str): The keytypes to look for either usually 'secrets' or 'files' - - Returns: - keys_paths(list): List of tuples containing (keyname, relative-vault-path) - """ - all_keys = syaml.keys() - keys_paths = [] - for key in all_keys: - # We skip any key that does not start with 'secrets' or 'files' - # (We should probably bail out in the presence of unexpected top-level keys) - if not key.startswith(keyname): - continue - - # If there is no '.' after secrets or files, assume the secrets need to - # go to the hub vault path - if key == keyname: - keys_paths.append((key, "hub")) - continue - - # We are in the presence of either 'secrets.region-one' or 'files.cluster1' top-level keys - tmp = key.split(".", 1) - if len(tmp) != 2: - module.fail_json(f"values-secrets.yaml key is non-conformant: {key}") - - keys_paths.append((key, tmp[1])) - - return keys_paths - # NOTE(bandini): we shell out to oc exec it because of # https://github.com/ansible-collections/kubernetes.core/issues/506 and @@ -441,9 +340,20 @@ def run(module): syaml = parse_values(values_secrets) version = get_version(syaml) - if version != "1.0": + if version not in ["1.0", "2.0"]: module.fail_json(f"Version {version} is currently not supported") + if version == "2.0": + # In the future we can use the version field to manage different formats if needed + secrets = sanitize_values_v2(module, syaml) + + #nr_secrets = inject_secrets(module, secrets, namespace, pod, basepath) + results["failed"] = False + results["changed"] = True + results["msg"] = f"{nr_secrets} secrets injected" + module.exit_json(**results) + + # Default version 1.0 # In the future we can use the version field to manage different formats if needed secrets = sanitize_values(module, syaml) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 6c325da8..7335bcb4 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -39,7 +39,7 @@ secret_template: "{{ pattern_dir }}/values-secret.yaml.template" - name: Loads secrets file into the vault of a cluster - no_log: true + no_log: false vault_load_secrets: values_secrets: ~/values-secret.yaml check_missing_secrets: false diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py new file mode 100644 index 00000000..3d5d890d --- /dev/null +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -0,0 +1,104 @@ +# Copyright 2022 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Simple module to test vault_load_secrets +""" + +import json +import os +import sys +import unittest +from unittest.mock import call, patch + +from ansible.module_utils import basic +from ansible.module_utils.common.text.converters import to_bytes + +sys.path.insert(1, "./ansible/plugins/modules") +import vault_load_secrets # noqa: E402 + + +def set_module_args(args): + """prepare arguments so that they will be picked up during module creation""" + args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + """Exception class to be raised by module.exit_json and caught by the test case""" + + pass + + +class AnsibleFailJson(Exception): + """Exception class to be raised by module.fail_json and caught by the test case""" + + pass + + +def exit_json(*args, **kwargs): + """function to patch over exit_json; package return data into an exception""" + if "changed" not in kwargs: + kwargs["changed"] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + """function to patch over fail_json; package return data into an exception""" + kwargs["failed"] = True + kwargs["args"] = args + raise AnsibleFailJson(kwargs) + + +class TestMyModule(unittest.TestCase): + def setUp(self): + self.mock_module_helper = patch.multiple( + basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json + ) + self.mock_module_helper.start() + self.addCleanup(self.mock_module_helper.stop) + self.testdir_v2 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v2") + self.testfile = open("/tmp/ca.crt", "w") + + def tearDown(self): + self.testfile.close() + try: + os.remove("/tmp/ca.crt") + except OSError: + pass + + def test_module_fail_when_required_args_missing(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + vault_load_secrets.main() + + def test_module_fail_when_values_secret_not_existing(self): + with self.assertRaises(AnsibleExitJson) as ansible_err: + set_module_args( + { + "values_secrets": "/tmp/nonexisting", + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + self.assertEqual(ret["error"], "Missing values-secrets.yaml file") + self.assertEqual( + ret["msg"], "Values secrets file does not exist: /tmp/nonexisting" + ) + +if __name__ == "__main__": + unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-base.yaml b/ansible/tests/unit/v2/values-secret-v2-base.yaml new file mode 100644 index 00000000..246b5086 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-base.yaml @@ -0,0 +1,45 @@ +# Needed to specify the new format (missing version means old version: 1.0 by default) +version: 2.0 +# NEVER COMMIT THESE VALUES TO GIT + +# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark +global: + backend: vault + +vaultPolicies: + # https://developer.hashicorp.com/vault/docs/concepts/password-policies + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + + advancedPolicy: | + length=20 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + +secrets: + - name: config-demo + vaultPrefixes: + - secret/region-one + - secret/snowflake.blueprints.rhecoeng.com + fields: + - name: secret + value: null + onMissingValue: error # One of: error,generate,prompt + vaultPolicy: basicPolicy + - name: secret2 + value: null + onMissingValue: prompt + - name: config-demo-file + vaultPrefixes: + - secret/region-one + - secret/snowflake.blueprints.rhecoeng.com + + file: + # promptable would prompt for the path of the file in this case + path: /tmp/ca.crt + onMissingValue: error # One of error, prompt (for path). generate makes no sense for file From b294222c7afcad0976f71d1bf88fcee4d010c67d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 09:56:54 +0100 Subject: [PATCH 0624/1263] Add module_utils path for shared common code --- ansible/ansible.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index 710a2469..3b643234 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -3,4 +3,5 @@ display_skipped_hosts=False localhost_warning=False library=./plugins/modules:~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules roles_path=./roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles +module_utils=~/.ansible/plugins/module_utils:./plugins/module_utils:/usr/share/ansible/plugins/module_utils callbacks_enabled = profile_roles, profile_tasks From f9a1a6927254a9286b0412af381f8963a7eedc12 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 09:57:06 +0100 Subject: [PATCH 0625/1263] Move v1 secret functions in module_utils --- .../load_secrets_v1.py | 0 .../plugins/module_utils/load_secrets_v2.py | 44 +++++++++++++++++++ ansible/plugins/modules/vault_load_secrets.py | 40 +++-------------- 3 files changed, 49 insertions(+), 35 deletions(-) rename ansible/plugins/{modules => module_utils}/load_secrets_v1.py (100%) create mode 100644 ansible/plugins/module_utils/load_secrets_v2.py diff --git a/ansible/plugins/modules/load_secrets_v1.py b/ansible/plugins/module_utils/load_secrets_v1.py similarity index 100% rename from ansible/plugins/modules/load_secrets_v1.py rename to ansible/plugins/module_utils/load_secrets_v1.py diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py new file mode 100644 index 00000000..3318b5fe --- /dev/null +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -0,0 +1,44 @@ +# Copyright 2022 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Module that implements V2 of the values-secret.yaml spec +""" + + +import base64 +import os + +def sanitize_values_v2(module, syaml): + """ + Sanitizes the secrets YAML object version 2.0 + ..TODO.. + + Parameters: + module(AnsibleModule): The current AnsibleModule being used + + syaml(obj): The parsed yaml object representing the secrets + + Returns: + syaml(obj): The parsed yaml object sanitized + """ + version = get_version(syaml) + if version != "2.0": + module.fail_json(f"Version expected is 2.0 but got: {version}") + + return syaml + +def get_secrets_vault_paths(module, syaml, keyname): + return diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index b13e50e8..3a466a60 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -58,8 +58,6 @@ import yaml from ansible.module_utils.basic import AnsibleModule -from .load_secrets_v1 import sanitize_values, get_secrets_vault_paths - ANSIBLE_METADATA = { "metadata_version": "1.1", @@ -218,27 +216,6 @@ def flatten(dictionary, parent_key=False, separator="."): return dict(items) -def sanitize_values_v2(module, syaml): - """ - Sanitizes the secrets YAML object version 2.0 - ..TODO.. - - Parameters: - module(AnsibleModule): The current AnsibleModule being used - - syaml(obj): The parsed yaml object representing the secrets - - Returns: - syaml(obj): The parsed yaml object sanitized - """ - version = get_version(syaml) - if version != "2.0": - module.fail_json(f"Version expected is 2.0 but got: {version}") - - - return syaml - - # NOTE(bandini): we shell out to oc exec it because of # https://github.com/ansible-collections/kubernetes.core/issues/506 and @@ -340,20 +317,13 @@ def run(module): syaml = parse_values(values_secrets) version = get_version(syaml) - if version not in ["1.0", "2.0"]: - module.fail_json(f"Version {version} is currently not supported") - if version == "2.0": - # In the future we can use the version field to manage different formats if needed - secrets = sanitize_values_v2(module, syaml) - - #nr_secrets = inject_secrets(module, secrets, namespace, pod, basepath) - results["failed"] = False - results["changed"] = True - results["msg"] = f"{nr_secrets} secrets injected" - module.exit_json(**results) + from ansible.module_utils.load_secrets_v2 import sanitize_values, get_secrets_vault_paths + elif version == "1.0": + from ansible.module_utils.load_secrets_v1 import sanitize_values, get_secrets_vault_paths + else: + module.fail_json(f"Version {version} is currently not supported") - # Default version 1.0 # In the future we can use the version field to manage different formats if needed secrets = sanitize_values(module, syaml) From e97f073eaa28c97e9ba1fb2c94c41a09e1dd8c42 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 10:51:50 +0100 Subject: [PATCH 0626/1263] Split out common function in separate file --- .../module_utils/load_secrets_common.py | 119 ++++++++++++++++++ ansible/plugins/modules/vault_load_secrets.py | 105 +--------------- 2 files changed, 124 insertions(+), 100 deletions(-) create mode 100644 ansible/plugins/module_utils/load_secrets_common.py diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py new file mode 100644 index 00000000..57eb0f8c --- /dev/null +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -0,0 +1,119 @@ +# Copyright 2022 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Module that implements some common functions +""" + +import os +import subprocess +import time +import yaml +from collections.abc import MutableMapping + +def parse_values(values_file): + """ + Parses a values-secrets.yaml file (usually placed in ~) + and returns a Python Obect with the parsed yaml. + + Parameters: + values_file(str): The path of the values-secrets.yaml file + to be parsed. + + Returns: + secrets_yaml(obj): The python object containing the parsed yaml + """ + with open(values_file, "r", encoding="utf-8") as file: + secrets_yaml = yaml.safe_load(file.read()) + if secrets_yaml is None: + return {} + return secrets_yaml + + +def get_version(syaml): + """ + Return the version: of the parsed yaml object. If it does not exist + return 1.0 + + Returns: + ret(str): The version value in of the top-level 'version:' key + """ + return syaml.get("version", "1.0") + + +def run_command(command, attempts=1, sleep=3): + """ + Runs a command on the host ansible is running on. A failing command + will raise an exception in this function directly (due to check=True) + + Parameters: + command(str): The command to be run. + attempts(int): Number of times to retry in case of Error (defaults to 1) + sleep(int): Number of seconds to wait in between retry attempts (defaults to 3s) + + Returns: + ret(subprocess.CompletedProcess): The return value from run() + """ + for attempt in range(attempts): + try: + ret = subprocess.run( + command, + shell=True, + env=os.environ.copy(), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + check=True, + ) + return ret + except subprocess.CalledProcessError as e: + # We reached maximum nr of retries. Re-raise the last error + if attempt >= attempts - 1: + raise e + time.sleep(sleep) + + +def flatten(dictionary, parent_key=False, separator="."): + """ + Turn a nested dictionary into a flattened dictionary and also + drop any key that has 'None' as their value + + Parameters: + dictionary(dict): The dictionary to flatten + + parent_key(str): The string to prepend to dictionary's keys + + separator(str): The string used to separate flattened keys + + Returns: + + dictionary: A flattened dictionary where the keys represent the + path to reach the leaves + """ + + items = [] + for key, value in dictionary.items(): + new_key = str(parent_key) + separator + key if parent_key else key + if isinstance(value, MutableMapping): + items.extend(flatten(value, new_key, separator).items()) + elif isinstance(value, list): + for k, v in enumerate(value): + items.extend(flatten({str(k): v}, new_key).items()) + else: + if value is not None: + items.append((new_key, value)) + return dict(items) + + diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 3a466a60..02866021 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -51,12 +51,10 @@ """ import os -import subprocess -import time -from collections.abc import MutableMapping import yaml from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.load_secrets_common import parse_values, get_version, run_command, flatten ANSIBLE_METADATA = { @@ -122,106 +120,13 @@ """ -def parse_values(values_file): - """ - Parses a values-secrets.yaml file (usually placed in ~) - and returns a Python Obect with the parsed yaml. - - Parameters: - values_file(str): The path of the values-secrets.yaml file - to be parsed. - - Returns: - secrets_yaml(obj): The python object containing the parsed yaml - """ - with open(values_file, "r", encoding="utf-8") as file: - secrets_yaml = yaml.safe_load(file.read()) - if secrets_yaml is None: - return {} - return secrets_yaml - - -def get_version(syaml): - """ - Return the version: of the parsed yaml object. If it does not exist - return 1.0 - - Returns: - ret(str): The version value in of the top-level 'version:' key - """ - return syaml.get("version", "1.0") - - -def run_command(command, attempts=1, sleep=3): - """ - Runs a command on the host ansible is running on. A failing command - will raise an exception in this function directly (due to check=True) - - Parameters: - command(str): The command to be run. - attempts(int): Number of times to retry in case of Error (defaults to 1) - sleep(int): Number of seconds to wait in between retry attempts (defaults to 3s) - - Returns: - ret(subprocess.CompletedProcess): The return value from run() - """ - for attempt in range(attempts): - try: - ret = subprocess.run( - command, - shell=True, - env=os.environ.copy(), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - check=True, - ) - return ret - except subprocess.CalledProcessError as e: - # We reached maximum nr of retries. Re-raise the last error - if attempt >= attempts - 1: - raise e - time.sleep(sleep) - - -def flatten(dictionary, parent_key=False, separator="."): - """ - Turn a nested dictionary into a flattened dictionary and also - drop any key that has 'None' as their value - - Parameters: - dictionary(dict): The dictionary to flatten - - parent_key(str): The string to prepend to dictionary's keys - - separator(str): The string used to separate flattened keys - - Returns: - - dictionary: A flattened dictionary where the keys represent the - path to reach the leaves - """ - - items = [] - for key, value in dictionary.items(): - new_key = str(parent_key) + separator + key if parent_key else key - if isinstance(value, MutableMapping): - items.extend(flatten(value, new_key, separator).items()) - elif isinstance(value, list): - for k, v in enumerate(value): - items.extend(flatten({str(k): v}, new_key).items()) - else: - if value is not None: - items.append((new_key, value)) - return dict(items) - # NOTE(bandini): we shell out to oc exec it because of # https://github.com/ansible-collections/kubernetes.core/issues/506 and # https://github.com/kubernetes/kubernetes/issues/89899. Until those are solved # it makes little sense to invoke the APIs via the python wrappers -def inject_secrets(module, syaml, namespace, pod, basepath): +def inject_secrets(module, syaml, namespace, pod, basepath, get_secrets_vault_paths_func): """ Walks a secrets yaml object and injects all the secrets into the vault via 'oc exec' calls @@ -241,7 +146,7 @@ def inject_secrets(module, syaml, namespace, pod, basepath): counter(int): The number of secrets injected """ counter = 0 - for i in get_secrets_vault_paths(module, syaml, "secrets"): + for i in get_secrets_vault_paths_func(module, syaml, "secrets"): path = f"{basepath}/{i[1]}" for secret in syaml[i[0]] or []: properties = "" @@ -255,7 +160,7 @@ def inject_secrets(module, syaml, namespace, pod, basepath): run_command(cmd, attempts=3) counter += 1 - for i in get_secrets_vault_paths(module, syaml, "files"): + for i in get_secrets_vault_paths_func(module, syaml, "files"): path = f"{basepath}/{i[1]}" for filekey in syaml[i[0]] or []: file = os.path.expanduser(syaml[i[0]][filekey]) @@ -332,7 +237,7 @@ def run(module): if check_missing_secrets: check_for_missing_secrets(module, syaml, values_secret_template) - nr_secrets = inject_secrets(module, secrets, namespace, pod, basepath) + nr_secrets = inject_secrets(module, secrets, namespace, pod, basepath, get_secrets_vault_paths) results["failed"] = False results["changed"] = True results["msg"] = f"{nr_secrets} secrets injected" From 7a2a45fc260681a2e7882a4dc98f452160596d5e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 11:22:45 +0100 Subject: [PATCH 0627/1263] Move the secret format implementation in classes Simple functions were fine when we supported a single simple format. This is not the case any longer, so hide the complexity inside proper classes now. --- .../plugins/module_utils/load_secrets_v1.py | 307 +++++++++++------- .../plugins/module_utils/load_secrets_v2.py | 6 +- ansible/plugins/modules/vault_load_secrets.py | 93 +----- 3 files changed, 201 insertions(+), 205 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v1.py b/ansible/plugins/module_utils/load_secrets_v1.py index 7e6a2a06..8849a72c 100644 --- a/ansible/plugins/module_utils/load_secrets_v1.py +++ b/ansible/plugins/module_utils/load_secrets_v1.py @@ -19,127 +19,192 @@ import base64 import os - -def sanitize_values(module, syaml): - """ - Sanitizes the secrets YAML object. If a specific secret key has - s3.accessKey and s3.secretKey but not s3Secret, the latter will be - generated as the base64 encoding of both s3.accessKey and s3.secretKey. - - secrets: - test: - s3.accessKey: "1234" - s3.secretKey: "4321" - - will push three secrets at 'secret/hub/test': - - s3.accessKey: 1234 - s3.secretKey: 4321 - s3Secret: czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ== - - Parameters: - module(AnsibleModule): The current AnsibleModule being used - - syaml(obj): The parsed yaml object representing the secrets - - Returns: - syaml(obj): The parsed yaml object sanitized - """ - if not ("secrets" in syaml or "files" in syaml): - module.fail_json( - f"Values secrets file does not contain 'secrets' or" - f"'files' keys: {syaml}" - ) - - secrets = syaml.get("secrets", {}) - # We need to explicitely check for None because the file might contain the - # top-level 'secrets:' or 'files:' key but have nothing else under it which will - # return None and not {} - if secrets is None: - secrets = {} - files = syaml.get("files", {}) - if files is None: - files = {} - if len(secrets) == 0 and len(files) == 0: - module.fail_json( - f"Neither 'secrets' nor 'files have any secrets to be parsed: {syaml}" - ) - - if isinstance(secrets, list) or isinstance(files, list): - module.fail_json(f"Neither 'secrets' nor 'files can be lists: {syaml}") - - for secret in secrets: - if not isinstance(secrets[secret], dict): - module.fail_json( - f"Each key under 'secrets' needs to point to " - f"a dictionary of key value pairs: {syaml}" +from ansible.module_utils.load_secrets_common import parse_values, get_version, run_command, flatten + +class LoadSecretsV1: + def __init__(self, module, values_secrets, basepath, namespace, pod, values_secret_template): + self.module = module + self.basepath = basepath + self.namespace = namespace + self.pod = pod + self.values_secret_template = values_secret_template + self.syaml = parse_values(values_secrets) + + def sanitize_values(self): + """ + Sanitizes the secrets YAML object. If a specific secret key has + s3.accessKey and s3.secretKey but not s3Secret, the latter will be + generated as the base64 encoding of both s3.accessKey and s3.secretKey. + + secrets: + test: + s3.accessKey: "1234" + s3.secretKey: "4321" + + will push three secrets at 'secret/hub/test': + + s3.accessKey: 1234 + s3.secretKey: 4321 + s3Secret: czMuYWNjZXNzS2V5OiAxMjM0CnMzLnNlY3JldEtleTogNDMyMQ== + + Parameters: + + Returns: + Nothing: Updates self.syaml(obj) + """ + if not ("secrets" in self.syaml or "files" in self.syaml): + self.module.fail_json( + f"Values secrets file does not contain 'secrets' or" + f"'files' keys: {self.syaml}" ) - for file in files: - path = files[file] - if not os.path.isfile(os.path.expanduser(path)): - module.fail_json(f"File {path} does not exist") - - # If s3Secret key does not exist but s3.accessKey and s3.secretKey do exist - # generate s3Secret so a user does not need to do it manually which tends to be error-prone - for secret in secrets: - tmp = secrets[secret] - if "s3.accessKey" in tmp and "s3.secretKey" in tmp and "s3Secret" not in tmp: - s3secret = ( - f"s3.accessKey: {tmp['s3.accessKey']}\n" - f"s3.secretKey: {tmp['s3.secretKey']}" + secrets = self.syaml.get("secrets", {}) + # We need to explicitely check for None because the file might contain the + # top-level 'secrets:' or 'files:' key but have nothing else under it which will + # return None and not {} + if secrets is None: + secrets = {} + files = self.syaml.get("files", {}) + if files is None: + files = {} + if len(secrets) == 0 and len(files) == 0: + self.module.fail_json( + f"Neither 'secrets' nor 'files have any secrets to be parsed" ) - s3secretb64 = base64.b64encode(s3secret.encode()) - secrets[secret]["s3Secret"] = s3secretb64.decode("utf-8") - - return syaml - - -def get_secrets_vault_paths(module, syaml, keyname): - """ - Walks a secrets yaml object to look for all top-level keys that start with - 'keyname' and returns a list of tuples [(keyname1, path1), (keyname2, path2)...] - where the path is the relative vault path - For example, given a yaml with the following: - secrets: - foo: bar - secrets.region1: - foo: baz - secrets.region2: - foo: barbaz - - a call with keyname set to 'secrets' will return the following: - [('secrets', 'hub'), ('secrets', 'region1'), ('secrets', 'region2')] - - Parameters: - module(AnsibleModule): The current AnsibleModule being used - - syaml(obj): The parsed yaml object representing the secrets - - keyname(str): The keytypes to look for either usually 'secrets' or 'files' - - Returns: - keys_paths(list): List of tuples containing (keyname, relative-vault-path) - """ - all_keys = syaml.keys() - keys_paths = [] - for key in all_keys: - # We skip any key that does not start with 'secrets' or 'files' - # (We should probably bail out in the presence of unexpected top-level keys) - if not key.startswith(keyname): - continue - - # If there is no '.' after secrets or files, assume the secrets need to - # go to the hub vault path - if key == keyname: - keys_paths.append((key, "hub")) - continue - - # We are in the presence of either 'secrets.region-one' or 'files.cluster1' top-level keys - tmp = key.split(".", 1) - if len(tmp) != 2: - module.fail_json(f"values-secrets.yaml key is non-conformant: {key}") - - keys_paths.append((key, tmp[1])) - - return keys_paths + + if isinstance(secrets, list) or isinstance(files, list): + self.module.fail_json("Neither 'secrets' nor 'files can be lists") + + for secret in secrets: + if not isinstance(secrets[secret], dict): + self.module.fail_json( + "Each key under 'secrets' needs to point to " + "a dictionary of key value pairs" + ) + + for file in files: + path = files[file] + if not os.path.isfile(os.path.expanduser(path)): + self.module.fail_json(f"File {path} does not exist") + + # If s3Secret key does not exist but s3.accessKey and s3.secretKey do exist + # generate s3Secret so a user does not need to do it manually which tends to be error-prone + for secret in secrets: + tmp = secrets[secret] + if "s3.accessKey" in tmp and "s3.secretKey" in tmp and "s3Secret" not in tmp: + s3secret = ( + f"s3.accessKey: {tmp['s3.accessKey']}\n" + f"s3.secretKey: {tmp['s3.secretKey']}" + ) + s3secretb64 = base64.b64encode(s3secret.encode()) + secrets[secret]["s3Secret"] = s3secretb64.decode("utf-8") + + + def get_secrets_vault_paths(self, keyname): + """ + Walks a secrets yaml object to look for all top-level keys that start with + 'keyname' and returns a list of tuples [(keyname1, path1), (keyname2, path2)...] + where the path is the relative vault path + For example, given a yaml with the following: + secrets: + foo: bar + secrets.region1: + foo: baz + secrets.region2: + foo: barbaz + + a call with keyname set to 'secrets' will return the following: + [('secrets', 'hub'), ('secrets', 'region1'), ('secrets', 'region2')] + + Parameters: + keyname(str): The keytypes to look for either usually 'secrets' or 'files' + + Returns: + keys_paths(list): List of tuples containing (keyname, relative-vault-path) + """ + all_keys = self.syaml.keys() + keys_paths = [] + for key in all_keys: + # We skip any key that does not start with 'secrets' or 'files' + # (We should probably bail out in the presence of unexpected top-level keys) + if not key.startswith(keyname): + continue + + # If there is no '.' after secrets or files, assume the secrets need to + # go to the hub vault path + if key == keyname: + keys_paths.append((key, "hub")) + continue + + # We are in the presence of either 'secrets.region-one' or 'files.cluster1' top-level keys + tmp = key.split(".", 1) + if len(tmp) != 2: + self.module.fail_json(f"values-secrets.yaml key is non-conformant: {key}") + + keys_paths.append((key, tmp[1])) + + return keys_paths + + # NOTE(bandini): we shell out to oc exec it because of + # https://github.com/ansible-collections/kubernetes.core/issues/506 and + # https://github.com/kubernetes/kubernetes/issues/89899. Until those are solved + # it makes little sense to invoke the APIs via the python wrappers + def inject_secrets(self): + """ + Walks a secrets yaml object and injects all the secrets into the vault via 'oc exec' calls + + Parameters: + + Returns: + counter(int): The number of secrets injected + """ + counter = 0 + for i in self.get_secrets_vault_paths("secrets"): + path = f"{self.basepath}/{i[1]}" + for secret in self.syaml[i[0]] or []: + properties = "" + for key, value in self.syaml[i[0]][secret].items(): + properties += f"{key}='{value}' " + properties = properties.rstrip() + cmd = ( + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f"\"vault kv put '{path}/{secret}' {properties}\"" + ) + run_command(cmd, attempts=3) + counter += 1 + + for i in self.get_secrets_vault_paths("files"): + path = f"{self.basepath}/{i[1]}" + for filekey in self.syaml[i[0]] or []: + file = os.path.expanduser(self.syaml[i[0]][filekey]) + cmd = ( + f"cat '{file}' | oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f"'cat - > /tmp/vcontent'; " + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | " + f"vault kv put {path}/{filekey} b64content=- content=@/tmp/vcontent; " + f"rm /tmp/vcontent'" + ) + run_command(cmd, attempts=3) + counter += 1 + return counter + + + def check_for_missing_secrets(self): + with open(self.values_secret_template, "r", encoding="utf-8") as file: + template_yaml = yaml.safe_load(file.read()) + if template_yaml is None: + self.module.fail_json(f"Template {self.values_secret_template} is empty") + + syaml_flat = flatten(self.syaml) + template_flat = flatten(template_yaml) + + syaml_keys = set(syaml_flat.keys()) + template_keys = set(template_flat.keys()) + + if template_keys <= syaml_keys: + return + + diff = template_keys - syaml_keys + self.module.fail_json( + f"Values secret yaml is missing needed secrets from the templates: {diff}" + ) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 3318b5fe..cfaf91c1 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -21,7 +21,11 @@ import base64 import os -def sanitize_values_v2(module, syaml): +class LoadSecretsV2: + def __init__(): + return + +def sanitize_values(module, syaml): """ Sanitizes the secrets YAML object version 2.0 ..TODO.. diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 02866021..5e5506e8 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -54,8 +54,10 @@ import yaml from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.load_secrets_common import parse_values, get_version, run_command, flatten +from ansible.module_utils.load_secrets_common import parse_values, get_version +from ansible.module_utils.load_secrets_v2 import LoadSecretsV2 +from ansible.module_utils.load_secrets_v1 import LoadSecretsV1 ANSIBLE_METADATA = { "metadata_version": "1.1", @@ -121,82 +123,6 @@ - -# NOTE(bandini): we shell out to oc exec it because of -# https://github.com/ansible-collections/kubernetes.core/issues/506 and -# https://github.com/kubernetes/kubernetes/issues/89899. Until those are solved -# it makes little sense to invoke the APIs via the python wrappers -def inject_secrets(module, syaml, namespace, pod, basepath, get_secrets_vault_paths_func): - """ - Walks a secrets yaml object and injects all the secrets into the vault via 'oc exec' calls - - Parameters: - module(AnsibleModule): The current AnsibleModule being used - - syaml(obj): The parsed yaml object representing the secrets - - namespace(str): The namespace in which the vault is - - pod(str): The pod name where the vault is - - basepath(str): The base string to which we concatenate the vault - relative paths - - Returns: - counter(int): The number of secrets injected - """ - counter = 0 - for i in get_secrets_vault_paths_func(module, syaml, "secrets"): - path = f"{basepath}/{i[1]}" - for secret in syaml[i[0]] or []: - properties = "" - for key, value in syaml[i[0]][secret].items(): - properties += f"{key}='{value}' " - properties = properties.rstrip() - cmd = ( - f"oc exec -n {namespace} {pod} -i -- sh -c " - f"\"vault kv put '{path}/{secret}' {properties}\"" - ) - run_command(cmd, attempts=3) - counter += 1 - - for i in get_secrets_vault_paths_func(module, syaml, "files"): - path = f"{basepath}/{i[1]}" - for filekey in syaml[i[0]] or []: - file = os.path.expanduser(syaml[i[0]][filekey]) - cmd = ( - f"cat '{file}' | oc exec -n {namespace} {pod} -i -- sh -c " - f"'cat - > /tmp/vcontent'; " - f"oc exec -n {namespace} {pod} -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | " - f"vault kv put {path}/{filekey} b64content=- content=@/tmp/vcontent; " - f"rm /tmp/vcontent'" - ) - run_command(cmd, attempts=3) - counter += 1 - return counter - - -def check_for_missing_secrets(module, syaml, values_secret_template): - with open(values_secret_template, "r", encoding="utf-8") as file: - template_yaml = yaml.safe_load(file.read()) - if template_yaml is None: - module.fail_json(f"Template {values_secret_template} is empty") - - syaml_flat = flatten(syaml) - template_flat = flatten(template_yaml) - - syaml_keys = set(syaml_flat.keys()) - template_keys = set(template_flat.keys()) - - if template_keys <= syaml_keys: - return - - diff = template_keys - syaml_keys - module.fail_json( - f"Values secret yaml is missing needed secrets from the templates: {diff}" - ) - - def run(module): """Main ansible module entry point""" results = dict(changed=False) @@ -223,21 +149,22 @@ def run(module): version = get_version(syaml) if version == "2.0": - from ansible.module_utils.load_secrets_v2 import sanitize_values, get_secrets_vault_paths + secret_obj = LoadSecretsV2() elif version == "1.0": - from ansible.module_utils.load_secrets_v1 import sanitize_values, get_secrets_vault_paths + secret_obj = LoadSecretsV1( + module, values_secrets, basepath, namespace, pod, values_secret_template + ) else: module.fail_json(f"Version {version} is currently not supported") - # In the future we can use the version field to manage different formats if needed - secrets = sanitize_values(module, syaml) + secret_obj.sanitize_values() # If the user specified check_for_missing_secrets then we read values_secret_template # and check if there are any missing secrets if check_missing_secrets: - check_for_missing_secrets(module, syaml, values_secret_template) + secret_obj.check_for_missing_secrets() - nr_secrets = inject_secrets(module, secrets, namespace, pod, basepath, get_secrets_vault_paths) + nr_secrets = secret_obj.inject_secrets() results["failed"] = False results["changed"] = True results["msg"] = f"{nr_secrets} secrets injected" From c99b2a20a9b8c1d3c2ee4fe958b03842c3efa19b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 12:08:34 +0100 Subject: [PATCH 0628/1263] A bunch of cleanups of unit-testing. Still not fully passing --- .../module_utils/load_secrets_common.py | 3 +- .../plugins/module_utils/load_secrets_v1.py | 25 ++++- .../plugins/module_utils/load_secrets_v2.py | 5 +- ansible/plugins/modules/vault_load_secrets.py | 1 - ansible/tests/unit/test_vault_load_secrets.py | 25 ++++- .../tests/unit/test_vault_load_secrets_v2.py | 104 ------------------ 6 files changed, 43 insertions(+), 120 deletions(-) delete mode 100644 ansible/tests/unit/test_vault_load_secrets_v2.py diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index 57eb0f8c..8045a18f 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -23,6 +23,7 @@ import yaml from collections.abc import MutableMapping + def parse_values(values_file): """ Parses a values-secrets.yaml file (usually placed in ~) @@ -115,5 +116,3 @@ def flatten(dictionary, parent_key=False, separator="."): if value is not None: items.append((new_key, value)) return dict(items) - - diff --git a/ansible/plugins/module_utils/load_secrets_v1.py b/ansible/plugins/module_utils/load_secrets_v1.py index 8849a72c..a66de116 100644 --- a/ansible/plugins/module_utils/load_secrets_v1.py +++ b/ansible/plugins/module_utils/load_secrets_v1.py @@ -19,10 +19,19 @@ import base64 import os -from ansible.module_utils.load_secrets_common import parse_values, get_version, run_command, flatten +import yaml +from ansible.module_utils.load_secrets_common import ( + parse_values, + get_version, + run_command, + flatten, +) + class LoadSecretsV1: - def __init__(self, module, values_secrets, basepath, namespace, pod, values_secret_template): + def __init__( + self, module, values_secrets, basepath, namespace, pod, values_secret_template + ): self.module = module self.basepath = basepath self.namespace = namespace @@ -91,7 +100,11 @@ def sanitize_values(self): # generate s3Secret so a user does not need to do it manually which tends to be error-prone for secret in secrets: tmp = secrets[secret] - if "s3.accessKey" in tmp and "s3.secretKey" in tmp and "s3Secret" not in tmp: + if ( + "s3.accessKey" in tmp + and "s3.secretKey" in tmp + and "s3Secret" not in tmp + ): s3secret = ( f"s3.accessKey: {tmp['s3.accessKey']}\n" f"s3.secretKey: {tmp['s3.secretKey']}" @@ -99,7 +112,6 @@ def sanitize_values(self): s3secretb64 = base64.b64encode(s3secret.encode()) secrets[secret]["s3Secret"] = s3secretb64.decode("utf-8") - def get_secrets_vault_paths(self, keyname): """ Walks a secrets yaml object to look for all top-level keys that start with @@ -139,7 +151,9 @@ def get_secrets_vault_paths(self, keyname): # We are in the presence of either 'secrets.region-one' or 'files.cluster1' top-level keys tmp = key.split(".", 1) if len(tmp) != 2: - self.module.fail_json(f"values-secrets.yaml key is non-conformant: {key}") + self.module.fail_json( + f"values-secrets.yaml key is non-conformant: {key}" + ) keys_paths.append((key, tmp[1])) @@ -188,7 +202,6 @@ def inject_secrets(self): counter += 1 return counter - def check_for_missing_secrets(self): with open(self.values_secret_template, "r", encoding="utf-8") as file: template_yaml = yaml.safe_load(file.read()) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index cfaf91c1..ab99f46d 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -21,10 +21,12 @@ import base64 import os + class LoadSecretsV2: - def __init__(): + def __init__(self): return + def sanitize_values(module, syaml): """ Sanitizes the secrets YAML object version 2.0 @@ -44,5 +46,6 @@ def sanitize_values(module, syaml): return syaml + def get_secrets_vault_paths(module, syaml, keyname): return diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 5e5506e8..5cd9096e 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -122,7 +122,6 @@ """ - def run(module): """Main ansible module entry point""" results = dict(changed=False) diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 55e18bdf..cce18612 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -26,7 +26,18 @@ from ansible.module_utils import basic from ansible.module_utils.common.text.converters import to_bytes +# TODO(bandini): I could not come up with something better to force the imports to be existing +# when we 'import vault_load_secrets' +sys.path.insert(1, "./ansible/plugins/module_utils") sys.path.insert(1, "./ansible/plugins/modules") +import load_secrets_common # noqa: E402 + +sys.modules["ansible.module_utils.load_secrets_common"] = load_secrets_common +import load_secrets_v1 # noqa: E402 +import load_secrets_v2 # noqa: E402 + +sys.modules["ansible.module_utils.load_secrets_v2"] = load_secrets_v2 +sys.modules["ansible.module_utils.load_secrets_v1"] = load_secrets_v1 import vault_load_secrets # noqa: E402 @@ -110,7 +121,7 @@ def test_ensure_empty_files_but_not_secrets_is_ok(self): } ) - with patch.object(vault_load_secrets, "run_command") as mock_run_command: + with patch.object(load_secrets_common, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -158,7 +169,7 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): } ) - with patch.object(vault_load_secrets, "run_command") as mock_run_command: + with patch.object(load_secrets_common, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -184,7 +195,9 @@ def test_ensure_command_called(self): {"values_secrets": os.path.join(self.testdir_v1, "values-secret-good.yaml")} ) - with patch.object(vault_load_secrets, "run_command") as mock_run_command: + with patch( + "ansible.module_utils.load_secrets_common", "run_command" + ) as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -249,7 +262,7 @@ def test_ensure_good_template_checking(self): ), } ) - with patch.object(vault_load_secrets, "run_command") as mock_run_command: + with patch.object(load_secrets_common, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -282,7 +295,7 @@ def test_ensure_bad_template_checking(self): ), } ) - with patch.object(vault_load_secrets, "run_command") as mock_run_command: + with patch.object(load_secrets_common, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -303,7 +316,7 @@ def test_ensure_fqdn_secrets(self): {"values_secrets": os.path.join(self.testdir_v1, "values-secret-fqdn.yaml")} ) - with patch.object(vault_load_secrets, "run_command") as mock_run_command: + with patch.object(load_secrets_common, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py deleted file mode 100644 index 3d5d890d..00000000 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright 2022 Red Hat, Inc. -# All Rights Reserved. -# -# 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. - -""" -Simple module to test vault_load_secrets -""" - -import json -import os -import sys -import unittest -from unittest.mock import call, patch - -from ansible.module_utils import basic -from ansible.module_utils.common.text.converters import to_bytes - -sys.path.insert(1, "./ansible/plugins/modules") -import vault_load_secrets # noqa: E402 - - -def set_module_args(args): - """prepare arguments so that they will be picked up during module creation""" - args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) - basic._ANSIBLE_ARGS = to_bytes(args) - - -class AnsibleExitJson(Exception): - """Exception class to be raised by module.exit_json and caught by the test case""" - - pass - - -class AnsibleFailJson(Exception): - """Exception class to be raised by module.fail_json and caught by the test case""" - - pass - - -def exit_json(*args, **kwargs): - """function to patch over exit_json; package return data into an exception""" - if "changed" not in kwargs: - kwargs["changed"] = False - raise AnsibleExitJson(kwargs) - - -def fail_json(*args, **kwargs): - """function to patch over fail_json; package return data into an exception""" - kwargs["failed"] = True - kwargs["args"] = args - raise AnsibleFailJson(kwargs) - - -class TestMyModule(unittest.TestCase): - def setUp(self): - self.mock_module_helper = patch.multiple( - basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json - ) - self.mock_module_helper.start() - self.addCleanup(self.mock_module_helper.stop) - self.testdir_v2 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v2") - self.testfile = open("/tmp/ca.crt", "w") - - def tearDown(self): - self.testfile.close() - try: - os.remove("/tmp/ca.crt") - except OSError: - pass - - def test_module_fail_when_required_args_missing(self): - with self.assertRaises(AnsibleFailJson): - set_module_args({}) - vault_load_secrets.main() - - def test_module_fail_when_values_secret_not_existing(self): - with self.assertRaises(AnsibleExitJson) as ansible_err: - set_module_args( - { - "values_secrets": "/tmp/nonexisting", - } - ) - vault_load_secrets.main() - - ret = ansible_err.exception.args[0] - self.assertEqual(ret["failed"], True) - self.assertEqual(ret["error"], "Missing values-secrets.yaml file") - self.assertEqual( - ret["msg"], "Values secrets file does not exist: /tmp/nonexisting" - ) - -if __name__ == "__main__": - unittest.main() From 037e8c5a85aedee90bfa2db8b1baa88010d40691 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 12:38:51 +0100 Subject: [PATCH 0629/1263] Have super-linter pass --- ansible/plugins/module_utils/load_secrets_common.py | 3 ++- ansible/plugins/module_utils/load_secrets_v1.py | 10 +++------- ansible/plugins/module_utils/load_secrets_v2.py | 13 ++++++++----- ansible/plugins/modules/vault_load_secrets.py | 5 ++--- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index 8045a18f..919d669f 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -20,9 +20,10 @@ import os import subprocess import time -import yaml from collections.abc import MutableMapping +import yaml + def parse_values(values_file): """ diff --git a/ansible/plugins/module_utils/load_secrets_v1.py b/ansible/plugins/module_utils/load_secrets_v1.py index a66de116..2d8e442a 100644 --- a/ansible/plugins/module_utils/load_secrets_v1.py +++ b/ansible/plugins/module_utils/load_secrets_v1.py @@ -19,13 +19,9 @@ import base64 import os + import yaml -from ansible.module_utils.load_secrets_common import ( - parse_values, - get_version, - run_command, - flatten, -) +from ansible.module_utils.load_secrets_common import flatten, parse_values, run_command class LoadSecretsV1: @@ -78,7 +74,7 @@ def sanitize_values(self): files = {} if len(secrets) == 0 and len(files) == 0: self.module.fail_json( - f"Neither 'secrets' nor 'files have any secrets to be parsed" + "Neither 'secrets' nor 'files have any secrets to be parsed" ) if isinstance(secrets, list) or isinstance(files, list): diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index ab99f46d..d3efcd7a 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -18,8 +18,14 @@ """ -import base64 -import os +# import base64 +# import os + +# from ansible.module_utils.load_secrets_common import ( +# parse_values, +# run_command, +# flatten, +# ) class LoadSecretsV2: @@ -40,9 +46,6 @@ def sanitize_values(module, syaml): Returns: syaml(obj): The parsed yaml object sanitized """ - version = get_version(syaml) - if version != "2.0": - module.fail_json(f"Version expected is 2.0 but got: {version}") return syaml diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 5cd9096e..47e1b505 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -54,10 +54,9 @@ import yaml from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.load_secrets_common import parse_values, get_version - -from ansible.module_utils.load_secrets_v2 import LoadSecretsV2 +from ansible.module_utils.load_secrets_common import get_version, parse_values from ansible.module_utils.load_secrets_v1 import LoadSecretsV1 +from ansible.module_utils.load_secrets_v2 import LoadSecretsV2 ANSIBLE_METADATA = { "metadata_version": "1.1", From af4f8a1253d58bb8a0a823637f1817ca9091b6f4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 12:51:32 +0100 Subject: [PATCH 0630/1263] Fix patching the right function https://docs.python.org/3/library/unittest.mock.html#where-to-patch --- ansible/tests/unit/test_vault_load_secrets.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index cce18612..5a51432f 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -121,7 +121,7 @@ def test_ensure_empty_files_but_not_secrets_is_ok(self): } ) - with patch.object(load_secrets_common, "run_command") as mock_run_command: + with patch.object(load_secrets_v1, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -169,7 +169,7 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): } ) - with patch.object(load_secrets_common, "run_command") as mock_run_command: + with patch.object(load_secrets_v1, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -195,9 +195,7 @@ def test_ensure_command_called(self): {"values_secrets": os.path.join(self.testdir_v1, "values-secret-good.yaml")} ) - with patch( - "ansible.module_utils.load_secrets_common", "run_command" - ) as mock_run_command: + with patch.object(load_secrets_v1, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -262,7 +260,7 @@ def test_ensure_good_template_checking(self): ), } ) - with patch.object(load_secrets_common, "run_command") as mock_run_command: + with patch.object(load_secrets_v1, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -295,7 +293,7 @@ def test_ensure_bad_template_checking(self): ), } ) - with patch.object(load_secrets_common, "run_command") as mock_run_command: + with patch.object(load_secrets_v1, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -316,7 +314,7 @@ def test_ensure_fqdn_secrets(self): {"values_secrets": os.path.join(self.testdir_v1, "values-secret-fqdn.yaml")} ) - with patch.object(load_secrets_common, "run_command") as mock_run_command: + with patch.object(load_secrets_v1, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 From dbfa8faba7ee678758b8cb196945045e5c897ed1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 13:50:21 +0100 Subject: [PATCH 0631/1263] Start unit testing V2 as well --- .../plugins/module_utils/load_secrets_v2.py | 49 ++++---- ansible/tests/unit/test_vault_load_secrets.py | 2 +- .../tests/unit/test_vault_load_secrets_v2.py | 114 ++++++++++++++++++ 3 files changed, 141 insertions(+), 24 deletions(-) create mode 100644 ansible/tests/unit/test_vault_load_secrets_v2.py diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index d3efcd7a..081fcafc 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -17,38 +17,41 @@ Module that implements V2 of the values-secret.yaml spec """ +import base64 +import os -# import base64 -# import os - -# from ansible.module_utils.load_secrets_common import ( -# parse_values, -# run_command, -# flatten, -# ) +import yaml +from ansible.module_utils.load_secrets_common import flatten, parse_values, run_command class LoadSecretsV2: - def __init__(self): - return + def __init__( + self, module, values_secrets, basepath, namespace, pod, values_secret_template + ): + self.module = module + self.basepath = basepath + self.namespace = namespace + self.pod = pod + self.values_secret_template = values_secret_template + self.syaml = parse_values(values_secrets) -def sanitize_values(module, syaml): - """ - Sanitizes the secrets YAML object version 2.0 - ..TODO.. + def sanitize_values(self): + """ + Sanitizes the secrets YAML object version 2.0 + ..TODO.. - Parameters: - module(AnsibleModule): The current AnsibleModule being used + Parameters: + module(AnsibleModule): The current AnsibleModule being used - syaml(obj): The parsed yaml object representing the secrets + syaml(obj): The parsed yaml object representing the secrets - Returns: - syaml(obj): The parsed yaml object sanitized - """ + Returns: + syaml(obj): The parsed yaml object sanitized + """ - return syaml + return syaml -def get_secrets_vault_paths(module, syaml, keyname): - return + def get_secrets_vault_paths(self, keyname): + return diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 5a51432f..0a2f7778 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -36,8 +36,8 @@ import load_secrets_v1 # noqa: E402 import load_secrets_v2 # noqa: E402 -sys.modules["ansible.module_utils.load_secrets_v2"] = load_secrets_v2 sys.modules["ansible.module_utils.load_secrets_v1"] = load_secrets_v1 +sys.modules["ansible.module_utils.load_secrets_v2"] = load_secrets_v2 import vault_load_secrets # noqa: E402 diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py new file mode 100644 index 00000000..42c99c14 --- /dev/null +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -0,0 +1,114 @@ +# Copyright 2022 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Simple module to test vault_load_secrets +""" + +import json +import os +import sys +import unittest +from unittest.mock import call, patch + +from ansible.module_utils import basic +from ansible.module_utils.common.text.converters import to_bytes + +# TODO(bandini): I could not come up with something better to force the imports to be existing +# when we 'import vault_load_secrets' +sys.path.insert(1, "./ansible/plugins/module_utils") +sys.path.insert(1, "./ansible/plugins/modules") +import load_secrets_common # noqa: E402 + +sys.modules["ansible.module_utils.load_secrets_common"] = load_secrets_common +import load_secrets_v1 # noqa: E402 +import load_secrets_v2 # noqa: E402 + +sys.modules["ansible.module_utils.load_secrets_v1"] = load_secrets_v1 +sys.modules["ansible.module_utils.load_secrets_v2"] = load_secrets_v2 +import vault_load_secrets # noqa: E402 + +def set_module_args(args): + """prepare arguments so that they will be picked up during module creation""" + args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + """Exception class to be raised by module.exit_json and caught by the test case""" + + pass + + +class AnsibleFailJson(Exception): + """Exception class to be raised by module.fail_json and caught by the test case""" + + pass + + +def exit_json(*args, **kwargs): + """function to patch over exit_json; package return data into an exception""" + if "changed" not in kwargs: + kwargs["changed"] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + """function to patch over fail_json; package return data into an exception""" + kwargs["failed"] = True + kwargs["args"] = args + raise AnsibleFailJson(kwargs) + + +class TestMyModule(unittest.TestCase): + def setUp(self): + self.mock_module_helper = patch.multiple( + basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json + ) + self.mock_module_helper.start() + self.addCleanup(self.mock_module_helper.stop) + self.testdir_v2 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v2") + self.testfile = open("/tmp/ca.crt", "w") + + def tearDown(self): + self.testfile.close() + try: + os.remove("/tmp/ca.crt") + except OSError: + pass + + def test_module_fail_when_required_args_missing(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + vault_load_secrets.main() + + def test_module_fail_when_values_secret_not_existing(self): + with self.assertRaises(AnsibleExitJson) as ansible_err: + set_module_args( + { + "values_secrets": "/tmp/nonexisting", + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + self.assertEqual(ret["error"], "Missing values-secrets.yaml file") + self.assertEqual( + ret["msg"], "Values secrets file does not exist: /tmp/nonexisting" + ) + +if __name__ == "__main__": + unittest.main() From 9bb8495e5f3b8f3edc80295d9f8b94c7be9e5ca6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 15:40:04 +0100 Subject: [PATCH 0632/1263] Add version check in v1 implementation --- ansible/plugins/module_utils/load_secrets_v1.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ansible/plugins/module_utils/load_secrets_v1.py b/ansible/plugins/module_utils/load_secrets_v1.py index 2d8e442a..df04bae0 100644 --- a/ansible/plugins/module_utils/load_secrets_v1.py +++ b/ansible/plugins/module_utils/load_secrets_v1.py @@ -21,7 +21,7 @@ import os import yaml -from ansible.module_utils.load_secrets_common import flatten, parse_values, run_command +from ansible.module_utils.load_secrets_common import get_version, flatten, parse_values, run_command class LoadSecretsV1: @@ -57,6 +57,10 @@ def sanitize_values(self): Returns: Nothing: Updates self.syaml(obj) """ + v = get_version(self.syaml) + if v != "1.0": + self.module.fail_json(f"Version is not 1.0: {v}") + if not ("secrets" in self.syaml or "files" in self.syaml): self.module.fail_json( f"Values secrets file does not contain 'secrets' or" From e6627d5f0da8aab3f7dd5f4190c5841b28195e7d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 15:40:26 +0100 Subject: [PATCH 0633/1263] Force get_version() to return a string for the time being --- ansible/plugins/module_utils/load_secrets_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index 919d669f..b08b72f9 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -52,7 +52,7 @@ def get_version(syaml): Returns: ret(str): The version value in of the top-level 'version:' key """ - return syaml.get("version", "1.0") + return str(syaml.get("version", "1.0")) def run_command(command, attempts=1, sleep=3): From 9ee4417c796b431d959114a901d5784c3f677f91 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 15:41:10 +0100 Subject: [PATCH 0634/1263] Pass proper parameters to V2 class --- ansible/plugins/modules/vault_load_secrets.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 47e1b505..e90a9690 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -147,7 +147,9 @@ def run(module): version = get_version(syaml) if version == "2.0": - secret_obj = LoadSecretsV2() + secret_obj = LoadSecretsV2( + module, values_secrets, basepath, namespace, pod, values_secret_template + ) elif version == "1.0": secret_obj = LoadSecretsV1( module, values_secrets, basepath, namespace, pod, values_secret_template From d4c0afb66d83ff81c315d8037a590a24a9367cb7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 16:05:01 +0100 Subject: [PATCH 0635/1263] Initial start of vault policies implementation --- .../plugins/module_utils/load_secrets_v2.py | 35 +++++++++--- .../tests/unit/test_vault_load_secrets_v2.py | 54 +++++++++++++++++++ .../unit/v2/values-secret-v2-nopolicies.yaml | 29 ++++++++++ 3 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 081fcafc..d0ca5ad8 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -21,8 +21,7 @@ import os import yaml -from ansible.module_utils.load_secrets_common import flatten, parse_values, run_command - +from ansible.module_utils.load_secrets_common import get_version, flatten, parse_values, run_command class LoadSecretsV2: def __init__( @@ -35,23 +34,43 @@ def __init__( self.values_secret_template = values_secret_template self.syaml = parse_values(values_secrets) + def _get_vault_policies(self): + return self.syaml.get("vaultPolicies", {}) + + + def inject_vault_policies(self): + for name, policy in self._get_vault_policies().items(): + cmd = ( + f"echo '{policy}' | oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f"'cat - > /tmp/{name}.hcl';" + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c 'vault write sys/policies/password/{name} " + f" policy=@/tmp/{name}.hcl'" + ) + run_command(cmd, attempts=3) + def sanitize_values(self): """ Sanitizes the secrets YAML object version 2.0 - ..TODO.. Parameters: - module(AnsibleModule): The current AnsibleModule being used - - syaml(obj): The parsed yaml object representing the secrets Returns: - syaml(obj): The parsed yaml object sanitized + Nothing: Updates self.syaml(obj) if needed """ + v = get_version(self.syaml) + if v != "2.0": + self.module.fail_json(f"Version is not 2.0: {v}") - return syaml + # Check if they are sane somehow? + vault_policies = self._get_vault_policies() def get_secrets_vault_paths(self, keyname): return + + def inject_secrets(self): + # This must come first as some passwords might depend on vault policies to exist. + # It is a noop when no policies are defined + self.inject_vault_policies() + return diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 42c99c14..d8867fae 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -110,5 +110,59 @@ def test_module_fail_when_values_secret_not_existing(self): ret["msg"], "Values secrets file does not exist: /tmp/nonexisting" ) + def test_ensure_no_vault_policies_is_ok(self): + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-nopolicies.yaml" + ), + } + ) + with patch.object(load_secrets_v2, "run_command") as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 0 + + def test_ensure_policies_are_injected(self): + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-base.yaml" + ), + } + ) + with patch.object(load_secrets_v2, "run_command") as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 2 + + calls = [ + call( + "echo 'length=10\nrule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 }\nrule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 }\nrule \"charset\" { charset = \"0123456789\" min-chars = 1 }\n' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/basicPolicy.hcl';oc exec -n vault vault-0 -i -- sh -c 'vault write sys/policies/password/basicPolicy policy=@/tmp/basicPolicy.hcl'", # noqa: E501 + attempts=3, + ), + call( + "echo 'length=20\nrule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 }\nrule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 }\nrule \"charset\" { charset = \"0123456789\" min-chars = 1 }\nrule \"charset\" { charset = \"!@#$%^&*\" min-chars = 1 }\n' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/advancedPolicy.hcl';oc exec -n vault vault-0 -i -- sh -c 'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl'", # noqa: E501 + attempts=3, + ) + ] + mock_run_command.assert_has_calls(calls) + if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml new file mode 100644 index 00000000..f6d9b250 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml @@ -0,0 +1,29 @@ +# Needed to specify the new format (missing version means old version: 1.0 by default) +version: 2.0 +# NEVER COMMIT THESE VALUES TO GIT + +# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark +global: + backend: vault + +secrets: + - name: config-demo + vaultPrefixes: + - secret/region-one + - secret/snowflake.blueprints.rhecoeng.com + fields: + - name: secret + value: null + onMissingValue: error # One of: error,generate,prompt + - name: secret2 + value: null + onMissingValue: prompt + - name: config-demo-file + vaultPrefixes: + - secret/region-one + - secret/snowflake.blueprints.rhecoeng.com + + file: + # promptable would prompt for the path of the file in this case + path: /tmp/ca.crt + onMissingValue: error # One of error, prompt (for path). generate makes no sense for file From fa16b8ae61596d80ba28a7f3250210793cdc54aa Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 17:57:13 +0100 Subject: [PATCH 0636/1263] More validation and more V2 testing --- .../plugins/module_utils/load_secrets_v2.py | 73 ++++++++++++++++++- ansible/plugins/modules/vault_load_secrets.py | 5 +- .../tests/unit/test_vault_load_secrets_v2.py | 30 ++++++++ .../tests/unit/v2/values-secret-v2-base.yaml | 4 +- .../unit/v2/values-secret-v2-nopolicies.yaml | 2 +- ...values-secret-v2-wrong-onmissingvalue.yaml | 26 +++++++ 6 files changed, 131 insertions(+), 9 deletions(-) create mode 100644 ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index d0ca5ad8..cdde19a7 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -23,12 +23,16 @@ import yaml from ansible.module_utils.load_secrets_common import get_version, flatten, parse_values, run_command +def lol(msg): + import datetime + with open('/tmp/lol.txt', 'a+', encoding='utf-8') as f: + f.write('[LOL] %s %s\n' % (str(datetime.datetime.now()), msg)) + class LoadSecretsV2: def __init__( - self, module, values_secrets, basepath, namespace, pod, values_secret_template + self, module, values_secrets, namespace, pod, values_secret_template ): self.module = module - self.basepath = basepath self.namespace = namespace self.pod = pod self.values_secret_template = values_secret_template @@ -37,6 +41,64 @@ def __init__( def _get_vault_policies(self): return self.syaml.get("vaultPolicies", {}) + def _get_secrets(self): + return self.syaml.get("secrets", {}) + + def _get_field_on_missing_value(self, f): + # By default if 'onMissingValue' is missing we assume we need to + # error out whenever the value is missing + return f.get("onMissingValue", "error") + + def _get_field_value(self, f): + return f.get("value", None) + + def _validate_field(self, f): + # These fields are mandatory + try: + name = f["name"] + except KeyError: + return (False, f"Field {f} is missing name") + + on_missing_value = self._get_field_on_missing_value(f) + value = self._get_field_value(f) + lol(on_missing_value) + lol(value) + if on_missing_value in ["error"] and value == None: + return (False, f"Secret has onMissingValue set to 'error' and has no value set") + + if on_missing_value in ["generate", "prompt"] and value != None: + return (False, f"Secret has onMissingValue set to 'generate' or 'prompt' and has a value set") + + return (True, '') + + def _validate_file(self, f): + return (True, '') + + def _validate_secrets(self): + secrets = self._get_secrets() + if len(secrets) == 0: + self.module.fail_json(f"No secrets found") + + for s in secrets: + # These fields are mandatory + for i in ["name", "vaultPrefixes"]: + try: + tmp = s[i] + except KeyError: + return (False, f"Secret {s} is missing {i}") + + fields = s.get("fields", []) + file = s.get("file", []) + if len(fields) == 0 and len(file) == 0: + return (False, f"Secret {s} does not have either fields nor file") + + for i in fields: + (ret, msg) = self._validate_field(i) + if ret == False: + return (False, msg) + + return (True, '') + def inject_vault_policies(self): for name, policy in self._get_vault_policies().items(): @@ -62,7 +124,7 @@ def sanitize_values(self): if v != "2.0": self.module.fail_json(f"Version is not 2.0: {v}") - # Check if they are sane somehow? + # Check if the vault_policies are sane somehow? vault_policies = self._get_vault_policies() @@ -70,7 +132,12 @@ def get_secrets_vault_paths(self, keyname): return def inject_secrets(self): + (ret, msg) = self._validate_secrets() + if not ret: + self.module.fail_json(msg) + # This must come first as some passwords might depend on vault policies to exist. # It is a noop when no policies are defined self.inject_vault_policies() + return diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index e90a9690..c3458799 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -92,7 +92,8 @@ default: vault-0 basepath: description: - - Vault's kv initial part of the path + - Vault's kv initial part of the path. This is only supported on version 1.0 of the + secret format required: false type: str default: secret @@ -148,7 +149,7 @@ def run(module): if version == "2.0": secret_obj = LoadSecretsV2( - module, values_secrets, basepath, namespace, pod, values_secret_template + module, values_secrets, namespace, pod, values_secret_template ) elif version == "1.0": secret_obj = LoadSecretsV1( diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index d8867fae..0e2f42e3 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -164,5 +164,35 @@ def test_ensure_policies_are_injected(self): ] mock_run_command.assert_has_calls(calls) + def test_ensure_error_wrong_onmissing_value(self): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-wrong-onmissingvalue.yaml" + ), + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert (ret["args"][1] == "Secret has onMissingValue set to 'generate' or 'prompt' and has a value set") + + def test_ensure_error_wrong_vaultpolicy(self): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-wrong-vaultpolicy.yaml" + ), + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert (ret["args"][1] == "Secret has onMissingValue set to 'generate' or 'prompt' and has a value set") + if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-base.yaml b/ansible/tests/unit/v2/values-secret-v2-base.yaml index 246b5086..31c5829a 100644 --- a/ansible/tests/unit/v2/values-secret-v2-base.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-base.yaml @@ -28,11 +28,9 @@ secrets: - secret/snowflake.blueprints.rhecoeng.com fields: - name: secret - value: null - onMissingValue: error # One of: error,generate,prompt + onMissingValue: generate # One of: error,generate,prompt vaultPolicy: basicPolicy - name: secret2 - value: null onMissingValue: prompt - name: config-demo-file vaultPrefixes: diff --git a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml index f6d9b250..11554d48 100644 --- a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml @@ -13,7 +13,7 @@ secrets: - secret/snowflake.blueprints.rhecoeng.com fields: - name: secret - value: null + value: value123 onMissingValue: error # One of: error,generate,prompt - name: secret2 value: null diff --git a/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml b/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml new file mode 100644 index 00000000..ba483c56 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml @@ -0,0 +1,26 @@ +# Needed to specify the new format (missing version means old version: 1.0 by default) +version: 2.0 +# NEVER COMMIT THESE VALUES TO GIT + +# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark +global: + backend: vault + +vaultPolicies: + # https://developer.hashicorp.com/vault/docs/concepts/password-policies + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + +secrets: + - name: config-demo + vaultPrefixes: + - secret/region-one + - secret/snowflake.blueprints.rhecoeng.com + fields: + - name: secret + value: password123 + onMissingValue: generate # One of: error,generate,prompt + vaultPolicy: nonExisting From 1be5b448f57449868a74c30d6f7349b56d480b85 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 18:01:59 +0100 Subject: [PATCH 0637/1263] Make sure we check if the vaultPolicy exists --- ansible/plugins/module_utils/load_secrets_v2.py | 15 ++++++--------- ansible/tests/unit/test_vault_load_secrets_v2.py | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index cdde19a7..e70a28cf 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -23,11 +23,6 @@ import yaml from ansible.module_utils.load_secrets_common import get_version, flatten, parse_values, run_command -def lol(msg): - import datetime - with open('/tmp/lol.txt', 'a+', encoding='utf-8') as f: - f.write('[LOL] %s %s\n' % (str(datetime.datetime.now()), msg)) - class LoadSecretsV2: def __init__( self, module, values_secrets, namespace, pod, values_secret_template @@ -61,13 +56,15 @@ def _validate_field(self, f): on_missing_value = self._get_field_on_missing_value(f) value = self._get_field_value(f) - lol(on_missing_value) - lol(value) if on_missing_value in ["error"] and value == None: - return (False, f"Secret has onMissingValue set to 'error' and has no value set") + return (False, "Secret has onMissingValue set to 'error' and has no value set") if on_missing_value in ["generate", "prompt"] and value != None: - return (False, f"Secret has onMissingValue set to 'generate' or 'prompt' and has a value set") + return (False, "Secret has onMissingValue set to 'generate' or 'prompt' and has a value set") + + vault_policy = f.get("vaultPolicy", None) + if vault_policy != None and vault_policy not in self._get_vault_policies(): + return (False, f"Secret has vaultPolicy set to {vault_policy} but no such policy exists") return (True, '') diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 0e2f42e3..c09a3ea6 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -192,7 +192,7 @@ def test_ensure_error_wrong_vaultpolicy(self): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert (ret["args"][1] == "Secret has onMissingValue set to 'generate' or 'prompt' and has a value set") + assert (ret["args"][1] == "Secret has vaultPolicy set to nonExisting but no such policy exists") if __name__ == "__main__": unittest.main() From d58d3bceba874c6fe8db8d9f3094068caa4b26d1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 18:03:29 +0100 Subject: [PATCH 0638/1263] Make error message clearer --- ansible/plugins/module_utils/load_secrets_v2.py | 4 ++-- ansible/tests/unit/test_vault_load_secrets_v2.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index e70a28cf..0d6bd2a5 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -56,11 +56,11 @@ def _validate_field(self, f): on_missing_value = self._get_field_on_missing_value(f) value = self._get_field_value(f) - if on_missing_value in ["error"] and value == None: + if on_missing_value in ["error"] and (value == None or len(value) < 1): return (False, "Secret has onMissingValue set to 'error' and has no value set") if on_missing_value in ["generate", "prompt"] and value != None: - return (False, "Secret has onMissingValue set to 'generate' or 'prompt' and has a value set") + return (False, "Secret has onMissingValue set to 'generate' or 'prompt' but has a value set") vault_policy = f.get("vaultPolicy", None) if vault_policy != None and vault_policy not in self._get_vault_policies(): diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index c09a3ea6..f12ec217 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -177,7 +177,7 @@ def test_ensure_error_wrong_onmissing_value(self): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert (ret["args"][1] == "Secret has onMissingValue set to 'generate' or 'prompt' and has a value set") + assert (ret["args"][1] == "Secret has onMissingValue set to 'generate' or 'prompt' but has a value set") def test_ensure_error_wrong_vaultpolicy(self): with self.assertRaises(AnsibleFailJson) as ansible_err: From 921b586c5907099a1c5d8b72fe121818df577e73 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 18:47:24 +0100 Subject: [PATCH 0639/1263] Add a lot more tests and start implementing the files: section --- .../plugins/module_utils/load_secrets_v2.py | 37 +++++++++++++-- .../tests/unit/test_vault_load_secrets_v2.py | 45 +++++++++++++++++++ .../tests/unit/v2/values-secret-v2-base.yaml | 8 +++- .../v2/values-secret-v2-files-emptypath.yaml | 32 +++++++++++++ ...-secret-v2-files-wrong-onmissingvalue.yaml | 33 ++++++++++++++ .../v2/values-secret-v2-files-wrongpath.yaml | 33 ++++++++++++++ .../unit/v2/values-secret-v2-nopolicies.yaml | 4 +- .../values-secret-v2-wrong-vaultpolicy.yaml | 25 +++++++++++ 8 files changed, 209 insertions(+), 8 deletions(-) create mode 100644 ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 0d6bd2a5..2b5c1b8d 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -55,6 +55,9 @@ def _validate_field(self, f): return (False, f"Field {f} is missing name") on_missing_value = self._get_field_on_missing_value(f) + if on_missing_value not in ["error", "generate", "prompt"]: + return (False, f"onMissingValue: {on_missing_value} is invalid") + value = self._get_field_value(f) if on_missing_value in ["error"] and (value == None or len(value) < 1): return (False, "Secret has onMissingValue set to 'error' and has no value set") @@ -69,6 +72,28 @@ def _validate_field(self, f): return (True, '') def _validate_file(self, f): + # These fields are mandatory + try: + name = f["name"] + except KeyError: + return (False, f"Field {f} is missing name") + + on_missing_value = self._get_field_on_missing_value(f) + if on_missing_value not in ["error", "prompt"]: + return (False, f"onMissingValue: {on_missing_value} is invalid") + + path = f.get("path", None) + if on_missing_value in ["error"] and path == None: + return (False, f"{name} has unset path") + + if on_missing_value in ["prompt"] and path != None: + return (False, f"{name} has onMissingValue set to 'prompt' but path is set") + + if on_missing_value in ["prompt"]: + # FIXME: implement proper prompting + path = "/tmp/ca.crt" + if not os.path.isfile(os.path.expanduser(path)): + return (False, f"{name} has non-existing path: {path}") return (True, '') def _validate_secrets(self): @@ -85,15 +110,19 @@ def _validate_secrets(self): return (False, f"Secret {s} is missing {i}") fields = s.get("fields", []) - file = s.get("file", []) - if len(fields) == 0 and len(file) == 0: - return (False, f"Secret {s} does not have either fields nor file") + files = s.get("files", []) + if len(fields) == 0 and len(files) == 0: + return (False, f"Secret {s} does not have either fields nor files") for i in fields: (ret, msg) = self._validate_field(i) - if ret == False: + if not ret: return (False, msg) + for i in files: + (ret, msg) = self._validate_file(i) + if not ret: + return (False, msg) return (True, '') diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index f12ec217..0dcebb8f 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -194,5 +194,50 @@ def test_ensure_error_wrong_vaultpolicy(self): self.assertEqual(ret["failed"], True) assert (ret["args"][1] == "Secret has vaultPolicy set to nonExisting but no such policy exists") + def test_ensure_error_file_wrong_onmissing_value(self): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-files-wrong-onmissingvalue.yaml" + ), + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert (ret["args"][1] == "onMissingValue: generate is invalid") + + def test_ensure_error_file_emptypath(self): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-files-emptypath.yaml" + ), + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert (ret["args"][1] == "ca_crt has unset path") + + def test_ensure_error_file_wrongpath(self): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-files-wrongpath.yaml" + ), + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert (ret["args"][1] == "ca_crt has non-existing path: /tmp/nonexisting") + if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-base.yaml b/ansible/tests/unit/v2/values-secret-v2-base.yaml index 31c5829a..a3c45c80 100644 --- a/ansible/tests/unit/v2/values-secret-v2-base.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-base.yaml @@ -37,7 +37,11 @@ secrets: - secret/region-one - secret/snowflake.blueprints.rhecoeng.com - file: - # promptable would prompt for the path of the file in this case + # Files are *always* base64 encoded first as the vault upload commands uses http+json + files: + - name: ca_crt path: /tmp/ca.crt onMissingValue: error # One of error, prompt (for path). generate makes no sense for file + + - name: ca_crt2 + onMissingValue: prompt # One of error, prompt (for path). generate makes no sense for file diff --git a/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml b/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml new file mode 100644 index 00000000..2c90be45 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml @@ -0,0 +1,32 @@ +# Needed to specify the new format (missing version means old version: 1.0 by default) +version: 2.0 +# NEVER COMMIT THESE VALUES TO GIT + +# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark +global: + backend: vault + +vaultPolicies: + # https://developer.hashicorp.com/vault/docs/concepts/password-policies + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + + advancedPolicy: | + length=20 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + +secrets: + - name: config-demo + vaultPrefixes: + - secret/region-one + - secret/snowflake.blueprints.rhecoeng.com + # Files are *always* base64 encoded first as the vault upload commands uses http+json + files: + - name: ca_crt + onMissingValue: error # One of error, prompt (for path). generate makes no sense for file diff --git a/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml b/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml new file mode 100644 index 00000000..f868569d --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml @@ -0,0 +1,33 @@ +# Needed to specify the new format (missing version means old version: 1.0 by default) +version: 2.0 +# NEVER COMMIT THESE VALUES TO GIT + +# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark +global: + backend: vault + +vaultPolicies: + # https://developer.hashicorp.com/vault/docs/concepts/password-policies + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + + advancedPolicy: | + length=20 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + +secrets: + - name: config-demo + vaultPrefixes: + - secret/region-one + - secret/snowflake.blueprints.rhecoeng.com + # Files are *always* base64 encoded first as the vault upload commands uses http+json + files: + - name: ca_crt + path: /tmp/ca.crt + onMissingValue: generate # One of error, prompt (for path). generate makes no sense for file diff --git a/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml b/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml new file mode 100644 index 00000000..9cad8bbd --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml @@ -0,0 +1,33 @@ +# Needed to specify the new format (missing version means old version: 1.0 by default) +version: 2.0 +# NEVER COMMIT THESE VALUES TO GIT + +# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark +global: + backend: vault + +vaultPolicies: + # https://developer.hashicorp.com/vault/docs/concepts/password-policies + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + + advancedPolicy: | + length=20 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + +secrets: + - name: config-demo + vaultPrefixes: + - secret/region-one + - secret/snowflake.blueprints.rhecoeng.com + # Files are *always* base64 encoded first as the vault upload commands uses http+json + files: + - name: ca_crt + path: /tmp/nonexisting + onMissingValue: error # One of error, prompt (for path). generate makes no sense for file diff --git a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml index 11554d48..fb72f5da 100644 --- a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml @@ -23,7 +23,7 @@ secrets: - secret/region-one - secret/snowflake.blueprints.rhecoeng.com - file: - # promptable would prompt for the path of the file in this case + files: + - name: ca_crt path: /tmp/ca.crt onMissingValue: error # One of error, prompt (for path). generate makes no sense for file diff --git a/ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml b/ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml new file mode 100644 index 00000000..d8c6f44b --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml @@ -0,0 +1,25 @@ +# Needed to specify the new format (missing version means old version: 1.0 by default) +version: 2.0 +# NEVER COMMIT THESE VALUES TO GIT + +# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark +global: + backend: vault + +vaultPolicies: + # https://developer.hashicorp.com/vault/docs/concepts/password-policies + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + +secrets: + - name: config-demo + vaultPrefixes: + - secret/region-one + - secret/snowflake.blueprints.rhecoeng.com + fields: + - name: secret + onMissingValue: generate # One of: error,generate,prompt + vaultPolicy: nonExisting From db156e9e8acfa7e4c76bbed0b216ce002599813c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 20 Nov 2022 18:48:13 +0100 Subject: [PATCH 0640/1263] Fix black errors --- .../plugins/module_utils/load_secrets_v1.py | 7 ++- .../plugins/module_utils/load_secrets_v2.py | 59 +++++++++++-------- .../tests/unit/test_vault_load_secrets_v2.py | 27 ++++++--- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v1.py b/ansible/plugins/module_utils/load_secrets_v1.py index df04bae0..6a106905 100644 --- a/ansible/plugins/module_utils/load_secrets_v1.py +++ b/ansible/plugins/module_utils/load_secrets_v1.py @@ -21,7 +21,12 @@ import os import yaml -from ansible.module_utils.load_secrets_common import get_version, flatten, parse_values, run_command +from ansible.module_utils.load_secrets_common import ( + flatten, + get_version, + parse_values, + run_command, +) class LoadSecretsV1: diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 2b5c1b8d..c058a3c0 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -17,16 +17,17 @@ Module that implements V2 of the values-secret.yaml spec """ -import base64 import os -import yaml -from ansible.module_utils.load_secrets_common import get_version, flatten, parse_values, run_command +from ansible.module_utils.load_secrets_common import ( + get_version, + parse_values, + run_command, +) + class LoadSecretsV2: - def __init__( - self, module, values_secrets, namespace, pod, values_secret_template - ): + def __init__(self, module, values_secrets, namespace, pod, values_secret_template): self.module = module self.namespace = namespace self.pod = pod @@ -50,7 +51,7 @@ def _get_field_value(self, f): def _validate_field(self, f): # These fields are mandatory try: - name = f["name"] + _ = f["name"] except KeyError: return (False, f"Field {f} is missing name") @@ -59,17 +60,29 @@ def _validate_field(self, f): return (False, f"onMissingValue: {on_missing_value} is invalid") value = self._get_field_value(f) - if on_missing_value in ["error"] and (value == None or len(value) < 1): - return (False, "Secret has onMissingValue set to 'error' and has no value set") + if on_missing_value in ["error"] and (value is None or len(value) < 1): + return ( + False, + "Secret has onMissingValue set to 'error' and has no value set", + ) - if on_missing_value in ["generate", "prompt"] and value != None: - return (False, "Secret has onMissingValue set to 'generate' or 'prompt' but has a value set") + if on_missing_value in ["generate", "prompt"] and value is not None: + return ( + False, + "Secret has onMissingValue set to 'generate' or 'prompt' but has a value set", + ) vault_policy = f.get("vaultPolicy", None) - if vault_policy != None and vault_policy not in self._get_vault_policies(): - return (False, f"Secret has vaultPolicy set to {vault_policy} but no such policy exists") + if vault_policy is not None and vault_policy not in self._get_vault_policies(): + return ( + False, + f"Secret has vaultPolicy set to {vault_policy} but no such policy exists", + ) - return (True, '') + if on_missing_value in ["generate"] and vault_policy is None: + return (False, f"Secret has no vaultPolicy but onMissingValue is set to 'generate'") + + return (True, "") def _validate_file(self, f): # These fields are mandatory @@ -83,10 +96,10 @@ def _validate_file(self, f): return (False, f"onMissingValue: {on_missing_value} is invalid") path = f.get("path", None) - if on_missing_value in ["error"] and path == None: + if on_missing_value in ["error"] and path is None: return (False, f"{name} has unset path") - if on_missing_value in ["prompt"] and path != None: + if on_missing_value in ["prompt"] and path is not None: return (False, f"{name} has onMissingValue set to 'prompt' but path is set") if on_missing_value in ["prompt"]: @@ -94,18 +107,19 @@ def _validate_file(self, f): path = "/tmp/ca.crt" if not os.path.isfile(os.path.expanduser(path)): return (False, f"{name} has non-existing path: {path}") - return (True, '') + + return (True, "") def _validate_secrets(self): secrets = self._get_secrets() if len(secrets) == 0: - self.module.fail_json(f"No secrets found") + self.module.fail_json("No secrets found") for s in secrets: # These fields are mandatory for i in ["name", "vaultPrefixes"]: try: - tmp = s[i] + _ = s[i] except KeyError: return (False, f"Secret {s} is missing {i}") @@ -123,8 +137,7 @@ def _validate_secrets(self): (ret, msg) = self._validate_file(i) if not ret: return (False, msg) - return (True, '') - + return (True, "") def inject_vault_policies(self): for name, policy in self._get_vault_policies().items(): @@ -136,7 +149,6 @@ def inject_vault_policies(self): ) run_command(cmd, attempts=3) - def sanitize_values(self): """ Sanitizes the secrets YAML object version 2.0 @@ -151,8 +163,7 @@ def sanitize_values(self): self.module.fail_json(f"Version is not 2.0: {v}") # Check if the vault_policies are sane somehow? - vault_policies = self._get_vault_policies() - + # vault_policies = self._get_vault_policies() def get_secrets_vault_paths(self, keyname): return diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 0dcebb8f..7339300b 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -40,6 +40,7 @@ sys.modules["ansible.module_utils.load_secrets_v2"] = load_secrets_v2 import vault_load_secrets # noqa: E402 + def set_module_args(args): """prepare arguments so that they will be picked up during module creation""" args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) @@ -154,13 +155,13 @@ def test_ensure_policies_are_injected(self): calls = [ call( - "echo 'length=10\nrule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 }\nrule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 }\nrule \"charset\" { charset = \"0123456789\" min-chars = 1 }\n' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/basicPolicy.hcl';oc exec -n vault vault-0 -i -- sh -c 'vault write sys/policies/password/basicPolicy policy=@/tmp/basicPolicy.hcl'", # noqa: E501 + 'echo \'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/basicPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/basicPolicy policy=@/tmp/basicPolicy.hcl\'', # noqa: E501 attempts=3, ), call( - "echo 'length=20\nrule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 }\nrule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 }\nrule \"charset\" { charset = \"0123456789\" min-chars = 1 }\nrule \"charset\" { charset = \"!@#$%^&*\" min-chars = 1 }\n' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/advancedPolicy.hcl';oc exec -n vault vault-0 -i -- sh -c 'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl'", # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/advancedPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl\'', # noqa: E501 attempts=3, - ) + ), ] mock_run_command.assert_has_calls(calls) @@ -177,7 +178,10 @@ def test_ensure_error_wrong_onmissing_value(self): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert (ret["args"][1] == "Secret has onMissingValue set to 'generate' or 'prompt' but has a value set") + assert ( + ret["args"][1] + == "Secret has onMissingValue set to 'generate' or 'prompt' but has a value set" + ) def test_ensure_error_wrong_vaultpolicy(self): with self.assertRaises(AnsibleFailJson) as ansible_err: @@ -192,14 +196,18 @@ def test_ensure_error_wrong_vaultpolicy(self): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert (ret["args"][1] == "Secret has vaultPolicy set to nonExisting but no such policy exists") + assert ( + ret["args"][1] + == "Secret has vaultPolicy set to nonExisting but no such policy exists" + ) def test_ensure_error_file_wrong_onmissing_value(self): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { "values_secrets": os.path.join( - self.testdir_v2, "values-secret-v2-files-wrong-onmissingvalue.yaml" + self.testdir_v2, + "values-secret-v2-files-wrong-onmissingvalue.yaml", ), } ) @@ -207,7 +215,7 @@ def test_ensure_error_file_wrong_onmissing_value(self): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert (ret["args"][1] == "onMissingValue: generate is invalid") + assert ret["args"][1] == "onMissingValue: generate is invalid" def test_ensure_error_file_emptypath(self): with self.assertRaises(AnsibleFailJson) as ansible_err: @@ -222,7 +230,7 @@ def test_ensure_error_file_emptypath(self): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert (ret["args"][1] == "ca_crt has unset path") + assert ret["args"][1] == "ca_crt has unset path" def test_ensure_error_file_wrongpath(self): with self.assertRaises(AnsibleFailJson) as ansible_err: @@ -237,7 +245,8 @@ def test_ensure_error_file_wrongpath(self): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert (ret["args"][1] == "ca_crt has non-existing path: /tmp/nonexisting") + assert ret["args"][1] == "ca_crt has non-existing path: /tmp/nonexisting" + if __name__ == "__main__": unittest.main() From 44588a4a1194da55653fcd9183a174f21fdb4697 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 21 Nov 2022 00:08:48 +0100 Subject: [PATCH 0641/1263] Cleanups and also call check_missing_secrets only on v1.0 --- ansible/plugins/module_utils/load_secrets_v2.py | 9 +++------ ansible/plugins/modules/vault_load_secrets.py | 14 +++++++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index c058a3c0..4aad3f5b 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -105,6 +105,7 @@ def _validate_file(self, f): if on_missing_value in ["prompt"]: # FIXME: implement proper prompting path = "/tmp/ca.crt" + if not os.path.isfile(os.path.expanduser(path)): return (False, f"{name} has non-existing path: {path}") @@ -165,16 +166,12 @@ def sanitize_values(self): # Check if the vault_policies are sane somehow? # vault_policies = self._get_vault_policies() - def get_secrets_vault_paths(self, keyname): - return - - def inject_secrets(self): (ret, msg) = self._validate_secrets() if not ret: self.module.fail_json(msg) + def inject_secrets(self): # This must come first as some passwords might depend on vault policies to exist. # It is a noop when no policies are defined self.inject_vault_policies() - - return + return 0 diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index c3458799..c8960fa7 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -151,20 +151,20 @@ def run(module): secret_obj = LoadSecretsV2( module, values_secrets, namespace, pod, values_secret_template ) + secret_obj.sanitize_values() elif version == "1.0": secret_obj = LoadSecretsV1( module, values_secrets, basepath, namespace, pod, values_secret_template ) + secret_obj.sanitize_values() + # If the user specified check_for_missing_secrets then we read values_secret_template + # and check if there are any missing secrets. Makes sense only for v1.0 + if check_missing_secrets: + secret_obj.check_for_missing_secrets() + else: module.fail_json(f"Version {version} is currently not supported") - secret_obj.sanitize_values() - - # If the user specified check_for_missing_secrets then we read values_secret_template - # and check if there are any missing secrets - if check_missing_secrets: - secret_obj.check_for_missing_secrets() - nr_secrets = secret_obj.inject_secrets() results["failed"] = False results["changed"] = True From 950f1ec318d36453542b43935da6b20cd09cee1e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 21 Nov 2022 00:09:55 +0100 Subject: [PATCH 0642/1263] Fix black errors --- ansible/plugins/module_utils/load_secrets_v2.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 4aad3f5b..f441f227 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -80,7 +80,10 @@ def _validate_field(self, f): ) if on_missing_value in ["generate"] and vault_policy is None: - return (False, f"Secret has no vaultPolicy but onMissingValue is set to 'generate'") + return ( + False, + "Secret has no vaultPolicy but onMissingValue is set to 'generate'", + ) return (True, "") From d8e1a994de925f3fab68b24b488e875a3896e4ce Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 21 Nov 2022 11:38:22 +0100 Subject: [PATCH 0643/1263] Add a test to cover for empty vaultPrefixes --- ansible/plugins/module_utils/load_secrets_v2.py | 12 +++++++++++- ansible/tests/unit/test_vault_load_secrets_v2.py | 15 +++++++++++++++ .../unit/v2/values-secret-v2-novaultprefix.yaml | 11 +++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index f441f227..af494c17 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -125,7 +125,11 @@ def _validate_secrets(self): try: _ = s[i] except KeyError: - return (False, f"Secret {s} is missing {i}") + return (False, f"Secret {s['name']} is missing {i}") + + vault_prefixes = s.get("vaultPrefixes", []) + if vault_prefixes is None or len(vault_prefixes) == 0: + return (False, f"Secret {s['name']} has empty vaultPrefixes") fields = s.get("fields", []) files = s.get("files", []) @@ -173,8 +177,14 @@ def sanitize_values(self): if not ret: self.module.fail_json(msg) + # This assumes that self.sanitize_values() has already been called + # so we do a lot less validation as it has already happened def inject_secrets(self): # This must come first as some passwords might depend on vault policies to exist. # It is a noop when no policies are defined self.inject_vault_policies() + secrets = self._get_secrets() + + #for s in secrets: + return 0 diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 7339300b..61d1f7e1 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -247,6 +247,21 @@ def test_ensure_error_file_wrongpath(self): self.assertEqual(ret["failed"], True) assert ret["args"][1] == "ca_crt has non-existing path: /tmp/nonexisting" + def test_ensure_error_empty_vaultprefix(self): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-novaultprefix.yaml" + ), + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert ret["args"][1] == "Secret config-demo has empty vaultPrefixes" + if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml b/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml new file mode 100644 index 00000000..04aaee0b --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml @@ -0,0 +1,11 @@ +# Needed to specify the new format (missing version means old version: 1.0 by default) +version: 2.0 +# NEVER COMMIT THESE VALUES TO GIT + +secrets: + - name: config-demo + vaultPrefixes: + fields: + - name: secret + value: value123 + onMissingValue: error # One of: error,generate,prompt From 1decb837b4c910bf4c53d0af76ab09726884ba67 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 21 Nov 2022 17:32:08 +0100 Subject: [PATCH 0644/1263] Add support and tests for the password generation via policy --- .../plugins/module_utils/load_secrets_v2.py | 50 ++++++++++++- .../tests/unit/test_vault_load_secrets_v2.py | 73 +++++++++++++++---- .../v2/values-secret-v2-onlygenerate.yaml | 36 +++++++++ 3 files changed, 144 insertions(+), 15 deletions(-) create mode 100644 ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index af494c17..7bc8e83c 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -17,6 +17,7 @@ Module that implements V2 of the values-secret.yaml spec """ +import getpass import os from ansible.module_utils.load_secrets_common import ( @@ -177,6 +178,38 @@ def sanitize_values(self): if not ret: self.module.fail_json(msg) + def _get_secret_value(self, name, field): + on_missing_value = self._get_field_on_missing_value(field) + # We cannot use match + case as RHEL8 has python 3.9 (it needs 3.10) + # We checked for errors in _validate_secrets() already + if on_missing_value == "error": + return field.get("value") + elif on_missing_value == "prompt": + return getpass.getpass(f"Type secret for {name}/{field['name']} : ") + return None + + def _inject_field(self, secret_name, f, mount, prefixes, first=False): + on_missing_value = self._get_field_on_missing_value(f) + # If we're generating the password then we just push the secret in the vault directly + verb = "put" if first else "patch" + if on_missing_value == "generate": + vault_policy = f.get("vaultPolicy") + gen_cmd = f"vault read -field=password sys/policies/password/{vault_policy}/generate" + for prefix in prefixes: + cmd = f"{gen_cmd} | vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=-" + run_command(cmd) + return + + # If we're not generating the secret inside the vault directly we either read it from the file ("error") + # or we are prompting the user for it + secret = self._get_secret_value(secret_name, f) + for prefix in prefixes: + cmd = f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}={secret}" + run_command(cmd) + + def _inject_file(self, secret_name, f, mount, prefixes, first=False): + return + # This assumes that self.sanitize_values() has already been called # so we do a lot less validation as it has already happened def inject_secrets(self): @@ -185,6 +218,19 @@ def inject_secrets(self): self.inject_vault_policies() secrets = self._get_secrets() - #for s in secrets: + counter = 0 + for s in secrets: + sname = s.get("name") + fields = s.get("fields", []) + files = s.get("files", []) + mount = s.get("vaultMount", "secret") + vault_prefixes = s.get("vaultPrefixes", []) + for i in fields: + self._inject_field(sname, i, mount, vault_prefixes, counter == 0) + counter += 1 + + for i in files: + self._inject_file(sname, i, mount, vault_prefixes, counter == 0) + counter += 1 - return 0 + return counter diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 61d1f7e1..b45c39ce 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -21,6 +21,7 @@ import os import sys import unittest +from unittest import mock from unittest.mock import call, patch from ansible.module_utils import basic @@ -73,6 +74,7 @@ def fail_json(*args, **kwargs): raise AnsibleFailJson(kwargs) +@mock.patch('getpass.getpass') class TestMyModule(unittest.TestCase): def setUp(self): self.mock_module_helper = patch.multiple( @@ -90,12 +92,12 @@ def tearDown(self): except OSError: pass - def test_module_fail_when_required_args_missing(self): + def test_module_fail_when_required_args_missing(self, getpass): with self.assertRaises(AnsibleFailJson): set_module_args({}) vault_load_secrets.main() - def test_module_fail_when_values_secret_not_existing(self): + def test_module_fail_when_values_secret_not_existing(self, getpass): with self.assertRaises(AnsibleExitJson) as ansible_err: set_module_args( { @@ -111,7 +113,7 @@ def test_module_fail_when_values_secret_not_existing(self): ret["msg"], "Values secrets file does not exist: /tmp/nonexisting" ) - def test_ensure_no_vault_policies_is_ok(self): + def test_ensure_no_vault_policies_is_ok(self, getpass): set_module_args( { "values_secrets": os.path.join( @@ -119,6 +121,7 @@ def test_ensure_no_vault_policies_is_ok(self): ), } ) + getpass.return_value = "foo" with patch.object(load_secrets_v2, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" @@ -130,9 +133,17 @@ def test_ensure_no_vault_policies_is_ok(self): self.assertTrue( result.exception.args[0]["changed"] ) # ensure result is changed - assert mock_run_command.call_count == 0 + assert mock_run_command.call_count == 4 - def test_ensure_policies_are_injected(self): + calls = [ + call('vault kv put -mount=secret secret/region-one/config-demo secret=value123'), + call('vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret=value123'), + call('vault kv patch -mount=secret secret/region-one/config-demo secret2=foo'), + call('vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret2=foo'), + ] + mock_run_command.assert_has_calls(calls) + + def test_ensure_policies_are_injected(self, getpass): set_module_args( { "values_secrets": os.path.join( @@ -151,7 +162,7 @@ def test_ensure_policies_are_injected(self): self.assertTrue( result.exception.args[0]["changed"] ) # ensure result is changed - assert mock_run_command.call_count == 2 + assert mock_run_command.call_count == 6 calls = [ call( @@ -163,9 +174,9 @@ def test_ensure_policies_are_injected(self): attempts=3, ), ] - mock_run_command.assert_has_calls(calls) + mock_run_command.assert_has_calls(calls, getpass) - def test_ensure_error_wrong_onmissing_value(self): + def test_ensure_error_wrong_onmissing_value(self, getpass): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -183,7 +194,7 @@ def test_ensure_error_wrong_onmissing_value(self): == "Secret has onMissingValue set to 'generate' or 'prompt' but has a value set" ) - def test_ensure_error_wrong_vaultpolicy(self): + def test_ensure_error_wrong_vaultpolicy(self, getpass): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -201,7 +212,7 @@ def test_ensure_error_wrong_vaultpolicy(self): == "Secret has vaultPolicy set to nonExisting but no such policy exists" ) - def test_ensure_error_file_wrong_onmissing_value(self): + def test_ensure_error_file_wrong_onmissing_value(self, getpass): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -217,7 +228,7 @@ def test_ensure_error_file_wrong_onmissing_value(self): self.assertEqual(ret["failed"], True) assert ret["args"][1] == "onMissingValue: generate is invalid" - def test_ensure_error_file_emptypath(self): + def test_ensure_error_file_emptypath(self, getpass): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -232,7 +243,7 @@ def test_ensure_error_file_emptypath(self): self.assertEqual(ret["failed"], True) assert ret["args"][1] == "ca_crt has unset path" - def test_ensure_error_file_wrongpath(self): + def test_ensure_error_file_wrongpath(self, getpass): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -247,7 +258,7 @@ def test_ensure_error_file_wrongpath(self): self.assertEqual(ret["failed"], True) assert ret["args"][1] == "ca_crt has non-existing path: /tmp/nonexisting" - def test_ensure_error_empty_vaultprefix(self): + def test_ensure_error_empty_vaultprefix(self, getpass): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -262,6 +273,42 @@ def test_ensure_error_empty_vaultprefix(self): self.assertEqual(ret["failed"], True) assert ret["args"][1] == "Secret config-demo has empty vaultPrefixes" + def test_ensure_only_generate_passwords_works(self, getpass): + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-onlygenerate.yaml" + ), + } + ) + with patch.object(load_secrets_v2, "run_command") as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 6 + + calls = [ + call( + 'echo \'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/basicPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/basicPolicy policy=@/tmp/basicPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/advancedPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call('vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=foo region-one/config-demo secret=-'), # noqa: E501 + call('vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=foo snowflake.blueprints.rhecoeng.com/config-demo secret=-'), # noqa: E501 + call('vault read -field=password sys/policies/password/advancedPolicy/generate | vault kv patch -mount=foo region-one/config-demo secret2=-'), # noqa: E501 + call('vault read -field=password sys/policies/password/advancedPolicy/generate | vault kv patch -mount=foo snowflake.blueprints.rhecoeng.com/config-demo secret2=-'), # noqa: E501 + ] + mock_run_command.assert_has_calls(calls) if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml b/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml new file mode 100644 index 00000000..e0e7808b --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml @@ -0,0 +1,36 @@ +# Needed to specify the new format (missing version means old version: 1.0 by default) +version: 2.0 +# NEVER COMMIT THESE VALUES TO GIT + +# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark +global: + backend: vault + +vaultPolicies: + # https://developer.hashicorp.com/vault/docs/concepts/password-policies + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + + advancedPolicy: | + length=20 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + +secrets: + - name: config-demo + vaultMount: foo + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: secret + onMissingValue: generate # One of: error,generate,prompt + vaultPolicy: basicPolicy + - name: secret2 + onMissingValue: generate + vaultPolicy: advancedPolicy From 19fdc2659b1d3bea7b9801880cfda1b30e31bb20 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 21 Nov 2022 17:49:17 +0100 Subject: [PATCH 0645/1263] Actually use the proper oc exec call to generate secrets --- .../plugins/module_utils/load_secrets_v2.py | 10 ++++-- .../tests/unit/test_vault_load_secrets_v2.py | 35 ++++++++++++++----- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 7bc8e83c..317f7915 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -196,7 +196,10 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): vault_policy = f.get("vaultPolicy") gen_cmd = f"vault read -field=password sys/policies/password/{vault_policy}/generate" for prefix in prefixes: - cmd = f"{gen_cmd} | vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=-" + cmd = ( + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f"\"{gen_cmd} | vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=-\"" + ) run_command(cmd) return @@ -204,7 +207,10 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): # or we are prompting the user for it secret = self._get_secret_value(secret_name, f) for prefix in prefixes: - cmd = f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}={secret}" + cmd = ( + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f"\"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}={secret}\"" + ) run_command(cmd) def _inject_file(self, secret_name, f, mount, prefixes, first=False): diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index b45c39ce..03541a80 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -74,7 +74,7 @@ def fail_json(*args, **kwargs): raise AnsibleFailJson(kwargs) -@mock.patch('getpass.getpass') +@mock.patch("getpass.getpass") class TestMyModule(unittest.TestCase): def setUp(self): self.mock_module_helper = patch.multiple( @@ -136,10 +136,18 @@ def test_ensure_no_vault_policies_is_ok(self, getpass): assert mock_run_command.call_count == 4 calls = [ - call('vault kv put -mount=secret secret/region-one/config-demo secret=value123'), - call('vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret=value123'), - call('vault kv patch -mount=secret secret/region-one/config-demo secret2=foo'), - call('vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret2=foo'), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv put -mount=secret secret/region-one/config-demo secret=value123"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret=value123"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret secret/region-one/config-demo secret2=foo"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret2=foo"' # noqa: E501 + ), ] mock_run_command.assert_has_calls(calls) @@ -303,12 +311,21 @@ def test_ensure_only_generate_passwords_works(self, getpass): 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/advancedPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl\'', # noqa: E501 attempts=3, ), - call('vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=foo region-one/config-demo secret=-'), # noqa: E501 - call('vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=foo snowflake.blueprints.rhecoeng.com/config-demo secret=-'), # noqa: E501 - call('vault read -field=password sys/policies/password/advancedPolicy/generate | vault kv patch -mount=foo region-one/config-demo secret2=-'), # noqa: E501 - call('vault read -field=password sys/policies/password/advancedPolicy/generate | vault kv patch -mount=foo snowflake.blueprints.rhecoeng.com/config-demo secret2=-'), # noqa: E501 + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=foo region-one/config-demo secret=-"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=foo snowflake.blueprints.rhecoeng.com/config-demo secret=-"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/advancedPolicy/generate | vault kv patch -mount=foo region-one/config-demo secret2=-"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/advancedPolicy/generate | vault kv patch -mount=foo snowflake.blueprints.rhecoeng.com/config-demo secret2=-"' # noqa: E501 + ), ] mock_run_command.assert_has_calls(calls) + if __name__ == "__main__": unittest.main() From cbbf686fc1419ad1c339d4d18bfcd0655e339aa3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 21 Nov 2022 18:53:41 +0100 Subject: [PATCH 0646/1263] Implement file uploading as base64 attribute as well --- .../plugins/module_utils/load_secrets_v2.py | 30 +++++++++++++- .../tests/unit/test_vault_load_secrets_v2.py | 40 +++++++++++-------- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 317f7915..516dcaf1 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -185,7 +185,15 @@ def _get_secret_value(self, name, field): if on_missing_value == "error": return field.get("value") elif on_missing_value == "prompt": - return getpass.getpass(f"Type secret for {name}/{field['name']} : ") + return getpass.getpass(f"Type secret for {name}/{field['name']}: ") + return None + + def _get_file_path(self, name, file): + on_missing_value = self._get_field_on_missing_value(file) + if on_missing_value == "error": + return file.get("path") + elif on_missing_value == "prompt": + return input(f"Type path for file {name}/{file['name']}: ") return None def _inject_field(self, secret_name, f, mount, prefixes, first=False): @@ -214,7 +222,25 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): run_command(cmd) def _inject_file(self, secret_name, f, mount, prefixes, first=False): - return + on_missing_value = self._get_field_on_missing_value(f) + # If we're generating the password then we just push the secret in the vault directly + verb = "put" if first else "patch" + tries = 2 + while tries > 0: + path = self._get_file_path(secret_name, f) + if os.path.isfile(os.path.expanduser(path)): + break + tries -= 1 + + for prefix in prefixes: + cmd = ( + f"cat '{path}' | oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f"'cat - > /tmp/vcontent'; " + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | " + f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=-; " + f"rm /tmp/vcontent'" + ) + run_command(cmd) # This assumes that self.sanitize_values() has already been called # so we do a lot less validation as it has already happened diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 03541a80..98f1aa4f 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -75,6 +75,7 @@ def fail_json(*args, **kwargs): @mock.patch("getpass.getpass") +@mock.patch("builtins.input") class TestMyModule(unittest.TestCase): def setUp(self): self.mock_module_helper = patch.multiple( @@ -92,12 +93,12 @@ def tearDown(self): except OSError: pass - def test_module_fail_when_required_args_missing(self, getpass): + def test_module_fail_when_required_args_missing(self, getpass, pathinput): with self.assertRaises(AnsibleFailJson): set_module_args({}) vault_load_secrets.main() - def test_module_fail_when_values_secret_not_existing(self, getpass): + def test_module_fail_when_values_secret_not_existing(self, getpass, pathinput): with self.assertRaises(AnsibleExitJson) as ansible_err: set_module_args( { @@ -113,7 +114,7 @@ def test_module_fail_when_values_secret_not_existing(self, getpass): ret["msg"], "Values secrets file does not exist: /tmp/nonexisting" ) - def test_ensure_no_vault_policies_is_ok(self, getpass): + def test_ensure_no_vault_policies_is_ok(self, getpass, pathinput): set_module_args( { "values_secrets": os.path.join( @@ -122,6 +123,7 @@ def test_ensure_no_vault_policies_is_ok(self, getpass): } ) getpass.return_value = "foo" + pathinput.return_value = "bar" with patch.object(load_secrets_v2, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" @@ -133,7 +135,7 @@ def test_ensure_no_vault_policies_is_ok(self, getpass): self.assertTrue( result.exception.args[0]["changed"] ) # ensure result is changed - assert mock_run_command.call_count == 4 + assert mock_run_command.call_count == 6 calls = [ call( @@ -143,15 +145,21 @@ def test_ensure_no_vault_policies_is_ok(self, getpass): 'oc exec -n vault vault-0 -i -- sh -c "vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret=value123"' # noqa: E501 ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret secret/region-one/config-demo secret2=foo"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret secret/region-one/config-demo secret2=bar"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret2=bar"' # noqa: E501 ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret2=foo"' # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/region-one/config-demo-file ca_crt=-; rm /tmp/vcontent'" # noqa: E501 + ), + call( + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file ca_crt=-; rm /tmp/vcontent'" # noqa: E501 ), ] mock_run_command.assert_has_calls(calls) - def test_ensure_policies_are_injected(self, getpass): + def test_ensure_policies_are_injected(self, getpass, pathinput): set_module_args( { "values_secrets": os.path.join( @@ -170,7 +178,7 @@ def test_ensure_policies_are_injected(self, getpass): self.assertTrue( result.exception.args[0]["changed"] ) # ensure result is changed - assert mock_run_command.call_count == 6 + assert mock_run_command.call_count == 10 calls = [ call( @@ -182,9 +190,9 @@ def test_ensure_policies_are_injected(self, getpass): attempts=3, ), ] - mock_run_command.assert_has_calls(calls, getpass) + mock_run_command.assert_has_calls(calls) - def test_ensure_error_wrong_onmissing_value(self, getpass): + def test_ensure_error_wrong_onmissing_value(self, getpass, pathinput): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -202,7 +210,7 @@ def test_ensure_error_wrong_onmissing_value(self, getpass): == "Secret has onMissingValue set to 'generate' or 'prompt' but has a value set" ) - def test_ensure_error_wrong_vaultpolicy(self, getpass): + def test_ensure_error_wrong_vaultpolicy(self, getpass, pathinput): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -220,7 +228,7 @@ def test_ensure_error_wrong_vaultpolicy(self, getpass): == "Secret has vaultPolicy set to nonExisting but no such policy exists" ) - def test_ensure_error_file_wrong_onmissing_value(self, getpass): + def test_ensure_error_file_wrong_onmissing_value(self, getpass, pathinput): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -236,7 +244,7 @@ def test_ensure_error_file_wrong_onmissing_value(self, getpass): self.assertEqual(ret["failed"], True) assert ret["args"][1] == "onMissingValue: generate is invalid" - def test_ensure_error_file_emptypath(self, getpass): + def test_ensure_error_file_emptypath(self, getpass, pathinput): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -251,7 +259,7 @@ def test_ensure_error_file_emptypath(self, getpass): self.assertEqual(ret["failed"], True) assert ret["args"][1] == "ca_crt has unset path" - def test_ensure_error_file_wrongpath(self, getpass): + def test_ensure_error_file_wrongpath(self, getpass, pathinput): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -266,7 +274,7 @@ def test_ensure_error_file_wrongpath(self, getpass): self.assertEqual(ret["failed"], True) assert ret["args"][1] == "ca_crt has non-existing path: /tmp/nonexisting" - def test_ensure_error_empty_vaultprefix(self, getpass): + def test_ensure_error_empty_vaultprefix(self, getpass, pathinput): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -281,7 +289,7 @@ def test_ensure_error_empty_vaultprefix(self, getpass): self.assertEqual(ret["failed"], True) assert ret["args"][1] == "Secret config-demo has empty vaultPrefixes" - def test_ensure_only_generate_passwords_works(self, getpass): + def test_ensure_only_generate_passwords_works(self, getpass, pathinput): set_module_args( { "values_secrets": os.path.join( From 4621e81a4bb0b0e0736facb930267e8fe41a40c8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 21 Nov 2022 19:08:25 +0100 Subject: [PATCH 0647/1263] Move more logic into _get_file_path --- .../plugins/module_utils/load_secrets_v2.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 516dcaf1..c5d41112 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -193,8 +193,14 @@ def _get_file_path(self, name, file): if on_missing_value == "error": return file.get("path") elif on_missing_value == "prompt": - return input(f"Type path for file {name}/{file['name']}: ") - return None + tries = 2 + while tries > 0: + path = input(f"Type path for file {name}/{file['name']}: ") + if os.path.isfile(os.path.expanduser(path)): + break + tries -= 1 + return path + self.module.fail_json("File with wrong onMissingValue") def _inject_field(self, secret_name, f, mount, prefixes, first=False): on_missing_value = self._get_field_on_missing_value(f) @@ -222,16 +228,9 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): run_command(cmd) def _inject_file(self, secret_name, f, mount, prefixes, first=False): - on_missing_value = self._get_field_on_missing_value(f) # If we're generating the password then we just push the secret in the vault directly verb = "put" if first else "patch" - tries = 2 - while tries > 0: - path = self._get_file_path(secret_name, f) - if os.path.isfile(os.path.expanduser(path)): - break - tries -= 1 - + path = self._get_file_path(secret_name, f) for prefix in prefixes: cmd = ( f"cat '{path}' | oc exec -n {self.namespace} {self.pod} -i -- sh -c " From 59005aa8963ec98013814b0e79dadeddebf0c91a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 08:25:16 +0100 Subject: [PATCH 0648/1263] Large rewrite to unify fields and files and add initial base64 support and tests --- .../module_utils/load_secrets_common.py | 91 ++++++++ .../plugins/module_utils/load_secrets_v2.py | 207 ++++++++++-------- .../tests/unit/test_vault_load_secrets_v2.py | 45 +++- .../tests/unit/v2/values-secret-v2-base.yaml | 14 +- .../v2/values-secret-v2-files-emptypath.yaml | 2 +- ...-secret-v2-files-wrong-onmissingvalue.yaml | 2 +- .../v2/values-secret-v2-files-wrongpath.yaml | 2 +- .../v2/values-secret-v2-generate-base64.yaml | 26 +++ .../unit/v2/values-secret-v2-nopolicies.yaml | 6 +- ...values-secret-v2-wrong-onmissingvalue.yaml | 1 - 10 files changed, 283 insertions(+), 113 deletions(-) create mode 100644 ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index b08b72f9..5d1bd749 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -17,14 +17,105 @@ Module that implements some common functions """ +import contextlib +import io import os import subprocess +import sys +import termios import time from collections.abc import MutableMapping import yaml +def _raw_input(prompt="", stream=None, input=None): + # This doesn't save the string in the GNU readline history. + if not stream: + stream = sys.stderr + if not input: + input = sys.stdin + prompt = str(prompt) + if prompt: + try: + stream.write(prompt) + except UnicodeEncodeError: + # Use replace error handler to get as much as possible printed. + prompt = prompt.encode(stream.encoding, "replace") + prompt = prompt.decode(stream.encoding) + stream.write(prompt) + stream.flush() + # NOTE: The Python C API calls flockfile() (and unlock) during readline. + line = input.readline() + if not line: + raise EOFError + if line[-1] == "\n": + line = line[:-1] + return line + + +def get_input(prompt="", stream=None): + """Prompt for a password, with echo turned off. + Args: + prompt: Written on stream to ask for the input. Default: 'Password: ' + stream: A writable file object to display the prompt. Defaults to + the tty. If no tty is available defaults to sys.stderr. + Returns: + The seKr3t input. + Raises: + EOFError: If our input tty or stdin was closed. + Always restores terminal settings before returning. + """ + passwd = None + with contextlib.ExitStack() as stack: + try: + # Always try reading and writing directly on the tty first. + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + tty = io.FileIO(fd, "w+") + stack.enter_context(tty) + input = io.TextIOWrapper(tty) + stack.enter_context(input) + if not stream: + stream = input + except OSError: + # If that fails, see if stdin can be controlled. + stack.close() + try: + fd = sys.stdin.fileno() + except (AttributeError, ValueError): + fd = None + input = sys.stdin + if not stream: + stream = sys.stderr + + if fd is not None: + try: + old = termios.tcgetattr(fd) # a copy to save + new = old[:] + # new[3] &= ~termios.ECHO # 3 == 'lflags' + tcsetattr_flags = termios.TCSAFLUSH + if hasattr(termios, "TCSASOFT"): + tcsetattr_flags |= termios.TCSASOFT + try: + termios.tcsetattr(fd, tcsetattr_flags, new) + passwd = _raw_input(prompt, stream, input=input) + finally: + termios.tcsetattr(fd, tcsetattr_flags, old) + stream.flush() # issue7208 + except termios.error: + if passwd is not None: + # _raw_input succeeded. The final tcsetattr failed. Reraise + # instead of leaving the terminal in an unknown state. + raise + # We can't control the tty or stdin. Give up and use normal IO. + if stream is not input: + # clean up unused file objects before blocking + stack.close() + + stream.write("\n") + return passwd + + def parse_values(values_file): """ Parses a values-secrets.yaml file (usually placed in ~) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index c5d41112..f05613d8 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -21,6 +21,7 @@ import os from ansible.module_utils.load_secrets_common import ( + get_input, get_version, parse_values, run_command, @@ -49,6 +50,24 @@ def _get_field_on_missing_value(self, f): def _get_field_value(self, f): return f.get("value", None) + def _get_field_path(self, f): + return f.get("path", None) + + def _get_field_kind(self, f): + value = self._get_field_value(f) + path = self._get_field_path(f) + if value is not None and path is not None: + return (False, "Both 'value' and 'path' cannot be used") + if path is not None: + return "path" + return "value" + + def _get_field_description(self, f): + return f.get("description", None) + + def _get_field_base64(self, f): + return bool(f.get("base64", False)) + def _validate_field(self, f): # These fields are mandatory try: @@ -61,17 +80,11 @@ def _validate_field(self, f): return (False, f"onMissingValue: {on_missing_value} is invalid") value = self._get_field_value(f) - if on_missing_value in ["error"] and (value is None or len(value) < 1): - return ( - False, - "Secret has onMissingValue set to 'error' and has no value set", - ) + path = self._get_field_path(f) + _ = self._get_field_kind(f) - if on_missing_value in ["generate", "prompt"] and value is not None: - return ( - False, - "Secret has onMissingValue set to 'generate' or 'prompt' but has a value set", - ) + # Test is base64 is a correct boolean (defaults to False) + _ = self._get_field_base64(f) vault_policy = f.get("vaultPolicy", None) if vault_policy is not None and vault_policy not in self._get_vault_policies(): @@ -80,38 +93,43 @@ def _validate_field(self, f): f"Secret has vaultPolicy set to {vault_policy} but no such policy exists", ) - if on_missing_value in ["generate"] and vault_policy is None: - return ( - False, - "Secret has no vaultPolicy but onMissingValue is set to 'generate'", - ) - - return (True, "") - - def _validate_file(self, f): - # These fields are mandatory - try: - name = f["name"] - except KeyError: - return (False, f"Field {f} is missing name") - - on_missing_value = self._get_field_on_missing_value(f) - if on_missing_value not in ["error", "prompt"]: - return (False, f"onMissingValue: {on_missing_value} is invalid") - - path = f.get("path", None) - if on_missing_value in ["error"] and path is None: - return (False, f"{name} has unset path") - - if on_missing_value in ["prompt"] and path is not None: - return (False, f"{name} has onMissingValue set to 'prompt' but path is set") + if on_missing_value in ["error"]: + if (value is None or len(value) < 1) and (path is None or len(path) < 1): + return ( + False, + "Secret has onMissingValue set to 'error' and has neither value nor path set", + ) + if path is not None and not os.path.isfile(os.path.expanduser(path)): + return (False, f"Field has non-existing path: {path}") + + if on_missing_value in ["generate"]: + if value is not None: + return ( + False, + "Secret has onMissingValue set to 'generate' but has a value set", + ) + if path is not None: + return ( + False, + "Secret has onMissingValue set to 'generate' but has a path set", + ) + if vault_policy is None: + return ( + False, + "Secret has no vaultPolicy but onMissingValue is set to 'generate'", + ) if on_missing_value in ["prompt"]: - # FIXME: implement proper prompting - path = "/tmp/ca.crt" - - if not os.path.isfile(os.path.expanduser(path)): - return (False, f"{name} has non-existing path: {path}") + if value is not None: + return ( + False, + "Secret has onMissingValue set to 'prompt' but has a value set", + ) + if value is None and path is not None: + return ( + False, + "Secret has onMissingValue set to 'prompt' but has value set to null and path set to a value", + ) return (True, "") @@ -133,19 +151,14 @@ def _validate_secrets(self): return (False, f"Secret {s['name']} has empty vaultPrefixes") fields = s.get("fields", []) - files = s.get("files", []) - if len(fields) == 0 and len(files) == 0: - return (False, f"Secret {s} does not have either fields nor files") + if len(fields) == 0: + return (False, f"Secret {s['name']} does not have any fields") for i in fields: (ret, msg) = self._validate_field(i) if not ret: return (False, msg) - for i in files: - (ret, msg) = self._validate_file(i) - if not ret: - return (False, msg) return (True, "") def inject_vault_policies(self): @@ -185,61 +198,78 @@ def _get_secret_value(self, name, field): if on_missing_value == "error": return field.get("value") elif on_missing_value == "prompt": - return getpass.getpass(f"Type secret for {name}/{field['name']}: ") + prompt = self._get_field_description(field) + if prompt is None: + prompt = f"Type secret for {name}/{field['name']}: " + return getpass.getpass(prompt) return None - def _get_file_path(self, name, file): - on_missing_value = self._get_field_on_missing_value(file) + def _get_file_path(self, name, field): + on_missing_value = self._get_field_on_missing_value(field) if on_missing_value == "error": - return file.get("path") + return field.get("path") elif on_missing_value == "prompt": - tries = 2 - while tries > 0: - path = input(f"Type path for file {name}/{file['name']}: ") - if os.path.isfile(os.path.expanduser(path)): - break - tries -= 1 - return path + prompt = self._get_field_description(field) + path = self._get_field_path(field) + if path is None: + path = "" + + if prompt is None: + text = f"Type path for file {name}/{field['name']} [{path}]: " + else: + text = f"{prompt} [{path}]: " + + path = get_input(text) + + if os.path.isfile(os.path.expanduser(path)): + return path + self.module.fail_json("File not found, exiting") + self.module.fail_json("File with wrong onMissingValue") def _inject_field(self, secret_name, f, mount, prefixes, first=False): on_missing_value = self._get_field_on_missing_value(f) + kind = self._get_field_kind(f) # If we're generating the password then we just push the secret in the vault directly verb = "put" if first else "patch" - if on_missing_value == "generate": - vault_policy = f.get("vaultPolicy") - gen_cmd = f"vault read -field=password sys/policies/password/{vault_policy}/generate" + b64 = self._get_field_base64(f) + if kind == "value": + if on_missing_value == "generate": + vault_policy = f.get("vaultPolicy") + gen_cmd = f"vault read -field=password sys/policies/password/{vault_policy}/generate" + if b64: + gen_cmd += " | base64 --wrap=0" + for prefix in prefixes: + cmd = ( + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f"\"{gen_cmd} | vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=-\"" + ) + run_command(cmd) + return + + # If we're not generating the secret inside the vault directly we either read it from the file ("error") + # or we are prompting the user for it + secret = self._get_secret_value(secret_name, f) for prefix in prefixes: cmd = ( f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " - f"\"{gen_cmd} | vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=-\"" + f"\"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}={secret}\"" ) run_command(cmd) - return - # If we're not generating the secret inside the vault directly we either read it from the file ("error") - # or we are prompting the user for it - secret = self._get_secret_value(secret_name, f) - for prefix in prefixes: - cmd = ( - f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " - f"\"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}={secret}\"" - ) - run_command(cmd) - - def _inject_file(self, secret_name, f, mount, prefixes, first=False): - # If we're generating the password then we just push the secret in the vault directly - verb = "put" if first else "patch" - path = self._get_file_path(secret_name, f) - for prefix in prefixes: - cmd = ( - f"cat '{path}' | oc exec -n {self.namespace} {self.pod} -i -- sh -c " - f"'cat - > /tmp/vcontent'; " - f"oc exec -n {self.namespace} {self.pod} -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | " - f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=-; " - f"rm /tmp/vcontent'" - ) - run_command(cmd) + else: # path. we upload files + # If we're generating the password then we just push the secret in the vault directly + verb = "put" if first else "patch" + path = self._get_file_path(secret_name, f) + for prefix in prefixes: + cmd = ( + f"cat '{path}' | oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f"'cat - > /tmp/vcontent'; " + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | " + f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=-; " + f"rm /tmp/vcontent'" + ) + run_command(cmd) # This assumes that self.sanitize_values() has already been called # so we do a lot less validation as it has already happened @@ -253,15 +283,10 @@ def inject_secrets(self): for s in secrets: sname = s.get("name") fields = s.get("fields", []) - files = s.get("files", []) mount = s.get("vaultMount", "secret") vault_prefixes = s.get("vaultPrefixes", []) for i in fields: self._inject_field(sname, i, mount, vault_prefixes, counter == 0) counter += 1 - for i in files: - self._inject_file(sname, i, mount, vault_prefixes, counter == 0) - counter += 1 - return counter diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 98f1aa4f..5321d713 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -151,7 +151,7 @@ def test_ensure_no_vault_policies_is_ok(self, getpass, pathinput): 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret2=bar"' # noqa: E501 ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/region-one/config-demo-file ca_crt=-; rm /tmp/vcontent'" # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/region-two/config-demo-file ca_crt=-; rm /tmp/vcontent'" # noqa: E501 ), call( "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file ca_crt=-; rm /tmp/vcontent'" # noqa: E501 @@ -207,7 +207,7 @@ def test_ensure_error_wrong_onmissing_value(self, getpass, pathinput): self.assertEqual(ret["failed"], True) assert ( ret["args"][1] - == "Secret has onMissingValue set to 'generate' or 'prompt' but has a value set" + == "Secret has vaultPolicy set to nonExisting but no such policy exists" ) def test_ensure_error_wrong_vaultpolicy(self, getpass, pathinput): @@ -242,7 +242,7 @@ def test_ensure_error_file_wrong_onmissing_value(self, getpass, pathinput): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert ret["args"][1] == "onMissingValue: generate is invalid" + assert ret["args"][1] == "Secret has onMissingValue set to 'generate' but has a path set" def test_ensure_error_file_emptypath(self, getpass, pathinput): with self.assertRaises(AnsibleFailJson) as ansible_err: @@ -257,7 +257,7 @@ def test_ensure_error_file_emptypath(self, getpass, pathinput): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert ret["args"][1] == "ca_crt has unset path" + assert ret["args"][1] == "Secret has onMissingValue set to 'error' and has neither value nor path set" def test_ensure_error_file_wrongpath(self, getpass, pathinput): with self.assertRaises(AnsibleFailJson) as ansible_err: @@ -272,7 +272,7 @@ def test_ensure_error_file_wrongpath(self, getpass, pathinput): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert ret["args"][1] == "ca_crt has non-existing path: /tmp/nonexisting" + assert ret["args"][1] == "Field has non-existing path: /tmp/nonexisting" def test_ensure_error_empty_vaultprefix(self, getpass, pathinput): with self.assertRaises(AnsibleFailJson) as ansible_err: @@ -334,6 +334,41 @@ def test_ensure_only_generate_passwords_works(self, getpass, pathinput): ] mock_run_command.assert_has_calls(calls) + def test_generate_password_base64_works(self, getpass, pathinput): + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-generate-base64.yaml" + ), + } + ) + with patch.object(load_secrets_v2, "run_command") as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 3 + + calls = [ + + call( + 'echo \'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/basicPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/basicPolicy policy=@/tmp/basicPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | base64 --wrap=0 | vault kv put -mount=secret region-one/config-demo secret=-"' + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | base64 --wrap=0 | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"' + ), + ] + mock_run_command.assert_has_calls(calls) if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-base.yaml b/ansible/tests/unit/v2/values-secret-v2-base.yaml index a3c45c80..0857cbb6 100644 --- a/ansible/tests/unit/v2/values-secret-v2-base.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-base.yaml @@ -24,24 +24,18 @@ vaultPolicies: secrets: - name: config-demo vaultPrefixes: - - secret/region-one - - secret/snowflake.blueprints.rhecoeng.com + - region-one + - snowflake.blueprints.rhecoeng.com fields: - name: secret onMissingValue: generate # One of: error,generate,prompt vaultPolicy: basicPolicy - name: secret2 onMissingValue: prompt - - name: config-demo-file - vaultPrefixes: - - secret/region-one - - secret/snowflake.blueprints.rhecoeng.com - - # Files are *always* base64 encoded first as the vault upload commands uses http+json - files: - name: ca_crt path: /tmp/ca.crt onMissingValue: error # One of error, prompt (for path). generate makes no sense for file - - name: ca_crt2 + path: null + base64: true # defaults to false onMissingValue: prompt # One of error, prompt (for path). generate makes no sense for file diff --git a/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml b/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml index 2c90be45..20cbaa41 100644 --- a/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml @@ -27,6 +27,6 @@ secrets: - secret/region-one - secret/snowflake.blueprints.rhecoeng.com # Files are *always* base64 encoded first as the vault upload commands uses http+json - files: + fields: - name: ca_crt onMissingValue: error # One of error, prompt (for path). generate makes no sense for file diff --git a/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml b/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml index f868569d..9972f7d0 100644 --- a/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml @@ -27,7 +27,7 @@ secrets: - secret/region-one - secret/snowflake.blueprints.rhecoeng.com # Files are *always* base64 encoded first as the vault upload commands uses http+json - files: + fields: - name: ca_crt path: /tmp/ca.crt onMissingValue: generate # One of error, prompt (for path). generate makes no sense for file diff --git a/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml b/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml index 9cad8bbd..157f2a67 100644 --- a/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml @@ -27,7 +27,7 @@ secrets: - secret/region-one - secret/snowflake.blueprints.rhecoeng.com # Files are *always* base64 encoded first as the vault upload commands uses http+json - files: + fields: - name: ca_crt path: /tmp/nonexisting onMissingValue: error # One of error, prompt (for path). generate makes no sense for file diff --git a/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml b/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml new file mode 100644 index 00000000..76eb0861 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml @@ -0,0 +1,26 @@ +# Needed to specify the new format (missing version means old version: 1.0 by default) +version: 2.0 +# NEVER COMMIT THESE VALUES TO GIT + +# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark +global: + backend: vault + +vaultPolicies: + # https://developer.hashicorp.com/vault/docs/concepts/password-policies + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + +secrets: + - name: config-demo + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: secret + onMissingValue: generate # One of: error,generate,prompt + base64: true # defaults to false + vaultPolicy: basicPolicy diff --git a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml index fb72f5da..d2fc4a7c 100644 --- a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml @@ -16,14 +16,14 @@ secrets: value: value123 onMissingValue: error # One of: error,generate,prompt - name: secret2 - value: null onMissingValue: prompt + - name: config-demo-file vaultPrefixes: - - secret/region-one + - secret/region-two - secret/snowflake.blueprints.rhecoeng.com - files: + fields: - name: ca_crt path: /tmp/ca.crt onMissingValue: error # One of error, prompt (for path). generate makes no sense for file diff --git a/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml b/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml index ba483c56..d8c6f44b 100644 --- a/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml @@ -21,6 +21,5 @@ secrets: - secret/snowflake.blueprints.rhecoeng.com fields: - name: secret - value: password123 onMissingValue: generate # One of: error,generate,prompt vaultPolicy: nonExisting From 84cd65d70195fc12f72d85d12c931eb646c6bf96 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 08:53:55 +0100 Subject: [PATCH 0649/1263] Add base64 support for secrets as well and fix up tests --- .../plugins/module_utils/load_secrets_v2.py | 11 +++- .../tests/unit/test_vault_load_secrets_v2.py | 54 ++++++++++++++----- .../unit/v2/values-secret-v2-nopolicies.yaml | 3 +- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index f05613d8..0ce8d6dd 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -17,6 +17,7 @@ Module that implements V2 of the values-secret.yaml spec """ +import base64 import getpass import os @@ -250,6 +251,8 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): # If we're not generating the secret inside the vault directly we either read it from the file ("error") # or we are prompting the user for it secret = self._get_secret_value(secret_name, f) + if b64: + secret = base64.b64encode(secret.encode()) for prefix in prefixes: cmd = ( f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " @@ -262,11 +265,15 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): verb = "put" if first else "patch" path = self._get_file_path(secret_name, f) for prefix in prefixes: + if b64: + b64_cmd = "base64 --wrap=0 /tmp/vcontent | " + else: + b64_cmd = "" cmd = ( f"cat '{path}' | oc exec -n {self.namespace} {self.pod} -i -- sh -c " f"'cat - > /tmp/vcontent'; " - f"oc exec -n {self.namespace} {self.pod} -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | " - f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=-; " + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c '{b64_cmd}" + f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=/tmp/vcontent; " f"rm /tmp/vcontent'" ) run_command(cmd) diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 5321d713..458d2baf 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -135,7 +135,7 @@ def test_ensure_no_vault_policies_is_ok(self, getpass, pathinput): self.assertTrue( result.exception.args[0]["changed"] ) # ensure result is changed - assert mock_run_command.call_count == 6 + assert mock_run_command.call_count == 4 calls = [ call( @@ -145,16 +145,10 @@ def test_ensure_no_vault_policies_is_ok(self, getpass, pathinput): 'oc exec -n vault vault-0 -i -- sh -c "vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret=value123"' # noqa: E501 ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret secret/region-one/config-demo secret2=bar"' # noqa: E501 - ), - call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret2=bar"' # noqa: E501 - ), - call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/region-two/config-demo-file ca_crt=-; rm /tmp/vcontent'" # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/region-two/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file ca_crt=-; rm /tmp/vcontent'" # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 ), ] mock_run_command.assert_has_calls(calls) @@ -167,6 +161,8 @@ def test_ensure_policies_are_injected(self, getpass, pathinput): ), } ) + getpass.return_value = "foo" + pathinput.return_value = "bar" with patch.object(load_secrets_v2, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" @@ -189,6 +185,30 @@ def test_ensure_policies_are_injected(self, getpass, pathinput): 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/advancedPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl\'', # noqa: E501 attempts=3, ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret region-one/config-demo secret=-"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret region-one/config-demo secret2=bar"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret2=bar"' # noqa: E501 + ), + call( + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret region-one/config-demo ca_crt=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 + ), + call( + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv patch -mount=secret region-one/config-demo ca_crt2=b'YmFy'\"" # noqa: E501 + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt2=b'YmFy'\"" # noqa: E501 + ), ] mock_run_command.assert_has_calls(calls) @@ -242,7 +262,10 @@ def test_ensure_error_file_wrong_onmissing_value(self, getpass, pathinput): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert ret["args"][1] == "Secret has onMissingValue set to 'generate' but has a path set" + assert ( + ret["args"][1] + == "Secret has onMissingValue set to 'generate' but has a path set" + ) def test_ensure_error_file_emptypath(self, getpass, pathinput): with self.assertRaises(AnsibleFailJson) as ansible_err: @@ -257,7 +280,10 @@ def test_ensure_error_file_emptypath(self, getpass, pathinput): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert ret["args"][1] == "Secret has onMissingValue set to 'error' and has neither value nor path set" + assert ( + ret["args"][1] + == "Secret has onMissingValue set to 'error' and has neither value nor path set" + ) def test_ensure_error_file_wrongpath(self, getpass, pathinput): with self.assertRaises(AnsibleFailJson) as ansible_err: @@ -356,19 +382,19 @@ def test_generate_password_base64_works(self, getpass, pathinput): assert mock_run_command.call_count == 3 calls = [ - call( 'echo \'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/basicPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/basicPolicy policy=@/tmp/basicPolicy.hcl\'', # noqa: E501 attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | base64 --wrap=0 | vault kv put -mount=secret region-one/config-demo secret=-"' + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | base64 --wrap=0 | vault kv put -mount=secret region-one/config-demo secret=-"' # noqa: E501 ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | base64 --wrap=0 | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"' + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | base64 --wrap=0 | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"' # noqa: E501 ), ] mock_run_command.assert_has_calls(calls) + if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml index d2fc4a7c..96fdbc9c 100644 --- a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml @@ -15,8 +15,6 @@ secrets: - name: secret value: value123 onMissingValue: error # One of: error,generate,prompt - - name: secret2 - onMissingValue: prompt - name: config-demo-file vaultPrefixes: @@ -26,4 +24,5 @@ secrets: fields: - name: ca_crt path: /tmp/ca.crt + base64: true onMissingValue: error # One of error, prompt (for path). generate makes no sense for file From 540e25ab86ca027a5ef7b309a0f62c17f9c84809 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 10:14:57 +0100 Subject: [PATCH 0650/1263] POC mostly working at this point --- .../plugins/module_utils/load_secrets_v2.py | 47 ++++++++++++------- .../tests/unit/v2/values-secret-v2-base.yaml | 1 + 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 0ce8d6dd..238d14d3 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -55,13 +55,18 @@ def _get_field_path(self, f): return f.get("path", None) def _get_field_kind(self, f): - value = self._get_field_value(f) - path = self._get_field_path(f) - if value is not None and path is not None: - return (False, "Both 'value' and 'path' cannot be used") - if path is not None: + # value: null will be interpreted with None, so let's just + # check for the existence of the field, as we use 'value: null' to say + # "we want a value/secret and not a file path" + if "value" in f and "path" in f: + self.module.fail_json("Both 'value' and 'path' cannot be used") + + if "value" in f: + return "value" + elif "path" in f: return "path" - return "value" + + return "" def _get_field_description(self, f): return f.get("description", None) @@ -121,15 +126,15 @@ def _validate_field(self, f): ) if on_missing_value in ["prompt"]: - if value is not None: + # When we prompt, the user needs to set one of the following: + # - value: null # prompt for a secret without a default value + # - value: 123 # prompt for a secret but use a default value + # - path: null # prompt for a file path without a default value + # - path: /tmp/ca.crt # prompt for a file path with a default value + if "value" not in f and "path" not in f: return ( False, - "Secret has onMissingValue set to 'prompt' but has a value set", - ) - if value is None and path is not None: - return ( - False, - "Secret has onMissingValue set to 'prompt' but has value set to null and path set to a value", + "Secret has onMissingValue set to 'prompt' but has no value nor path fields", ) return (True, "") @@ -220,11 +225,13 @@ def _get_file_path(self, name, field): else: text = f"{prompt} [{path}]: " - path = get_input(text) + newpath = get_input(text) + if newpath == "": # Set the default if no string was entered + newpath = path - if os.path.isfile(os.path.expanduser(path)): - return path - self.module.fail_json("File not found, exiting") + if os.path.isfile(os.path.expanduser(newpath)): + return newpath + self.module.fail_json(f"File {newpath} not found, exiting") self.module.fail_json("File with wrong onMissingValue") @@ -234,8 +241,12 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): # If we're generating the password then we just push the secret in the vault directly verb = "put" if first else "patch" b64 = self._get_field_base64(f) - if kind == "value": + if kind in ["value", ""]: if on_missing_value == "generate": + if kind == "path": + self.module.fail_json( + "You cannot have onMissingValue set to 'generate' and have 'path' too" + ) vault_policy = f.get("vaultPolicy") gen_cmd = f"vault read -field=password sys/policies/password/{vault_policy}/generate" if b64: diff --git a/ansible/tests/unit/v2/values-secret-v2-base.yaml b/ansible/tests/unit/v2/values-secret-v2-base.yaml index 0857cbb6..c9d70918 100644 --- a/ansible/tests/unit/v2/values-secret-v2-base.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-base.yaml @@ -31,6 +31,7 @@ secrets: onMissingValue: generate # One of: error,generate,prompt vaultPolicy: basicPolicy - name: secret2 + value: null onMissingValue: prompt - name: ca_crt path: /tmp/ca.crt From ec1d464e9a51f9232802f5936774cf04a2e8e6bb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 11:18:11 +0100 Subject: [PATCH 0651/1263] For the time being always stay on getpass.getpass() for input --- .../module_utils/load_secrets_common.py | 87 ------------------- .../plugins/module_utils/load_secrets_v2.py | 5 +- .../tests/unit/test_vault_load_secrets_v2.py | 38 ++++---- 3 files changed, 20 insertions(+), 110 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index 5d1bd749..99e56b0a 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -29,93 +29,6 @@ import yaml -def _raw_input(prompt="", stream=None, input=None): - # This doesn't save the string in the GNU readline history. - if not stream: - stream = sys.stderr - if not input: - input = sys.stdin - prompt = str(prompt) - if prompt: - try: - stream.write(prompt) - except UnicodeEncodeError: - # Use replace error handler to get as much as possible printed. - prompt = prompt.encode(stream.encoding, "replace") - prompt = prompt.decode(stream.encoding) - stream.write(prompt) - stream.flush() - # NOTE: The Python C API calls flockfile() (and unlock) during readline. - line = input.readline() - if not line: - raise EOFError - if line[-1] == "\n": - line = line[:-1] - return line - - -def get_input(prompt="", stream=None): - """Prompt for a password, with echo turned off. - Args: - prompt: Written on stream to ask for the input. Default: 'Password: ' - stream: A writable file object to display the prompt. Defaults to - the tty. If no tty is available defaults to sys.stderr. - Returns: - The seKr3t input. - Raises: - EOFError: If our input tty or stdin was closed. - Always restores terminal settings before returning. - """ - passwd = None - with contextlib.ExitStack() as stack: - try: - # Always try reading and writing directly on the tty first. - fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) - tty = io.FileIO(fd, "w+") - stack.enter_context(tty) - input = io.TextIOWrapper(tty) - stack.enter_context(input) - if not stream: - stream = input - except OSError: - # If that fails, see if stdin can be controlled. - stack.close() - try: - fd = sys.stdin.fileno() - except (AttributeError, ValueError): - fd = None - input = sys.stdin - if not stream: - stream = sys.stderr - - if fd is not None: - try: - old = termios.tcgetattr(fd) # a copy to save - new = old[:] - # new[3] &= ~termios.ECHO # 3 == 'lflags' - tcsetattr_flags = termios.TCSAFLUSH - if hasattr(termios, "TCSASOFT"): - tcsetattr_flags |= termios.TCSASOFT - try: - termios.tcsetattr(fd, tcsetattr_flags, new) - passwd = _raw_input(prompt, stream, input=input) - finally: - termios.tcsetattr(fd, tcsetattr_flags, old) - stream.flush() # issue7208 - except termios.error: - if passwd is not None: - # _raw_input succeeded. The final tcsetattr failed. Reraise - # instead of leaving the terminal in an unknown state. - raise - # We can't control the tty or stdin. Give up and use normal IO. - if stream is not input: - # clean up unused file objects before blocking - stack.close() - - stream.write("\n") - return passwd - - def parse_values(values_file): """ Parses a values-secrets.yaml file (usually placed in ~) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 238d14d3..73f6c53d 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -22,7 +22,6 @@ import os from ansible.module_utils.load_secrets_common import ( - get_input, get_version, parse_values, run_command, @@ -225,7 +224,7 @@ def _get_file_path(self, name, field): else: text = f"{prompt} [{path}]: " - newpath = get_input(text) + newpath = getpass.getpass(text) if newpath == "": # Set the default if no string was entered newpath = path @@ -245,7 +244,7 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): if on_missing_value == "generate": if kind == "path": self.module.fail_json( - "You cannot have onMissingValue set to 'generate' and have 'path' too" + "You cannot have onMissingValue set to 'generate' with a path" ) vault_policy = f.get("vaultPolicy") gen_cmd = f"vault read -field=password sys/policies/password/{vault_policy}/generate" diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 458d2baf..0b1b882a 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -75,7 +75,6 @@ def fail_json(*args, **kwargs): @mock.patch("getpass.getpass") -@mock.patch("builtins.input") class TestMyModule(unittest.TestCase): def setUp(self): self.mock_module_helper = patch.multiple( @@ -93,12 +92,12 @@ def tearDown(self): except OSError: pass - def test_module_fail_when_required_args_missing(self, getpass, pathinput): + def test_module_fail_when_required_args_missing(self, getpass): with self.assertRaises(AnsibleFailJson): set_module_args({}) vault_load_secrets.main() - def test_module_fail_when_values_secret_not_existing(self, getpass, pathinput): + def test_module_fail_when_values_secret_not_existing(self, getpass): with self.assertRaises(AnsibleExitJson) as ansible_err: set_module_args( { @@ -114,7 +113,7 @@ def test_module_fail_when_values_secret_not_existing(self, getpass, pathinput): ret["msg"], "Values secrets file does not exist: /tmp/nonexisting" ) - def test_ensure_no_vault_policies_is_ok(self, getpass, pathinput): + def test_ensure_no_vault_policies_is_ok(self, getpass): set_module_args( { "values_secrets": os.path.join( @@ -123,7 +122,6 @@ def test_ensure_no_vault_policies_is_ok(self, getpass, pathinput): } ) getpass.return_value = "foo" - pathinput.return_value = "bar" with patch.object(load_secrets_v2, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" @@ -153,7 +151,7 @@ def test_ensure_no_vault_policies_is_ok(self, getpass, pathinput): ] mock_run_command.assert_has_calls(calls) - def test_ensure_policies_are_injected(self, getpass, pathinput): + def test_ensure_policies_are_injected(self, getpass): set_module_args( { "values_secrets": os.path.join( @@ -161,8 +159,8 @@ def test_ensure_policies_are_injected(self, getpass, pathinput): ), } ) - getpass.return_value = "foo" - pathinput.return_value = "bar" + # this will be used for both a secret and a file path + getpass.return_value = "/tmp/ca.crt" with patch.object(load_secrets_v2, "run_command") as mock_run_command: stdout = "configuration updated" stderr = "" @@ -192,10 +190,10 @@ def test_ensure_policies_are_injected(self, getpass, pathinput): 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"' # noqa: E501 ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret region-one/config-demo secret2=bar"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret region-one/config-demo secret2=/tmp/ca.crt"' # noqa: E501 ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret2=bar"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret2=/tmp/ca.crt"' # noqa: E501 ), call( "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret region-one/config-demo ca_crt=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 @@ -204,15 +202,15 @@ def test_ensure_policies_are_injected(self, getpass, pathinput): "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv patch -mount=secret region-one/config-demo ca_crt2=b'YmFy'\"" # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret region-one/config-demo ca_crt2=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt2=b'YmFy'\"" # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt2=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 ), ] mock_run_command.assert_has_calls(calls) - def test_ensure_error_wrong_onmissing_value(self, getpass, pathinput): + def test_ensure_error_wrong_onmissing_value(self, getpass): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -230,7 +228,7 @@ def test_ensure_error_wrong_onmissing_value(self, getpass, pathinput): == "Secret has vaultPolicy set to nonExisting but no such policy exists" ) - def test_ensure_error_wrong_vaultpolicy(self, getpass, pathinput): + def test_ensure_error_wrong_vaultpolicy(self, getpass): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -248,7 +246,7 @@ def test_ensure_error_wrong_vaultpolicy(self, getpass, pathinput): == "Secret has vaultPolicy set to nonExisting but no such policy exists" ) - def test_ensure_error_file_wrong_onmissing_value(self, getpass, pathinput): + def test_ensure_error_file_wrong_onmissing_value(self, getpass): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -267,7 +265,7 @@ def test_ensure_error_file_wrong_onmissing_value(self, getpass, pathinput): == "Secret has onMissingValue set to 'generate' but has a path set" ) - def test_ensure_error_file_emptypath(self, getpass, pathinput): + def test_ensure_error_file_emptypath(self, getpass): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -285,7 +283,7 @@ def test_ensure_error_file_emptypath(self, getpass, pathinput): == "Secret has onMissingValue set to 'error' and has neither value nor path set" ) - def test_ensure_error_file_wrongpath(self, getpass, pathinput): + def test_ensure_error_file_wrongpath(self, getpass): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -300,7 +298,7 @@ def test_ensure_error_file_wrongpath(self, getpass, pathinput): self.assertEqual(ret["failed"], True) assert ret["args"][1] == "Field has non-existing path: /tmp/nonexisting" - def test_ensure_error_empty_vaultprefix(self, getpass, pathinput): + def test_ensure_error_empty_vaultprefix(self, getpass): with self.assertRaises(AnsibleFailJson) as ansible_err: set_module_args( { @@ -315,7 +313,7 @@ def test_ensure_error_empty_vaultprefix(self, getpass, pathinput): self.assertEqual(ret["failed"], True) assert ret["args"][1] == "Secret config-demo has empty vaultPrefixes" - def test_ensure_only_generate_passwords_works(self, getpass, pathinput): + def test_ensure_only_generate_passwords_works(self, getpass): set_module_args( { "values_secrets": os.path.join( @@ -360,7 +358,7 @@ def test_ensure_only_generate_passwords_works(self, getpass, pathinput): ] mock_run_command.assert_has_calls(calls) - def test_generate_password_base64_works(self, getpass, pathinput): + def test_generate_password_base64_works(self, getpass): set_module_args( { "values_secrets": os.path.join( From d453f882b0050482e6bd733b1cb5d03ff43f4103 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 11:34:42 +0100 Subject: [PATCH 0652/1263] Remove unused modules --- ansible/plugins/module_utils/load_secrets_common.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index 99e56b0a..b08b72f9 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -17,12 +17,8 @@ Module that implements some common functions """ -import contextlib -import io import os import subprocess -import sys -import termios import time from collections.abc import MutableMapping From ac4c3ad4f377c0977d1f3b5830821addbce30b6d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 11:59:15 +0100 Subject: [PATCH 0653/1263] Add checks for duplicate secret names and field names --- .../module_utils/load_secrets_common.py | 20 +++++++++++ .../plugins/module_utils/load_secrets_v2.py | 13 ++++++- .../tests/unit/test_vault_load_secrets_v2.py | 35 +++++++++++++++++++ .../v2/values-secret-v2-same-field-names.yaml | 14 ++++++++ .../values-secret-v2-same-secret-names.yaml | 20 +++++++++++ 5 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 ansible/tests/unit/v2/values-secret-v2-same-field-names.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-same-secret-names.yaml diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index b08b72f9..be5b478e 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -25,6 +25,26 @@ import yaml +def find_dupes(l): + """ + Returns duplicate items in a list + + Parameters: + l(list): Array to check for duplicate entries + + Returns: + dupes(list): Array containing all the duplicates and [] is there are none + """ + seen = set() + dupes = [] + for x in l: + if x in seen: + dupes.append(x) + else: + seen.add(x) + return dupes + + def parse_values(values_file): """ Parses a values-secrets.yaml file (usually placed in ~) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 73f6c53d..794e35b2 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -22,6 +22,7 @@ import os from ansible.module_utils.load_secrets_common import ( + find_dupes, get_version, parse_values, run_command, @@ -143,6 +144,7 @@ def _validate_secrets(self): if len(secrets) == 0: self.module.fail_json("No secrets found") + names = [] for s in secrets: # These fields are mandatory for i in ["name", "vaultPrefixes"]: @@ -150,6 +152,7 @@ def _validate_secrets(self): _ = s[i] except KeyError: return (False, f"Secret {s['name']} is missing {i}") + names.append(s["name"]) vault_prefixes = s.get("vaultPrefixes", []) if vault_prefixes is None or len(vault_prefixes) == 0: @@ -159,11 +162,19 @@ def _validate_secrets(self): if len(fields) == 0: return (False, f"Secret {s['name']} does not have any fields") + field_names = [] for i in fields: (ret, msg) = self._validate_field(i) if not ret: return (False, msg) - + field_names.append(i['name']) + field_dupes = find_dupes(field_names) + if len(field_dupes) > 0: + return (False, f"You cannot have duplicate field names: {field_dupes}") + + dupes = find_dupes(names) + if len(dupes) > 0: + return (False, f"You cannot have duplicate secret names: {dupes}") return (True, "") def inject_vault_policies(self): diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 0b1b882a..3a721e23 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -393,6 +393,41 @@ def test_generate_password_base64_works(self, getpass): ] mock_run_command.assert_has_calls(calls) + def test_ensure_error_secrets_same_name(self, getpass): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-same-secret-names.yaml" + ), + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert ( + ret["args"][1] + == "You cannot have duplicate secret names: ['config-demo']" + ) + + def test_ensure_error_fields_same_name(self, getpass): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-same-field-names.yaml" + ), + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert ( + ret["args"][1] + == "You cannot have duplicate field names: ['secret']" + ) if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-same-field-names.yaml b/ansible/tests/unit/v2/values-secret-v2-same-field-names.yaml new file mode 100644 index 00000000..fb163f3a --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-same-field-names.yaml @@ -0,0 +1,14 @@ +version: 2.0 + +secrets: + - name: config-demo + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: secret + value: foo + onMissingValue: error + - name: secret + value: bar + onMissingValue: prompt diff --git a/ansible/tests/unit/v2/values-secret-v2-same-secret-names.yaml b/ansible/tests/unit/v2/values-secret-v2-same-secret-names.yaml new file mode 100644 index 00000000..ccc036ec --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-same-secret-names.yaml @@ -0,0 +1,20 @@ +version: 2.0 + +secrets: + - name: config-demo + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: secret + value: foo + onMissingValue: error + + - name: config-demo + vaultPrefixes: + - region-two + - snowflake.blueprints.rhecoeng.com + fields: + - name: secret2 + value: bar + onMissingValue: prompt From 5881ec3a31aa8d229888c84aab5759ead5aec772 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 13:36:23 +0100 Subject: [PATCH 0654/1263] Switch to using AnsibleModule.run_command --- .../module_utils/load_secrets_common.py | 39 +------------------ .../plugins/module_utils/load_secrets_v1.py | 38 ++++++++++++++---- .../plugins/module_utils/load_secrets_v2.py | 38 +++++++++++++++--- ansible/tests/unit/test_vault_load_secrets.py | 24 +++++++++--- .../tests/unit/test_vault_load_secrets_v2.py | 25 +++++++----- 5 files changed, 97 insertions(+), 67 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index be5b478e..ba2ec312 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -17,15 +17,12 @@ Module that implements some common functions """ -import os -import subprocess -import time from collections.abc import MutableMapping import yaml -def find_dupes(l): +def find_dupes(array): """ Returns duplicate items in a list @@ -37,7 +34,7 @@ def find_dupes(l): """ seen = set() dupes = [] - for x in l: + for x in array: if x in seen: dupes.append(x) else: @@ -75,38 +72,6 @@ def get_version(syaml): return str(syaml.get("version", "1.0")) -def run_command(command, attempts=1, sleep=3): - """ - Runs a command on the host ansible is running on. A failing command - will raise an exception in this function directly (due to check=True) - - Parameters: - command(str): The command to be run. - attempts(int): Number of times to retry in case of Error (defaults to 1) - sleep(int): Number of seconds to wait in between retry attempts (defaults to 3s) - - Returns: - ret(subprocess.CompletedProcess): The return value from run() - """ - for attempt in range(attempts): - try: - ret = subprocess.run( - command, - shell=True, - env=os.environ.copy(), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - check=True, - ) - return ret - except subprocess.CalledProcessError as e: - # We reached maximum nr of retries. Re-raise the last error - if attempt >= attempts - 1: - raise e - time.sleep(sleep) - - def flatten(dictionary, parent_key=False, separator="."): """ Turn a nested dictionary into a flattened dictionary and also diff --git a/ansible/plugins/module_utils/load_secrets_v1.py b/ansible/plugins/module_utils/load_secrets_v1.py index 6a106905..f2879641 100644 --- a/ansible/plugins/module_utils/load_secrets_v1.py +++ b/ansible/plugins/module_utils/load_secrets_v1.py @@ -19,14 +19,10 @@ import base64 import os +import time import yaml -from ansible.module_utils.load_secrets_common import ( - flatten, - get_version, - parse_values, - run_command, -) +from ansible.module_utils.load_secrets_common import flatten, get_version, parse_values class LoadSecretsV1: @@ -40,6 +36,32 @@ def __init__( self.values_secret_template = values_secret_template self.syaml = parse_values(values_secrets) + def _run_command(self, command, attempts=1, sleep=3): + """ + Runs a command on the host ansible is running on. A failing command + will raise an exception in this function directly (due to check=True) + + Parameters: + command(str): The command to be run. + attempts(int): Number of times to retry in case of Error (defaults to 1) + sleep(int): Number of seconds to wait in between retry attempts (defaults to 3s) + + Returns: + ret(subprocess.CompletedProcess): The return value from run() + """ + for attempt in range(attempts): + ret = self.module.run_command( + command, + check_rc=True, + use_unsafe_shell=True, + environ_update=os.environ.copy(), + ) + if ret[0] == 0: + return ret + if attempt >= attempts - 1: + return ret + time.sleep(sleep) + def sanitize_values(self): """ Sanitizes the secrets YAML object. If a specific secret key has @@ -189,7 +211,7 @@ def inject_secrets(self): f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " f"\"vault kv put '{path}/{secret}' {properties}\"" ) - run_command(cmd, attempts=3) + self._run_command(cmd, attempts=3) counter += 1 for i in self.get_secrets_vault_paths("files"): @@ -203,7 +225,7 @@ def inject_secrets(self): f"vault kv put {path}/{filekey} b64content=- content=@/tmp/vcontent; " f"rm /tmp/vcontent'" ) - run_command(cmd, attempts=3) + self._run_command(cmd, attempts=3) counter += 1 return counter diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 794e35b2..006b4851 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -20,12 +20,12 @@ import base64 import getpass import os +import time from ansible.module_utils.load_secrets_common import ( find_dupes, get_version, parse_values, - run_command, ) @@ -37,6 +37,32 @@ def __init__(self, module, values_secrets, namespace, pod, values_secret_templat self.values_secret_template = values_secret_template self.syaml = parse_values(values_secrets) + def _run_command(self, command, attempts=1, sleep=3): + """ + Runs a command on the host ansible is running on. A failing command + will raise an exception in this function directly (due to check=True) + + Parameters: + command(str): The command to be run. + attempts(int): Number of times to retry in case of Error (defaults to 1) + sleep(int): Number of seconds to wait in between retry attempts (defaults to 3s) + + Returns: + ret(subprocess.CompletedProcess): The return value from run() + """ + for attempt in range(attempts): + ret = self.module.run_command( + command, + check_rc=True, + use_unsafe_shell=True, + environ_update=os.environ.copy(), + ) + if ret[0] == 0: + return ret + if attempt >= attempts - 1: + return ret + time.sleep(sleep) + def _get_vault_policies(self): return self.syaml.get("vaultPolicies", {}) @@ -167,7 +193,7 @@ def _validate_secrets(self): (ret, msg) = self._validate_field(i) if not ret: return (False, msg) - field_names.append(i['name']) + field_names.append(i["name"]) field_dupes = find_dupes(field_names) if len(field_dupes) > 0: return (False, f"You cannot have duplicate field names: {field_dupes}") @@ -185,7 +211,7 @@ def inject_vault_policies(self): f"oc exec -n {self.namespace} {self.pod} -i -- sh -c 'vault write sys/policies/password/{name} " f" policy=@/tmp/{name}.hcl'" ) - run_command(cmd, attempts=3) + self._run_command(cmd, attempts=3) def sanitize_values(self): """ @@ -266,7 +292,7 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " f"\"{gen_cmd} | vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=-\"" ) - run_command(cmd) + self._run_command(cmd) return # If we're not generating the secret inside the vault directly we either read it from the file ("error") @@ -279,7 +305,7 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " f"\"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}={secret}\"" ) - run_command(cmd) + self._run_command(cmd) else: # path. we upload files # If we're generating the password then we just push the secret in the vault directly @@ -297,7 +323,7 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=/tmp/vcontent; " f"rm /tmp/vcontent'" ) - run_command(cmd) + self._run_command(cmd) # This assumes that self.sanitize_values() has already been called # so we do a lot less validation as it has already happened diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 0a2f7778..e4c7f310 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -121,7 +121,9 @@ def test_ensure_empty_files_but_not_secrets_is_ok(self): } ) - with patch.object(load_secrets_v1, "run_command") as mock_run_command: + with patch.object( + load_secrets_v1.LoadSecretsV1, "_run_command" + ) as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -169,7 +171,9 @@ def test_ensure_empty_secrets_but_not_files_is_ok(self): } ) - with patch.object(load_secrets_v1, "run_command") as mock_run_command: + with patch.object( + load_secrets_v1.LoadSecretsV1, "_run_command" + ) as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -195,7 +199,9 @@ def test_ensure_command_called(self): {"values_secrets": os.path.join(self.testdir_v1, "values-secret-good.yaml")} ) - with patch.object(load_secrets_v1, "run_command") as mock_run_command: + with patch.object( + load_secrets_v1.LoadSecretsV1, "_run_command" + ) as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -260,7 +266,9 @@ def test_ensure_good_template_checking(self): ), } ) - with patch.object(load_secrets_v1, "run_command") as mock_run_command: + with patch.object( + load_secrets_v1.LoadSecretsV1, "_run_command" + ) as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -293,7 +301,9 @@ def test_ensure_bad_template_checking(self): ), } ) - with patch.object(load_secrets_v1, "run_command") as mock_run_command: + with patch.object( + load_secrets_v1.LoadSecretsV1, "_run_command" + ) as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -314,7 +324,9 @@ def test_ensure_fqdn_secrets(self): {"values_secrets": os.path.join(self.testdir_v1, "values-secret-fqdn.yaml")} ) - with patch.object(load_secrets_v1, "run_command") as mock_run_command: + with patch.object( + load_secrets_v1.LoadSecretsV1, "_run_command" + ) as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 3a721e23..06afdb57 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -122,7 +122,9 @@ def test_ensure_no_vault_policies_is_ok(self, getpass): } ) getpass.return_value = "foo" - with patch.object(load_secrets_v2, "run_command") as mock_run_command: + with patch.object( + load_secrets_v2.LoadSecretsV2, "_run_command" + ) as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -161,7 +163,9 @@ def test_ensure_policies_are_injected(self, getpass): ) # this will be used for both a secret and a file path getpass.return_value = "/tmp/ca.crt" - with patch.object(load_secrets_v2, "run_command") as mock_run_command: + with patch.object( + load_secrets_v2.LoadSecretsV2, "_run_command" + ) as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -321,7 +325,9 @@ def test_ensure_only_generate_passwords_works(self, getpass): ), } ) - with patch.object(load_secrets_v2, "run_command") as mock_run_command: + with patch.object( + load_secrets_v2.LoadSecretsV2, "_run_command" + ) as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -366,7 +372,9 @@ def test_generate_password_base64_works(self, getpass): ), } ) - with patch.object(load_secrets_v2, "run_command") as mock_run_command: + with patch.object( + load_secrets_v2.LoadSecretsV2, "_run_command" + ) as mock_run_command: stdout = "configuration updated" stderr = "" ret = 0 @@ -407,8 +415,7 @@ def test_ensure_error_secrets_same_name(self, getpass): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) assert ( - ret["args"][1] - == "You cannot have duplicate secret names: ['config-demo']" + ret["args"][1] == "You cannot have duplicate secret names: ['config-demo']" ) def test_ensure_error_fields_same_name(self, getpass): @@ -424,10 +431,8 @@ def test_ensure_error_fields_same_name(self, getpass): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert ( - ret["args"][1] - == "You cannot have duplicate field names: ['secret']" - ) + assert ret["args"][1] == "You cannot have duplicate field names: ['secret']" + if __name__ == "__main__": unittest.main() From 0b8569c4ea944e853088fc216c4ed8e1f5599769 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 13:49:55 +0100 Subject: [PATCH 0655/1263] Make the secret prompting a little prettier --- ansible/plugins/module_utils/load_secrets_v2.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 006b4851..35010b00 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -243,6 +243,10 @@ def _get_secret_value(self, name, field): prompt = self._get_field_description(field) if prompt is None: prompt = f"Type secret for {name}/{field['name']}: " + value = self._get_field_value(field) + if value is not None: + prompt += f" [{value}]" + prompt += ": " return getpass.getpass(prompt) return None From 7c70a67eb7a69a54bbf2b8f7c5749749eec12e22 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 15:04:13 +0100 Subject: [PATCH 0656/1263] Implement looking for ~/values-secret-.yaml first If that fails look for ~/values-secret.yaml --- Makefile | 2 +- ansible/roles/vault_utils/tasks/push_secrets.yaml | 8 +++++++- scripts/vault-utils.sh | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a0b94bd9..36653954 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ vault-unseal: ## unseals the vault common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init load-secrets: ## loads the secrets into the vault - common/scripts/vault-utils.sh push_secrets common/pattern-vault.init + common/scripts/vault-utils.sh push_secrets common/pattern-vault.init $(NAME) super-linter: ## Runs super linter locally rm -rf .mypy_cache diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 7335bcb4..bb8fcd3f 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -41,6 +41,12 @@ - name: Loads secrets file into the vault of a cluster no_log: false vault_load_secrets: - values_secrets: ~/values-secret.yaml + values_secrets: "{{ item }}" check_missing_secrets: false values_secret_template: "{{ secret_template }}" + with_first_found: + - files: + - "~/values-secret-{{ pattern_name }}.yaml" + - "~/values-secret.yaml" + # Note(bandini): Do we always want to load this secret automatically? + # - "{{ pattern_dir }}/values-secret.yaml.template" diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 3650466c..3aabcb8c 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -22,10 +22,11 @@ fi TASK="${1}" OUTFILE=${2:-"$COMMONPATH"/pattern-vault.init} +PATTERN_NAME=${3:-$(basename "`pwd`")} if [ -z ${TASK} ]; then echo "Task is unset" exit 1 fi -ansible-playbook -t "${TASK}" -e pattern_dir="${PATTERNPATH}" -e output_file="${OUTFILE}" "${PLAYBOOKPATH}/vault/vault.yaml" +ansible-playbook -t "${TASK}" -e pattern_name="${PATTERN_NAME}" -e pattern_dir="${PATTERNPATH}" -e output_file="${OUTFILE}" "${PLAYBOOKPATH}/vault/vault.yaml" From 19075e718ff82e2336fba380f8f4e79664810773 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 16:24:33 +0100 Subject: [PATCH 0657/1263] Test base64 secret Also escape the single secrets when passed around as attributes --- .../plugins/module_utils/load_secrets_v2.py | 4 +- ansible/roles/vault_utils/README.md | 2 +- .../tests/unit/test_vault_load_secrets_v2.py | 38 +++++++++++++++++-- .../v2/values-secret-v2-secret-base64.yaml | 12 ++++++ 4 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 35010b00..80ab5886 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -303,11 +303,11 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): # or we are prompting the user for it secret = self._get_secret_value(secret_name, f) if b64: - secret = base64.b64encode(secret.encode()) + secret = base64.b64encode(secret.encode()).decode("utf-8") for prefix in prefixes: cmd = ( f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " - f"\"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}={secret}\"" + f"\"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}='{secret}'\"" ) self._run_command(cmd) diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index e83471b9..bf914ece 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -39,7 +39,7 @@ external_secrets_sa: golang-external-secrets Use the local file system (output_file variable) to store the vault's unseal keys. If set to false they will be stored inside a secret defined by `unseal_secret` -in the `unseal_namespace` namespace: +in the `imperative` namespace: ```yaml file_unseal: true diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 06afdb57..f546d1a0 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -139,10 +139,10 @@ def test_ensure_no_vault_policies_is_ok(self, getpass): calls = [ call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv put -mount=secret secret/region-one/config-demo secret=value123"' # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/region-one/config-demo secret='value123'\"" # noqa: E501 ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret=value123"' # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret='value123'\"" # noqa: E501 ), call( "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/region-two/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 @@ -194,10 +194,10 @@ def test_ensure_policies_are_injected(self, getpass): 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"' # noqa: E501 ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret region-one/config-demo secret2=/tmp/ca.crt"' # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv patch -mount=secret region-one/config-demo secret2='/tmp/ca.crt'\"" # noqa: E501 ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret2=/tmp/ca.crt"' # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret2='/tmp/ca.crt'\"" # noqa: E501 ), call( "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret region-one/config-demo ca_crt=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 @@ -433,6 +433,36 @@ def test_ensure_error_fields_same_name(self, getpass): self.assertEqual(ret["failed"], True) assert ret["args"][1] == "You cannot have duplicate field names: ['secret']" + def test_password_base64_secret(self, getpass): + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-secret-base64.yaml" + ), + } + ) + with patch.object( + load_secrets_v2.LoadSecretsV2, "_run_command" + ) as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 1 + + calls = [ + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret test/config-demo secret='Zm9v'\"" # noqa: E501 + ), + ] + mock_run_command.assert_has_calls(calls) + if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml b/ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml new file mode 100644 index 00000000..46a9e42e --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml @@ -0,0 +1,12 @@ +version: 2.0 +# NEVER COMMIT THESE VALUES TO GIT + +secrets: + - name: config-demo + vaultPrefixes: + - test + fields: + - name: secret + value: foo + onMissingValue: error + base64: true From 5cd2e700a20f1ddd2383d6095e8a77eb9b3ed9fe Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 17:08:58 +0100 Subject: [PATCH 0658/1263] Silence yaml indentation linting errors and more New ansible-lint brought in some new valid complaints. Fix them up. --- .ansible-lint | 1 + ansible/roles/vault_utils/tasks/main.yml | 10 +++++----- ansible/roles/vault_utils/tasks/push_secrets.yaml | 7 ++++--- ansible/roles/vault_utils/tasks/vault_delete.yaml | 6 ++++-- ansible/roles/vault_utils/tasks/vault_init.yaml | 6 ++++-- ansible/roles/vault_utils/tasks/vault_pki_init.yaml | 3 ++- .../roles/vault_utils/tasks/vault_secrets_init.yaml | 3 ++- ansible/roles/vault_utils/tasks/vault_unseal.yaml | 6 ++++-- 8 files changed, 26 insertions(+), 16 deletions(-) diff --git a/.ansible-lint b/.ansible-lint index f8066ff0..67a7552c 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -5,3 +5,4 @@ skip_list: - name[template] # Allow Jinja templating inside task and play names - template-instead-of-copy # Templated files should use template instead of copy - yaml[line-length] # too long lines + - yaml[indentation] # Forcing lists to be always indented by 2 chars is silly IMO diff --git a/ansible/roles/vault_utils/tasks/main.yml b/ansible/roles/vault_utils/tasks/main.yml index 1da3e440..3175de20 100644 --- a/ansible/roles/vault_utils/tasks/main.yml +++ b/ansible/roles/vault_utils/tasks/main.yml @@ -1,20 +1,20 @@ --- - name: Delete vault - import_tasks: vault_delete.yaml + ansible.builtin.import_tasks: vault_delete.yaml tags: vault_delete - name: Run vault init tasks - import_tasks: vault_init.yaml + ansible.builtin.import_tasks: vault_init.yaml tags: vault_init - name: Unseal vault - import_tasks: vault_unseal.yaml + ansible.builtin.import_tasks: vault_unseal.yaml tags: vault_unseal - name: Vault secrets init - import_tasks: vault_secrets_init.yaml + ansible.builtin.import_tasks: vault_secrets_init.yaml tags: vault_secrets_init - name: Load secrets - import_tasks: push_secrets.yaml + ansible.builtin.import_tasks: push_secrets.yaml tags: push_secrets diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index bb8fcd3f..52da6bb3 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -1,7 +1,8 @@ --- -- include_tasks: pre_check.yaml -- include_tasks: vault_status.yaml - +- name: Vault pre checks + ansible.builtin.include_tasks: pre_check.yaml +- name: Vault status check + ansible.builtin.include_tasks: vault_status.yaml # Unfortunately we cannot loop vault_status and just check if the vault is unsealed # https://github.com/ansible/proposals/issues/136 diff --git a/ansible/roles/vault_utils/tasks/vault_delete.yaml b/ansible/roles/vault_utils/tasks/vault_delete.yaml index f0e289e7..6f5266de 100644 --- a/ansible/roles/vault_utils/tasks/vault_delete.yaml +++ b/ansible/roles/vault_utils/tasks/vault_delete.yaml @@ -1,6 +1,8 @@ --- -- include_tasks: pre_check.yaml -- include_tasks: vault_status.yaml +- name: Vault pre checks + ansible.builtin.include_tasks: pre_check.yaml +- name: Vault status check + ansible.builtin.include_tasks: vault_status.yaml # Note: We do not wait on purpose here as the pod will be recreated # anyways and that would race. We are fine with having it gone once diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 6d65740f..b339a257 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -1,6 +1,8 @@ --- -- include_tasks: pre_check.yaml -- include_tasks: vault_status.yaml +- name: Vault pre checks + ansible.builtin.include_tasks: pre_check.yaml +- name: Vault status check + ansible.builtin.include_tasks: vault_status.yaml # If the vault is already initialized we skip all the tasks below - name: Is the vault initialized? diff --git a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml index ca5fe697..4321a7c5 100644 --- a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml @@ -1,6 +1,7 @@ # NOTE: This task is currently not used --- -- include_tasks: pre_check.yaml +- name: Vault pre checks + ansible.builtin.include_tasks: pre_check.yaml - name: Fetch Ingress object kubernetes.core.k8s_info: diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index bfd38e9b..6a521504 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -1,5 +1,6 @@ --- -- include_tasks: pre_check.yaml +- name: Vault pre checks + ansible.builtin.include_tasks: pre_check.yaml - name: Is secrets backend already enabled kubernetes.core.k8s_exec: diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index 10e63629..9bd1bd3d 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -1,6 +1,8 @@ --- -- include_tasks: pre_check.yaml -- include_tasks: vault_status.yaml +- name: Vault pre checks + ansible.builtin.include_tasks: pre_check.yaml +- name: Vault status check + ansible.builtin.include_tasks: vault_status.yaml # If the vault is already unsealed we skip all the tasks below - name: Is the vault sealed? From 893718d86490029770ca8ff90e7b9ce0148e95fe Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 18:30:24 +0100 Subject: [PATCH 0659/1263] Added values-secret examples --- Changes.md | 4 + README.md | 4 + .../values-secret.v1.yaml} | 31 +++---- examples/secrets/values-secret.v2.yaml | 81 +++++++++++++++++++ 4 files changed, 101 insertions(+), 19 deletions(-) rename examples/{values-secret.yaml => secrets/values-secret.v1.yaml} (65%) create mode 100644 examples/secrets/values-secret.v2.yaml diff --git a/Changes.md b/Changes.md index 4c4daf37..65a8c260 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## November 22, 2022 + +* Implemented a new format for the values-secret.yaml. Example can be found in examples/ folder + ## November 6, 2022 * Add support for /values--.yaml (e.g. /values-AWS-group-one.yaml) diff --git a/README.md b/README.md index 9c6fa0cb..e8f75219 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,7 @@ do it manually by doing the following: git remote add -f upstream-common https://github.com/hybrid-cloud-patterns/common.git git merge -s subtree -Xtheirs -Xsubtree=common upstream-common/ha-vault ``` + +## Secrets + +There are two different secret formats parsed by the ansible bits. Both are documented [here](https://github.com/hybrid-cloud-patterns/common/tree/main/examples/secrets) diff --git a/examples/values-secret.yaml b/examples/secrets/values-secret.v1.yaml similarity index 65% rename from examples/values-secret.yaml rename to examples/secrets/values-secret.v1.yaml index 0dafeebf..30766109 100644 --- a/examples/values-secret.yaml +++ b/examples/secrets/values-secret.v1.yaml @@ -1,21 +1,16 @@ -# version is optional. When not specified it is assumed it is 1.0 -version: 1.0 +--- +# By default when a top-level 'version: 1.0' is missing it is assumed to be '1.0' +# NEVER COMMIT THESE VALUES TO GIT -# These secrets will be pushed in the vault at secret/hub/test The vault will -# have secret/hub/imageregistry with account and token as keys with their associated -# values (secrets) secrets: - # NEVER COMMIT THESE VALUES TO GIT - imageregistry: - # Quay -> Robot Accounts -> Robot Login - account: test-account - token: test-quay-token - - git: - # Go to: https://github.com/settings/tokens - username: test-user - token: test-git-token + # These secrets will be pushed in the vault at secret/hub/test The vault will + # have secret/hub/test with secret1 and secret2 as keys with their associated + # values (secrets) + test: + secret1: foo + secret2: bar + # This ends up as the s3Secret attribute to the path secret/hub/aws aws: s3Secret: test-secret @@ -30,12 +25,11 @@ secrets: server: https://api.example.openshiftapps.com:6443 bearerToken: -# This will create the vault key secret/hub/cluster_example_ca which will have two +# This will create the vault key secret/hub/testfoo which will have two # properties 'b64content' and 'content' which will be the base64-encoded # content and the normal content respectively files: - cluster_example_ca: /path/to/ca.file - + testfoo: ~/ca.crt # These secrets will be pushed in the vault at secret/region1/test The vault will # have secret/region1/test with secret1 and secret2 as keys with their associated # values (secrets) @@ -43,7 +37,6 @@ secrets.region1: test: secret1: foo1 secret2: bar1 - # This will create the vault key secret/region2/testbar which will have two # properties 'b64content' and 'content' which will be the base64-encoded # content and the normal content respectively diff --git a/examples/secrets/values-secret.v2.yaml b/examples/secrets/values-secret.v2.yaml new file mode 100644 index 00000000..db5abf4c --- /dev/null +++ b/examples/secrets/values-secret.v2.yaml @@ -0,0 +1,81 @@ +# NEVER COMMIT THESE VALUES TO GIT (unless your file only uses generated +# passwords or only points to files) + +# Needed to specify the new format (missing version means old version: 1.0 by default) +version: 2.0 + +# These are the vault policies to be created in the vault +# these are used when we let the vault generate the passwords +# by setting the 'onMissingValue' attribute to 'generate' +# See https://developer.hashicorp.com/vault/docs/concepts/password-policies +vaultPolicies: + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + + advancedPolicy: | + length=20 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + +# This is the mandatory top-level secrets entry +secrets: + # This will create the following keys + attributes: + # - secret/region-one/config-demo: + # secret: ...... + # secretprompt: ...... + # secretprompt2: ...... + # secretfile: ...... + # ca_crt_b64: ...... + # - secret/snowflake.blueprints.rhecoeng.com: + # secret: ...... + # secretprompt: ...... + # secretprompt2: ...... + # secretfile: ...... + # ca_crt_b64: ...... + - name: config-demo + vaultMount: secret + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: secret + onMissingValue: generate # One of: error,generate,prompt + vaultPolicy: basicPolicy + - name: secretprompt + value: null + onMissingValue: prompt + description: "Insert secretprompt NOW" + - name: secretprompt2 + value: defaultvalue + onMissingValue: prompt + description: "Insert secretprompt with default value NOW" + - name: secretfile + path: /tmp/ca.crt + onMissingValue: prompt + description: "Insert path to secretfile NOW" + - name: ca_crt + path: /tmp/ca.crt + onMissingValue: error # One of error, prompt (for path). generate makes no sense for file + - name: ca_crt_b64 + path: /tmp/ca.crt + base64: true # defaults to false + onMissingValue: prompt # One of error, prompt (for path). generate makes no sense for file + + - name: config-demo2 + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: ca_crt2 + path: null + onMissingValue: prompt # One of error, prompt (for path). generate makes no sense for file + - name: ca_crt + path: /tmp/ca.crt + onMissingValue: error # One of error, prompt (for path). generate makes no sense for file From 6ac8587744656a188cd53bab03f51ffd3a2b43ba Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 18:42:04 +0100 Subject: [PATCH 0660/1263] Drop callbacks_enabled It brings little to the table and is making the output quite confusing for users. It's also not really too relevant with our fairly short-lived tasks. --- ansible/ansible.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index 710a2469..9a742d0a 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -3,4 +3,3 @@ display_skipped_hosts=False localhost_warning=False library=./plugins/modules:~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules roles_path=./roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles -callbacks_enabled = profile_roles, profile_tasks From afe0f906c6f3c024567fd6aba73faaf690897a01 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 18:51:16 +0100 Subject: [PATCH 0661/1263] Add another relevant change in Changes.md --- Changes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changes.md b/Changes.md index 65a8c260..b9f23a9c 100644 --- a/Changes.md +++ b/Changes.md @@ -3,6 +3,9 @@ ## November 22, 2022 * Implemented a new format for the values-secret.yaml. Example can be found in examples/ folder +* Now the order of values-secret file lookup is the following: + 1. ~/values-secret-.yaml + 2. ~/values-secret.yaml ## November 6, 2022 From bf725edb511aa6de2406e54ed700a6ac0fe5982d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 19:30:00 +0100 Subject: [PATCH 0662/1263] Error out nicely if yaml.safe_load() cannot parse the file The yaml library simply returns a string in case it fails to parse the yaml file. Let's error out nicely in that case --- ansible/plugins/module_utils/load_secrets_common.py | 5 ++++- ansible/plugins/module_utils/load_secrets_v1.py | 6 +++++- ansible/plugins/module_utils/load_secrets_v2.py | 5 ++++- ansible/plugins/modules/vault_load_secrets.py | 3 +++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index ba2ec312..a19de119 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -52,12 +52,15 @@ def parse_values(values_file): to be parsed. Returns: - secrets_yaml(obj): The python object containing the parsed yaml + secrets_yaml(obj): The python object containing the parsed yaml or False + if the file could not be parsed as a yaml file """ with open(values_file, "r", encoding="utf-8") as file: secrets_yaml = yaml.safe_load(file.read()) if secrets_yaml is None: return {} + if isinstance(secrets_yaml, str): + return False return secrets_yaml diff --git a/ansible/plugins/module_utils/load_secrets_v1.py b/ansible/plugins/module_utils/load_secrets_v1.py index f2879641..33a1f7f7 100644 --- a/ansible/plugins/module_utils/load_secrets_v1.py +++ b/ansible/plugins/module_utils/load_secrets_v1.py @@ -34,7 +34,11 @@ def __init__( self.namespace = namespace self.pod = pod self.values_secret_template = values_secret_template - self.syaml = parse_values(values_secrets) + syaml = parse_values(values_secrets) + if syaml is False: + self.module.fail_json(f"Could not parse {values_secrets} file as yaml") + + self.syaml = syaml def _run_command(self, command, attempts=1, sleep=3): """ diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 80ab5886..50ea5377 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -35,7 +35,10 @@ def __init__(self, module, values_secrets, namespace, pod, values_secret_templat self.namespace = namespace self.pod = pod self.values_secret_template = values_secret_template - self.syaml = parse_values(values_secrets) + syaml = parse_values(values_secrets) + if syaml is False: + self.module.fail_json(f"Could not parse {values_secrets} file as yaml") + self.syaml = syaml def _run_command(self, command, attempts=1, sleep=3): """ diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index c8960fa7..5cdb410b 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -145,6 +145,9 @@ def run(module): module.exit_json(**results) syaml = parse_values(values_secrets) + if syaml is False: + module.fail_json(f"Could not parse {values_secrets} file as yaml") + version = get_version(syaml) if version == "2.0": From 732d0165f71e4409c36c7f6e6175e4a8c7ce1af9 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 22 Nov 2022 20:37:32 +0100 Subject: [PATCH 0663/1263] Add support for ansible-vault The way this works is the following: 1. If we detect that the values-secret file is unencrypted, we just pass it as usual to the vault_load_secrets plugin using the values_secrets parameter 2. If we detect that it is encrypted we call `ansible-vault view` on the file and then invoke the vault_load_secrets passing the values_secrets_plaintext parameter as a string instead Tested with both an encrypted and an unencrypted ~/values-secret.yaml file. --- Changes.md | 3 ++ .../module_utils/load_secrets_common.py | 24 --------- .../plugins/module_utils/load_secrets_v1.py | 10 +--- .../plugins/module_utils/load_secrets_v2.py | 12 +---- ansible/plugins/modules/vault_load_secrets.py | 48 +++++++++++++----- .../roles/vault_utils/tasks/push_secrets.yaml | 50 ++++++++++++++++--- ansible/tests/unit/test_vault_load_secrets.py | 2 +- .../tests/unit/test_vault_load_secrets_v2.py | 2 +- 8 files changed, 87 insertions(+), 64 deletions(-) diff --git a/Changes.md b/Changes.md index b9f23a9c..370e2ada 100644 --- a/Changes.md +++ b/Changes.md @@ -6,6 +6,9 @@ * Now the order of values-secret file lookup is the following: 1. ~/values-secret-.yaml 2. ~/values-secret.yaml +* Add support for ansible vault encrypted values-secret files. You can now encrypt your values-secret file + with `ansible-vault encrypt ~/values-secret.yaml`. The load-secrets mechanism will detect that and prompt + for the password to decrypt it ## November 6, 2022 diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index a19de119..e6d339f9 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -19,8 +19,6 @@ from collections.abc import MutableMapping -import yaml - def find_dupes(array): """ @@ -42,28 +40,6 @@ def find_dupes(array): return dupes -def parse_values(values_file): - """ - Parses a values-secrets.yaml file (usually placed in ~) - and returns a Python Obect with the parsed yaml. - - Parameters: - values_file(str): The path of the values-secrets.yaml file - to be parsed. - - Returns: - secrets_yaml(obj): The python object containing the parsed yaml or False - if the file could not be parsed as a yaml file - """ - with open(values_file, "r", encoding="utf-8") as file: - secrets_yaml = yaml.safe_load(file.read()) - if secrets_yaml is None: - return {} - if isinstance(secrets_yaml, str): - return False - return secrets_yaml - - def get_version(syaml): """ Return the version: of the parsed yaml object. If it does not exist diff --git a/ansible/plugins/module_utils/load_secrets_v1.py b/ansible/plugins/module_utils/load_secrets_v1.py index 33a1f7f7..650d01d3 100644 --- a/ansible/plugins/module_utils/load_secrets_v1.py +++ b/ansible/plugins/module_utils/load_secrets_v1.py @@ -22,22 +22,16 @@ import time import yaml -from ansible.module_utils.load_secrets_common import flatten, get_version, parse_values +from ansible.module_utils.load_secrets_common import flatten, get_version class LoadSecretsV1: - def __init__( - self, module, values_secrets, basepath, namespace, pod, values_secret_template - ): + def __init__(self, module, syaml, basepath, namespace, pod, values_secret_template): self.module = module self.basepath = basepath self.namespace = namespace self.pod = pod self.values_secret_template = values_secret_template - syaml = parse_values(values_secrets) - if syaml is False: - self.module.fail_json(f"Could not parse {values_secrets} file as yaml") - self.syaml = syaml def _run_command(self, command, attempts=1, sleep=3): diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 50ea5377..785f1d3e 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -22,22 +22,14 @@ import os import time -from ansible.module_utils.load_secrets_common import ( - find_dupes, - get_version, - parse_values, -) +from ansible.module_utils.load_secrets_common import find_dupes, get_version class LoadSecretsV2: - def __init__(self, module, values_secrets, namespace, pod, values_secret_template): + def __init__(self, module, syaml, namespace, pod): self.module = module self.namespace = namespace self.pod = pod - self.values_secret_template = values_secret_template - syaml = parse_values(values_secrets) - if syaml is False: - self.module.fail_json(f"Could not parse {values_secrets} file as yaml") self.syaml = syaml def _run_command(self, command, attempts=1, sleep=3): diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 5cdb410b..194a4c11 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -54,7 +54,7 @@ import yaml from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.load_secrets_common import get_version, parse_values +from ansible.module_utils.load_secrets_common import get_version from ansible.module_utils.load_secrets_v1 import LoadSecretsV1 from ansible.module_utils.load_secrets_v2 import LoadSecretsV2 @@ -75,8 +75,17 @@ options: values_secrets: description: - - Path to the values-secrets file - required: true + - Path to the values-secrets file (only one of values_secrets and + values_secrets_plaintext can be passed) + required: false + default: '' + type: str + values_secrets_plaintext: + description: + - The content of the values-secrets file (only one of values_secrets and + values_secrets_plaintext can be passed) + required: false + default: '' type: str namespace: description: @@ -127,6 +136,10 @@ def run(module): results = dict(changed=False) args = module.params + values_secrets = os.path.expanduser(args.get("values_secrets", "")) + values_secrets_plaintext = args.get("values_secrets_plaintext", "") + if values_secrets != "" and values_secrets_plaintext != "": + module.fail_json("Cannot pass both values_secret and values_secret_plaintext") values_secrets = os.path.expanduser(args.get("values_secrets")) basepath = args.get("basepath") namespace = args.get("namespace") @@ -138,26 +151,37 @@ def run(module): "No values_secret_template defined and check_missing_secrets set to True" ) - if not os.path.exists(values_secrets): + if values_secrets != "" and not os.path.exists(values_secrets): results["failed"] = True - results["error"] = "Missing values-secrets.yaml file" + results["error"] = f"Missing {values_secrets} file" results["msg"] = f"Values secrets file does not exist: {values_secrets}" module.exit_json(**results) - syaml = parse_values(values_secrets) - if syaml is False: - module.fail_json(f"Could not parse {values_secrets} file as yaml") + # We were passed a filename (aka the unencrypted path) + if values_secrets != "": + with open(values_secrets, "r", encoding="utf-8") as file: + syaml = yaml.safe_load(file.read()) + if syaml is None: + syaml = {} + elif isinstance(syaml, str): + module.fail_json(f"Could not parse {values_secrets} file as yaml") + elif values_secrets_plaintext != "": + syaml = yaml.safe_load(values_secrets_plaintext) + if syaml is None: + syaml = {} + elif isinstance(syaml, str): + module.fail_json("Could not parse values_secrets_plaintext as yaml") + else: + module.fail_json("Both values_secrets and values_secrets_plaintext are unset") version = get_version(syaml) if version == "2.0": - secret_obj = LoadSecretsV2( - module, values_secrets, namespace, pod, values_secret_template - ) + secret_obj = LoadSecretsV2(module, syaml, namespace, pod) secret_obj.sanitize_values() elif version == "1.0": secret_obj = LoadSecretsV1( - module, values_secrets, basepath, namespace, pod, values_secret_template + module, syaml, basepath, namespace, pod, values_secret_template ) secret_obj.sanitize_values() # If the user specified check_for_missing_secrets then we read values_secret_template diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 52da6bb3..35448ed1 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -39,15 +39,49 @@ ansible.builtin.set_fact: secret_template: "{{ pattern_dir }}/values-secret.yaml.template" -- name: Loads secrets file into the vault of a cluster - no_log: false - vault_load_secrets: - values_secrets: "{{ item }}" - check_missing_secrets: false - values_secret_template: "{{ secret_template }}" - with_first_found: - - files: +- name: Find first existing values-secret yaml file + no_log: true + ansible.builtin.set_fact: + found_file: "{{ lookup('ansible.builtin.first_found', findme) }}" + vars: + findme: - "~/values-secret-{{ pattern_name }}.yaml" - "~/values-secret.yaml" # Note(bandini): Do we always want to load this secret automatically? # - "{{ pattern_dir }}/values-secret.yaml.template" + +- name: Is found values secret file encrypted + no_log: true + ansible.builtin.shell: | + set -o pipefail + head -1 "{{ found_file }}" | grep -q \$ANSIBLE_VAULT + changed_when: false + register: encrypted + failed_when: (encrypted.rc not in [0,1]) + +- name: Set encryption bool fact + ansible.builtin.set_fact: + is_encrypted: "{{ encrypted.rc == 0 | bool }}" + +- name: Get decrypted content if {{ found_file }} was encrypted + no_log: true + ansible.builtin.command: + ansible-vault view "{{ found_file }}" + register: values_secret_plaintext + when: is_encrypted + +- name: Loads secrets file into the vault of a cluster (not encrypted) + no_log: false + vault_load_secrets: + values_secrets: "{{ found_file }}" + check_missing_secrets: false + values_secret_template: "{{ secret_template }}" + when: not is_encrypted + +- name: Loads secrets file into the vault of a cluster (encrypted) + no_log: false + vault_load_secrets: + values_secrets_plaintext: "{{ values_secret_plaintext.stdout }}" + check_missing_secrets: false + values_secret_template: "{{ secret_template }}" + when: is_encrypted diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index e4c7f310..7d57eadc 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -106,7 +106,7 @@ def test_module_fail_when_values_secret_not_existing(self): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - self.assertEqual(ret["error"], "Missing values-secrets.yaml file") + self.assertEqual(ret["error"], "Missing /tmp/nonexisting file") self.assertEqual( ret["msg"], "Values secrets file does not exist: /tmp/nonexisting" ) diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index f546d1a0..1d04ac6f 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -108,7 +108,7 @@ def test_module_fail_when_values_secret_not_existing(self, getpass): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - self.assertEqual(ret["error"], "Missing values-secrets.yaml file") + self.assertEqual(ret["error"], "Missing /tmp/nonexisting file") self.assertEqual( ret["msg"], "Values secrets file does not exist: /tmp/nonexisting" ) From d4be1cf911b89d2b28383671bd57047c9e3303a5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Nov 2022 09:27:35 +0100 Subject: [PATCH 0664/1263] Clean up comments in test-cases files and implement backingStore Currently the only supported (and defaulted to) backingStore: is 'vault' but we do error out properly in case it gets assigned to something else. --- .../module_utils/load_secrets_common.py | 11 +++++++++ ansible/plugins/modules/vault_load_secrets.py | 7 ++++-- .../tests/unit/test_vault_load_secrets_v2.py | 16 +++++++++++++ .../tests/unit/v2/values-secret-v2-base.yaml | 15 ++++-------- .../v2/values-secret-v2-files-emptypath.yaml | 11 ++------- ...-secret-v2-files-wrong-onmissingvalue.yaml | 11 ++------- .../v2/values-secret-v2-files-wrongpath.yaml | 11 ++------- .../v2/values-secret-v2-generate-base64.yaml | 12 +++------- ...es-secret-v2-nonexisting-backingstore.yaml | 23 +++++++++++++++++++ .../unit/v2/values-secret-v2-nopolicies.yaml | 10 +++----- .../v2/values-secret-v2-novaultprefix.yaml | 4 +--- .../v2/values-secret-v2-onlygenerate.yaml | 9 ++------ .../v2/values-secret-v2-secret-base64.yaml | 1 - ...values-secret-v2-wrong-onmissingvalue.yaml | 9 ++------ .../values-secret-v2-wrong-vaultpolicy.yaml | 9 ++------ examples/secrets/values-secret.v2.yaml | 2 ++ 16 files changed, 81 insertions(+), 80 deletions(-) create mode 100644 ansible/tests/unit/v2/values-secret-v2-nonexisting-backingstore.yaml diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index e6d339f9..76182ed4 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -51,6 +51,17 @@ def get_version(syaml): return str(syaml.get("version", "1.0")) +def get_backingstore(syaml): + """ + Return the backingStore: of the parsed yaml object. If it does not exist + return 'vault' + + Returns: + ret(str): The value of the top-level 'backingStore:' key + """ + return str(syaml.get("backingStore", "vault")) + + def flatten(dictionary, parent_key=False, separator="."): """ Turn a nested dictionary into a flattened dictionary and also diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 194a4c11..0923a876 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -54,7 +54,7 @@ import yaml from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.load_secrets_common import get_version +from ansible.module_utils.load_secrets_common import get_backingstore, get_version from ansible.module_utils.load_secrets_v1 import LoadSecretsV1 from ansible.module_utils.load_secrets_v2 import LoadSecretsV2 @@ -140,6 +140,7 @@ def run(module): values_secrets_plaintext = args.get("values_secrets_plaintext", "") if values_secrets != "" and values_secrets_plaintext != "": module.fail_json("Cannot pass both values_secret and values_secret_plaintext") + values_secrets = os.path.expanduser(args.get("values_secrets")) basepath = args.get("basepath") namespace = args.get("namespace") @@ -175,8 +176,10 @@ def run(module): module.fail_json("Both values_secrets and values_secrets_plaintext are unset") version = get_version(syaml) - if version == "2.0": + backing_store = get_backingstore(syaml) + if backing_store != "vault": # we currently only support vault + module.fail_json("Currently only the 'vault' backingStore is supported") secret_obj = LoadSecretsV2(module, syaml, namespace, pod) secret_obj.sanitize_values() elif version == "1.0": diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 1d04ac6f..662f0696 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -463,6 +463,22 @@ def test_password_base64_secret(self, getpass): ] mock_run_command.assert_has_calls(calls) + def test_ensure_error_on_unsupported_backingstore(self, getpass): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, + "values-secret-v2-nonexisting-backingstore.yaml", + ), + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert ret["args"][1] == "Currently only the 'vault' backingStore is supported" + if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-base.yaml b/ansible/tests/unit/v2/values-secret-v2-base.yaml index c9d70918..7f39e719 100644 --- a/ansible/tests/unit/v2/values-secret-v2-base.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-base.yaml @@ -1,13 +1,8 @@ -# Needed to specify the new format (missing version means old version: 1.0 by default) version: 2.0 -# NEVER COMMIT THESE VALUES TO GIT -# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark -global: - backend: vault +backingStore: vault vaultPolicies: - # https://developer.hashicorp.com/vault/docs/concepts/password-policies basicPolicy: | length=10 rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } @@ -28,15 +23,15 @@ secrets: - snowflake.blueprints.rhecoeng.com fields: - name: secret - onMissingValue: generate # One of: error,generate,prompt + onMissingValue: generate vaultPolicy: basicPolicy - name: secret2 value: null onMissingValue: prompt - name: ca_crt path: /tmp/ca.crt - onMissingValue: error # One of error, prompt (for path). generate makes no sense for file + onMissingValue: error - name: ca_crt2 path: null - base64: true # defaults to false - onMissingValue: prompt # One of error, prompt (for path). generate makes no sense for file + base64: true + onMissingValue: prompt diff --git a/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml b/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml index 20cbaa41..13b54824 100644 --- a/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml @@ -1,13 +1,7 @@ -# Needed to specify the new format (missing version means old version: 1.0 by default) version: 2.0 -# NEVER COMMIT THESE VALUES TO GIT - -# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark -global: - backend: vault +backingStore: vault vaultPolicies: - # https://developer.hashicorp.com/vault/docs/concepts/password-policies basicPolicy: | length=10 rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } @@ -26,7 +20,6 @@ secrets: vaultPrefixes: - secret/region-one - secret/snowflake.blueprints.rhecoeng.com - # Files are *always* base64 encoded first as the vault upload commands uses http+json fields: - name: ca_crt - onMissingValue: error # One of error, prompt (for path). generate makes no sense for file + onMissingValue: error diff --git a/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml b/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml index 9972f7d0..184c7da7 100644 --- a/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml @@ -1,13 +1,7 @@ -# Needed to specify the new format (missing version means old version: 1.0 by default) version: 2.0 -# NEVER COMMIT THESE VALUES TO GIT - -# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark -global: - backend: vault +backingStore: vault vaultPolicies: - # https://developer.hashicorp.com/vault/docs/concepts/password-policies basicPolicy: | length=10 rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } @@ -26,8 +20,7 @@ secrets: vaultPrefixes: - secret/region-one - secret/snowflake.blueprints.rhecoeng.com - # Files are *always* base64 encoded first as the vault upload commands uses http+json fields: - name: ca_crt path: /tmp/ca.crt - onMissingValue: generate # One of error, prompt (for path). generate makes no sense for file + onMissingValue: generate diff --git a/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml b/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml index 157f2a67..81129b07 100644 --- a/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml @@ -1,13 +1,7 @@ -# Needed to specify the new format (missing version means old version: 1.0 by default) version: 2.0 -# NEVER COMMIT THESE VALUES TO GIT - -# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark -global: - backend: vault +backingStore: vault vaultPolicies: - # https://developer.hashicorp.com/vault/docs/concepts/password-policies basicPolicy: | length=10 rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } @@ -26,8 +20,7 @@ secrets: vaultPrefixes: - secret/region-one - secret/snowflake.blueprints.rhecoeng.com - # Files are *always* base64 encoded first as the vault upload commands uses http+json fields: - name: ca_crt path: /tmp/nonexisting - onMissingValue: error # One of error, prompt (for path). generate makes no sense for file + onMissingValue: error diff --git a/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml b/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml index 76eb0861..d9d113ae 100644 --- a/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml @@ -1,13 +1,7 @@ -# Needed to specify the new format (missing version means old version: 1.0 by default) version: 2.0 -# NEVER COMMIT THESE VALUES TO GIT - -# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark -global: - backend: vault +backingStore: vault vaultPolicies: - # https://developer.hashicorp.com/vault/docs/concepts/password-policies basicPolicy: | length=10 rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } @@ -21,6 +15,6 @@ secrets: - snowflake.blueprints.rhecoeng.com fields: - name: secret - onMissingValue: generate # One of: error,generate,prompt - base64: true # defaults to false + onMissingValue: generate + base64: true vaultPolicy: basicPolicy diff --git a/ansible/tests/unit/v2/values-secret-v2-nonexisting-backingstore.yaml b/ansible/tests/unit/v2/values-secret-v2-nonexisting-backingstore.yaml new file mode 100644 index 00000000..d6bf54b9 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-nonexisting-backingstore.yaml @@ -0,0 +1,23 @@ +version: 2.0 + +backingStore: nonexisting + +secrets: + - name: config-demo + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: secret + onMissingValue: generate + vaultPolicy: basicPolicy + - name: secret2 + value: null + onMissingValue: prompt + - name: ca_crt + path: /tmp/ca.crt + onMissingValue: error + - name: ca_crt2 + path: null + base64: true + onMissingValue: prompt diff --git a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml index 96fdbc9c..5b8f24cc 100644 --- a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml @@ -1,10 +1,6 @@ -# Needed to specify the new format (missing version means old version: 1.0 by default) version: 2.0 -# NEVER COMMIT THESE VALUES TO GIT -# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark -global: - backend: vault +backingStore: vault secrets: - name: config-demo @@ -14,7 +10,7 @@ secrets: fields: - name: secret value: value123 - onMissingValue: error # One of: error,generate,prompt + onMissingValue: error - name: config-demo-file vaultPrefixes: @@ -25,4 +21,4 @@ secrets: - name: ca_crt path: /tmp/ca.crt base64: true - onMissingValue: error # One of error, prompt (for path). generate makes no sense for file + onMissingValue: error diff --git a/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml b/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml index 04aaee0b..995ce253 100644 --- a/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml @@ -1,6 +1,4 @@ -# Needed to specify the new format (missing version means old version: 1.0 by default) version: 2.0 -# NEVER COMMIT THESE VALUES TO GIT secrets: - name: config-demo @@ -8,4 +6,4 @@ secrets: fields: - name: secret value: value123 - onMissingValue: error # One of: error,generate,prompt + onMissingValue: error diff --git a/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml b/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml index e0e7808b..724c8367 100644 --- a/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml @@ -1,13 +1,8 @@ -# Needed to specify the new format (missing version means old version: 1.0 by default) version: 2.0 -# NEVER COMMIT THESE VALUES TO GIT -# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark -global: - backend: vault +backingStore: vault vaultPolicies: - # https://developer.hashicorp.com/vault/docs/concepts/password-policies basicPolicy: | length=10 rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } @@ -29,7 +24,7 @@ secrets: - snowflake.blueprints.rhecoeng.com fields: - name: secret - onMissingValue: generate # One of: error,generate,prompt + onMissingValue: generate vaultPolicy: basicPolicy - name: secret2 onMissingValue: generate diff --git a/ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml b/ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml index 46a9e42e..0eeac8ad 100644 --- a/ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml @@ -1,5 +1,4 @@ version: 2.0 -# NEVER COMMIT THESE VALUES TO GIT secrets: - name: config-demo diff --git a/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml b/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml index d8c6f44b..0886e2cb 100644 --- a/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml @@ -1,13 +1,8 @@ -# Needed to specify the new format (missing version means old version: 1.0 by default) version: 2.0 -# NEVER COMMIT THESE VALUES TO GIT -# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark -global: - backend: vault +backingStore: vault vaultPolicies: - # https://developer.hashicorp.com/vault/docs/concepts/password-policies basicPolicy: | length=10 rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } @@ -21,5 +16,5 @@ secrets: - secret/snowflake.blueprints.rhecoeng.com fields: - name: secret - onMissingValue: generate # One of: error,generate,prompt + onMissingValue: generate vaultPolicy: nonExisting diff --git a/ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml b/ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml index d8c6f44b..0886e2cb 100644 --- a/ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml @@ -1,13 +1,8 @@ -# Needed to specify the new format (missing version means old version: 1.0 by default) version: 2.0 -# NEVER COMMIT THESE VALUES TO GIT -# global + backend is the default. No need to specify it but one day we could have an override implementing it for, say, cyberark -global: - backend: vault +backingStore: vault vaultPolicies: - # https://developer.hashicorp.com/vault/docs/concepts/password-policies basicPolicy: | length=10 rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } @@ -21,5 +16,5 @@ secrets: - secret/snowflake.blueprints.rhecoeng.com fields: - name: secret - onMissingValue: generate # One of: error,generate,prompt + onMissingValue: generate vaultPolicy: nonExisting diff --git a/examples/secrets/values-secret.v2.yaml b/examples/secrets/values-secret.v2.yaml index db5abf4c..aa3a5c31 100644 --- a/examples/secrets/values-secret.v2.yaml +++ b/examples/secrets/values-secret.v2.yaml @@ -4,6 +4,8 @@ # Needed to specify the new format (missing version means old version: 1.0 by default) version: 2.0 +backingStore: vault # 'vault' is the default when omitted + # These are the vault policies to be created in the vault # these are used when we let the vault generate the passwords # by setting the 'onMissingValue' attribute to 'generate' From 896bf813aba5a389f00b2a5e791ba1babbd3e34b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Nov 2022 09:34:19 +0100 Subject: [PATCH 0665/1263] Use sensible prompt strings --- examples/secrets/values-secret.v2.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/secrets/values-secret.v2.yaml b/examples/secrets/values-secret.v2.yaml index aa3a5c31..41e2f3a5 100644 --- a/examples/secrets/values-secret.v2.yaml +++ b/examples/secrets/values-secret.v2.yaml @@ -53,15 +53,15 @@ secrets: - name: secretprompt value: null onMissingValue: prompt - description: "Insert secretprompt NOW" + description: "Please specify the password for application ABC" - name: secretprompt2 value: defaultvalue onMissingValue: prompt - description: "Insert secretprompt with default value NOW" + description: "Please specify the API key for XYZ" - name: secretfile path: /tmp/ca.crt onMissingValue: prompt - description: "Insert path to secretfile NOW" + description: "Insert path to Certificate Authority" - name: ca_crt path: /tmp/ca.crt onMissingValue: error # One of error, prompt (for path). generate makes no sense for file From bf353810c9ad7a4193e75b3131389a6a21f82bdc Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Nov 2022 09:58:32 +0100 Subject: [PATCH 0666/1263] Be clearer in the text about ansible-vault --- Changes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changes.md b/Changes.md index 370e2ada..b665cb81 100644 --- a/Changes.md +++ b/Changes.md @@ -7,8 +7,8 @@ 1. ~/values-secret-.yaml 2. ~/values-secret.yaml * Add support for ansible vault encrypted values-secret files. You can now encrypt your values-secret file - with `ansible-vault encrypt ~/values-secret.yaml`. The load-secrets mechanism will detect that and prompt - for the password to decrypt it + at rest with `ansible-vault encrypt ~/values-secret.yaml`. When running `make load-secrets` if an encrypted + file is encountered the user will be prompted automatically for the password to decrypt it. ## November 6, 2022 From 18a927ec9fb2d0655b95e4db88044a5bbb63252f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Nov 2022 10:27:23 +0100 Subject: [PATCH 0667/1263] Move checks that are specific to the version inside classes Let's encapsulate those checks inside their respective version classes. This avoids littering the main module code with version specific checks. --- .../module_utils/load_secrets_common.py | 11 --------- .../plugins/module_utils/load_secrets_v1.py | 21 +++++++++++++++- .../plugins/module_utils/load_secrets_v2.py | 15 ++++++++++++ ansible/plugins/modules/vault_load_secrets.py | 24 +++++++------------ .../tests/unit/test_vault_load_secrets_v2.py | 5 +++- 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index 76182ed4..e6d339f9 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -51,17 +51,6 @@ def get_version(syaml): return str(syaml.get("version", "1.0")) -def get_backingstore(syaml): - """ - Return the backingStore: of the parsed yaml object. If it does not exist - return 'vault' - - Returns: - ret(str): The value of the top-level 'backingStore:' key - """ - return str(syaml.get("backingStore", "vault")) - - def flatten(dictionary, parent_key=False, separator="."): """ Turn a nested dictionary into a flattened dictionary and also diff --git a/ansible/plugins/module_utils/load_secrets_v1.py b/ansible/plugins/module_utils/load_secrets_v1.py index 650d01d3..6478ac26 100644 --- a/ansible/plugins/module_utils/load_secrets_v1.py +++ b/ansible/plugins/module_utils/load_secrets_v1.py @@ -26,12 +26,22 @@ class LoadSecretsV1: - def __init__(self, module, syaml, basepath, namespace, pod, values_secret_template): + def __init__( + self, + module, + syaml, + basepath, + namespace, + pod, + values_secret_template, + check_missing_secrets, + ): self.module = module self.basepath = basepath self.namespace = namespace self.pod = pod self.values_secret_template = values_secret_template + self.check_missing_secrets = check_missing_secrets self.syaml = syaml def _run_command(self, command, attempts=1, sleep=3): @@ -92,6 +102,15 @@ def sanitize_values(self): f"'files' keys: {self.syaml}" ) + if self.check_missing_secrets and self.values_secret_template == "": + self.module.fail_json( + "No values_secret_template defined and check_missing_secrets set to True" + ) + # If the user specified check_for_missing_secrets then we read values_secret_template + # and check if there are any missing secrets. Makes sense only for v1.0 + if self.check_missing_secrets: + self.check_for_missing_secrets() + secrets = self.syaml.get("secrets", {}) # We need to explicitely check for None because the file might contain the # top-level 'secrets:' or 'files:' key but have nothing else under it which will diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 785f1d3e..c6eeacd6 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -58,6 +58,16 @@ def _run_command(self, command, attempts=1, sleep=3): return ret time.sleep(sleep) + def _get_backingstore(self): + """ + Return the backingStore: of the parsed yaml object. If it does not exist + return 'vault' + + Returns: + ret(str): The value of the top-level 'backingStore:' key + """ + return str(self.syaml.get("backingStore", "vault")) + def _get_vault_policies(self): return self.syaml.get("vaultPolicies", {}) @@ -221,6 +231,11 @@ def sanitize_values(self): if v != "2.0": self.module.fail_json(f"Version is not 2.0: {v}") + backing_store = self._get_backingstore() + if backing_store != "vault": # we currently only support vault + self.module.fail_json( + f"Currently only the 'vault' backingStore is supported: {backing_store}" + ) # Check if the vault_policies are sane somehow? # vault_policies = self._get_vault_policies() diff --git a/ansible/plugins/modules/vault_load_secrets.py b/ansible/plugins/modules/vault_load_secrets.py index 0923a876..725b69b4 100644 --- a/ansible/plugins/modules/vault_load_secrets.py +++ b/ansible/plugins/modules/vault_load_secrets.py @@ -54,7 +54,7 @@ import yaml from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.load_secrets_common import get_backingstore, get_version +from ansible.module_utils.load_secrets_common import get_version from ansible.module_utils.load_secrets_v1 import LoadSecretsV1 from ansible.module_utils.load_secrets_v2 import LoadSecretsV2 @@ -147,10 +147,6 @@ def run(module): pod = args.get("pod") check_missing_secrets = args.get("check_missing_secrets") values_secret_template = args.get("values_secret_template") - if check_missing_secrets and values_secret_template == "": - module.fail_json( - "No values_secret_template defined and check_missing_secrets set to True" - ) if values_secrets != "" and not os.path.exists(values_secrets): results["failed"] = True @@ -177,24 +173,22 @@ def run(module): version = get_version(syaml) if version == "2.0": - backing_store = get_backingstore(syaml) - if backing_store != "vault": # we currently only support vault - module.fail_json("Currently only the 'vault' backingStore is supported") secret_obj = LoadSecretsV2(module, syaml, namespace, pod) - secret_obj.sanitize_values() elif version == "1.0": secret_obj = LoadSecretsV1( - module, syaml, basepath, namespace, pod, values_secret_template + module, + syaml, + basepath, + namespace, + pod, + values_secret_template, + check_missing_secrets, ) - secret_obj.sanitize_values() - # If the user specified check_for_missing_secrets then we read values_secret_template - # and check if there are any missing secrets. Makes sense only for v1.0 - if check_missing_secrets: - secret_obj.check_for_missing_secrets() else: module.fail_json(f"Version {version} is currently not supported") + secret_obj.sanitize_values() nr_secrets = secret_obj.inject_secrets() results["failed"] = False results["changed"] = True diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 662f0696..c91ab60a 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -477,7 +477,10 @@ def test_ensure_error_on_unsupported_backingstore(self, getpass): ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) - assert ret["args"][1] == "Currently only the 'vault' backingStore is supported" + assert ( + ret["args"][1] + == "Currently only the 'vault' backingStore is supported: nonexisting" + ) if __name__ == "__main__": From 9e24f6cf9f55c2773b051ced097ea5cffbd2e339 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Nov 2022 10:37:07 +0100 Subject: [PATCH 0668/1263] Add a test for erroring out when values_secret_template is not defined and check_missing_secrets is set to true --- ansible/tests/unit/test_vault_load_secrets.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 7d57eadc..10aba108 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -356,5 +356,32 @@ def test_ensure_fqdn_secrets(self): mock_run_command.assert_has_calls(calls) + def test_ensure_check_missing_secrets_errors_out(self): + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v1, "mcg-values-secret.yaml" + ), + "check_missing_secrets": True, + "values_secret_template": "", + } + ) + with patch.object( + load_secrets_v1.LoadSecretsV1, "_run_command" + ) as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr + + with self.assertRaises(AnsibleFailJson) as result: + vault_load_secrets.main() + self.assertTrue(result.exception.args[0]["failed"]) + # In case of failure args[1] contains the msg of the failure + assert ( + result.exception.args[0]["args"][1] + == "No values_secret_template defined and check_missing_secrets set to True" + ) + assert mock_run_command.call_count == 0 if __name__ == "__main__": unittest.main() From 36d06e1fccd43f52a57d4d8c56aafa25b4de6ec1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Nov 2022 12:02:05 +0100 Subject: [PATCH 0669/1263] Add a default validatedPatternDefaultPolicy password policy This is hard-coded and we can always assume it to be there when using version: 2.0 of the secret spect. This is we can avoid redefining demo policies everywhere. --- .../plugins/module_utils/load_secrets_v2.py | 22 +++++- ansible/tests/unit/test_vault_load_secrets.py | 3 +- .../tests/unit/test_vault_load_secrets_v2.py | 77 +++++++++++++++++-- 3 files changed, 92 insertions(+), 10 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index c6eeacd6..99b11753 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -24,6 +24,16 @@ from ansible.module_utils.load_secrets_common import find_dupes, get_version +default_vp_vault_policies = { + "validatedPatternDefaultPolicy": ( + "length=20\n" + 'rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\n' + 'rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\n' + 'rule "charset" { charset = "0123456789" min-chars = 1 }\n' + 'rule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n' + ) +} + class LoadSecretsV2: def __init__(self, module, syaml, namespace, pod): @@ -68,8 +78,14 @@ def _get_backingstore(self): """ return str(self.syaml.get("backingStore", "vault")) - def _get_vault_policies(self): - return self.syaml.get("vaultPolicies", {}) + def _get_vault_policies(self, enable_default_vp_policies=True): + # We start off with the hard-coded default VP policy and add the user-defined ones + if enable_default_vp_policies: + policies = default_vp_vault_policies.copy() + else: + policies = {} + policies.update(self.syaml.get("vaultPolicies", {})) + return policies def _get_secrets(self): return self.syaml.get("secrets", {}) @@ -236,8 +252,6 @@ def sanitize_values(self): self.module.fail_json( f"Currently only the 'vault' backingStore is supported: {backing_store}" ) - # Check if the vault_policies are sane somehow? - # vault_policies = self._get_vault_policies() (ret, msg) = self._validate_secrets() if not ret: diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 10aba108..12deeb3f 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -355,7 +355,6 @@ def test_ensure_fqdn_secrets(self): ] mock_run_command.assert_has_calls(calls) - def test_ensure_check_missing_secrets_errors_out(self): set_module_args( { @@ -383,5 +382,7 @@ def test_ensure_check_missing_secrets_errors_out(self): == "No values_secret_template defined and check_missing_secrets set to True" ) assert mock_run_command.call_count == 0 + + if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index c91ab60a..cf136418 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -135,9 +135,13 @@ def test_ensure_no_vault_policies_is_ok(self, getpass): self.assertTrue( result.exception.args[0]["changed"] ) # ensure result is changed - assert mock_run_command.call_count == 4 + assert mock_run_command.call_count == 5 calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), call( "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/region-one/config-demo secret='value123'\"" # noqa: E501 ), @@ -176,9 +180,13 @@ def test_ensure_policies_are_injected(self, getpass): self.assertTrue( result.exception.args[0]["changed"] ) # ensure result is changed - assert mock_run_command.call_count == 10 + assert mock_run_command.call_count == 11 calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), call( 'echo \'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/basicPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/basicPolicy policy=@/tmp/basicPolicy.hcl\'', # noqa: E501 attempts=3, @@ -338,9 +346,13 @@ def test_ensure_only_generate_passwords_works(self, getpass): self.assertTrue( result.exception.args[0]["changed"] ) # ensure result is changed - assert mock_run_command.call_count == 6 + assert mock_run_command.call_count == 7 calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), call( 'echo \'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/basicPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/basicPolicy policy=@/tmp/basicPolicy.hcl\'', # noqa: E501 attempts=3, @@ -385,9 +397,13 @@ def test_generate_password_base64_works(self, getpass): self.assertTrue( result.exception.args[0]["changed"] ) # ensure result is changed - assert mock_run_command.call_count == 3 + assert mock_run_command.call_count == 4 calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), call( 'echo \'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/basicPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/basicPolicy policy=@/tmp/basicPolicy.hcl\'', # noqa: E501 attempts=3, @@ -454,9 +470,13 @@ def test_password_base64_secret(self, getpass): self.assertTrue( result.exception.args[0]["changed"] ) # ensure result is changed - assert mock_run_command.call_count == 1 + assert mock_run_command.call_count == 2 calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), call( "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret test/config-demo secret='Zm9v'\"" # noqa: E501 ), @@ -482,6 +502,53 @@ def test_ensure_error_on_unsupported_backingstore(self, getpass): == "Currently only the 'vault' backingStore is supported: nonexisting" ) + def test_password_default_vp_policy(self, getpass): + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-defaultvp-policy.yaml" + ), + } + ) + with patch.object( + load_secrets_v2.LoadSecretsV2, "_run_command" + ) as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 6 + + calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + 'echo \'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/basicPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/basicPolicy policy=@/tmp/basicPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret region-one/config-demo secret=-"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/validatedPatternDefaultPolicy/generate | vault kv patch -mount=secret region-one/config-demo secret2=-"' # noqa: E501 + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/validatedPatternDefaultPolicy/generate | vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret2=-"' # noqa: E501 + ), + ] + mock_run_command.assert_has_calls(calls) + if __name__ == "__main__": unittest.main() From 90df3a5ba42e061d680798bd32c33ce0fcfa038b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Nov 2022 12:05:57 +0100 Subject: [PATCH 0670/1263] Forgot to add new test for default vp-policy --- .../v2/values-secret-v2-defaultvp-policy.yaml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml diff --git a/ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml b/ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml new file mode 100644 index 00000000..12d49439 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml @@ -0,0 +1,23 @@ +version: 2.0 + +backingStore: vault + +vaultPolicies: + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + +secrets: + - name: config-demo + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: secret + onMissingValue: generate + vaultPolicy: basicPolicy + - name: secret2 + onMissingValue: generate + vaultPolicy: validatedPatternDefaultPolicy From 290d07676b8f573961219836bb26018a293094b4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Nov 2022 12:14:38 +0100 Subject: [PATCH 0671/1263] Make sure we re-attempt all vault commands in case of failure There are cases where the vault has a small API hiccup and giving up right away is not a great user experience. --- .../plugins/module_utils/load_secrets_v2.py | 6 +- .../tests/unit/test_vault_load_secrets_v2.py | 69 ++++++++++++------- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 99b11753..112a8b09 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -320,7 +320,7 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " f"\"{gen_cmd} | vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=-\"" ) - self._run_command(cmd) + self._run_command(cmd, attempts=3) return # If we're not generating the secret inside the vault directly we either read it from the file ("error") @@ -333,7 +333,7 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " f"\"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}='{secret}'\"" ) - self._run_command(cmd) + self._run_command(cmd, attempts=3) else: # path. we upload files # If we're generating the password then we just push the secret in the vault directly @@ -351,7 +351,7 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=/tmp/vcontent; " f"rm /tmp/vcontent'" ) - self._run_command(cmd) + self._run_command(cmd, attempts=3) # This assumes that self.sanitize_values() has already been called # so we do a lot less validation as it has already happened diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index cf136418..a00100d3 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -143,16 +143,20 @@ def test_ensure_no_vault_policies_is_ok(self, getpass): attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/region-one/config-demo secret='value123'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/region-one/config-demo secret='value123'\"", # noqa: E501 + attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret='value123'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret='value123'\"", # noqa: E501 + attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/region-two/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/region-two/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, ), ] mock_run_command.assert_has_calls(calls) @@ -196,28 +200,36 @@ def test_ensure_policies_are_injected(self, getpass): attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret region-one/config-demo secret=-"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret region-one/config-demo secret=-"', # noqa: E501 + attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"', # noqa: E501 + attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv patch -mount=secret region-one/config-demo secret2='/tmp/ca.crt'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv patch -mount=secret region-one/config-demo secret2='/tmp/ca.crt'\"", # noqa: E501 + attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret2='/tmp/ca.crt'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret2='/tmp/ca.crt'\"", # noqa: E501 + attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret region-one/config-demo ca_crt=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret region-one/config-demo ca_crt=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret region-one/config-demo ca_crt2=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret region-one/config-demo ca_crt2=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt2=/tmp/vcontent; rm /tmp/vcontent'" # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt2=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, ), ] mock_run_command.assert_has_calls(calls) @@ -362,16 +374,20 @@ def test_ensure_only_generate_passwords_works(self, getpass): attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=foo region-one/config-demo secret=-"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=foo region-one/config-demo secret=-"', # noqa: E501 + attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=foo snowflake.blueprints.rhecoeng.com/config-demo secret=-"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=foo snowflake.blueprints.rhecoeng.com/config-demo secret=-"', # noqa: E501 + attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/advancedPolicy/generate | vault kv patch -mount=foo region-one/config-demo secret2=-"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/advancedPolicy/generate | vault kv patch -mount=foo region-one/config-demo secret2=-"', # noqa: E501 + attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/advancedPolicy/generate | vault kv patch -mount=foo snowflake.blueprints.rhecoeng.com/config-demo secret2=-"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/advancedPolicy/generate | vault kv patch -mount=foo snowflake.blueprints.rhecoeng.com/config-demo secret2=-"', # noqa: E501 + attempts=3, ), ] mock_run_command.assert_has_calls(calls) @@ -409,10 +425,12 @@ def test_generate_password_base64_works(self, getpass): attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | base64 --wrap=0 | vault kv put -mount=secret region-one/config-demo secret=-"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | base64 --wrap=0 | vault kv put -mount=secret region-one/config-demo secret=-"', # noqa: E501 + attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | base64 --wrap=0 | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | base64 --wrap=0 | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"', # noqa: E501 + attempts=3, ), ] mock_run_command.assert_has_calls(calls) @@ -478,7 +496,8 @@ def test_password_base64_secret(self, getpass): attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret test/config-demo secret='Zm9v'\"" # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret test/config-demo secret='Zm9v'\"", # noqa: E501 + attempts=3, ), ] mock_run_command.assert_has_calls(calls) @@ -535,16 +554,20 @@ def test_password_default_vp_policy(self, getpass): attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret region-one/config-demo secret=-"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret region-one/config-demo secret=-"', # noqa: E501 + attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"', # noqa: E501 + attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/validatedPatternDefaultPolicy/generate | vault kv patch -mount=secret region-one/config-demo secret2=-"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/validatedPatternDefaultPolicy/generate | vault kv patch -mount=secret region-one/config-demo secret2=-"', # noqa: E501 + attempts=3, ), call( - 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/validatedPatternDefaultPolicy/generate | vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret2=-"' # noqa: E501 + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/validatedPatternDefaultPolicy/generate | vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret2=-"', # noqa: E501 + attempts=3, ), ] mock_run_command.assert_has_calls(calls) From 3b3b5d51db2b1ffcf30e642fa433b2dd0bab2cba Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 24 Nov 2022 12:03:27 +0100 Subject: [PATCH 0672/1263] Add some more documentation --- README.md | 2 +- ansible/roles/vault_utils/README.md | 176 +++++++++++++++++++++++-- examples/secrets/values-secret.v2.yaml | 25 +--- 3 files changed, 170 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index e8f75219..bb60d248 100644 --- a/README.md +++ b/README.md @@ -19,4 +19,4 @@ git merge -s subtree -Xtheirs -Xsubtree=common upstream-common/ha-vault ## Secrets -There are two different secret formats parsed by the ansible bits. Both are documented [here](https://github.com/hybrid-cloud-patterns/common/tree/main/examples/secrets) +There are two different secret formats parsed by the ansible bits. Both are documented [here](https://github.com/hybrid-cloud-patterns/common/tree/main/ansible/roles/vault_utils/README.md) diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index bf914ece..ec7cf691 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -1,15 +1,12 @@ -Role Name -========= +# Role Name Bunch of utilities to manage the vault inside k8s imperatively -Requirements ------------- +## Requirements ansible-galaxy collection install kubernetes.core (formerly known as community.kubernetes) -Role Variables --------------- +## Role Variables Defaults as to where the values-secret.yaml file is and the two ways to connect to a kubernetes cluster (KUBERCONFIG and ~/.kube/config respectively): @@ -50,11 +47,168 @@ unseal_secret: "vaultkeys" unseal_namespace: "imperative" ``` -Dependencies ------------- +## Dependencies This relies on [kubernetes.core](https://docs.ansible.com/ansible/latest/collections/kubernetes/core/k8s_module.html) +## Values secret file format + +Currently this role supports two formats: version 1.0 (which is the assumed default when not specified) and version 2.0. +The latter is more fatureful and supports generating secrets directly into the vault and also prompting the user for a secret. +By default, the first file that will looked up is `~/values-secret-.yaml` and should that not exist it will look +for `~/values-secret.yaml`. + +The values secret yaml files can be encrypted with `ansible-vault`. If the role detects they are encrypted, the password to +decrypt them will be prompted when needed. + +### Version 1.0 + +Here is a well-commented example of a version 1.0 file: + +```yaml +--- +# By default when a top-level 'version: 1.0' is missing it is assumed to be '1.0' +# NEVER COMMIT THESE VALUES TO GIT + +secrets: + # These secrets will be pushed in the vault at secret/hub/test The vault will + # have secret/hub/test with secret1 and secret2 as keys with their associated + # values (secrets) + test: + secret1: foo + secret2: bar + + # This ends up as the s3Secret attribute to the path secret/hub/aws + aws: + s3Secret: test-secret + + # The cluster_xxxx pattern is used for creating externalSecrets that + # will be used by ArgoCD to push manifests to other clusters. + # + # Create a service account with enough permissions and extract the token + # + # CLUSTER_TOKEN=$(oc describe secret -n default argocd-external-token | grep 'token:' | awk '{print$2}') + # CLUSTER_CA=$(oc extract -n openshift-config cm/kube-root-ca.crt --to=- --keys=ca.crt | base64 | awk '{print}' ORS='') + cluster_example: + server: https://api.example.openshiftapps.com:6443 + bearerToken: + +# This will create the vault key secret/hub/testfoo which will have two +# properties 'b64content' and 'content' which will be the base64-encoded +# content and the normal content respectively +files: + testfoo: ~/ca.crt +# These secrets will be pushed in the vault at secret/region1/test The vault will +# have secret/region1/test with secret1 and secret2 as keys with their associated +# values (secrets) +secrets.region1: + test: + secret1: foo1 + secret2: bar1 +# This will create the vault key secret/region2/testbar which will have two +# properties 'b64content' and 'content' which will be the base64-encoded +# content and the normal content respectively +files.region2: + testbar: ~/ca.crt +``` + +### Version 2.0 + +Here is a version 2.0 example file (specifying `version: 2.0` is mandatory in this case): + +```yaml +# NEVER COMMIT THESE VALUES TO GIT (unless your file only uses generated +# passwords or only points to files) + +# Needed to specify the new format (missing version means old version: 1.0 by default) +version: 2.0 + +backingStore: vault # 'vault' is the default when omitted + +# These are the vault policies to be created in the vault +# these are used when we let the vault generate the passwords +# by setting the 'onMissingValue' attribute to 'generate' +# See https://developer.hashicorp.com/vault/docs/concepts/password-policies +vaultPolicies: + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + + advancedPolicy: | + length=20 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + +# This is the mandatory top-level secrets entry +secrets: + # This will create the following keys + attributes: + # - secret/region-one/config-demo: + # secret: ...... + # secretprompt: ...... + # secretprompt2: ...... + # secretfile: ...... + # ca_crt_b64: ...... + # - secret/snowflake.blueprints.rhecoeng.com: + # secret: ...... + # secretprompt: ...... + # secretprompt2: ...... + # secretfile: ...... + # ca_crt_b64: ...... + - name: config-demo + # This is the default and passes the -mount=secret option to the vault commands + vaultMount: secret + # These represent the paths inside the vault maint + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: secret + onMissingValue: generate # One of: error,generate,prompt (generate is only valid for normal secrets) + vaultPolicy: basicPolicy + - name: secretprompt + value: null + onMissingValue: prompt # when prompting for something you need to set either value: null or path: null as + # we need to know if it is a secret plaintext or a file path + description: "Please specify the password for application ABC" + - name: secretprompt2 + value: defaultvalue + onMissingValue: prompt + description: "Please specify the API key for XYZ" + - name: secretprompt3 + onMissingValue: generate + vaultPolicy: validatedPatternDefaultPolicy # This is an always-existing hard-coded policy + - name: secretfile + path: /tmp/ca.crt + onMissingValue: prompt + description: "Insert path to Certificate Authority" + - name: ca_crt + path: /tmp/ca.crt + onMissingValue: error # One of error, prompt (for path). generate makes no sense for file + - name: ca_crt_b64 + path: /tmp/ca.crt + base64: true # defaults to false + onMissingValue: prompt # One of error, prompt (for path). generate makes no sense for file + + - name: config-demo2 + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: ca_crt2 + path: /tmp/ca.crt # this will be the default shown when prompted + description: "Specify the path for ca_crt2" + onMissingValue: prompt # One of error, prompt (for path). generate makes no sense for file + - name: ca_crt + path: /tmp/ca.crt + onMissingValue: error # One of error, prompt (for path). generate makes no sense for file +``` + Internals --------- @@ -68,12 +222,10 @@ Here is the rough high-level algorithm used to unseal the vault: (file_unseal controls this) 5. Configure the vault (should be idempotent) -License -------- +## License Apache -Author Information ------------------- +## Author Information Michele Baldessari diff --git a/examples/secrets/values-secret.v2.yaml b/examples/secrets/values-secret.v2.yaml index 41e2f3a5..53892f8a 100644 --- a/examples/secrets/values-secret.v2.yaml +++ b/examples/secrets/values-secret.v2.yaml @@ -26,21 +26,6 @@ vaultPolicies: # This is the mandatory top-level secrets entry secrets: - # This will create the following keys + attributes: - # - secret/region-one/config-demo: - # secret: ...... - # secretprompt: ...... - # secretprompt2: ...... - # secretfile: ...... - # ca_crt_b64: ...... - # - secret/snowflake.blueprints.rhecoeng.com: - # secret: ...... - # secretprompt: ...... - # secretprompt2: ...... - # secretfile: ...... - # ca_crt_b64: ...... - name: config-demo vaultMount: secret vaultPrefixes: @@ -48,7 +33,7 @@ secrets: - snowflake.blueprints.rhecoeng.com fields: - name: secret - onMissingValue: generate # One of: error,generate,prompt + onMissingValue: generate vaultPolicy: basicPolicy - name: secretprompt value: null @@ -64,11 +49,11 @@ secrets: description: "Insert path to Certificate Authority" - name: ca_crt path: /tmp/ca.crt - onMissingValue: error # One of error, prompt (for path). generate makes no sense for file + onMissingValue: error - name: ca_crt_b64 path: /tmp/ca.crt base64: true # defaults to false - onMissingValue: prompt # One of error, prompt (for path). generate makes no sense for file + onMissingValue: prompt - name: config-demo2 vaultPrefixes: @@ -77,7 +62,7 @@ secrets: fields: - name: ca_crt2 path: null - onMissingValue: prompt # One of error, prompt (for path). generate makes no sense for file + onMissingValue: prompt - name: ca_crt path: /tmp/ca.crt - onMissingValue: error # One of error, prompt (for path). generate makes no sense for file + onMissingValue: error From 988cf3deb495742c32ccbb655b787fda8449dc6a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 24 Nov 2022 12:04:07 +0100 Subject: [PATCH 0673/1263] Add initial v2 schema json file --- .../vault_utils/values-secrets.v2.schema.json | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 ansible/roles/vault_utils/values-secrets.v2.schema.json diff --git a/ansible/roles/vault_utils/values-secrets.v2.schema.json b/ansible/roles/vault_utils/values-secrets.v2.schema.json new file mode 100644 index 00000000..40ef43de --- /dev/null +++ b/ansible/roles/vault_utils/values-secrets.v2.schema.json @@ -0,0 +1,131 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$ref": "#/definitions/valuesSecretsV2", + "definitions": { + "valuesSecretsV2": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "type": [ "number", "string" ], + "description": "Version of the secret specification" + }, + "backingStore": { + "type": "string", + "description": "Secrets backing store type", + "default": "vault" + }, + "vaultPolicies": { + "$ref": "#/definitions/VaultPolicies", + "description": "A dictionary of {name}:{policy} of custom vault password policies" + }, + "secrets": { + "$ref": "#/definitions/Secrets", + "description": "The list of actual secrets to be uploaded in the vault" + } + }, + "required": [ + "version", + "secrets" + ], + "title": "Values Secrets V2 Format" + }, + "VaultPolicies": { + "type": "object", + "description": "A dictionary of {name}:{policy} of custom vault password policies", + "items": { + "$ref": "#/definitions/VaultPolicy" + } + }, + "VaultPolicy": { + "type": "string", + "description": "A password policy to be created in the vault. See https://developer.hashicorp.com/vault/docs/concepts/password-policies" + }, + "Secrets": { + "type": "array", + "description": "The list of secrets to be injected into the vault", + "items": { + "$ref": "#/definitions/Secret" + } + }, + "Secret": { + "type": "object", + "description": "The single secret to be injected into the vault", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "This is the name of the top level key that will be created at the vaultMount point and that will contain one secret per field inside its attributes" + }, + "vaultMount": { + "type": "string", + "description": "This is the vault -mount=<...> mount point used in vault commands", + "default": "secret" + }, + "vaultPrefixes": { + "type": "array", + "description": "This is the list of prefixes the secret will be uploaded to", + "items": { + "type": "string", + "minItems": 1, + "uniqueItems": true + } + }, + "fields": { + "type": "array", + "description": "This is the list of actual secret material that will be placed in a vault key's attributes", + "items": { + "type": "object", + "$ref": "#/definitions/Field", + "minItems": 1, + "uniqueItems": true + } + } + } + }, + "Field": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "onMissingValue" + ], + "properties": { + "name": { + "type": "string", + "description": "This is the name of the attribute inside vault" + }, + "onMissingValue": { + "type": "string", + "description": "'error' will generate an error if the secret (via value or via path attributes) are not defined. 'generate' will create a secret using a defined vaultPolicy. 'prompt' will ask the user for input and it requires to set a value or a path depending if the user should input a secret or a path to a secret file. Non-null entries represent the default value when prompted.", + "enum": [ + "error", + "generate", + "prompt" + ] + }, + "prompt": { + "type": "string", + "description": "Represents the prompt used when onMissingValue is set to prompt" + }, + "value": { + "type": "string", + "description": "Is the value of a secret. Represents the default value when onMissingValue is set to prompt" + }, + "path": { + "type": "string", + "description": "Is the path to a secret file. Represents the default path when onMissingValue is set to prompt" + }, + "vaultPolicy": { + "type": "string", + "description": "When onMissingValue is set to 'generate', uses the policy to create the secret inside the vault directly" + }, + "base64": { + "type": "boolean", + "description": "Before uploading the secret the content is base-64 encoded. It is recommended to set this to true when dealing with files", + "default": "false" + } + } + } + } +} From ab7720f16864dab3d67ee44a2ea7ea3c8311264f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 24 Nov 2022 12:06:27 +0100 Subject: [PATCH 0674/1263] Rename the description field to prompt as that is what it is really used for --- ansible/plugins/module_utils/load_secrets_v2.py | 8 ++++---- examples/secrets/values-secret.v2.yaml | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 112a8b09..d384c6cf 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -115,8 +115,8 @@ def _get_field_kind(self, f): return "" - def _get_field_description(self, f): - return f.get("description", None) + def _get_field_prompt(self, f): + return f.get("prompt", None) def _get_field_base64(self, f): return bool(f.get("base64", False)) @@ -264,7 +264,7 @@ def _get_secret_value(self, name, field): if on_missing_value == "error": return field.get("value") elif on_missing_value == "prompt": - prompt = self._get_field_description(field) + prompt = self._get_field_prompt(field) if prompt is None: prompt = f"Type secret for {name}/{field['name']}: " value = self._get_field_value(field) @@ -279,7 +279,7 @@ def _get_file_path(self, name, field): if on_missing_value == "error": return field.get("path") elif on_missing_value == "prompt": - prompt = self._get_field_description(field) + prompt = self._get_field_prompt(field) path = self._get_field_path(field) if path is None: path = "" diff --git a/examples/secrets/values-secret.v2.yaml b/examples/secrets/values-secret.v2.yaml index 53892f8a..532283c4 100644 --- a/examples/secrets/values-secret.v2.yaml +++ b/examples/secrets/values-secret.v2.yaml @@ -38,15 +38,15 @@ secrets: - name: secretprompt value: null onMissingValue: prompt - description: "Please specify the password for application ABC" + prompt: "Please specify the password for application ABC" - name: secretprompt2 value: defaultvalue onMissingValue: prompt - description: "Please specify the API key for XYZ" + prompt: "Please specify the API key for XYZ" - name: secretfile path: /tmp/ca.crt onMissingValue: prompt - description: "Insert path to Certificate Authority" + prompt: "Insert path to Certificate Authority" - name: ca_crt path: /tmp/ca.crt onMissingValue: error From 4ffd9fb02fc1a86280f42a8eae141ba03ca2a6be Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 24 Nov 2022 12:10:17 +0100 Subject: [PATCH 0675/1263] value and path can be 'null' when onMissingValue is set to 'prompt' --- .../roles/vault_utils/values-secrets.v2.schema.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/values-secrets.v2.schema.json b/ansible/roles/vault_utils/values-secrets.v2.schema.json index 40ef43de..9d408ad0 100644 --- a/ansible/roles/vault_utils/values-secrets.v2.schema.json +++ b/ansible/roles/vault_utils/values-secrets.v2.schema.json @@ -109,11 +109,17 @@ "description": "Represents the prompt used when onMissingValue is set to prompt" }, "value": { - "type": "string", + "type": [ + "string", + "null" + ], "description": "Is the value of a secret. Represents the default value when onMissingValue is set to prompt" }, "path": { - "type": "string", + "type": [ + "string", + "null" + ], "description": "Is the path to a secret file. Represents the default path when onMissingValue is set to prompt" }, "vaultPolicy": { From 3f6cbf5a93ad4fadf9928c21883e59935e4d11de Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 24 Nov 2022 15:42:56 +0100 Subject: [PATCH 0676/1263] Make version a string all the time --- ansible/tests/unit/v2/values-secret-v2-base.yaml | 2 +- ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml | 2 +- ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml | 2 +- .../unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml | 2 +- ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml | 2 +- ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml | 2 +- .../unit/v2/values-secret-v2-nonexisting-backingstore.yaml | 2 +- ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml | 2 +- ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml | 2 +- ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml | 2 +- ansible/tests/unit/v2/values-secret-v2-same-field-names.yaml | 2 +- ansible/tests/unit/v2/values-secret-v2-same-secret-names.yaml | 2 +- ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml | 2 +- .../tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml | 2 +- ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml | 2 +- examples/secrets/values-secret.v1.yaml | 2 +- examples/secrets/values-secret.v2.yaml | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ansible/tests/unit/v2/values-secret-v2-base.yaml b/ansible/tests/unit/v2/values-secret-v2-base.yaml index 7f39e719..bcad9832 100644 --- a/ansible/tests/unit/v2/values-secret-v2-base.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-base.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" backingStore: vault diff --git a/ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml b/ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml index 12d49439..8991237d 100644 --- a/ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" backingStore: vault diff --git a/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml b/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml index 13b54824..6b269ae3 100644 --- a/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" backingStore: vault vaultPolicies: diff --git a/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml b/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml index 184c7da7..75eb1287 100644 --- a/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" backingStore: vault vaultPolicies: diff --git a/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml b/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml index 81129b07..bfd849a9 100644 --- a/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" backingStore: vault vaultPolicies: diff --git a/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml b/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml index d9d113ae..ed290c42 100644 --- a/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" backingStore: vault vaultPolicies: diff --git a/ansible/tests/unit/v2/values-secret-v2-nonexisting-backingstore.yaml b/ansible/tests/unit/v2/values-secret-v2-nonexisting-backingstore.yaml index d6bf54b9..906e3167 100644 --- a/ansible/tests/unit/v2/values-secret-v2-nonexisting-backingstore.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-nonexisting-backingstore.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" backingStore: nonexisting diff --git a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml index 5b8f24cc..3b465700 100644 --- a/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-nopolicies.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" backingStore: vault diff --git a/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml b/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml index 995ce253..df1d420a 100644 --- a/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" secrets: - name: config-demo diff --git a/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml b/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml index 724c8367..80ad08b9 100644 --- a/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" backingStore: vault diff --git a/ansible/tests/unit/v2/values-secret-v2-same-field-names.yaml b/ansible/tests/unit/v2/values-secret-v2-same-field-names.yaml index fb163f3a..4845e269 100644 --- a/ansible/tests/unit/v2/values-secret-v2-same-field-names.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-same-field-names.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" secrets: - name: config-demo diff --git a/ansible/tests/unit/v2/values-secret-v2-same-secret-names.yaml b/ansible/tests/unit/v2/values-secret-v2-same-secret-names.yaml index ccc036ec..3e17e536 100644 --- a/ansible/tests/unit/v2/values-secret-v2-same-secret-names.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-same-secret-names.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" secrets: - name: config-demo diff --git a/ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml b/ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml index 0eeac8ad..b361b34d 100644 --- a/ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-secret-base64.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" secrets: - name: config-demo diff --git a/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml b/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml index 0886e2cb..2d53807e 100644 --- a/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-wrong-onmissingvalue.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" backingStore: vault diff --git a/ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml b/ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml index 0886e2cb..2d53807e 100644 --- a/ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-wrong-vaultpolicy.yaml @@ -1,4 +1,4 @@ -version: 2.0 +version: "2.0" backingStore: vault diff --git a/examples/secrets/values-secret.v1.yaml b/examples/secrets/values-secret.v1.yaml index 30766109..b2d21138 100644 --- a/examples/secrets/values-secret.v1.yaml +++ b/examples/secrets/values-secret.v1.yaml @@ -1,5 +1,5 @@ --- -# By default when a top-level 'version: 1.0' is missing it is assumed to be '1.0' +# By default when a top-level 'version: "1.0"' is missing it is assumed to be '1.0' # NEVER COMMIT THESE VALUES TO GIT secrets: diff --git a/examples/secrets/values-secret.v2.yaml b/examples/secrets/values-secret.v2.yaml index 532283c4..32eb8e13 100644 --- a/examples/secrets/values-secret.v2.yaml +++ b/examples/secrets/values-secret.v2.yaml @@ -2,7 +2,7 @@ # passwords or only points to files) # Needed to specify the new format (missing version means old version: 1.0 by default) -version: 2.0 +version: "2.0" backingStore: vault # 'vault' is the default when omitted From 438efbb8f5804f0ef3bdee403f0115aff17510a9 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 24 Nov 2022 16:55:59 +0100 Subject: [PATCH 0677/1263] Require some more fields --- Makefile | 1 - .../vault_utils/values-secrets.v2.schema.json | 20 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 36653954..5415703d 100644 --- a/Makefile +++ b/Makefile @@ -112,4 +112,3 @@ ansible-unittest: ## run ansible unit tests pytest -r a --fulltrace --color yes ansible/tests/unit/test_*.py .phony: install test - diff --git a/ansible/roles/vault_utils/values-secrets.v2.schema.json b/ansible/roles/vault_utils/values-secrets.v2.schema.json index 9d408ad0..b182e17a 100644 --- a/ansible/roles/vault_utils/values-secrets.v2.schema.json +++ b/ansible/roles/vault_utils/values-secrets.v2.schema.json @@ -1,14 +1,26 @@ { "$schema": "http://json-schema.org/draft-06/schema#", "$ref": "#/definitions/valuesSecretsV2", + "meta:license": [ + "Copyright 2022 Red Hat, Inc. All rights reserved.", + "This file is licensed to you 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" + ], + "title": "Hybrid Cloud Patterns - values-secret.yaml files schema V2", + "description": "This schema defines the values-secret.yaml file as used by [Validated Patterns](https://hybrid-cloud-patterns.io)", + "type": "object", + "examples": [ + ], "definitions": { "valuesSecretsV2": { "type": "object", "additionalProperties": false, "properties": { "version": { - "type": [ "number", "string" ], - "description": "Version of the secret specification" + "type": [ "string", "null" ], + "description": "Version of the secret specification", + "default": "1.0" }, "backingStore": { "type": "string", @@ -25,7 +37,6 @@ } }, "required": [ - "version", "secrets" ], "title": "Values Secrets V2 Format" @@ -52,6 +63,7 @@ "type": "object", "description": "The single secret to be injected into the vault", "additionalProperties": false, + "required": [ "name", "fields", "vaultPrefixes" ], "properties": { "name": { "type": "string", @@ -86,7 +98,7 @@ "Field": { "type": "object", "additionalProperties": false, - "required": [ + "required": [ "name", "onMissingValue" ], From d813f57ba99cc4ba62e26e6fbe855f4f0df2f3fa Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 24 Nov 2022 17:02:48 +0100 Subject: [PATCH 0678/1263] Add some more examples to the schema file --- .../vault_utils/values-secrets.v2.schema.json | 83 ++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/values-secrets.v2.schema.json b/ansible/roles/vault_utils/values-secrets.v2.schema.json index b182e17a..c3a1e21a 100644 --- a/ansible/roles/vault_utils/values-secrets.v2.schema.json +++ b/ansible/roles/vault_utils/values-secrets.v2.schema.json @@ -11,6 +11,79 @@ "description": "This schema defines the values-secret.yaml file as used by [Validated Patterns](https://hybrid-cloud-patterns.io)", "type": "object", "examples": [ + { + "version": "2.0", + "backingStore": "vault", + "vaultPolicies": { + "basicPolicy": "length=10\nrule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 }\nrule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 }\nrule \"charset\" { charset = \"0123456789\" min-chars = 1 }\n", + "advancedPolicy": "length=20\nrule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 }\nrule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 }\nrule \"charset\" { charset = \"0123456789\" min-chars = 1 }\nrule \"charset\" { charset = \"!@#$%^&*\" min-chars = 1 }\n" + }, + "secrets": [ + { + "name": "config-demo", + "vaultMount": "secret", + "vaultPrefixes": [ + "region-one", + "snowflake.blueprints.rhecoeng.com" + ], + "fields": [ + { + "name": "secret", + "onMissingValue": "generate", + "vaultPolicy": "basicPolicy" + }, + { + "name": "secretprompt", + "value": null, + "onMissingValue": "prompt", + "prompt": "Please specify the password for application ABC" + }, + { + "name": "secretprompt2", + "value": "defaultvalue", + "onMissingValue": "prompt", + "prompt": "Please specify the API key for XYZ" + }, + { + "name": "secretfile", + "path": "/tmp/ca.crt", + "onMissingValue": "prompt", + "prompt": "Insert path to Certificate Authority" + }, + { + "name": "ca_crt", + "path": "/tmp/ca.crt", + "onMissingValue": "error" + }, + { + "name": "ca_crt_b64", + "path": "/tmp/ca.crt", + "base64": true, + "onMissingValue": "prompt" + } + ] + }, + { + "name": "config-demo2", + "vaultPrefixes": [ + "region-one", + "snowflake.blueprints.rhecoeng.com" + ], + "fields": [ + { + "name": "ca_crt2", + "path": null, + "onMissingValue": "prompt" + }, + { + "name": "ca_crt", + "path": "/tmp/ca.crt", + "onMissingValue": "error" + } + ] + } + ] + } ], "definitions": { "valuesSecretsV2": { @@ -46,7 +119,15 @@ "description": "A dictionary of {name}:{policy} of custom vault password policies", "items": { "$ref": "#/definitions/VaultPolicy" - } + }, + "examples": [ + { + "vaultPolicies": { + "basicPolicy": "length=10\nrule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 }\nrule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 }\nrule \"charset\" { charset = \"0123456789\" min-chars = 1 }\n", + "advancedPolicy": "length=20\nrule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 }\nrule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 }\nrule \"charset\" { charset = \"0123456789\" min-chars = 1 }\nrule \"charset\" { charset = \"!@#$%^&*\" min-chars = 1 }\n" + } + } + ] }, "VaultPolicy": { "type": "string", From f05e7695783710902a2d3b3d2fc06f5e91cb2987 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 24 Nov 2022 17:20:32 +0100 Subject: [PATCH 0679/1263] Test json schema CI job --- .github/workflows/jsonschema.yaml | 52 ++++++++++++++++++++++++++ examples/secrets/values-secret.v2.yaml | 2 + 2 files changed, 54 insertions(+) create mode 100644 .github/workflows/jsonschema.yaml diff --git a/.github/workflows/jsonschema.yaml b/.github/workflows/jsonschema.yaml new file mode 100644 index 00000000..5a27845f --- /dev/null +++ b/.github/workflows/jsonschema.yaml @@ -0,0 +1,52 @@ +--- +name: Verify json schema + +# +# Documentation: +# https://help.github.com/en/articles/workflow-syntax-for-github-actions +# + +############################# +# Start the job on all push # +############################# +on: [push, pull_request] + +############### +# Set the Job # +############### +jobs: + jsonschema_tests: + # Name the Job + name: Json Schema tests + strategy: + matrix: + python-version: [3.9.15] + # Set the agent to run on + runs-on: ubuntu-latest + + ################## + # Load all steps # + ################## + steps: + ########################## + # Checkout the code base # + ########################## + - name: Checkout Code + uses: actions/checkout@v2 + with: + # Full git history is needed to get a proper list of changed files within `super-linter` + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install check-jsonschema + + - name: Verify json schema + run: | + check-jsonschema --schemafile ./ansible/roles/vault_utils/values-secrets.v2.schema.json examples/secrets/values-secret.v2.yaml diff --git a/examples/secrets/values-secret.v2.yaml b/examples/secrets/values-secret.v2.yaml index 32eb8e13..19ff6e9b 100644 --- a/examples/secrets/values-secret.v2.yaml +++ b/examples/secrets/values-secret.v2.yaml @@ -1,6 +1,8 @@ # NEVER COMMIT THESE VALUES TO GIT (unless your file only uses generated # passwords or only points to files) +# NOTE: If you edit this file, make sure to also reflect the changes in the corresponding +# schema file # Needed to specify the new format (missing version means old version: 1.0 by default) version: "2.0" From e859efbe6e8aa3a2f52c4381b2204f34c37841de Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 25 Nov 2022 09:36:50 +0100 Subject: [PATCH 0680/1263] Fix small ansible lint warning --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 35448ed1..243a179f 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -57,7 +57,7 @@ head -1 "{{ found_file }}" | grep -q \$ANSIBLE_VAULT changed_when: false register: encrypted - failed_when: (encrypted.rc not in [0,1]) + failed_when: (encrypted.rc not in [0, 1]) - name: Set encryption bool fact ansible.builtin.set_fact: From 7918d39b7ecb7d9bd935304fa80b6577951a3a00 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 25 Nov 2022 09:51:31 +0100 Subject: [PATCH 0681/1263] Also try {{ pattern_dir }}/values-secret.yaml.template if all others failed This puts a requirement on the templates that they must have onMissingValue entries so that the loading of secret either succeeds directly when using the template *or* it should fail with an error, prompt the user. We must not wrong secrets that make the pattern fail in silence. --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 243a179f..b195fbc5 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -47,8 +47,7 @@ findme: - "~/values-secret-{{ pattern_name }}.yaml" - "~/values-secret.yaml" - # Note(bandini): Do we always want to load this secret automatically? - # - "{{ pattern_dir }}/values-secret.yaml.template" + - "{{ pattern_dir }}/values-secret.yaml.template" - name: Is found values secret file encrypted no_log: true From 81346e6fe3814d884c387167fcf8fea3983b06a1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 25 Nov 2022 10:00:21 +0100 Subject: [PATCH 0682/1263] Fix Changes.md with a small nit --- Changes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changes.md b/Changes.md index b665cb81..7da77d8b 100644 --- a/Changes.md +++ b/Changes.md @@ -6,6 +6,7 @@ * Now the order of values-secret file lookup is the following: 1. ~/values-secret-.yaml 2. ~/values-secret.yaml + 3. /values-secret.yaml.template * Add support for ansible vault encrypted values-secret files. You can now encrypt your values-secret file at rest with `ansible-vault encrypt ~/values-secret.yaml`. When running `make load-secrets` if an encrypted file is encountered the user will be prompted automatically for the password to decrypt it. From 392ef8fb4c2a01abb59bb4341ec6c43519b821d4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 25 Nov 2022 10:37:24 +0100 Subject: [PATCH 0683/1263] Prompt ansible-vault password via ansible.builtin.pause This is triggered only when the file is detected to be encrypted. It makes for a better UX as the prompt makes it clear for which file we're asking the password. --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index b195fbc5..f6dfff1d 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -62,10 +62,17 @@ ansible.builtin.set_fact: is_encrypted: "{{ encrypted.rc == 0 | bool }}" +- name: Get password for "{{ found_file }}" + ansible.builtin.pause: + prompt: "Input the password for {{ found_file }}" + echo: false + when: is_encrypted + register: vault_pass + - name: Get decrypted content if {{ found_file }} was encrypted no_log: true - ansible.builtin.command: - ansible-vault view "{{ found_file }}" + ansible.builtin.shell: + ansible-vault view --vault-password-file <(cat <<<"{{ vault_pass.user_input }}") "{{ found_file }}" register: values_secret_plaintext when: is_encrypted From c95bafe6bf4979a033946200e640d24cc342f937 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 25 Nov 2022 11:43:56 +0100 Subject: [PATCH 0684/1263] Make the tasks text less intimidating --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index f6dfff1d..ced27e1c 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -76,7 +76,7 @@ register: values_secret_plaintext when: is_encrypted -- name: Loads secrets file into the vault of a cluster (not encrypted) +- name: Loads secrets file into the vault of a cluster no_log: false vault_load_secrets: values_secrets: "{{ found_file }}" @@ -84,7 +84,7 @@ values_secret_template: "{{ secret_template }}" when: not is_encrypted -- name: Loads secrets file into the vault of a cluster (encrypted) +- name: Loads secrets file into the vault of a cluster no_log: false vault_load_secrets: values_secrets_plaintext: "{{ values_secret_plaintext.stdout }}" From 3aa961cb8815f5e57533943aaf8ff809159c831e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 26 Nov 2022 14:51:45 +0100 Subject: [PATCH 0685/1263] Add json schema for the v1 secret format --- .github/workflows/jsonschema.yaml | 1 + .../vault_utils/values-secrets.v1.schema.json | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 ansible/roles/vault_utils/values-secrets.v1.schema.json diff --git a/.github/workflows/jsonschema.yaml b/.github/workflows/jsonschema.yaml index 5a27845f..a89f6ab8 100644 --- a/.github/workflows/jsonschema.yaml +++ b/.github/workflows/jsonschema.yaml @@ -49,4 +49,5 @@ jobs: - name: Verify json schema run: | + check-jsonschema --schemafile ./ansible/roles/vault_utils/values-secrets.v1.schema.json examples/secrets/values-secret.v1.yaml check-jsonschema --schemafile ./ansible/roles/vault_utils/values-secrets.v2.schema.json examples/secrets/values-secret.v2.yaml diff --git a/ansible/roles/vault_utils/values-secrets.v1.schema.json b/ansible/roles/vault_utils/values-secrets.v1.schema.json new file mode 100644 index 00000000..3cb8c530 --- /dev/null +++ b/ansible/roles/vault_utils/values-secrets.v1.schema.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$ref": "#/definitions/valuesSecretsV1", + "meta:license": [ + "Copyright 2022 Red Hat, Inc. All rights reserved.", + "This file is licensed to you 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" + ], + "title": "Hybrid Cloud Patterns - values-secret.yaml files schema V1", + "description": "This schema defines the values-secret.yaml file as used by [Validated Patterns](https://hybrid-cloud-patterns.io)", + "type": "object", + "examples": [], + "definitions": { + "valuesSecretsV1": { + "title": "Values Secrets V1 Format", + "type": "object", + "additionalProperties": true, + "properties": { + "version": { + "type": [ "string", "null" ], + "description": "Version of the secret specification", + "default": "1.0" + } + }, + "patternProperties": { + "secrets[a-z0-9.]*$": { + "type": "object", + "additionalProperties": true + }, + "files[a-z0-9.]*$": { + "type": "object", + "additionalProperties": true + } + } + } + } +} From 3832092790c5806bfc98d2c4642c5a03f45e719b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 26 Nov 2022 15:30:42 +0100 Subject: [PATCH 0686/1263] Allow picking a custom values-secret.yaml file via the VALUES_SECRET env var Tested with: 1. VALUES_SECRET=~/values-secret.yaml.v2 make load-secrets TASK [vault_utils : Set values-secret yaml file to /home/michele/values-secret.yaml.v2] *** 2. make load-secrets TASK [vault_utils : Find first existing values-secret yaml file] ** Both worked correctly --- .../roles/vault_utils/tasks/push_secrets.yaml | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index ced27e1c..d5bb8d15 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -35,12 +35,30 @@ delay: 45 changed_when: false +# Once V1 support is dropped we can remove the whole secret_template support - name: Set secret_template fact + no_log: true ansible.builtin.set_fact: secret_template: "{{ pattern_dir }}/values-secret.yaml.template" +- name: Is a VALUES_SECRET env variable set? + ansible.builtin.set_fact: + custom_env_values_secret: "{{ lookup('ansible.builtin.env', 'VALUES_SECRET') }}" + +- name: Check if VALUES_SECRET file exists + ansible.builtin.stat: + path: "{{ custom_env_values_secret }}" + register: custom_file_values_secret + when: custom_env_values_secret | default('') | length > 0 + +- name: Set values-secret yaml file to {{ custom_file_values_secret.stat.path }} + ansible.builtin.set_fact: + found_file: "{{ custom_file_values_secret.stat.path }}" + when: + - custom_env_values_secret | default('') | length > 0 + - custom_file_values_secret.stat.exists + - name: Find first existing values-secret yaml file - no_log: true ansible.builtin.set_fact: found_file: "{{ lookup('ansible.builtin.first_found', findme) }}" vars: @@ -48,6 +66,7 @@ - "~/values-secret-{{ pattern_name }}.yaml" - "~/values-secret.yaml" - "{{ pattern_dir }}/values-secret.yaml.template" + when: custom_env_values_secret | default('') | length == 0 - name: Is found values secret file encrypted no_log: true @@ -59,6 +78,7 @@ failed_when: (encrypted.rc not in [0, 1]) - name: Set encryption bool fact + no_log: true ansible.builtin.set_fact: is_encrypted: "{{ encrypted.rc == 0 | bool }}" From d871254006fb1717244cd376c3d5b31cec57df9f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 28 Nov 2022 10:31:27 +0100 Subject: [PATCH 0687/1263] Add filter_plugins path in common While it is not needed just yet, we are likely to need a filter plugin sooner rather than later, so best if we just prepare the path for it in common. --- ansible/ansible.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index 3a1afa1c..4cceda11 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -3,4 +3,5 @@ display_skipped_hosts=False localhost_warning=False library=./plugins/modules:~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules roles_path=./roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles -module_utils=~/.ansible/plugins/module_utils:./plugins/module_utils:/usr/share/ansible/plugins/module_utils \ No newline at end of file +module_utils=~/.ansible/plugins/module_utils:./plugins/module_utils:/usr/share/ansible/plugins/module_utils +filter_plugins=~/.ansible/plugins/filter:./plugins/filter:/usr/share/ansible/plugins/filter From fc259037436cb9eb1e068210e92f1eb5fea52efe Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 28 Nov 2022 22:30:54 +0100 Subject: [PATCH 0688/1263] Add override attribute support This attribute defaults to 'false' and it is only valid with onMissingValue set to 'generate'. If a secret already exists in the vault and onMissingValue is set to 'generate' it will not regenerate at every 'make load-secrets' invocation. When set to 'true' we will regenerate the password at every load-secrets call. This seems a default that is saner and more in line with general expectations. --- .../plugins/module_utils/load_secrets_v2.py | 37 +++++++++- ansible/roles/vault_utils/README.md | 3 + .../vault_utils/values-secrets.v2.schema.json | 8 ++- .../tests/unit/test_vault_load_secrets_v2.py | 68 +++++++++++++++++++ .../tests/unit/v2/values-secret-v2-base.yaml | 1 + .../v2/values-secret-v2-defaultvp-policy.yaml | 2 + .../v2/values-secret-v2-generate-base64.yaml | 1 + .../v2/values-secret-v2-onlygenerate.yaml | 2 + .../v2/values-secret-v2-test-override.yaml | 28 ++++++++ .../v2/values-secret-v2-wrong-override.yaml | 11 +++ examples/secrets/values-secret.v2.yaml | 2 + 11 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 ansible/tests/unit/v2/values-secret-v2-test-override.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-wrong-override.yaml diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index d384c6cf..46f9788d 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -121,6 +121,9 @@ def _get_field_prompt(self, f): def _get_field_base64(self, f): return bool(f.get("base64", False)) + def _get_field_override(self, f): + return bool(f.get("override", False)) + def _validate_field(self, f): # These fields are mandatory try: @@ -136,8 +139,9 @@ def _validate_field(self, f): path = self._get_field_path(f) _ = self._get_field_kind(f) - # Test is base64 is a correct boolean (defaults to False) + # Test if base64 is a correct boolean (defaults to False) _ = self._get_field_base64(f) + _ = self._get_field_override(f) vault_policy = f.get("vaultPolicy", None) if vault_policy is not None and vault_policy not in self._get_vault_policies(): @@ -155,6 +159,12 @@ def _validate_field(self, f): if path is not None and not os.path.isfile(os.path.expanduser(path)): return (False, f"Field has non-existing path: {path}") + if "override" in f: + return ( + False, + "'override' attribute requires 'onMissingValue' to be set to 'generate'", + ) + if on_missing_value in ["generate"]: if value is not None: return ( @@ -184,6 +194,12 @@ def _validate_field(self, f): "Secret has onMissingValue set to 'prompt' but has no value nor path fields", ) + if "override" in f: + return ( + False, + "'override' attribute requires 'onMissingValue' to be set to 'generate'", + ) + return (True, "") def _validate_secrets(self): @@ -299,8 +315,21 @@ def _get_file_path(self, name, field): self.module.fail_json("File with wrong onMissingValue") + def _vault_secret_attr_exists(self, mount, prefix, secret_name, attribute): + cmd = ( + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f'"vault kv get -mount={mount} -field={attribute} {prefix}/{secret_name}"' + ) + # we ignore stdout and stderr + (ret, _, _) = self._run_command(cmd, attempts=1) + if ret == 0: + return True + + return False + def _inject_field(self, secret_name, f, mount, prefixes, first=False): on_missing_value = self._get_field_on_missing_value(f) + override = self._get_field_override(f) kind = self._get_field_kind(f) # If we're generating the password then we just push the secret in the vault directly verb = "put" if first else "patch" @@ -316,6 +345,12 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): if b64: gen_cmd += " | base64 --wrap=0" for prefix in prefixes: + # if the override field is False and the secret attribute exists at the prefix then we just + # skip, as we do not want to overwrite the existing secret + if not override and self._vault_secret_attr_exists( + mount, prefix, secret_name, f["name"] + ): + continue cmd = ( f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " f"\"{gen_cmd} | vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=-\"" diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index ec7cf691..8ed3a3a8 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -170,6 +170,9 @@ secrets: fields: - name: secret onMissingValue: generate # One of: error,generate,prompt (generate is only valid for normal secrets) + # This override attribute is false by default. The attribute is only valid with 'generate'. If the secret already exists in the + # vault it won't be changed unless override is set to true + override: true vaultPolicy: basicPolicy - name: secretprompt value: null diff --git a/ansible/roles/vault_utils/values-secrets.v2.schema.json b/ansible/roles/vault_utils/values-secrets.v2.schema.json index c3a1e21a..358eb186 100644 --- a/ansible/roles/vault_utils/values-secrets.v2.schema.json +++ b/ansible/roles/vault_utils/values-secrets.v2.schema.json @@ -30,6 +30,7 @@ { "name": "secret", "onMissingValue": "generate", + "override": true, "vaultPolicy": "basicPolicy" }, { @@ -217,12 +218,17 @@ }, "vaultPolicy": { "type": "string", - "description": "When onMissingValue is set to 'generate', uses the policy to create the secret inside the vault directly" + "description": "When onMissingValue is set to 'generate', uses this policy to create the secret inside the vault directly" }, "base64": { "type": "boolean", "description": "Before uploading the secret the content is base-64 encoded. It is recommended to set this to true when dealing with files", "default": "false" + }, + "override": { + "type": "boolean", + "description": "When onMissingValue is set to 'generate' and the secret already exists in the vault update it", + "default": "false" } } } diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index a00100d3..563b67f3 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -572,6 +572,74 @@ def test_password_default_vp_policy(self, getpass): ] mock_run_command.assert_has_calls(calls) + def test_ensure_error_on_wrong_override(self, getpass): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, + "values-secret-v2-wrong-override.yaml", + ), + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert ( + ret["args"][1] + == "'override' attribute requires 'onMissingValue' to be set to 'generate'" + ) + + def test_ensure_override_works(self, getpass): + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-test-override.yaml" + ), + } + ) + # this will be used for both a secret and a file path + getpass.return_value = "/tmp/ca.crt" + with patch.object( + load_secrets_v2.LoadSecretsV2, "_run_command" + ) as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 5 + + calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + 'echo \'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/basicPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/basicPolicy policy=@/tmp/basicPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/advancedPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv get -mount=secret -field=secret region-one/config-demo"', # noqa: E501 + attempts=1, + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault kv get -mount=secret -field=secret snowflake.blueprints.rhecoeng.com/config-demo"', # noqa: E501 + attempts=1, + ), + ] + mock_run_command.assert_has_calls(calls) + if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-base.yaml b/ansible/tests/unit/v2/values-secret-v2-base.yaml index bcad9832..dd3d67b6 100644 --- a/ansible/tests/unit/v2/values-secret-v2-base.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-base.yaml @@ -24,6 +24,7 @@ secrets: fields: - name: secret onMissingValue: generate + override: true vaultPolicy: basicPolicy - name: secret2 value: null diff --git a/ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml b/ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml index 8991237d..e284d300 100644 --- a/ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-defaultvp-policy.yaml @@ -17,7 +17,9 @@ secrets: fields: - name: secret onMissingValue: generate + override: true vaultPolicy: basicPolicy - name: secret2 onMissingValue: generate + override: true vaultPolicy: validatedPatternDefaultPolicy diff --git a/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml b/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml index ed290c42..eed8b402 100644 --- a/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-generate-base64.yaml @@ -17,4 +17,5 @@ secrets: - name: secret onMissingValue: generate base64: true + override: true vaultPolicy: basicPolicy diff --git a/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml b/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml index 80ad08b9..e72c1e74 100644 --- a/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml @@ -25,7 +25,9 @@ secrets: fields: - name: secret onMissingValue: generate + override: true vaultPolicy: basicPolicy - name: secret2 onMissingValue: generate + override: true vaultPolicy: advancedPolicy diff --git a/ansible/tests/unit/v2/values-secret-v2-test-override.yaml b/ansible/tests/unit/v2/values-secret-v2-test-override.yaml new file mode 100644 index 00000000..04c3c05b --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-test-override.yaml @@ -0,0 +1,28 @@ +version: "2.0" + +backingStore: vault + +vaultPolicies: + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + + advancedPolicy: | + length=20 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + +secrets: + - name: config-demo + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: secret + onMissingValue: generate + override: false + vaultPolicy: basicPolicy diff --git a/ansible/tests/unit/v2/values-secret-v2-wrong-override.yaml b/ansible/tests/unit/v2/values-secret-v2-wrong-override.yaml new file mode 100644 index 00000000..650e93b5 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-wrong-override.yaml @@ -0,0 +1,11 @@ +version: "2.0" + +secrets: + - name: config-demo + vaultPrefixes: + - region-one + fields: + - name: secret + value: null + onMissingValue: prompt + override: true diff --git a/examples/secrets/values-secret.v2.yaml b/examples/secrets/values-secret.v2.yaml index 19ff6e9b..8a2c307a 100644 --- a/examples/secrets/values-secret.v2.yaml +++ b/examples/secrets/values-secret.v2.yaml @@ -3,6 +3,7 @@ # NOTE: If you edit this file, make sure to also reflect the changes in the corresponding # schema file + # Needed to specify the new format (missing version means old version: 1.0 by default) version: "2.0" @@ -36,6 +37,7 @@ secrets: fields: - name: secret onMissingValue: generate + override: true vaultPolicy: basicPolicy - name: secretprompt value: null From 4d7d6e0845fea045b15338ad7c557104efbd6132 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 29 Nov 2022 14:01:31 +0100 Subject: [PATCH 0689/1263] Move to draft-07 of the json schema for V2 This allows us to use if..then..else statements: https://json-schema.org/understanding-json-schema/reference/conditionals.html Now we can require one of 'path' or 'value' attributes whenever 'onMissingValue' is set to 'prompt'. Also when 'onMissingValue is 'generate' we can require 'vaultPolicy' Note that the ifs need to be wrapped inside an allOf in order to not be ignored by the JSON parser (due to multiple keys being the same: 'if', 'then') --- .../vault_utils/values-secrets.v2.schema.json | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/values-secrets.v2.schema.json b/ansible/roles/vault_utils/values-secrets.v2.schema.json index 358eb186..63c6aee9 100644 --- a/ansible/roles/vault_utils/values-secrets.v2.schema.json +++ b/ansible/roles/vault_utils/values-secrets.v2.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-06/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "$ref": "#/definitions/valuesSecretsV2", "meta:license": [ "Copyright 2022 Red Hat, Inc. All rights reserved.", @@ -230,7 +230,32 @@ "description": "When onMissingValue is set to 'generate' and the secret already exists in the vault update it", "default": "false" } - } + }, + "allOf": [ + { + "if": { + "properties": { "onMissingValue": { "const": "prompt" } } + }, + "then": { + "oneOf": [ + { + "required": ["path"] + }, + { + "required": [ "value" ] + } + ] + } + }, + { + "if": { + "properties": { "onMissingValue": { "const": "generate" } } + }, + "then": { + "required": [ "vaultPolicy" ] + } + } + ] } } } From c0124b0786b638071379f8c28e1d80d61a97fb89 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 29 Nov 2022 14:38:53 +0100 Subject: [PATCH 0690/1263] Do not raise exception if secret's existence check fails By default _run_command uses check_rc=True. In the secret existence case, we need to set it to False in order to not raise an exception when the secret is missing. Otherwise we'll get the following error: TASK [vault_utils : Loads secrets file into the vault of a cluster] fatal: [localhost]: FAILED! => {"changed": false, "cmd": "oc exec -n vault vault-0 -i -- sh -c 'vault kv get -mount=secret -field=secret hub/config-demo'", "msg": "No value found at secret/data/hub/config-demo\ncommand terminated with exit code 2", "rc": 2, "stderr": "No value found at secret/data/hub/config-demo\ncommand terminated with exit cod e 2\n", "stderr_lines": ["No value found at secret/data/hub/config-demo", "command terminated with exit code 2"], "stdout": "", "stdout_lines": []} --- ansible/plugins/module_utils/load_secrets_v2.py | 6 +++--- ansible/tests/unit/test_vault_load_secrets_v2.py | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 46f9788d..8ad5d1e2 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -42,7 +42,7 @@ def __init__(self, module, syaml, namespace, pod): self.pod = pod self.syaml = syaml - def _run_command(self, command, attempts=1, sleep=3): + def _run_command(self, command, attempts=1, sleep=3, checkrc=True): """ Runs a command on the host ansible is running on. A failing command will raise an exception in this function directly (due to check=True) @@ -58,7 +58,7 @@ def _run_command(self, command, attempts=1, sleep=3): for attempt in range(attempts): ret = self.module.run_command( command, - check_rc=True, + check_rc=checkrc, use_unsafe_shell=True, environ_update=os.environ.copy(), ) @@ -321,7 +321,7 @@ def _vault_secret_attr_exists(self, mount, prefix, secret_name, attribute): f'"vault kv get -mount={mount} -field={attribute} {prefix}/{secret_name}"' ) # we ignore stdout and stderr - (ret, _, _) = self._run_command(cmd, attempts=1) + (ret, _, _) = self._run_command(cmd, attempts=1, checkrc=False) if ret == 0: return True diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 563b67f3..0ea3af6f 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -632,10 +632,12 @@ def test_ensure_override_works(self, getpass): call( 'oc exec -n vault vault-0 -i -- sh -c "vault kv get -mount=secret -field=secret region-one/config-demo"', # noqa: E501 attempts=1, + checkrc=False, ), call( 'oc exec -n vault vault-0 -i -- sh -c "vault kv get -mount=secret -field=secret snowflake.blueprints.rhecoeng.com/config-demo"', # noqa: E501 attempts=1, + checkrc=False, ), ] mock_run_command.assert_has_calls(calls) From 3523a4dc2b92764ddcc3e6b6afd08c3674ef1ec9 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 29 Nov 2022 14:53:22 +0100 Subject: [PATCH 0691/1263] Upgrade vault-helm to v0.23.0 Tested with MCG main and got a correctly deployed pattern, secrets loading worked and the UI was accessible through the route as well. --- hashicorp-vault/Chart.yaml | 2 +- hashicorp-vault/charts/vault-0.22.1.tgz | Bin 51939 -> 0 bytes hashicorp-vault/charts/vault-0.23.0.tgz | Bin 0 -> 52881 bytes hashicorp-vault/values.yaml | 2 +- ...ault-industrial-edge-factory.expected.yaml | 21 +++++++++--------- ...rp-vault-industrial-edge-hub.expected.yaml | 21 +++++++++--------- ...-vault-medical-diagnosis-hub.expected.yaml | 21 +++++++++--------- tests/hashicorp-vault-naked.expected.yaml | 21 +++++++++--------- tests/hashicorp-vault-normal.expected.yaml | 21 +++++++++--------- 9 files changed, 52 insertions(+), 57 deletions(-) delete mode 100644 hashicorp-vault/charts/vault-0.22.1.tgz create mode 100644 hashicorp-vault/charts/vault-0.23.0.tgz diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index eea57ea0..5e929ec1 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -6,5 +6,5 @@ name: hashicorp-vault version: 0.0.1 dependencies: - name: vault - version: "0.22.1" + version: "0.23.0" repository: "https://helm.releases.hashicorp.com" diff --git a/hashicorp-vault/charts/vault-0.22.1.tgz b/hashicorp-vault/charts/vault-0.22.1.tgz deleted file mode 100644 index 28a41700cf3f76a119b26dde90bfd5c0b237eddc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51939 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%0D4{oncBp$cuPkAdg66` zPV0bIcnih1kL<8KjT#K}fo#Z58$_264te zt{e4rYMD<3T0!GB9CDJ>=MKs_!@-bX2)43e7aCOt)1OrlPYcOKbGZx{KQqro$=CO5 zum^Z%>&o4r8x_1uX-f{I3cJ=9dE$E_hm{!;$Jz7&<5vA42M$ux<3r{h^Gq(Yieso9 z+90;EFScYK5Z0@l_w3)tVaZW-KGMvSc$Ufay{~@fz)Qh?4qG{iY&^`yQHvdXLkj6!nl>YHZIz~MDc54qqCy}#l)*I8+Hwd8jYF5Pa9kkAYa=JV#v*Vl3V7!HF-d-Q#x6{6*pTntB}@Ge*`js6DvwCVCZJd zgux~eVdLXH?>!F}+UsjrIMKp#rY6SJ9G^0fkMS%!2ZZOkFe$ z@6v8kExE0m@u?YwC+M~L>J(M|dc`fhnfHU+yn_X>mlyJ6cx_NFNylh;KppJ*P0Qn zNYZiewQ9$U9r*^zaI59*gWs((uV0Qwbux|G? zwOO0`I>qIQ+K<3U@WbZeA{7-&xIWUqyIdzkk=DaT8r#pD9ZQ6(aTM_r^Wmawu%tZ) z3_T|S7R;8{OVxVHCLHv-Ak%RB3x>p1XAG70Q??J5XrTe<@$uNIDLq?&b}{Ms+!ha7?DM z6Zfop)5rIl()_klEc2m4--=u~{^|8`fBhx(A0S}%Rl{~FfeKgw>3%KsB;urBsxZnN zz@L--s<*KF7u^9R0qf!tluC~oS!!V2h>+iHjS5FVJWQz7(KaHRd3yZ7+AqNLiOzA+lER&CnU?(C36!fV20^`Dc#U;r#M#l zz3XQga{X6tX|Y+*VD&X*Ft#iyD8F(&xI-4Q^c_JIJ><+_J}xNv&(ltUV5NnF-TkBuxhi9bX;dR;@`U~hK8=p&$y zkh~nSM4eV6P={4JOhotmhuJL|umbx$$?JXlQ0*|28nw$~>a1w~azhd7d5-P|mwCK- z@aa37Zh__Wfy|&2o=zKhOriU(hD6sxX37oQgR_np`CTy%jL4*1_92FSqYoN02I-IA zK-Azmr-~5QJ6aCCySx_D(5qoqBJIoZU?Z#9 z*R0`fEi{A$3ipbVtAhRCGdyqvO_8I%pt0*;DeykaH>Kn@Z?-%b?tRB3)x#y6#=m?%`n14uhc;j9rZY!Evr zdDVje7*n6dpaJ)>h}L3Q#m_dE$3{GsH6C_X*y%$byk|`dM_j8J2F4};XCvcDi!OIk zt!4=pCbS863}JgA7>Ttf0O+m_1K_b(II4pIDscW&xFls5Md<3&wUfhv?O?(XyFqp~ zP$STvv6aC@B-e(STDZhdh`!C^ML~jHmXy5uIq7*#GVERw7LLD?L}65I@^N^iJ{Ru4 z!@z9}HRKJ3!QZXz~k0om}wBq)_G9Vjw~j!S7R3+?AAy;=NkBbFgj1`qMPE zDv&TvU{I|-5$*hu;{cZD$C*;6aSSDN&Xe6lGHR^*O6OiG(_EnJB*4x@PeoDLMUB5Z zcF3ukfwRfvv32e8u_x@eshe+4v})BK-=1nNJMwv|?H8&t^#rMr$;;6@UIPv5G84g$>_LcrU>si~^36VsQm%u}~|6WydAR8Wu9< z<5?&wuxWBo(9M+PlZ2irkAVWd9l*V~LpaGgkMm>xeYMZBG@<;nhr17Sf~jnp8$-X- zR0$_z^EZ<2YJz=ojrWd8q;3zB4>k$wng(g^KK8A`lhzOWXB*`3pqIEd%6EE?Etx=X z^qyW(vqBkJA{?c+s9dHLoaW!&8bPMv>>y~x?0J6;A|bxPfMR4ZxI5@LE2@+PT*U7& zq4}l)MNR577_qZAY2MMr*SXcXe%;(Vlczu6*4>>5$2Wby916fs?*F<2}4F=ym5fzjgP3PzWu! zc&95Y(knR&!JNy#$PI1g{m8}wHTSLoOaxrNv+NybFP4|CRf~M2o{NdOw_b?8crjrs zp}WB$%n@HuVd5-xR_P#U2g&N7A@4N%Y==q?l+V;G%6nL(pg-Ca%ITiG-l8w{8&neP zZ8dL+3{|An4{ zLfD})o^HU{IW-OtuLNJ#So4PVGm=itCH_s56h+XHi|pkAOB~$Z`OlggZjYazqv!qi zXiw1Mmkz4sDF-@;)ljxRh3@4V@Q-F z%A3C0l69>=H7_5l`pjzd$WTPqSajAzNQ!aAWtilYinj--xit@YxF#X${!MwH^H8rl zu&6<3%FD1aO-E1mY2^o9zqTUx)wU_T2<8sD&{{}99A4|$7)rrD2xDqiMwgY3IBDY< z!X!eWb$SyUkhlmpJu~Lyjn?rcxkB_4h=Q>43A#iZVdFl{MYsThbBm;n**L{ji5yDK zOm4x+^vq^!AVjl>GCNhF+BjdDQio=L8_rXk$=V72kfa^*?!L7c4>gmbv$EAwkqA5F)N|v9$dZO@N!`Ko0*bX~f007#&B&yt5ck;4v_%0K-@fPZhN(-%^++UM)_nko8$ zf89Qop%C^w#`E~#U;wZbDWmex$+mHY;2kFu;rwoT(MeLWW$HR}|0-8lGnfx18YVMc zSJ8Eufa!n$s)?xn`$vjyQsXtD9tTJu{+EjwrJuB2W!E)fnopBW2h~Jnc9WgGYC4gVJjJ5edn44<)KD^ub5XyevT^Lk3S%K7 z;wYl{phR>F16}fXK9oaIT2Wykc#Iab;gRn&1YhPECDE4g!AZ7A6fbiP~OEy&}wsG0j| zCu(rF`Y&6%2A%!Okz3I+bM)47!2=2g{Nr$L9}IwViFw|ujcq7|dk9#zzzKhAyin3^ zqCOlz+Q+9<<7&o+uU^ zm1E-iwL$4dFjn)*9;4ESnEfSo1@%ZBKg{HUI-IfzWXVhXmE91DOs!%j=%vYNQ$mHH z){_N&K#m(Vz;=Kw+-eTsI>91L6mbBak=BbeXAF{_6XQkLcsnmBdEhCGebLz-bP^Bq z#?y_>8U%K-@$45SGZu;`)s81=3EWo3vs@B+v7cw+39$%~Y-8XXAPh@*fhB!PbRMl| z&BTtLEZn7r6MtvLVfZDY1cM4h_fV5$IiM==rKEYSzsU4Is{RXFYG{1e6HGu;@vC|qy;43-w&K{$K zQ$Gj1^=|L(TssfXGBd|SJM}e2R(Wlv-(iBhfnz~Si9V15V__%WLtTVMq##6%KT}TC zq}DJ`3UZ67+hUcymDI6xSEv*PV5=a;h=7Q71+dQ0W}M*!eeY=pL{A7bRv%Try$}F| zE|pT$QGQr^bZinZ7$${G4d}FG`))f4=Ix+tjUji@LN^oT66n)NO!5`aDO^X13=R@?=4Yc9G5>{eTL(+?%Ljt5{^c=mLTC~{erMsav%fTwb9k2wdAA=0puB6SFpwnx7@9+77Kg3t)d z(NCs?El#K$aDswiOF{eHOsgI?eEp{3Y^0z~a8qe~l7_@osa1f`Bib#_HpwseL982m z%9VB*3QhGYw1Y#E@K|`m0RT3l52%y*NP=BVs)q)RM59)V%wv-3)2qJ#4(^nNcmBFJ zs9g-wMth%$jg=VTEktd|Y?^P>fXk8j(D@jid_LFovhlD{Nko$46?@Us2r_GVS2A=xwbI^%A%v zwFxk#+IIriMJebI;Y8^`^x{r3or`}rI9JO$09(~G&F#pDm1Ok#J=-39NJl-7)kdB? zYJ*B#Pw#kU5D;uTLlb0mi6>KI{U$mlutH^A)op&eZwt91(lccs1~E zI{$U*14IxQOAsm&zDHr9sn)LAoaK;VN;duKb~3dXO0Ady6HyBBPo_Bxbh^20`Q+BK ztGv$+e0)z#5$Z!56;u07aNW6#AHMYRzNAEFOUKCLd$RC>#>dYmZMBCR=SbwYD7?9c{9+^W;$r*0KwZpC8LjE+v(0Wwm)iNpCY@tdfG(9i)oyPx)XeHY8=y z4C4lIXtC|*V3mefxHO6MtWmz4{jF?>W|o^^Zv)|1HW9~Y)T}?lr}>z*KqLy{ka>oM zg?C7YO5tyTpnXz=ubUcmT4bhIfALHC;o49vzm~bfG>Q_%Kyt)*jP$4eE({tX2IZ9y z%BghLaEW0!#}ir8-4uKN=k+N1UiUjT(%fjE!OK0%H7G?0{d)veCkZpCbmk>vi3gey zNq>y@78Jdg*VQz%9fL1w#o8cMjir*oN!N(xOZaEUQC^{DkF}>3TWt=`b%bJspa?KJ zUls=>P#4ibH8FYsRS%01zaR(GqH-U2zFw>bE|q>NVk@VVDb7!`RS9FLhhTwdU?=Xq zA#4(}0neA8^8Rw(78-l6N&otfjJZ?khUc|*Or|Wxq>OF8HM$I$7| z@OiK?s3r$I*%Iy&{u!{wu$2ZGb&nMv2* zA_qQ3k}uqMP2YkjjyKCpF;|X|)f1<7J*^Hs!b1e#%`fulT?JY<%AQQY&k4v$re0zw ztOwoj3L~KE0zWHCtkWnJa&{cZFA}b*^zbkwh6Y453zFOPeUl0jR6>ZNc6tAUY;34aJH2tS3QRjYa3 z_w@U2Ky@bU6WVZu(>$>X>bquWb5BoDn4UO-R^!B<(cKY(+d<#Q%8rE{bJ}%Yw7Se7 ztp~A(w7psOKCXMvmGw>ZcD<*b!E|USD|OP8i@&Ccn&2lF{`C(m`(X|oFTna}-{47E zm_PTA4S3HA5)Zwb_H#c#PWmi0(@!p+yE9QSC@+5ddR0{yR8~I=8hTpW-v0?K0nNIs zaM7{;kf^@_SyA|mnAbrB`e6Y4fDEdBY&Gxt73GEKZzQ34yTDa&wFQvguQYc^>nYjCV;S!2M) z70T`wm6VlmZOuRmC=Lg^?3~hI&9Eque;0V9bYWh+q0#;&jAh{UdYC7m`W}0zD+iIG z$*%l7yF&(~6Rtb6t{Abj^v}$fR~uHw#2#e7uZNBV+V?xDbJ&l6A00V16yRpihJ@@G z89wHIyIkyfDi_t6x;=(?^aF{vRAW*@(LiK6N^5YVk2r}2@!IZTt?o7NT8#-=C|7Fg zjV=D_^t5n?CJw_PQ2i>gh;XqC0St&R;Ov3A5m0vFq;vxuM3rsJDC=+^MOBI{ddy+M zJP0rJGSQrxHouwl4+_b&7LW?`kw-fWUhIJyJpVDPkvd9LZ5Hn*cP1J&U5AlPD9e#7 z3m>K!GS%QfmC1P|7Nayq0G8m&!FH1+zVQrJ(~QAe%flpE*d%5gi!7_-g$fwii=!%d zXC%q-HcIDzjW9>=c|edhato~`1X0W0)(ZZz1u7k`hZ0VCl1>BbLGTN;{;pi(tp?2GC6jm+6m&)pT>~UB}dh$W2_*RW6|Pcwh_0qF3BZy zQ80aHYn1hdDQJlShmOPu)b2)EaVbigP+(G&{l~_~68%|A_&L96;!}>}U(J>&sP6Il$e0v{!+M%qx39_ccYi?N_jOyci%IGghv4e_kt9l0 zu|=DVG9lD?=ppi3zpPwX@Ty4$^AGHQ5L?c(X4k{bhVig6Qb6BjV42Wxn0Oc=B9Y`&5*e{!~W8jC)56JXT>f<(?M(EYuo~r|&=g%uqXL+dcad*P$OFgnwZH;(m z04-txNl6?FjmWg03JS-Qj7lEBdxxD_ZiGuRtSk9H*_u+fJL3wfpN4i43?U*_&S}$B zw=-qLx%K=DxG|LVJuu(s+wYNmV+P)uS=SLMmtm>=mI=Xqcp&9V%$ZySun3bPyyuT0 z2k)WH<=0cJ7d%^gMEze@)Q#V_&K)4E+aRItv1$KQ$s8!4=oOL_0^pIbT==t%R$EA( zooWBD(k0*+Q-m)hqce0rq3Q*a?9V=3>_y3I3L5}^BD-D~@5*dg(T)qWd9>o4LcRnn zB$pSov#D;>IR3Pra_)yOvmY4?i;i4cPB}6i$0Gudoa_W8bIsgQg=uUFpR5(g(v>ka zxZj8@q|{6tY_U}S4^wl(E%3s!;(sdF@FC6M|Bq7x$7>Lenm+iX;PeX$szk^Z7o`K+ z{4;t)38hZHrU9%{+nkWAxR(1DQj?+o1g7C_YMS-=YXOCrJf{5c_Z^yPpEk6Ellafq z2P5Ms!S=4|8qttFqqPL30$#;@Aiy+@!VE74d}e=L`9qA%Fer^GKu$)s!o6)Ae{_;L ziS}H0EXtCi^ff|OJ+Wqj&JyYIKed|?4i5o8!*CDZ#?gSVk%JcI>voad_Psx0I+BDK zV7cwJ)ZmO->4Q7dD5k#tK^x$M0jjD?=8%7qb^a!_pexWHvkEiPXTV-E${RxbqIZ4q-iE+<>imel(|2qy1d} zUzYab5j;Y%S~Ne{iLJ$w^Y<{Lzx}m-QM{LrdACXbn3XYW&9ho0uuY=_ubq?OMm9d* z`gUG?p;I<_VvMKI}YbP~`l4G>oci2q${@~aF_KEnDn>`{KHE7kRf zliZEvj+~XMs&HK;*$B*2N2;bQFI~>^^S~26AYT)pqD^1 z!sno(VDbYSNW5jU#M%6?((pM(@CuJwm)NHlaMYSD!0@+rKuTVeBDD$8KE-_7rkYhi zQQxmHAAN2X-bo_fwf|!D_|oJDqLMfLgu3SxNWd26bu-nc)1LD{WeL1Fzfj!_uI;^4 z70mW^y~7*WWU6YRZ*I!C_%CXZ0Dk{l{s4-C`f?>mo?W4nVrBy~Jsk(*MhW@9(#(mn zcrEc!i)8>IT%TGKATt3#;B~j~3qeFF`{3PgWZ5GAb`ZjfR+;!@6MqL0)J<5&Rm*aQ z21Os3HHXBR%LE$<-kFy*A=H|p2-6xps$`ki#HbNcGT6pV39R8Vv@e*CdDwY{n+N`X zaT%0_r#(5TM{6a*bTA|H2)**}T|zHRj7Li;f{YGECPwKJA%pCB;h7b85AHM&(Jnc6 z+=m0M7;VRO$@thC($UUUn;jNQMx5=A5yi!wY{gV&aCc{e1!EeCn#VFJ6`E5>koV%*uEjH>(XrCk_Rj{$ku*_yywk$>x69%1^(yXrBf@yPm43|LCimt%;3gwl}9) z-4zpN?{ib0t61Krrf-(Vrdshn+T7J8(^;hD=nV45G})@k`3n&aE$r?Nbo zz$}Bi^#4Fa{%X{#?MuugqnV}Zru(LjLwk>u=SFq0uDpr&&*H^0_2E|hl%Yv9Abd9l zpMdgebDRx83wjt^*c?)>w4%F?3iKm{(au6PDPT#Cai3AA1Q=`#k(bQ=k@E;#iku+Vmp`(6MV~xi+oMT@BAv{{>khaW;6wkexjMp4dWNKD%-a-5b zlcnprsXLBbElG+(v47c>KmzUEl%Yg0*%WirLI_#d(7$EgQ!OVkQAbom1G2kGK59T% zv@Xo(;ySABT+1T{+g(Y{1oq-Zv3>PYb!g57!w!#4_IgOqb6{MUkd6qdc}2xOuTJ%; zpk-#yQBv`I4fP*CWc6m$&NT^btziY(5ksLcPsdHlBD)Lk)=;RT9KAR{hEH9#Crt5{ z5X{Q?B_NbSS}Q`BVyY7!Oue3BuD_t@?;r)sXIb%P)x2~GPe-xnUk%od^u4$SRLmWw z_!W%TyrS_OJ10-9;{LSLLS9!9Pelutq{QC}S;=no0L5N6q>7Q0pbZg~z0KTQqPd`M_$0)%5g*XhtmWz_Cu#^du=EoA+JzrZ%6?{!toL0DOW*LIThz0lGhW| zljIiSmX7T-#pTa|8q04~YUyRh_so?fMf8x`aOHKyC-k}sHi}jtZ`6M@D-xgixJk-n z{TT3HF8v!~$Q50mHbQpQk9m&34p8qjerui?CSR!ec;loyv&k#JgfrZ*Jw%IqC!za> z7JB7KG6>gLOIuWB#6{LnAg<^fA5)q>*xrd%d{Q;nY(q;TKdKPj<2rc!)~XV#z&NZ8 zG0R-ygvz++b*dQdP!DQ*I`=x@QiZy-(f{m_s=rTCK-*BkXY(9!0CbZ^a^2vr1*q=h^QDq12VY-_mXe2IV=8!l(B7Bl=(lyRqo^o1t>0X0XJ?(4G7u@umhjA zptIo|sDStXOA5V2kWcr5QV2n)EeRfZ(kdPT`gyiFl!}FZ@pUU`B{> zB+VRhmZ~=yKC9x3j%P84M)|{43dU8ZN7)V7^HdWdxCx;4vFvdAiQ&FaV75eum^Zbj zDEPr7!kmfLYbP>wP{O83jvnaZnw>Fj!&aAI`NVcA0&xo1hZX&iOvb09%@}XDl*)F9 zk=3d_>r@}hl;Bh{#hIQHRfP*OUBer|mwD((Rs+7z(xCyZsU;Xxho32`;ivZ+aO6b? z{du;-wdfOo)>lEnof9a;J{2RC(1XEM%Fyo~GNMTzh5{t|pdxcINqc`Mflk#{aKHANS#wDce! z)c>HMSN}*)VW@b3>? z%s9G+RkIEF`rhFgX|s1p^K?66&T=IazIUV&I@2v3^Dm-SSShVmRM9y^Hz*B&Qd#`U z)LT35gH2UX?3UmE*VF|5ni~6GQ!_x4so(JDUyXyoGn?m^ScxN0K=>IEYa z6IQoyKY&-^Ojqn*9eFBZS$0L&OJOa@+!4PvS#AET2V#LIXXeC_ezi)see8UdlTmkI z=|5Sj^)OJ=j;_unD z)Q0?Sp$q(ssTkHmhZuw3y8m;OKEHGE^#ZPGKlF2Td;0aCev36|ke0&6=WZK3{OmlrtM$EICe8|45w(VAZie|ZQnA)0@PrHR@Ncwo&Q*5IlKaMo4*e4;X` zC3uYaU#bLS04gjM+Bqhxfr&NO<}ajBWguf&*|y(9_1HK$zpam`gjgw^D5$>prg~3^ z4RIaTKyy=4-~yX~kA_MgdB2JP3K0PSJFymM3q=g@Dvm6hT@v zG9|lAabRg`C@b|6akV%!Ot7K_YNkX*oYpe%T8-%@NRnUIPDXUXtz78oybks}*kT z)z&4tHh{IbqLn=82^cpDFnyOD%gOEDYkSm;m$yIx@rLO98o zP&B$GsMHrGT)@>I5vGB(^-)Oip))=!)<;F4Gw!0Cxd%qS+|U%z!rO*_zqyt-k<7b!`~}Ls-1l9tl!w zj%lBoij4J<#odx2cYGy{c2eS3y^XV`q2`dWy`mM$(cW$o;jD6fu}-X&%2*=xp~ZC> z6+LTemPE4y!*R-{@Xze*AblQbD53u^P|;)ESN?wkmFE8f6g-0JBCZt z*QM}qjI6@5;h}%-N?z8o7PW8YJlbFGd6cpfQ4bX65eA$nP6GVj?LwPZAsl4A`gsP? zXl)g0rHp{re`xJ%1x%0EArAPe&KIWE#-&F9mut{`N>5R=56TQ2g-oO{0cmd1^D0m7 zNAGrJ{CTgX{&kIyW*3@a826^5puWMY|`xBP;4KxIVy*D4&yj+Q&>P3+H4I66NAjGl0WHL$Gp zP+L4V^te+KZ#OFb+-IcF&2Bu`NtT7l#vF_T0|y4|7em%df6oX;6xvbKJoD9Tl8fdjHDT!7(oo+W zil_8X{;$#31}$od_A^ogexybtGLN~EYlOQ|BtTdu#tN3oTo4~u`dCpgfGk8aQ3;d& z%<7j=TGq{hxDvy#^Uw%8)W&ly7(2(tK@!!F>sssHFaajg=>;Sa^eNH!ow>*{UNFQ* z2hU+`WMy4mcye(&eS91|9zO>=183i~&@7MGFo7+Gvh~b#d)`q05h;~dSEo5PzrYlw zl8(Uxs6d>WNmg%#UL^V$9kU+v*EHUE*7 zpulnx!$04#l;UicQEd9b(>a1HDpIv{WWDF#(DZ4^a$argLJ6U4BM7brdk0}QoQ@*q z>;ch7rsvn1c!?3#oPQgG$+3_O3s0p`rpX4+| z0MV(@+|+QC^f^-!DQhx2^=Nu%t-%Mf`GYbOd8%?hd!*cpdTS&0WrOj|Ggg0(HSET| zsW1x&#ux;)QdOUcnM(5R(KEShIrdInb{vU>fKb(|MbpQrM`rNX9R^69pGrr#ihIpX+KfvT=NP54I*GD04X zi~TJ}Per`t)pqDonXm*Ja4p?DPoh0MREBz7tu6^9VqErj;UW-k7iTlt2>X`BTgX}* zYa=lv1Q0`{Gmmf2j_Rcu=>Xeuy> zdLp|2@rCM+x1~Ie($W1yVCn<_|1=2ubDtMrlZ!-A9W#Pp`8~NaPKm7S5V@(u3OCMn z`hh7Y#zOF5FPo4|9!s+1qzy)Y;Maw!V`Y$`xR02nN z@)ePfMu_XN!31Wff<6Z&>!NUfTkx8IgkBP5f!XgJE&Q z`K-EXtL7gsX5Ris8O->Do_QbV?FvH6^qgi~-6Z*sOYz_rqs98H3ZVhAo_3>gm}#-s z$t$t3I+M0-q0r^{VFXnpL5ZA7LPEbbIKwFVN@2xAY{nRizZ9O30h!~Eg?w!n)%jFKOd zlf*c9yZ?^>xd=x0Uv3ioU{dCnku&clX+D~)S(Y{{`9PZ>hWbOW^mpnd(>9$JMNaRA$5c$2_!)@-C}~k>Fx2QtOZ?+# zi6!-`OL$VHfH{?Y3Ui~VaJqlE^>V{bZ?44b7vf2evwB=>b$xTAv;MnL=WIWArwkgO zUj@~tC&+`TIbKpV;9WArl2f)wmD8b%{F?9{HbB0KvnwY@td0#yXuG+0TwfAgQs^Ydl)!>+scOcDJAFQM1HZrQcr;A!{yb*R!7`CL(|AW_amdI!l#Au zo=JFjsJW7van>o=@5K$kh!iBlI>v%u9|~A7U6Se|o5!ZNn+*ypHchY$)OEY@zda}P z$yKq9M5+72I5XM_`puG@^h4?#&8|>9L@MGK@=)-%c4+9iK6%j-jRHx74*e2*Q==R7 zTqXxO$Y^(s78Gd6DAVEv?W!ikI(PxaZlV$@Rh3BQJWyX>wPLid8}#9q7G8K$&N56no@`!592&~GlNR>ITf7y8{7@Vm-}4Svgpilz--0cIy2 zx32thqq>~3M!$a8Z;a#m9_1#SL!=5U=$RI4E`Hc2s!xN+jpveRrLsU6t*d6Lh_jD* zSfRshR6l4O$$DEdi92M1!)P}*X0qu=$U5uv35tCFo}O3Id~MpF zY?z*eT_{kzV7XHKoLTz)f?XdV)OC8G>z5C6ogU=6(06upOfvQnwT#uAjh4-F=xo~W z+Am(VSJ&EW%WJC)b9Wq24QI{=0HxS1SM?Rr0lXEISeaHM&RS&eXmzu=)dR8{r7iKS zq-+6bz%TVANG>UfaWf4U^ulO>6%}$zmQ1B)8BL|Gx*_3*QHUa1|8_~O9}87wl`QJD zviK6wt$M%(nYabBw(1@-%l%37wPiKj8;Un+3pQH`pF8rZ2+b|ismPVsy8kOusC@_$N@8;8-TR79_?5a<`mE7&NFEw3qhpep>}?#in7>__!F?xuRd4zOVX6*S zO46;(HKy06BpT1!59oJc&AZbvR`ff1pcAbaxiSO$L#cXkp`@c*F0b9FuIu>wt9kcS zS#eTjMT;!;r4@0UirR`#sPg+q6*lEaP#JZa(WWsZb>kOa&Duj`Dq(sa1AT&ncPiA{ zyc6<`73t?3$vIwp28`iHLuLz7tdJN+%S=n-E6lADEe{98T`v0JWwB^3a`pwA?x&h} z7dr%XrRg2>9}f_AVoPB9J{4PXRyYA6u&l8CdCWDK_W+K>SW?bJw94IhYx zJ!*ZhgketP?lhfx{L`|X%NXtrksb;5e$njXODJU-~gWmbB}03n={qc zbLVQ*bjnz63L90K;`1bx#F;fa?hnl!M*lBX52ZF#)hH@qH(BBWKsR53wp(VN$gLk# z1&EoVFzCtp7z__YJEf480SBC@B2estm3d()Zu>h2`x1?6`4(aEt=HvH*-=zQ3$&O) z%>Fv!X@sxDM3LybrsUq3Bl6>n-0wRSHKC2?Y|MkIL?#bFs@CW##q}yr?PJNTu@D7- z$rW%xXHI;P(gk>3;!e51@+uUJVf>R-wfhZ(T2jX)zopQ+77Yg1K8>PfG|nx{7ia}& zIj_MQx}TJ<7Ae$S*P?K!`fkn<3hVVl;0rCuEp{EVC9)9l(s zK>T6V&rU)cl@MG~7ggrgy^{6`Th5geLxec}Ts3JQ3HLR!_gsVv=Q3p7G{n|TLbOEg z;uo+4hk?=w%N@nQV0oWsr+>?Xm|IRS+a(^2`;jT>_t4k!3th~~SJOPQ%PG7*pTqsy z%KZ`{hw$6(M``|!a=BCK-4x~xs?H++splatA4GCUifygC|=#>3Ic5jfrMu*BoL9&`XWGO@Nq29ma`djCY973cq( zC3L+08k)v7a4-hHnoMN;9}Q1W1pmjA@!6R1|Kw~kJOaaQ3?}=0=lq8@{)8MC6YCtT z#vN$e(f^M6qj7)K(UGe;*!K8u*Yi2nFf_nB=q~Xa;@IF5lOVt)sIw#{ItNS7v)%K- z;Qs!;&*jT@$nQw=`j~XE#=wLv7T8<*v%W^m!IA;gD8PXbNeA8!=HszG9*#8~QG@V$ z{`bl0sg5*iN=BpC@b9zNlhfC)(D3zmz{l|U#=$0Z)*r}%eE!jZ+Fjqa7M(k^z9)|E zo_CG`2TlKVk>)$tThl+0r8#RpJMp`NJeU4uLJKaCi7eI}`W`W1&<0y`2fCi)Yo70* z&YeF)5y_ta&ku}julmE|;q#6K88rk>m-vo`_#mn$uhi(6ileF4^6s^()=+ctF=f^Y z>&WS>!dVUbyom zJJ|nSJYPG2KHX=6_1$yOWoCj}2_^)`fj|&vF%W6nTZOs}ZOoirPWTA|7`FBKFAri1RnI;S*RKs=exr5CcIgKZhci$YPw5t6jC4Mfl1LZV6TMdQ&(~<_RyHgu*Lp9kf_SszgYJu&bKS;%#795UM( zcjwgT`3|~WI>=oTquP;0_%RJa7S#D~M09Ln4@2V;8E`#CG?LJ3M2d|tQ>tL1?-r-) zYZ_(ju8Hhn6A|Cr!dx3jd0DV@$gq;nUS(Jwb(T*1w6v&mQK6)8j@u`B6Tzl-g=c35 zblodxDwuHDV2i*z21tR>bg4^b2Eewd|Cng%uu2mZq^sEhQ8K-J>yfivKS$_U^(UGibD3a#u~_YnBGS zc*wD!!PjsvbX{Ja+{h2}-1~+yU@F#jTTSdKy)1W$=L#087ENr|KG?j(v!Ttil*)tE zEQ*jduy>x}UShX&Knk*=p!?WjlqY#LYN-8iK4)Q`yvn0s2rH$cg%TcBcgwp(b7oG| zqmqVNq}l!lY~mVgJWWbq6x&DihmYJH;WLrB!`V@6JDr_}Oxu874MWiewl~C@;~JIB z?X0ArFW9@bJ|fRq|0Gh!`fC}LW;1+LmQ!5ji!|Y@KO##<&U>1^IIDtargC8WyvV|> z)cako3w*bAdX)%gUwoJEEpMQ=Y&QhfsCewo6T%;TORp1lHS&aXNTs#AR<$}h{d)ce}YcSH8z!!&_;lRB6P(f^hQ)!64$#z z-aT>dT28qSYxFJR+Z*3c`A{?eu;3zBH89{Iey3SDiZ86ECl_<*v2O0$ZMm zO=x_-p4w~|8}&+z1$ZYLHMTmhdo)`TqEI?z|4SNL@7O{rq`5D zT+%3F0l(X&4;^~&U~6*-54QGTYqx^wyS24CTJ6ffP*m*NoLh2-8 zY!71FR&58n=mXY}Eq>5?Cj;4l~D4U9?hlAEsltzA51EQCyjFm~d(;dPcpo-N<<_Zj)wvH@!A)}5wi0s-hf~4m)My^X zod%Y!daHiaqYXoE?C?56#=TnsXBRH4&a|q`Km!lO@L4jVHCzyUQ%TR$jf$ z%cpmJTAHi;V=vt8V9Bbq#b&jwgXwCo#Le$3yLH&HE^U`fP}p)kXf-%UdC&wuua{Y^9kRUmIWqROO2pFR3#2A`&w&{TYZyqnz#3oB;q5S?(Q zSGz?TNaQxKHV$+u#2GpbKZNs701c@s6LbKjWOxbpDe zpQMlvUzAtqarmPAzI;)>f#L9wV`7V|@jo2u*KU-SUN&0_Jlx#2@AL9)o%)e!a1&tZ zPOyzuYY$%|Y;lY&UDDqv{=ek4S3b@mD0g|)S6=bz0{^DN4biVHLt!&at$_Lo-BtBu zj@k#6?b6E3W}cy5(R?G*Q@hr>VIn6|^2!QU3kIaL=B38cV6^QUdWGt!f~3eyQ%C&w zTj%JhJS%4D2H{8P+`%?*4C!-rddmJCot+Mo|Ax=eXmTirXavi!fgzI{9Gdtba3aWDqInoOkpKb?#O|HqT@*_iSF zbTTc)^Abq}b{&#Md(7FR_;)6MH0AHy!3~ElnJLoR)8{*i2M??S7>0FU};4PuY zTQA`X0Ug-}StDIEdC6b6OX3?kFbKH=toTd2(1t{F0kMGZQqUdV8r)-V34Zox)WISu zdLTN24wlG4=bg^d^KAEgFu1?J?{hh&!5fQu`j`wDz(J?e`4{mW;84au7Xj=7R^!&e z*RP=eQT*6vl~M5RTOZsKK!0&8U=jxbI3vEt#}@Q{2|Q;_u>$an4!|%0D_!|+5v;fk z^(DZB7M>&5D{Q&%o*=9LpXsz{fb8}?+h_|%x&EI`MrXr}{=YhzoF4Rl7thzPgTHm& z@MSLmsXp`1CWASR@YdiD9~yX$sXjs5=9<;-{1Z_%@hy$F_rR+Ype>=}f*I1FPgT$* zRPGZ>&3XX}!_cXMQ?)bLcZ4ZKk952x)z=rZHCMvRt0`;C43BFgt=9+d=aF7>AVIOw zs)Ap?9d@Wn829K6SK(EkJt zAGy)XYihe3tfQ!nw84WQ2_pvk6Pr_?CJSFc_c%s?3^#`Kj1T_&hlt+&H*8MAXoP4A z$3Vv!(Lq2-TbyXDsb6ak(53;kHwWFnxW|8SyBTN#uM$j4Fa5idl)eO8A$?D0?$RWg zv6QnmkQ330Z(5YHm$&IkCzGVL`1@0U>jAKf37|$H{Z|)sKX+@*Qq1P<7n=>Ajg`-Z z%;RxON1PT$WvQq?x;MnMeM)Q`jLOA8cQEtPR2PGMUrtvSzYR(Pu_%9La_mw!*azKY zgBQf1Zt?~C1H1^wPG9!}9BjN~%Ja4!O}HE%b$hHiBb{2G<8CkAa$%4eG|CY4dTLa` z*6)0qnhH(Aw&jG$wJM-a*rqm#$~$h6PU6ye6)K6Aaa;g;OLK556 zSJ1iGh%sZ7hET@BQgWzK!^bKV@GBg-U+TyG`fgm-WgGUFk->ytu<4t?@=cnGuprEj zuebx@^;5j$!J6OXKhW9l+_L;6h{aqw4yrbTaqoBp?pb{TSd7p-%!_cCi2db;F<&5= z!6lLX&V^1h6rSE-XaJ36Hf#XlXnq*I3_#PgCa9u>p&-RRWTEgqGURcut6Np!pdFI;~HUhuz6-%yy% zum{HUANH3*A~HpyAhd!G<^y?d5*83dkv2AVwIige3(_}+97Y&MAv zoC(*W@XwtXFAY+gtSv?1OiV}Irah_A3pcT(jTsLbXrq7!OlNx{=#@*qm{zYy zje}RckP+-T<~5n~fEtNaT=By%&^}Ks?TXfvU0JgnR}lLY{&55^=B${O1FZ$h%1`n( z@1oH$QVWR3%zs+ZqC@zwU)drx{q;QcGp8dD?D^|%G_7tg4IweLLin-qNrZ&uCpJo?OO`4be71G12S1l=wA9S;mdwWHL-sqs+iVXCZ>=$l91D zw?~B9eo9>tWs7CMlqKrSF^#jh#dv=MV7T{%bu{6fh~?Qj9gV1Gd_cRhg$QmcMYWiNXL})O%=iu&rqnL6X{6mr;JWiJrI-Ajx{Hib z9D|1xZ8aMV#8R7>{(#$nK*{G0F^^Y*Z%fSp1JASv!N+t&6ZZ3ervUy2jxErA_VwNM z^!D@nn{Qq4Z}8W@0_Itd-(ot{hv3aa)aHM`JuJGlfZtqS7Q+@V9>Ny&A+sS}UHtM^ zGH&+jA#PP4{>9Dv&li`M9}D1zuO8x7_2J)syttaazm?%gzJ5rzu8-^E#ZOX1(YuF4 zJ9QKT-*pBC(V#JyVT&!Q-y!Fi8VzS{6PjK1-K8)_Ve%7rx*v*x7a5@Nb1Z>=6oy+o zJTKLs`U7RrQRQ%|12GuIN%VE}fsZIfbkor+n~v1F(Eie3HBW)q8+^L>^~3Gw%eS9C zf4siF{d{}<^V_S>?=LgE(zyL7ase!LN`5q|8qJ6I(_4yyiPGEYv=~qKxAXIVpT8at zhg~H)-Y+o=6Ju(I{x>Le%#a9k`O>ghn+gw?baEt4WC0B2?3eN=WL}Im9v1|Oqz(gyTQm~ zJdC}AG%n?cTpX=X4ahD~bUcn~ZWrK*2aE*!h6HMbr#)tmHs8 zwu-%I=}wkMsM~B1FA7G|c??YCK{l2+H7u~Zd@hz|_F?^Ub` ztn?G(iNAO2CiBwc3Zou(OIjRu0OFqx_Ruwz!IU$JF8)g*35xJ;CLtCms@w|uENZ?; zkvCtdK8X*UCi^CTmu2EAa0&_~Nq3PLWb;TyE;Q+wra)?VgPn;@YF>{cH>n}03QoFH z1IbYWR3SRm0Xi?^%;)TG)ip2W*hB|>GaOHNxOZ`U%VIVNTOc-J+&I3w5cPX`X(a%y zV&Sv%4@VWvEd1p>A*JA%XS0(9K{|ZF4DYME!)n->AxKPS@?hF9VHf}JD0OY#+C?1k|mQAOS0@JV?_im39PtuU(N!t z0gY)%qPQe2E+p$ug#-`N>$qVeQ;mN-Psv+(MWF~(v!X%*V!t%@t5Z-&096Yo)i@KW zM>&y z2xdQTu8O!7I*fuos)g%o1kqb_R+SW`obSyS6fpBqK+wnk+Nz-eTZk4 zLZhY`wX`F~l+=;YxCn7xQR*s7jI9#a=6X}f5NH_W{>H&8Y@h{t>uS*8X8Ih=p^@2~ zp+OtYuz}e%k>{D2>khHAzp;hOiw_?*mF@73Y<9?oS(fmyL4jXpyMYRnjTL1T9wrDjiEg4QRSuyJh#ID-k@~dm zC2HYp(O=ZUyWgHeCd>+XSa26bt$ZxJpFy6MmDYF_z%@?Uq%fOjZbhKmOW=y2H_GFR zxPOmkOLje?8rOr?7gBDs&4n-W=fH!0yO5yWNT{{#)ag%shKQEEPbi2*yBQ4Ouai_Es$#F_Kl zf^E^wP2r5wH#)?-W`jSvbzqfU_7F;Nt=OiJgc}An#p7=x`icRB=F4#>>((j0(p|OU~&<=S0gIrOF!{%7O%5Ls^hPZ7d5iz}uAtai+Ok7|M?( z)|ZCjW5cDb+K>ksz=i@L1>0C6q>x*xTE!~?(TUFJ##=fBu!JrUwtp?%6CL%e2x(l>P-S^(}^($u=CW)T?YawfM`n!RYV9(Rpnj# zZ``%c{d4=5+-H1;7&HLJIiOyhWu%CAc;4rI=D|$RHXFx}lS*n$Keg`~gOI6QFhk7g z{dQ@rXwx=YesG?xR=oWcHj8}$rtQ;vW}YqjFV4L?7D={`_kevx1Goj#gc6*OADvAg zseOKKdNXCD`6*5PfOn@c^<&fr#>Iu;VlNUHGkYL8QXK+g2_1sHyJnZRDix7AKB$ylwFcU63OyV3a6CRl% zZ?57=3xK)O<|d9b?I?|hYtm>Bl9E$3@IxfzSdKbMlj|4@icZZt(6!9QP76=kl~!aO zOh2Z|k!Crj@|`OVR}<7W=K)PkG`RBt&E@^U<;?8+arf|mKAu^IyBoRuBHo=T1K&#` z+9QG}hgm3hnPD0#Qca+cgD?+1MYo&RxTF@sWt5D@C?NG}i(lfHyLQNrFrx1zt$M;d zz*X|Tfk|7zH#9AtWc54nO52vVw%y;F^nTzpdFyDCxAAC1eC2QBBp2}0Bs7A|=log& z#5n+J;Dsg9xZYeg2Oy8>jp^X5e69f0Nl*Hq#0sXia5O>Xhcp?`1v54Cj-m^pllHxe zn$w6r&TsJIBxsf(8yt79q5=0$xr)MmeigtM-A`oIp*penJe7tG>TJL?LZzo(jMaHR z+le!`mq`;_(l$$;b^Rr4PDhD&5MD-y<9?qZo&}fDP^#~buyLgzLx4r0gD8d@mj+C2 zFu(@e0bKJU8(a3TU@*?8@E+d%&$MDGy@we83{T=&mBpa277Gk2nOy)!Y32BE@yksY zvs~CB)!oZ1emLxcg0$%Va**_*Lx5aU zL;fnq6XH`jmAj96RNy1;JL~Yv(3=Qz{QnO|h?#t{lg`hb$~z*i+~*#JSEA1YRp6Yg zJK)|S1Iz!8p;UgZ02030?qkPf=Q_eGo0SK4fAuf7+(+-`*5AUy*I&Eu@zaMt#s1$Q zUbdxSSqe&A=N4qo`_3`B|2K93U-SEauP!gIt~~YsevA)k7=`9GZW~{ZhdaN}*WoDs z$gPt%gY-P?Qb)@qjsH$Wwe#s!)`^p)>y^eQVC9>^HsjS1;8nvdK00%k9l%Z1|6L?S ziiHiKMb!h&-1%fWkmCx>1civonz3s}iHBH)rz^sT*AiQ#+VVSta0v2IuYG>&zokJw z@M1ddBV}#F+cBk4(Kgz^+8c%XrR_oo(`+fng8nFrN9nmd!ttao*`0X)Q||vc92{__ z`;mbu_kU$|?PcNqudHpXJ>CDu_t@L05Y~iUgX?*~K znvE&Ng2)n2aO|0!?l_0MmW{$HxY5@IN%Cu7kKg^LivAlX*@H-b)9C-o^7`tAPybgy zB7CC%kMemi`VZ7Dv!s4U-FWw=@H5hk-!##891JziP_ohP(+-_LmZ?5e_chlu(Ei1D z4VXMTdoUAXQb^s`dj4D!@7Sqa7m%&bD+b^X=D(tyEL|wf^j@KBw2T1!s}EAZ2YOaL zcK)NYFRgkC&!LieX^4X`?_TXYnD|h*co%Hc!<3~J+CB8WRZd)I@w!f4=-n<(-5o%0 zWi|FsadB4T{wr?)AOHEA!W}5NkUP3!x1+^`t5X>h)Cs&xTy#dKaYC8BDh zPk7lwT-t&s)g9c8JezYU!Oe@1aJFnhIUMwJ4Eu9ki%RG_k%66{fG{&3loQlb7=)Z2 zn?bVxdHNb#Lnm1b6akIXo0X5`;U)amERKk76xZTidT(SN@YjQVOZ}P^?V#OO2iWT+ z1=*5#zLM^z($l@@e0=%3NEq=;f38Y|Ep~Yye@9m?O=#)3n#5t zyjki_pr*82ytfVylL@A{IPZ*rn42t!ePDSvd$jN|@U2#cBOGwM-jXO+U)99MVpVN3jIN zC#xJNO-A9RRBw5kWcf+5ZDRf=;0(@J)yK|%p#se(x3il9|9*TO4WlfBRi8)x0id_} zO|ca;a0hNX#~$$|Y$tGAd;$)CGkCT9YT5hY0z{l~8l7AL<-AJzy%My&wRDP)7vX`O zQR1+_6ZON}V{8!H%chK2@kVh_Z2e4Wcry&=JzE?o8&emlSvOmJyf|DQ529q8&$clC zVSgw*ulUz;;QnGB&ze~p+?PB!L*GB25 z&m5m3{nwnBb~j4%tj*D=_hbQ6<9}Wj=|8cAC;I;gpDOyVI8eg`3O^TPLcM<0CRrG0 z9+1^twv|urm3^64ypJ*aaW~{_+n|MJAG`x)RrvAGR!<#>D(3@%g z8u4GY{K418-KWp}KeN#P`v!qdjsILP#Q&_WuCF}Be?Q9StJDAcM1mIBzyk(=epMFm zm?5AOIOzihftJ8ITNvmQqxs^WTKaF>p5EI4JSF~fW22=1FTY%Vvj0BH=Yi<|La<}o zi~+bUghN(6 znkS>ApIqJ^kI=Ke4Ku;=<6)l7^lo(&V)fI0!}a<&P4mK?gEeOvVWx=9=DdWmIS~Ri z0jupIBxY-`g;4|tQGS!8pQ?B7JTPJ3lR`$bxEFQ9w0sTgc32-CS`pc4-kK8LS$J-S z7|#i_c#)pX;+>v;X7#bFwb7GXZ*~t(PWKM}b9ejXbbs&oWcQ#pa#4%wZ2s$YvjU$n zz_1uFQ$5Z5H>dl%A9wd_XDT}qlFNN;{OYt%0x(=8rA~L16aFegM60OE-R7S7`tL75 zb@Cy9IC*<|xOMV&cHBb!8Y7jjoeV(fi@?)j01ZH$!I` z)KhfYRiE6=v}VP}yZHaxACC4;{&Kqg{@`Ty|E>X9lNM2Namq(hM}t7!O5<*3JpD6W zx842Sce^J?f0;nGn@ckCJga-`u!N~Q{cyYsa?jSz?$IgidHdx3QO#~?^2+fJw%+X? zA8u_|QcF{o&-+}&QNL&F^Ob+&JPXy7JI0uD=exOm_yI1;|Mg+_xDL~5+1?J{2jC;4 z@lo_Qcp#sRli-~Dd++w{HRFCfh-aMd-R`@4?{N?f=Gr69dA~ivjC1U9XZHt^c20KR z9qwA*kXrq)_vUo>|K2-6ihD#v;)kuh{SQa?U&}W6$3bL{^Y_Ce zpf0yRaI(Y90@uWGv2jk^<)EY^xHwU|2Rd2`>zt7&3Ji`3D=z^Yd}<@?J6E)8c>D*NgF=t1HVdpZvd%@u~G{ zB)uJj0pAk_6a%gSld%r$b~p;p<37+kc>l-oc_a^X-o-=U>dn;^tguez$n-X7j>xLL zbPeCyf1d;QT_L@j-NJ|E*}s;57!G~V$*z{VSD`SEf^m;ssepVs3 zCDW^JnEda(R&g!;*C67yfb`4J|FxA;{Ld;p@kIY0idho;v0pM^+ll_Ejp2PsbM zN|jRr>3P}WDg!PqcD%2NWaX}%o!2vL&MwO;EZK^`42xqpnWiypQ@T2HDj&AgpVe$uDqZVL|h*W=N z?%t09XBeTCYm2kh>O^sWk<RdA3L#B?O&h%`$zxO%6~%+ z$!fEXcCQ%lY5M;f2z!P6&-It9Px9Ymd>&T*bA7BOA9dNQT9n%a=P=i-m@y;~g3N;- z#uhN6BsQN8JFh6FTJ{J{!M{mG8%_(jp~)t_@VqeX3U(Fz!^FoXY3^|Bic)2xG|7{0 z(%%eDwhuj)^Tb6SuvR=te!r1_)Mbz57ho?8vS2gefhH3|7q>1{@>$#eqHq64+ycr zAjcx1eg*T-uY{UcofCq#zA@7%jj!W=1mkXUQVD#w5MY>joWiN)2|N*9qcuOux?!Kk zz^~IJ$va5v`~BqRP)FG{582_oIJ~yB)P5I^X6ToC(?nVU|&Sa-5CQ$f;ZSI0J+awnn3^ zbdaP$8p6+5A0r$F$GbF^J0s1uHN?o(Y&;tEBMg`h`?fJ`X=(hO^yg-)*+TF`lAg!C zAnc)>L<^!_Ug9tvV{jY9LpQK=5PseaUTkcvVW8?o#*F?Er_>Npg!B(^7};$GD6hx+ z#Q49&r_o%*9xT9liXH|-auBx$h@X`x6d{mD%mo3kxTf;FmE;ZHg$ zOxnY~CQ8nJkkHh_gJ zSL=)N@pYVF@6%u^4YS8IX*ShxVAcBi8b_puDM-49Gvq@1sb60cmKBj_PoV=3@tH>d zAEF81)c#MaCIA1%+LQn1Q9h4A|L@ZY@Q+#Gc_Is#)Prw+f>^x&qcnkYx{AhG+y0Zx zK6fVp)8xOG%ccC!wY8`C&qw*-EfBrkuf};2q3QS71gP;9iUB+} zwc(`BrEdeaqmt`RCBH1_CEX0x$|aVV2n07*(J;D>xXQmIkYxdYDY*#%0>jIw6MVbm z^%nicj=1?cnDzASOU_V$uisSuAUSZ(k8Y<^=RvmsXv!d=mfJG7eU7QAO4ygi3EK|p zjE5{?|p{zf7f@+IMHkxU+F}bS;DCRp^u_=7zRm zH;=E2#bV2KuC@Cu7lyBTT+5olKSoJBRBMu{nm3^wJPi{|sYuVPGzA0VLM_a!n}(z4 zgo_}|Gm!V99gPe4O7G5My-wDINiNi!ZbiUS&qVK|m+dMsjT)wpm({|1*>yvisvK_1}v2A8Y{p#Qz`T zQ$e~I1-8cd)edak0r(da0)R&jYojl>1ZNit=|T zT|8j3N(uMG_u4(ht9x~!ELhAu7|rI@{SA3?@Aj#q|HQl6CUEOZ+W*&AJjMS! z&S#qb$DO_+6+gAFP_xVqD#C&6d%ftospWqP8l^V>q3#>~WEgcA>q-h_=PEQm)%qN^ zO%B3cs5cLPCpW|IkhUxIsD8Up1T62-7FF@83!?r%+YO_p;IVqD%A)(XJ>lk}`Z+*v z%x^~#s*L!*?=OPqsL*{bNc}v-q8z_{nN=em0>(!>fl-foafS$aI_M|ePr+*ywo4k7 zvn;Da2KlIHpetjjSKN6WgW1w}U>B#oy)-DU2$l-;PQ#15IyjCyvMf$sp}8QBxiSgc zo3=0-V20IMHPlyO3lWA7v}BlX5T%z<@VpmybD&c$(liNY7AK>myDyyNFpcv3_CK>^ zXv?Lomp63(iRYNpgJ{Jc^ZfH`Gpwi2gMMn{KS0NB0{^&uh&-UF^?%^s1^I8~ss7L7 zd}fmW>Lt9AVX`6-*zfMB%9#AQqlh+vQa@Ct&vb!qRyonX9>0G{u(G5Or`J>`*GToC zpDCxNr5QXeN!@9goSl~D+5EM#xZ6{iw=eyfiT>Zy1UxnVXSGQGfg?P{|2@j*w?hAa z%ckY~+LS9VMEo;+@F4NEyBN5|?IJV&{pcI!^FbWp28RpoVN`K=Bm#p&} z>vr|T5xQPU?LqkY!!W!K<31u%B>+dg^N#i#|D{@2j| zt5Cv<9wHBPn*VQoxfuVsvhhU!ALnyl|6g_Rh}_1S1fr^E?x$3A7+I(25DO}$VenSK zuLK{d`^hNjeSsWSn&F8o+c?ykfJG;jiJRF}i_C?TCx_G*x>y#F1KoqP0Q^TqOriIn z=Nxf8Pc`p|(6D#NFPc9m%~N=%%|w8#-2M+|2<-_f8Y^$)sXfRIvYiL)m7BVSZInNW{KnA2Kfr;U%gCZ%!!8)cz8} zOKr$YP_2CGR#=@;MqHbxRM_Ba>{v9+FM{U3X6=6kX6yahC7 zml+z!V{n`#yiExot$N)C>r9(SqdzOdM)5`rX)5nYH%xSo0r?dAb3YjtQ4d>t)!`|F zz_-k3MYh_4LaxoOTrO2Ea!qOG$@DMRwd8y*_%yZFn)(6?l-L5zt1Tz5()V_cS~>>y z_qTD97YYnHc9%f9vK{EbcI@9J6K3I`^?Q{6-R_53W=AT1TO~|S8o^t-DY7>eejaET zf;=<+s|+9%);A3pk;t(IgjK2>E+jlsDpWZ?n1sxg<y$M&3vD83^#w}R)GFB3GE znv09>D^zfZ3Kh|*dcRN-?VL2!eOu``jco~kd5*$1@N*t^Kf%|V-k0}XP!I@DDh}(A zN00lJ-WTnv^eqf99sr5)hVbpS?tZ;;T7|V8%(fC|ju|H{xtiKFb9ledqbpP!Ah$a~YN zMcST$RX;fhegmH=^#4IRfKILdvQ~=!Tw7jwqW_Qa`R&sG`()Jrj@f`shrd&zJ40rc zO!|jyy!pf}zRstf{y&WG7eCYDzt%TO@;|)rME@V-^DCtPexAI|kv|rJ^)akRzY=O* zdD{iPm(`+$<6R4OnY00dsD)Z@uZLQ zBn61g5P=NSA~t0?Z1f{7ce@|PgR(yleLiB8v>U`P_YSxHFAn#1>=)^HxRrf?r7;i; zfT_|103;9NAr_fP@JaGUVp9qPu|1%yL+_&Awi&zwGIWH49wfsfc#ML*_T%CBv(9jG z6=!laf-uYQ1ZXylO3woHsEjkLb+I)XZKZ=G4bl*P#zGh2FgV_&vD_JHwyh!Nrex#M z2)k;)viomLkE_zs_&X|%p*F*X;D;nVk9$GbGle#2m((h&V+@9a81Tm$3kTun&EUnx z#@Ysab&;{C@rYBVQP_Jw?B61!e}Kb~%JXu(e<(?$hW`UB^M^71{mXRzzr0?G|9a~G z_(-3EoxVEuYyRji#-CDb*Zd(`^F&nfq>mX-r79N8>#MM|5(7oiU7rGb?kor?~tdU|2k*O#PuhHT?e}(!R`RYX1N7aw-1%iT^*&=MS6z^~719a`KB&bPt!4 zzYzQs1vepZSzCLO%q-_oH-Qr~^#%hp#^46wx!Y%HM41?e|^50{8YWTeo&%~AMNOE<8od0BfhrWW3cqY*;^WK@iX8IpMiZi6V zh9xQNC^d!Sl}-QlNtS~Nue(;2c7cqyH5PZuQI_p1Pf@#%`KhD-a3&EbOxZn4|I=qO z{a;;KUM<#tUfy_$|9za#10;4k?*GX#1EAguS{-wxesqqaeiU}|aoFeTBZWf!ei444 zjhT%Cb4GhK3&3xI6;#?~EUYHjw2R4XvzO2rCB1jFHgqGihP)XZhH2REM}2a+j^Mj6 zs~lE;G~nXyV4z&Ue=l#v@Lgj*@SnB0iuWnt{{_-?s+F3+OeM>7&=@~`Z^c!jOL3`1 z+2y`Fz3X1K@1Jo3tW-0# zDCsAcx5pzi?QFvwuxhwH+4L9tYTKFiCzTcM ze|&Pfcj*0wW$yqSKX{3=`niaseoq4GD}OtrG7PxPG1rzTjIJ{uzTf$R69cKJnfK$? zhy9b&A734x;tCG7-t9j48dPW@$~t8cxParst?e(o3Zljnma?_8b5ym0sTBh^mzGvm zUv`$^|5i3J{-QZ)mczZOnW`z?RoDSl2VE~dE<0D zzWZXSc8xpjWK1A86dkyNZ?QAG{)gDHG9z{rg8C{t0jA-_et3WMZtG;8^_h|9S>SWT(V`%U7!Rrs2IqG{ z=8oL@STjERV{|(O6ZiMFcMpztPY<_F-d1muPkpJ+122^~Jz?^y?N&mebp1iBDvND} zpb2>r1@7J9b(|(cvstFw-IOuM-I$gHirLq#xiuWY3qbX?TX{lhBg zHu#};_{KZ8(|O1%xuU}vLH4j*frUSFL|xPmx>7l4WduU~Aggy+SsT=uf}md!G9F>G zR?wzFsXn){jwXD}X( zqLM&R$ITal?W?H!NmPRW9*p7EhX_K~uATu5^Km*1mJ9EmTtz|jInLq8VJ&HNftI99 z!k5CnjJjEPD+rbWOLV5vk1LoDmZnKs`el_;!AdVhpRK)3KdOKRKO3#H#j0jYU?z(T z+Ic@~XP@Fx8+ICB+&2F@3!%FRC+df{6UIV)mkwc(6E*|>|=t9dqFe5E*K9uJ~qoX@^M|An$apmbg}(nEY5O`#p$ zBb)c30RCi;PXJ~?SbvQVgs=Xf4}q=)P~I0?{o(G9tp0d&<7z*?h`P7kepDoz654DM zMSDq<4WD7)XE(y8w8wjIK!bco#oU4)jqjx}3d*OT^H7p!6kMXATp*51$y)R#P+43o zjQHc;{(dK*MAE`Q-EfS-o^h7J-Z{sHMgw&($gajYrs-yQ09Qen1-&SZE-|+jb*muG zv#5Vj7?9SDZVnjW`QMYlc^s*+K3nYgD3V6Uc`_>Ay(&iFvVsb2%i|8f94a1J2(~zD zGz_wS6pezb2$Ob`;1Z#ZP#hufV4diC2*S!CuswV?;#BHn6kdYJ*b1@)4)1ROz8nGA z?~|1tpg#zEMH!Jtf!A|R>FotKS8+cANIuJ~t7LdtwMAZal{bc--1uL zn6@I+H_u0LFS%fmV6g&5WsX_lI!dzqI1kGeOU)1Soo49=w<2!M)FllHKXpZp{gG=l zzKTk#HExM2mju5u$^9_J^-!@0PX{t~eAa-nrkB?CLOuUA?wKy8zn3Gdo@yoj(Lcrb zFZ3W~OAk8-MtQy5*r<#D#Sh1StuMb^UBmpZjg^hHe+f1ob{W;5KY09C_4!K_ApY?H zDS*@R|5i4N@!!j9t55lVkMend_`ixszskVx!=zUcrq`?pA!T8GqR_u*6zQm5`X zJK1%&0t(fcV-I10e&t2!`C@515Y58*rrmY^_@@J#sC$(JO|rzHqCt(O*$fvCFB14q z@XWPYJqtji9E|{1K&Zbx2hP^X$?m(uljGMbuF+ul+Wl)h%;SFWbI|-|*bLerDSxAH zc_Mr-@y%quxe@7*4Sg|M6&3g#T zpbwY*u86gFtx3zMDU(tCsFUSU z$nl^*(+C-;3Hd#Uo^thmOP`|rH;x~+_5*&V)qh&sDAs>^@p9v-|L>!G9=iU+IIb!B zV1A%)AHJRMIG$NheDqvEUffsD0U5^=Q$K!1y=5H+=4JKc4yY0r8PGwYz$$)=h3a6& zG+1aCZ$rjB1~yZ@1eo}dY9zo+ddUuFsgD59PpFCDJf*okrOE`A^$aGx;DN7{mSf*M z@d|p(TUKKnPk6VVguU;>KH^nce8o-oc#=M*e3<`tkPOC`!zBF@3~mblUw*M#tpB{e zvi{Wn_fb9z!C{!=J*4V1i~_+8=vcw|IPR0L3>fF9km}V+6uOo7wJKqB#hXIDsC9$92`6!Jp;?E!tkiYT2Ep`GV<^eDQd=r<1&di`6 z52H?_vvYik<$og>VjJ{x7~tdfanOs?tkJoQ^CkK(FTa85LqJnNd4;df|DN)H^~bBr zCH#;6nOzT;?4a1da6AH~uODTNZ#&t|2>yExI_ zZgk>a6fW^|@KpoH+#~KqMxahNPP@QiV_xai_?-U%vPiodcWwv$20pj+*kp?D|MuIh zgEzbT@85I=y@#J;^8MdD!ksfo)}`9wE1i|q&Pwp}o44&(E9-xKu3_bj z@f{HKmaf9=D()ufh(jaW$a$8~9JvH*JH+i*p%;liecDek%W z#t%`Lk5dK@i?W*-aS)t2X{u*VaXT?X`U#4>g5?+Uu3-7aVsYAn*_1_L4Uak_>QsCN zh4tR~t$ipNnHuq|BftRT+Y|x^{X@DQeX)Ji*3@!0K!x~q8_0%}UWO*i4DgmhRdB|D)YG6B zjrz$gnz?1oYjdx84bb=d@ui2l{}+W(oK>froUt1~CS0C-!sSIJH+buwzXv!FwRtfg zwGo=IJok>5*CtY%WhDi78`*3PDCu!D%9)h9l+>jah$Dw-GKk<@$Ju+T=ZEHN7PN?9 zbd$bff=4^xoiy%d9a`+mm#cFw7T&0WGXq2b!61J5YF?TMZy<D;*bM<*Wfv2QviaMCK(Q@W!mn zYWX3iD1&eHe#3HT7)rAgX;*ph>jItixlewbPJSl>^`T3vK8uI{B}7yV3Uoh+M0N0H z;WLsaUw)#6!FL$fI2!fih)Z-PppPJd-IIY?iia<@kN5DJ(qZTZ=Ekx04cve`6qEKL zQF|^JdS>0OXXuwzCnk{GG$vLC>x^P)@DT-?y;0zANvaJ=F5=5E_J2_TAlpXs)xCC6 zFYZiF-X`LLyf;O5hCz@BWE&BCR(xJo`r_rhD}C`2;SKjl)&~FT^uh?VySa$gCQg(+?L#QXjX-I>xaDgxG`}EAynSnPPc~lvd(lsedisg`zYIf6zw(pcuR_O?>Ra|RKbbww9L)g)@#{i4L;%dgp0<#GNIfws_fhYG6pY5ZdFcCE77JOf_NWCNBNvs|u z7r|Nk%vP+9U8X9hgqBB`9{D-%9gAVZ)1(0v_ujeR>X<`n2o*&`iQu!`Srf*r0$1f@ zon?KnM%(#pUo;| zs;wx6aV`Q@gs|Z#Ob|Eb)eqo}3X%XoLT_zeVc1~S3W(bxv8H9t*}b^5mha{!&fhI7 zcQKA$#vR^cyAPm<(zYnoq|3Jgr;V|!1w;IJ39sfwB6!2)52jTi1~=}+Svn^5{rhnb zDI%PNFZHhcyKt1!@i0!$&8}WDyLuOXVw1o&tLK3@Cxhp1Es8}W2oeDx0Ibf6{?tu4<42y@@b?^c!TtbB)D z=Rd>YI7~rQsL@kDIuuG*yVCitv+7YY|92RB9Udr}DWaZ#rlShH_sBexow@JX#+pU! ziN)Y5F)Y$~=S6RLW3hUpEMoK)#w+PK7tjRea@nO;%pYU0m_+;t?F*H6WL3 z+hg-B=Oq?+nAPz}pu^Mn*+~8d0cTn@ys&6dmOG2sp-|+YEYNUh%?X1DlO5x1KoWGs zH!+em!ssIg+xpz7u>RfR1iG|5jV`T}k(aNmhCDIxB?Cm<<)HqfXb6km!>wJTJWPgj zF}GhNsi)8hbDaA^(_u3v5pAE(9mku=H^pt>&|MH zoz<*lxmnGR2k(D62=n^&uKYQce_JtvR*(M?j6q3<7Yl>sV&bAc+^Gg{Ndo z3C`eIlZVZ;sZ%1zA~tx2fHP_V4(MCdX|Ch2h_w1rMkq^nd@@!O9?xyhc!Bnez~YRH z00MaiTfi>lgfGm-i+L{$ys;>2oYy`EZIh>d)G-t8~1N*B+W|M|63MbFxTk)@F>dD+aI`o z%M3!x#*4XG*^5eDK(I=YT_O+yryzNY9T8Fjoee!`pf{o9WY;7WoL4rllL_8fbkvfb zu`7D^HtMHGdNl<_=x9NbUINwmd+1>Pyy&}O-5IX9o{on@<@RB>3sjv8f>7}f^>+f% zPP%Xh&oH&&Oxs!9e<4Ld%WHRi#sCp_pW4D>6@adialc3OQo3%S`$VMiI(R2N!ox*C?>afgY*mxKm@)5hGc`i*rH)Ivg$?^G;uqC-#++?c*Fy(G63nP;kFQZl+$DB_SaU@N`M zSf<&STYK3+?WNK##hec%CrKOQ>Q`*1!{4~%Un~NGpxoRB^^KKfdpu&Z{jUs#59iS5 zj;`BSA#2o@Z{u@V=_Kpk#@uFzja9nwANJHWOGHqer9~gW`rNl>oxGlUFUc2FPgO(e zX?s)XTkG$Yp*lBtSozK%Hb-j#Me7GE>l1~nGeZKcD>eFz{fFWlL^P}(8Blm;zt6hY z$07+{pW8~f{)&`x6Hi2;KAJ>93&E*S#Zc=6Pc(M~);dRE?O6x>q=SDoFT~*uZ+HgH z7CHU3)rE>_nK}9L#NW+(f#40Cco*plSrI@;2Ne@XKaYe8P(igwb#)ba95G^jZqa!C z1yO_jx=*$>fq%09-S)!Y>k_DmGp1z-cGSBnjcRkm=L$8FUVL4Q(0 zus%1C)>jr=^6tFaKfIypB!^e-?0!7m-#b3pJs|)1?(uOz?%eBua*27CxdFHC0M7Bv z)}dfEQ}Af!a7;$SPbh5`ATOa07TT9`z$TBh$5lMd@A(DdGB# zLf%`KVc5QKxjwg8w9aCYnI%mUq(q-EXsm)odTtFs3jBXG*`44UvWAtp3Frz(|5}3i zy@aMXF??s&5*n~JH-TRJj>a#9B2*5(zC1VAS$^fAk5YC2xSSYPiTt#tHJEvQb#9og zZq#F~7yWG`kzks+cenb=ivDMmeRG(`*Kt3(jCQkb*rzRQa@-w>jUR=!_3YYgjWAro z-15~b$yeH!O$u~X-Ug8q`H@aU(hh8UZEl%v?Uj+DUwKYKjU}nF4w8XfMB)Q?B^bvS zjUuiW1O&?(0|=jA&3k9zjp}#|<}Ywc$f(avSP#!KuR;TF(5=zt4LtT3lwdktEJYGz zaJ*=7tdpkK<`!z#Sg7?Z&ox4=BhZUhxHfm_)Eb9QowycbXFexMfGA(6Wya-TnPki@ zK`%S{v}0DN%4y&uP03A9;l?qBvf5hN7~{oTpZ99R8_fBADb(g+qWcc`-xZ!N^1%Uy z4NxSk#ul@8azd(!h8pt<=ao&?D5T3Vb&>Opv`{HiCJdNjL_e--kvg94<=g;yX?&nB zUAN4-bLT5mX9?R8UB)d>#_nx0UEwNE!{O~I`LU4*h+&JCI(K3%ys_A+4#~=m*KlNV z=Ykw$lL0yzZ&9rWnkimFS6AJ0Eoy6XyOGvjR>i~$j6~#p2NK3vX}KIO`%%D1v(GKY ztg#sL-Xw}Dp5URN75$Kc<0u`3@9wj#Y)dW$pRT-|mukWrltt$Ex;v|-kgIb?S*&uD zg@#P^Y|@w5&Z1yE#Q5i1Eb=l+;$iNkX%K*4%uSbHtf@-_$fKK4oL41*xIN7Ykl)SA zg5eD>iq|`+LShN4*A}?6ILCOuHn(qSjeScBWV=K1gs|h7!&X56KC`OP+T7NuwU;Zi z0`~CZwjSTy1mnfJ@9#j8UltucaOltK+)1LVuQm!^iP3F-m7wCtf)7bS?80FuCGBHw zE9;u|O?1QW$HN|Kg49$1DJ)gE?@{Ll{1e2VxW}jFc5&d00$#w}q5o?etGWO~Dbzg4 zf$y>wX4%Rl&9g)Y@<5xh@FM2sR2zm$>QKsdaW)C*GkvWP=pt#?#dJAIpuhe~I7c_aXurXRP@(kNg8 z$lP^qeUbIi@1s22Ecx%$GR8yCtyrv7C6gU9RpZ@y0Yw|BUGy8nLrM~uK`-7k|Ur|c#iAx>)|8J>%) z?lJA37h&Ly#lNDnCKJOf1z&R()Bq*~9H3XEb{tQ$x;pnRSJ!A7PMDw!XR7K;!FVMw zdpwjToLfeMH+aHIu}$-wj{uW7W<;&k=mcQ{FwOg7UaW&RXx?G~WI3Q7j*qn|W&?9? z@Rhqk*NCjX2V3GYII}^golmc*ETYW;;p;W8Yy@vi0C1NII(QMKhR(g+stU4LTbY|d zt-LJhEG8RHU_cR{g0u57a(IJw$RoQri93N^NYd|;zZ*T#x#Qg64ZezEEXy#kh?5wN zPYXcH{t5tO%%O+G>!p0I9Om-ev~u}7Lj?+FL#dezOk%HBpI2uR#=LdcM8(M9bEnV48_uOftAg4bj;Ljp$m`BFvinWtDS{N?y7gxF;N*1g;6Hb_ zPfkTO`*8A>ddZ$u#0(PKnp+5lH=K>uTXXu!<)tQGrqOlM6@_J3qmMHWpPT5f@R|Cc zs9%)_0k?J%O}{obD_C<`fencCVkI>?QayR5uGS0-nwxU2G39(0!aZTrfF+(KcCRS7 zIxho&HxLEsLQ{~dbrNcZN!fU8gb?@=yYo5OX4zFp;s-Fsn<_IMy-7Z+)chzjW@H;}M+S8o~7Ek3YUTZm(_FGh1=; zr-!}S=2No^#v`_~Te!RfRwg(HJ&o{Tw0Y2ME9-VPpprXLSF(Ua^dUvr6Mc3I=s6|Y z38${0X&h|`NQ80vLhRCd%1Evzk#lwER5UF(}lV zL4>Mp$WkVoE+~hlz6?91>6nEBbHEGy_T=Pn3IB5}opgremCk%4@LBL2QCq(ich=dWdC&A%Ucv^peflRsOq`{98Ia&m{(`DfdJCx{ zqfKRAb@0?|RV^lGI2)F3#^8t_@G8@NI5U?-4U(w6sGSnNwqlN*)%8EuUJgD_q>;h7 zq*ChPn_o@*U*Y?@dK8j#`CI}s_6}QtMwqt(H)*pKM0wXW7*Q07)M}y4w77c9cu46l zj9X~ss}h80O;!M6Ws})}wW@LiSf=t^MSzf$d_m>cCKF*Ug$+0csu6TyZ=Y=1MGdXEQo^! zG*$p_sWsUv%LCwv@>el{A?WzBZ3&Imejjm+%cKp1b?XZ2c?EDnrTIo=pbd$bRM6uKyreHq`$v;lX7GH33 zGC&HsMWMO4a-E3om_k}kYl<`MUXcV%eZE<7LmjrJ%msV7)3tCok=aj);842zS=smN z_#^n>X`}2YmyD=$?1v^u(+RY!7AIn`%(jFl#allk`JdR1GIuEwx>~vo7Tc6j-Oz-T z>#wQ$@c?5=@#rL;(~5K03sKiNQtXWx^cI|kp&MZk_rsLMCo_w^#w+F;znska&I*EwR(+!QYmFx~ zuqnn~1M0%^&VkyZJ!&+qVlr5g87ZD9Nl z(~<4>2Pa2+-+wsSJNN=EASU;JSY2CLUN824S$*pN@;D#td@=;7?R=bjR0HWcT>Ebi z>DHhW6K#I`ZSWJ^R}533hbXaw4I3mLCF7K@{4>%9pvjtc$-;{TU_1Tz6Rw_(CW9yiiK5lW!~Q2WA^talAwmG;ByR4C-~t9UzP!q{ zdYGMnfa&h4!SL_)0Sf|>*g%=Oj!F0jv{57;Vun^KrX>iBgDGC)2Ehvi0L4c91l1k zt2lQW-`{GvZgQo-U}Qs1sN831uog)wVVp%Q=O9XS$sC8EY=8vQ{e%WM1UKQl)QT)Y z4`yz_b~Xbw9`<;%9q&>tby*fAHoQkG#(yq=M5X(H1Ee1sCUP8)4ACTaTLVy`7wTxO z%(|x*kxsBj2s27Dy!|Lh+@NQJ?ME#3;p-^+$)%1ykAUz*!!F%|T`UbO0y~T&SW1!! zx9HYIoDJZA&!|W+>53R>av?^Q%Q1*=06G9qFpO?=ar{sMDlE0bm6cO1-rxcr8vsnN z5SZU;#dosyb9_$(Vpeb56>B*{iEmwkCAJ~B=69# zoj@qKq^AW~9=t_bX$EQ?szz5J3H~>>=pcEe(wP`gJIaZ&*ma{7D1acU8iNw} zoM<@w3pE$Co8Q7IN1w9Ama-4+1!L|kR$OVfap-fpUjOYs@Xt^v02~UAq}San1s1$s z0HQp@wu%DTrX-gE@Idz+Ia3@zjV>^Rpvy0F`vH6$2u_xvQ6x)Djc53&C$t;yn1NRx zi#FkH!tQUM=$tnSOJwW;)KVz5*tS@MWq^>MqG*Ie+A{$4hb&z<->@(~N0(tX%V$9* zTL6MJsNiCZiWwkC1gbI2FZmQ?|8FP2v7;V0&qX(9m@;-#Hx$&N8#o(Ii)Kqx~)^HPs19yZvf(O9yN$W2}7X$XO8i~I&CgO ztVR+=J?xGb^tg^n5A&Ck5i+WSvmaxNhD*6DmM$V?Gee+hL2O z3Fl-8aD8yD)uP@;P-(|q>sK#XE;Wxa)G>{Zg=bv=5*^XPGmxw@q>A#OMA-x7HMRHA zPH_gu7j=;WaPN7dV=@%(07cFN5uS5z(I(8(LL8$#O(i355|&L8LL!o2Til5lWrF2EM5D+6G&v^Io88_>JmP6t8zI%x6&HfaUT;=FviygK?1UJY8z z9!hsE`$8s7qjOh@xLd{yVe`mnN#a5|C}?=J-`@sN8jjONS=R2hsm|}FQbOtr{3Uja z=V+&+z1utVPJFKSxHP~IiJw!q0=C_Es;WP)YKK~MMTBIT@j$3D_PExNMu7@BWe;#OmC3>Dsapb@Gdou{0Z z4Xb?f<8Fb03fF2Fro}07Oss_qbhuk?&G6X>_DG`>?bPA9uT6pDgE(oJ{V6D;aAcX zajAlwZ4)vo6?2Y3N94)dzkao|_+woH4|1Pv2;KL(%4o{r}c0p0-1~liSHDcWk zS|)NXZJ{P$u%uf`&^)I~tSafo2n-YrsAck^zn%X{b2R@)ra>$Y4F0?s{0}&DY9k3c zVa!34QhAl$>%!Xp+zkF7ehLYv)jL& zu`eB^N~(4==0t9`C9~MYx0p-y;2}`iUg--oY-xKxiXUQ?nr+4;(Z&v!1Gf)R<^6!!-sUX1(fly@zlL^nWf<@5^Cak&XZh+$v>>7j7*EuoawhPG58 z12&>&jZEq>Fy?P#lyuKY*T?7_v#OAj$I>`sa)$!0n%9JtXyZpg7kH?ICLI0TZ`pi0^aksNj;hMIn(lba~xto08AXI@X!! zkoAHZ#+}){osmJ5#aCs2LI?TEph-*&<`uW*`5A8b%y2>!9~PBZ;cZTd8lBa_39+?^ zY37^_${B`%QioOOIap{4>dDg_Vi|o!w4&Reo=oIlbqPntr#bHsgQug*DCr7PRMsR%~JV<96u84^b zoz<{xN5)vP7Yc_C2|3R=P*Y@ia`^$QgG<5a+xZQlHCrw+hGoPaJjT&qS-gAgrWIZOfWZ- znSi||X5;r$eNFZ3N?hijLDaoUf@ZtjZZ1w|I?q^-2>u)_&%<$O?lP;v|0U7%hNaW4 z99JajN)ZJ_Mj`Am-32s&$CVWvxhBhR`xFroL}3>9$s3yiKZ3a=lE&dEiV;TU5S52r zdfV0k8)!ZKt;;l`=*dQdjHqm@OH@Ek2nJCIEILB#pwprp>jgxYR2?#>v?cP+Pyt2|77TSWP75B39$8rU?Ck`&}wl(nz&G0nDCZ!={l?IX4?&pBa0DQ=s*bvjsx6gkKdAn@pReL`koUIOB)&@l0%Ufk@nPAN9#))~ zg5h|qcS#Qw{LnzZ=7o63h=o=M;bGjoDzzRMTtgZq7X*?G0tukHEhY66>ZROa81sS! z<1+-4g_1xl>{yHWERa#4u(Rcygb5=|DVV-jrMh@h|2@DwU3&K1nYe@pn0iLxc{Xfm zJY;`ZWMfS=;Z&!`BovHv;GTJk_vXS=;A4_YFOTwYp&6(IJA(8Jf^^~Ri@1lD(F?(e zoP(i1scQ(8h~kOU$S-N+eQbA<20y_YrBQhtCMk5aql()@_6VK}y*z1*L3`{Q619ZK zBzX`m(&))(8FMqjrjqi!;^i?Y%GAaQ7N*C)a~?-H72miDuhmDwv>Su7Q_yf)iZ+Ar zb37OisNBLZH!(11{f=mRj5l%_Fy1h^@GzK$lYsXO74u=hE~HXa(dXx{hv% z{^=EiZMmnvvP|3Z?s3)O%ecnfxoIyN;fDB-%QOm4L4#sB1Yv0ECjekDHliQpF23Va z#54xZqO5i?%&GGc{5|;^;<7Lxc=FPW9SR?>M3WU}nTH`A+IByT2M8f1>JCkjnH`vo z_?0fc@*Iu8mhd4?Q*0V)`Lw~7WO=o;21`rEqmvyGqNCf7=4br3Q&_A-PDeD@rFOVe z8O^Bn@$druMCfft3ZeyX1jULp=2j4xtkNLnmoab24>*d zN8=PIbd+g#T^X)tnVS&GAO~_HZ(wJI=p=$0sdkb#NKH5=Xxo?=@d==*{F`79 z#UywF8W3XGP-m+=l)9wtbOCQA_38VLSSaK~@-MEf>Y%Q+(#= ziS1*GGvX-6*J1Y?#>eN5NF$_`sZ}q+l!8UWqoxsDO0#X*Op+E}Fh}ldnUUzm-6Eo7 zqdo?Iz=AGFX6|8aI?}B;`@|eCwMI)YEI-f8I3CqBfeO@;4|dM?8m~Z@#prKGRQH%Z zQyx2>T6M#~5#atiN&be#GDJPjXz-x`4%~i=fr1P}xP?aT)5oxksK7A%%^TiP$1==- z6IsV9YM#5W2#(6%K{1lw#9#p>vy6b8HgPOB6zi}M{L)AQk`j>~>>@B@KN-k%ip(q* zB*(kZk-~dK1JuCoE|`O~EGo$Ed=Tr7*6`-3KtTb*bjgC_E};38-H~KywrB#8IXIhd zhdy1T+JkXIHAzRQLQeKjC3=S2!5C(0tWWthWsh!$sdy}P9PBdguj9YpVYrrLJ z2G8R~Bzj7*9LP&#mpRRGloqi`oJxnq#sVh6?IkT%FNF2Yk#RKw?#&Sx!C z2toeT?evq>cn-!Xs2IJS2v*>=9ffb*+1b7`IN5OJ~+P*kl8)HArB`M&UDd z57{EzId>gMXT4sr!a>pAA$k}tpRlTXmBc0mEpUiTwe^BeEOeAlV{8-=N!P~XJx9Nf zZU*$s)kQ?VDhox~GC}6M5r$&rx%m02-n5`vV^D2$FvS)239BQ_u9GiPeJXS9HTHK& zhtKNR3n3&?xA>C6#nj>niXJl_q6FFa0wpHu#mTs)Rso9!_J^|e<0$Y&-F+1;H$Q}N ziUo?R@Lg;lDDVuWIvuH-F6RG5v;F08N7Pu3lX>x;8bfq1>0(?Vi^CTomsxJ{zj{$9 z8el490x|j;*!hqm(O1zD#X8`)|lG^>a&m0yrim21?%#LLn@C# z=vqfIXS0~WtMx~xRCPFdcDDwNP58tLYIJOiML*h~FBY1Ja!f7w6?>q8C{BEe| z7gf*AM|PFm7%T2AIYE=jgU(!IJ8Nv5bQeOHzGQe3jm6^)fbUnJod^E70x= z1LPV?c*WB$*g56I_lP386tL>Ni}rDhy>lc3k;w}0(V38K_B<*Yf2(lFFf^bZt~J`B zbt??<2`BC;b3tk0a8?tJsnUdREQoX{VU}=@C)RoCbu?H~=h)#86<-pJFs9-Xjq7f_ z35gzAZia&?z1t?%QnQ_c(xG_c92Tv(xXB83;bE<-CS+unVXB$Sn{aI@!aL{pTbixV zv-e5gakZE#M~aa=96V=IOzrZdZ-|fI7TOc>yh;p4)L$#3zNg6vJQbKij;P#-g{$|5 z&ZNL}LZ>c*V*^REbC;z-p~cMiu_0mI*kQQH9?)WD<{d7FSiv-z&>EnLUZz)t~xh6tC5RERH+sVQGG{LA#m$ z2H}js*MaK*jS*@$&Z)BkS4Rh~KTe6w&n7XJmuGCT4lw8z)~uQ^dY#leDvIgjE=Po+ z#^qlplv6ojPvmvIt}J(>i&)upQQDCwTB;(Nzp20x=ql)8hEZXcD2A|E(c1De!&v*V zl>1zCs>(_a#mejKGh-jsO#H67U$}kVaMk{Vkg~f;`6=DK9!KI0qTX%rX^18AE~G*U zT%vAwoQ5E?W(0PSsJB=u)XTMHm5UQR)*>mnO3+Y^iQPy*<@zMb3qmYfz$I}k^rS$e z&=D-~FrcQ?1XWWr8EEvSEs^TpTA%e*0QvOxMev=g13Pz9I!K@X9$Y(=ql`A zXmbGS=FMN3Q!OZej_^fRJ`}Ofv!p+!GEh`FL&F-`Rw(4Cw0*ntR^8T-+z?!ubiu?T ze_-6Jl?%jt8e*IpwTd_P{ zi|SY^XjIdxYe(DdyB06!#@Xxxw$JfM|20nItjG0mxyZ`i(5c9~&Ae)dEB<1r`Ix2j zBdS1PG|IgTDT{(ON-1T4a1<);vOLFoS}BM$lruTzM9Tb;R=RcKd5_na@fxupT90h= z4H9X3i0Gu}>bgl>HJG|dj*22{u3g|{4>n0?}tUqtdXS7T*4CViQA_wyCP&(3$!Yu;jR!>6TYOoOB0IwkSkzu!NRn~ zH=|0K+2`pB>fWyqE(Yo6R=L|?r@qKBjiR$ ziZRt1Y7Ltu3#{b0vnAWx$g$ zQ(zNX%OE*BRg&Rl!WB8tqayUK!ui1ok~tL!KmyEUPlAqyZ3R`AJxRuCP*Sp}SsIS> zVmatx>O2?dH5MEKJQV|?G-Eq%M-35alqmnxO-%-O5z@4A;ezh&EB8&?jiYShhDBV( zr586W!g8$X$y9w2gJR5%xe9mGQ$wnXmxWn`x)pJ(xM0gXUEhttWDEbbBK%Wc$tbtm z#P!fcUG1oGfrEH*B#XH+bIsRt+}OyqIUA2EBC!Cf+|>FBI1q%)&SG)uMer3>PP;{3 za7j;vU2*PFnc*mHOZP4!34f(?$rgY#7X z5snyfb7%Dp2zG^<9FTd(qx=as#GSX$5ehKXx&egK=TRg#j4G$~Z(W}h-6TOdZ@daR zs_1HshPpL|MmA2b(Ix5p0&sajW&@G65GFr~s6|pwm5IJ;oRb z$Nij)39XGRCvx?t7^~%rhA8!(Fi1&Uh9uN5F9t++EUqn;ql#fwGC>@9mj3caFp_|5 zQ4tCyd_@2VnBz#M98h~>Qx?#YO9>MRh~f>~asjBmgB8ZiOa31&2H4A`MkxYjh@&Yy{6(g2hC@WCiFs*`ru2b}ri@QlGKZKtVH| zo{M#7v1^PB{%JG{<5b;alpZR@TDjP&hf3z8K@-k^)=|(nrut;)hry^4n@Ps5W!JHc ziPM)fell#l_`eb^O6 z+~^gpyr23icP7k8?uMHNR094|n-L}=qUhRU>s^*7qY=E-BK|z48ciT(Nx|p==<4HP zmxmX3y~M|oazw{M1+>zxw|H6r+3Aoc7^#Ll?Q$L-k!Hht)YD7L`jF}H#JT|wE;pj@ zp_tKC8-_{ItsITG0JGI)FQ^3}_HD(;G+-KJeu;;m&fbb1PLY=?*x-OBK)}H_-zMbv z`+|OWV>F(Omu?r!6bGd=SAWI;X$d{=vqvG2_$!WM^EYep$B>H(3=#t+6<^Syd@bKP zjTB5o5*x4Z0;^<2cXo?xVn*1a4l`0u-D6YYus=_!gf$z4OqtYinky29-tL_iAxLRC znH5W$aXc~Agh_GJBcyDo$RZTZF=`elUPHt$0kl~pV*#b!S_3tlO{SH-y)}58)D(GD zS29*agX7KH$wga^mzAW9OsQ9m|$u$XWD~v=J1vN$S*XmYfUl z&#S2469s!bjPU+;Bj)br+L+hPS)`HP^C4rLbA`Mx4>A7{yRl<;7!%)$9!u>SMtSsnp*>KW5Z=!lWl(<#HjL~9hTMFw9RpII>= zg zxCtttcfHeK)FMEn6@@q>O{^KkYlMcM$jY1;cx@JMrad%@TrqAR2c2ghUj{4BQtQX}PQ=Mov4Ia2Iektp zU-2Z>=B#OfSSe5QBZGE%EBYQgB^8BrDKG$aSiUALEQkcm(5jF7I z;lB2?b7&JEK+iFLap6a4-XTv)><3a-lOcq41vZzK2D}<7qz6+h!Q@LFs&gBgv^wMZ zAn7!VO;4Y z{pVQPP41Izae~$EELjMx<<59ldp$RrlAWWu^|4Y_Hgy}DtOy5pz6acyJ(G1-Y$}W> zkJpde;&7sic<3YpPQg0Dx2HN1U*W^`AFwM6qWG7YV{-k6o1=Ae?7|Q zWcT>wbno5Po8A9-U99K-=KcFO`@5&xN4q<_2Pb=5`^TkcI{%e{^t`?Q{=?4k&X1?t z?~is*50Bpe=kE52_iQh^Zm+_juB|4;$>e-eYS(BbLo`7*0M%dVLL)) z50u<2YWLC(*0gMFZEx=$o@^a#@1Ei=U~z7h^aZ5ZsG(39b*Ebf&)jKzIM_Sk7f*lK z+dtVo`j6MoI^RBP*abf0nqIopV!d@K^UZ7lZONvr`u_Xvt^L#T`l#FyPT)RGVp-$I zy@Q?8?fnnOu#nS(t#{sWm{&f%GQtSI@#CxG(~rAH$9wM&;DwdW>IT;0Sdc7GU>dXNX-Jhzy*NNp*eZ+|%2**jXC+?hh3l1cEKojItr z0?mLu60QU7tYEt-`12C3(BviTSPbl2ZXJJ`7T?R}oP|e-us%5JAo;%GmEBT>Og1yb1h`_*n731Vk=ehM@o8(e*HDJh}B+1V{yI zZ8*T^A`cdk^J_zMww=r3+e+YIw7;}J=hDYoY+ zUVC_hl^&`PFWH)fRz-Z>xrR0?tKPkLW{Zc{$)~8-@QK23CaQ4R?I+_NcLk83gLij^n#JNW z&`QU-`A|Uller)isuB1NO5C5>@zA~wQ>Fd&7)?*%c&FWdO!Y{EHU?d_sc*-1RGg*; zInj>vkRVnHSq5-{s%*L&*SEYK;F68C&6A;~T`SX>%d$C`j)uTR$uL>)Pi*`NIQqAs z`Tux(Xx}!r$>{1UI%9-;|LYVU6ien=Eow^9206kf4Gr zgkuYJBB=`&!(C{kF%6m(+f=>gb509sS53j(iyR;bF;9Ty?>#Wdqq@~eYX<;rHX5D7 zx9<=BvKe^dV@U+E;*x1MRiR$eX_ z_5anE>reXsV|*6eo~YZX95xz%4mNK>@`7##UI*}RzumlF2XHhgH}28`oYvP0PRotJ z18oF;8Z@d}f&X0H3;eIo2cX^N$!7J>i`v8)yZB@=MLL@LT~w^%Ldw(Fdb&;vEiSb{ zjpHy5$L$%0E8{Hz^|Bqiv{@K;;Nr}NEEr(0#i&o7)dm(cBr9;m>)7@!;zATs;zPRO zsIwFMMq-38_My>QbD=E>0#iH7#|)246Y@^OVNY}IncP6P6)%FGvCA->+A$v=eGk$m z8v)=h)?I*KUYO{Sh&r7(p-xCkSmc`8zkPa@VSk-QxliW_hi#ExANP=vtG*Td{I;sk z-_oM-!8jdA@zk)*VWZsJ4oxL_E)C(FpK)UZIPR1|{dg&I0L!4DyOcmHb8ZG9wZQHhO+qP}&wrzLswr$(CZQIU1CnGmEH#eX1A8M?sRrS1c26^5k z{AC&mv3aN%NS224flf=-)4uF_d$sRPvCno?=c0ZSo@2YlrYQ4|H3fhu}BhH{jy3Z5Shj^fXj;t3czFyen_@ z(gtP{U$>S@gEdvD!KEk8bp%&|k&*aNJjDJ)i}5fOcSaP=^hD?S%bs~MOBMoX5RC>m z*bm=|Q%rlAZv?=UbCc-6L%~9ORFV_6^Ss|On+=sgsahA4(%s{+MJ1d0ro^MiFOB7~ zYY)pO%1d65Il2eE@o$Xwo@m+|-BCM=u3cc#aPTO1EVQ|}@S6GK=<>b!kH!zO$Uw=m ze=_xz3BTb=+_U)FYk!CAO+Y+SJ4&$qGj0PxiXiA2@*!6EuF?B+Y>(B zt9M_v@ldu{(5AG(r(}>^7NBSlYm+eJ55)~Ytcv16Hzv#C8Im!SUA^`=0wjDL*B*O{ zk6r)>t7R|`h+=aD>1f*K;bU8b4Pq$%iT7)6F~gvhxoC;O=@RgDv0W;?&(F^HsP=CE zU3FNF@xgbPbZ>o+R@*N&^}ikM=08h4kXD%(4Eh(2s3Axaj#gF23CnH$iz4;%{KtKZ zyB9%19}|@soG`=aN!za8XxOqvfJ}s-ZPYk7@EwfIF58tlB7zHU>LNXL($~qDU zBhm!>HV=T>nHqJVKZ4FdT}}Z&`QeQ5WFNuDDmBA3Sr2F6Xq3gh1@r4m9jogJ*azGHNUr+u{gsWbb9(Y*JNpXX zxqH;GHiz7H3NWeX$#?7cDpYYn*wrLCCf$tL*9L2=(3p)k>pW#a0M}EFwIbg2ne9c-E%RO~ zY)O#g;7svijTkj^t`?f(A+6f|lP7bL+-s3VBhXn)<+d!R0Qp5+-Eg#}P<%YQfv8}I zu%z*c*oFp{IDArvPKbK==}551Z}Lv%2D`|O`-fBFwh>03cSmM2MLqkTEhzKeOlw!> zkQGR<1)1Pb;KW~e67vSGSVRp^SL)YOxddQj$mo2YL%6CiTix?g!=;G_6Z9LROwiF| z@%DpfARE{JRw86l!@b7O3vR!f`$}F!zdv|>7klvUEHoJU*}hu zpdRxLw@(LZ-DWWbm=Y|>buU@{h_5__BJc}gNf>ha%EBe&@L3jBY7x&A`x8rfmDw4mqzAbt`b*9 zwm+qR)d*O?t_WO}B&OweO{8Po7cGxDu|nVk?&xa2IO0lOPz%p(Ezi3r)LK=dy;0)$ z3zUhDEH?{aH3-Cx=J@c=s=ANQbDYLYg)Yr+qvMF@1@tmwifiL#ngu6~fPOap5(bw3 zPJb=b{CIagu-Tx>KF1PkMCDy?=In^7BB>tZExpU2_1I1_t&SKgbD$hfW5xE$d@-9?BiX=TF)f-T0PiCQ+?sg%LO_*_xBI z`Xj+X*aDI1U6|E#@?gT5V>*iQW@d1A-SQOG|rA<)qtVJCM8oQ zC)*w^jI)P$w-Chop8OxYkv>1Jx4Eh#r0?K{l$m>_bR3|x0sv%IHrYSvYh}PjQzB7y zwv+E~4Wer@oG*iF2*s3)MjqDq`hleTv(J}%Q3_fjhJarvb=_p{9iS=AQw7 z9q=h+Bd{X9jpr%nhADWdf$ZkHgc$~Ikp-701`X82RvS=S^y@VT;_J#Ove{Av>5%75 z>aP~g)R1rvlk`!Cf&ogd@IuvGJ5xsiirz|i!geEGqO=cGDiQ50h4T|Z*mAA3JQ@Ig z4Wl-<;x=XbF#;+t8eIpP1)L!}M>A+w!ys(QgL#_GVzi~O?|+6sHTDn$F+q1Qre&k( z#})k-rYa>4MWDq9i{A{7Vz1S2C=N(BK#&cXz{kNB%z6?^q6yT5m>^~p5xN#?Qs&o; z8f=w$C7vS0bjB6Jlt#II1}4*omZy~EgTx~% zg&jsmz{K&IV+fRDQrQ(&FtDLNOmR{jR9p@9Pf7F^vDT~#G7S`=z5rS}8Kg`q@C_X> zPX~zqQ)}v{Jbxb2`W^gvewjP%^^dEZIG z%8$Chp*dF(lWVSn&?2RGLd~p~1YG@-yainW_3s9ivK|XO7Ts_vP!q{zMCUN_l4DHQ#uJ^(IpUor7$M99q7BZ=&Ao=nOsCk_YD5Jou7k^ZeT!^G6n z6SIi#bsOFvdAT+=s&<2&{)UZI6~-+;Z{>_&FVG^)VZk3iZQgDLmUG8$(Xkl$Ax7w0 z?kN6JFEcuuU-4#pW7i%;G@eNpL<70G2+);Qia(}a9Z%DtN3jo*xPck205D_nzkq7G zb$cG8R|{{9T_wRbw)LzMVtwG|j!Q0yFmWKxB9K@rSdOYo?Fqzz>=A-&QK@|(8q$?6 zAr&_6MN7P;OgGf5y8i{E^u)2~C3{A}*ilKYtNsbO8N%tb{CzsR+J);6}t1 z623LflywpI>OiekEXp5B=rVC1TyTs7au~gIG(*#B+wEf=ofs~XY)&fcKPW95UX-gH z?Pf{^!&H=6Uc#daon(MEzP`8f{=o_!-tt7a!I&5qd_VMe1?)KV{78 zjPjT4w_cy&q!Y7ak0gEid#HWF4WC0;d?W-zO*x6*_V}6?NJ6iKWs@HGFj+xc;~sz% z-r*f((ZIr$Ik0u|IYnq^wsk3*_ZYwW1o2p6!{ev5HTnNOyCCU{y0qlu3sjGM8a8D_Q^%rMQjwHPddDf*{n> zjSLt#qH6tZvO-J|Bio5K5Vgc7-B1xaoZAbeq%a??(28-ABFr}20O;AgGozmEEA3LZp*jZqsu3Nn&|5+qy&^rjrFZmaYjDc~tw)0}KQYPU zz(oXQggiSNBFuJYiGd3HIZm*fF$%H2osD`Ad>OV&?xPNmhQRE!*gE~mZ3=yNktRf{1;Oyw(6O&NFv;MYyH}2XlL%1O`hZDH7@LY}nsfI=4_7MUb|AI8$+gYe5RvOmn zg$S>8rcp)+4TM9MTx|hgYS7J5A>X1L$pTy493uhh;5b@fuDk4a$=-LJ-={hlyZB6E zPSpHr44*?aO(wZikfTi6(+{&|MzXuPo#8$A%E%2;aGfIfzv7(1`m9At2o-LI85IeXLP*y6lsdJ5L2*=y8^?YNd za|3`X!kdYpJ?m5D@y8h?T=|XBBS6lD&kcN}wm3MJwuEmxVw`ZunA2V6+1nN^P~M2F z7`couHB=MP{ogxBuSc=>!G+fhdpr)=dr5;&(Mb_vMp7s^)DgV%8q{BiS(${5e6dc# zDkqD`Izet^h5g%_S_YF^+GDmdq(<-Dx)YFyQzxJMaH=&FBj*+-bHz(4sT8FAiF0D5 z{1#;kL(LLJ3sukw@uZzdurgMP*R7%W%}ZabT2}vZYmzMm)PT1je=ThQb%eJSzC|(t zFfM7l#VsfjskUfNM0`;>3bqy$-Oks@4yGoJ4!dHbKu+^ll#iC15J{Pa;yZU0-=58_ zrVE-txR*17T@IjqSvd48cg##>7<`vo6PYfrp~Fou$~!Du^eD&A7k^oPsWOo)4&W>W zvjE-V-*p}>c4jvW)6Uo;;bb2*46NPxkM5+Z#7gLC2Nc9uxH7!_+CpNzVsJP(7*QH6 z%Ob68=a~$|wnrBNa|&!r^DrZ!ooPR{H3{?a&hFi}CMEB2jVXs&1yen&`dvj)mefek zl8J*!^ZOu8*|vY<#o-ln3ycuqp}}=@u!j}Ya@zTR#^B>pM8MWZ$kZ&`!uhFXe1*o( zuF<%Q>uy8Ci&gLrQrhn`|LL2LTydRQ;)`aJq8f3o6e9yp3#eZ79-3kDHs%Bvx^lLs z?fNozHWxHkQSt(i->swU2Dc%#2sqV5rW2N}iWB?(0TtT9T1#Ha5XG}diInFnLfj;w zBdQjlKi44^P6-^o3ULvyLSE1Ly>J?z;Y6-!J+@~t!=XNiXf6x%QXjN_YhD?q-)wpK z<77Lt$t(Ll$l*q9k|Cu~?j*OJlYy@73whw{Y&=$@u5w0#afKHISTYR34$f>6n`)58 z8`_e2Q3V*DH^Jj~))mgXkwE-srB}WYzqf=h#j0O?Kvle4eIB8`%g*O3N#%r^z&ri6^eat8@4#5 zGO$16wyt6uPcw?cIr3+$>Fua|PVLr7^ICpjvy zN^6|UC67=8(dF9xfmfZ#Aj*@@C_shAz?6Pr4=HOPvI-G^2 z={HkkiQgPXv0!OUk%zr7B$XX%er2w0GE}S-Ta8-O2*k#+SVmgf4uK$1cR4hNy;}u( z4(L{>nRYn+#E}a7IEbxLrqr%cTbVB61f2+VA*6xxbvz-5JpRn^%HOx8jc9g=q#2gu z=ZfbR(peiyLk4o=#3%#~-He$r*(D-ve!LaD7vRG9d=H7FSX(dD#+q3WQU?h#U&R^3 zm%QVBMg!UcI%VlL2fD2yY!k@P^J+nXG@#QQ2Pxj!|DGf~U^l%{Cf@s&YxP68#8QBJ zB}gWs2Z=YtH@4I`N=l#5IL;gR9a{l#nA#Rk~R z4S6=Y@%?-pE)oY@?eB7)b!Z5Y>`)gt3iA8q`Idde%H=fON0|wY%Sp?6_wFy{1TD2A zIwf1(Hj0?Zd?!K&YZThYL)6a;u|5&6D@Fzkw$;OkR;r0!A zc@DvMROHED3hjl_o#jHuEj?O^wEeJ~-JTyO@*R}*cqx)JW%4B(xG9;ojSYsY9uRtH z%-@3B)#F8_%_>K-rP1P zbHc815{M3}Bx?Ynt^`04y)He?3A_3R<+a(`pP*>S)7J6QRW&PwezKuM-cypus^bP) zyRV!bD}?KDREblI;lgaNq&-JWeP=;dtmd~XvpT9qT#VWvvv7w?#>7<_5txyELrVAy z{Ym7zk0wkfGAit!WpF)=oy?_3*o<>p3)OK@G6sI|0h&fyB>RJwa-8zu!G^yf|l3G-hjsHyRHF2VC~ZhxPL`BBaf0TjOyYd0H7j zR43dsRW5LS5~QPb=~f4Z72}>a#@b9lg>rjnfh(W&D`ou>E7{?u)4PL$e^xlxrO^6~bj?i6g3=%` zDipC^>~JGG^VB4C9Ez^iB5II$Vt(y%TKw5gB?B+c!Vfgdm)g&CIlwLE-x+rRl$%O$ zX##{AlXb`SuWNj#6XDTKyb=mC8z9cy$MljmKa&}@Ot z!kAK6CC&Oe4vpJ14gEqH_{$%Bn?B8MzXQ0#bUk-Y6rh^0V3 zO=;9IwV>L52NNa(WFTK?I{f+bad@L2?Q*4+QX~6~@d?cdUKy4bvt)`NZf3>=$A#90 zFKSr{Py#3ttW0mMVRqr3ttsl!c;|M4sEuP#E`+y=o7w*Mf{B}x`D zTf1UiDnEr9W{4p|^@ffo-MK2@%^=s92e5Wum&1w z#pq}O){Nc7JO}9}p*mI4NGISP#Hn*0ondNaWQ=~vp;iD*33zKs+VZ2uWZ~IH&RT@F zGwU!F!8rZIeLZ87qs1kYudo49%39bmO=hi79aha4DdW=+t49h@HTG4S&)eRq=3zE1 zdaw7?S<&3ht}@K?65|aH%XI7L`}beQMYfX{N}~=0MqQ9;<*uh%GCeQ#DGw|!?gmnn zZ{;L#60LpT?A9xbNMCi;fho#HHrK z7RAl~(Z!b|*haJc@u6j_(AL{tPPp%?xTMVBeS%=+mpwsofoT*#t^U}SaQ=f-akvn( zc?|_2xeE!5gbZeBzwnVWd}54^wnE|2RC!g@SsdAb(;tQhwChQwCpG@;MjSr&OL3@p z1eT+d%d8B3_r+=#5QBd4#ykw8*x_EfxR=I^rO<=C!h8!h6#wGD4Uq$AluDSF2rfxKy+pL1cPn~u%$}8~pZ)!kQtB^YSXN*O5dbk=PNWw0|&Z09@ZDLAGS754|#?W^n zXB0dT+1-rj2$hzbEQq)=;1-J_tl(XX0PfNmY0$bxV7~#+5r`ufIF|)>Uf<8J!Rg+b zYrMkJSly~z(xZm+u_6TY%pf10`Ud4+Nc@Tpaj4@{7(i!XV@b(J0O-yQv%#ExR7`k1 zP5>~UbPN#_&}}~Rxup0Fo^AYq)qV5Qd0GZL4Y1F*U}PosSx%8sLLGu=uoUdgWI zj=+=+Q-E>+ibm4=C+Hky%MxU*=GTc}{3#K^_ZbdhYUQ{~?R>b@t+`|L>#pq3%M7l> z1`K=F>8q2d37Q(WEJ1p>Xz=ovYeOeXQydwwxKvY6~o>2ul8h%G!@1UzV?kX?kd8!Tc9+!^p2n zpMy8*#~wk_Mjs(=(hlV-ynQN515aF>0;Qc040gaBxnaVzct9NcT}A1pp%ee11;2H{ z285&*UexC2uO4sHQJvoDz~qHU zSO*))(gvG_nt`c5;?53yWXW%szV>O@sx#SJm6XnihlW1rNN*No!rv~E0hMOlSfny# zqY01m6VMf}EBBTb<7^pTcMQO}>g&GHl18>PE+_K{8U-dJh_;kvg3f{5uy}|ZpXFOS zV%J(ZU|hgXdk)!4zW+M!Vc{b$t09rJR_hS1@`xBF>A)x#p{fvW1>H-ipQmhDv8)ha zpN{f1QHN`Cyp+2WUp{W9m!Ff@^Y`$k|9l4yn)L|>CXh8ot}d9xEi=%My%H{^vHv44 z$HN@^)6+kM%24L700mN10jw4AFXH8&I%=y#4FR-Vd~BLiYq7)qAz70V+2aAJ29+0K z(i5tlE+Ceczx*P!h*5X$DuNw``du1@41=+V2Sy3mE^>1#-{=N(<>N1Qjpnql_R)Z^ z1AU|L>~2Sqa}Ge5lhYKt^*!>)3GU&?%!+JN>rr4uM0gljF}B1s&Th#RBVK_NL>11_ zMcRn#cd4#J1P~osx}L?y$wiB$k^M-210Jqle8mMpHi)RQQ59E@)Mv`4nD#bXJ|dW} zW)loUgqX@XHx=X1!S;f{Rx0aWuv1AtJblXim*bx`TpNR~w$j|l61m>Dmb?%3B#u)#qTHso zntI>Em=zr(l`{@hN<6N~F$SnFwika@g@}>|;pFa-YN?B~{3<1(WBuJ(w zg?=_2RkBM44w-c3Mtk?i-NxFQ@%&4}>LFwsN*FWDrJ=#OTVO(*w&Hb7`u7ilJ{>ON zXFT&Ep;XaqzNo%ygTH|FWT|5@*L4-!kqedq3hpU{5paIHdcbN+e9)3G$PI6_g9spq zf0Rf13b?jInJ};*0aN}`y5y716-!#0zO4V~;bQ$a_uNLNkp%n{g)?A7VI16d5}+j7 z58BSWSUC?WJY@VXndr$N$9ht;(KI(@)2+f+x4ZnNBTDDxI`z1QqSC1vGN^F?CnHG8 zQ*t3q)xsRFq$(d9G_{b(d8;uMF(rWmObn^msOsD+Uj}Tu);Kmhj|tb)4DH}yvn;gJ za@65(FpgiFnz@PD94jWRl9uTDtw zM6j)5^NExk$cmy&axGAPQN~xQr?H->pCEW^DCIn0vq5E{7bk5fToT^K$neGl(L2S7 zo2=cm%65=+za{=5_)ZviKaTgjW{+pADGdC-k`_8Qsz4S7npH}-QaMB;fhk1K4<>*m zU1N%4l$4($v?C_w_Q7*PD%e3;yHIzi?gn+>0Q&%{M{~d1|zU*=i}>zs*snD z2Um7RF76oaGzl-Bw_#*0O~QF+-RF(|X^&{6CU32eg_V>FZFkoJr{V#xzi>pAV|wH_ zaFh!wb*GxeW(k>p zqLAd+WvUP)3}nYhOO|lp4wxZlT0X3l#a8Y!`Zi%Z2wy8w8H!$P1>%sDpO_H7X93=) zwo7_gwWu)=h3$_>DG}-#4cZ%vS!d8evOH`MCkroxjiP3AV~AOH>2~PnEq9hwY1Emd zlos!3)`a|&Map>|wzQwEo9_{>V#9Kjzh{3QH5ac~GTgqQsyfQm#+`*Cpi{Qt-Ai#WU@)v;q^V5V(-bYGhl-tq$1(6n({AGA zSPAXPp?1m~^cKB5?N(8hy9+N>aY)P#SNSI~GCBN67cskkv1!{fo1#9HIP_{r6MbA% z_RUIIMK7y&??kkKPDc_u8~kFDT|N56)z~%LRpr2;-$WD>Ndi!&<&T=hOJa&8m}t5g zX~`TDuY6AkcyBXk56Yjl$Rb0M+zLAS8TsQ9Y9pI+>-K{#?TbgE+7%biefG+at|^A% z>vAKp%$*(5c;9?y!DghtsbFu@x^G@8PeJ$G11e9!_ZOYq`@gQs+t|y?8>|-ZgL=j| z&PtP#k&byZR6D`K{7~Y-f>r3*Xwn_NYs6-Pt%4(n5kU7zy^D#WP0tEehu>}NVJ6l z_eMC*705QdKfhIp(%Z%FUgcTQ`8~F$*mmLrQF0EPH8Q zNF-^=A|)B2&|ZcteL!^Wq|zk_UC|{2PTTQ!gA^j;9e$fih08*!sO(W**EBsME|qu= zf@F4sRo5M1Q%-(L@A7Jc)u$dwEqGn3Q*HaLR(vcRt4ql2P)TMAHlKzU=&f5FM@X$( z%Yp3~u~HRkZroP`kLe*du{uj7N3Bqa>*?)FT!KQa=c~HxrmN)WO25RZymJNMIv2~S zr*)`<(jh!$G*!TL{C2p(Om|cRdi$eh!mxqEk>KtXUvkSEwh`{yOFpL0DU=s_G+%tj z#a6tLmWGN^6YYOdbXkI9JtOJM^`Cc_ys9nIlx$x!`lk%czA8S_b%#Ns2J(^gkSfPn zezT)jl1XP1a7Mks{+^+QnXg5a=$ond6t$xq>xP-CI6yxJ1|nG6;8TZR96f!L|DZK9 zW4?3A)Vqx)ejVfrBJ3~ ziCy^LvCd1sFa^* zGm#0iwLi;Z1o_Wt1ke{Sn19fS;VMhli0?EqBlF+I>CaH;p5K;*v!VTa%5Jq}AK?k! zx3`HAFHbYtfXbH47$#tL%*W}d0~lNlN5Xqe1916}sWZPlfYe%_$S9uaNVOI9TXS5&o0z*=GGs&=w@0)bhPZxi_U=!u4z=zhhEV4p`%ttuVo02JiVw!?_M8qe%GTtZLFryn{Zm_pE4}1D)`Stnd{Kxrp=;oi8ADDF#JO-IikX zY3i$|+ea%;I4R(nY3ER(BR>yqbZ+;Z#yH%#R8^`0usdJnDCWQzPHlnZgH^ua> zB9t#x^}f*8L_`To4@u<9qh5HqAaqr}pEZ=yS(GXT2QK6{DR*Uhco;HcJrcSl>0SE1 zX*oCU_rB`@%mxhlGeuVneeGVJ7)2#<<~q0!cEBgER$XH>7CqrW@dMZDyF6XhQuw@Z z|H#9GKZG@epQ8S`^}OCkdU+3^1~bknT{yy7u6Q}keG{~Wmlr5Zcig<9N#gJ5?g-)C zfbUaB`_{Tei;Z^9>U4kgd*QqEoq6^y?mN(=mmSPj?S~%j4rmA~O;QE0@vRoE{*$Xf z+XL%GAZFOzce;Tm7@98J0FDt1hf01bX0-BTgj9Cak`g>vg^8t&?ee5=Fd*^3`>P(-E zVTb@WqA-IJUlB@uO{CQzN)iv!VP{wkBJ?_Sq%d9yit@0(**YU0Z&bx)KgpD!hTT{o ztdHVVETSg*%ZUDFT5V_8656BilT)@UVh)VQ%+B-I+QN|_UIZqBQ^-86D5G+sp!O}3 zKcm9(RM}e5gUL|$r4!dCWG*HK#H@+bQw#xg);PB)nQ4{(tFRUo=k0y+#!jo2MLz?) zN*jAe{g)gU<2qeHSZtrSPY)ZGVbSZ!ziG(P(YLnD(PUklka2W-7V(eK?}uIryBzeR ze>orD33GDj)4_F*55Wmn5=+cj@baDJpY3dwx9vf{FLM92Lnz-%+!7lb{F>X^MFc;NEn6|b)#)d96`p-?-K&r%| z$5+oH*=k==NZY|-`L5D->kL!Ukt7Tsixaxv_p$L&7K5VEgsF^;kJ}{qt)zTEY{KJi zya;?;pCu$}zVd>dpMnZHj|NnyBbXMbz=wU|Lh`VD+}wxrBG zQ$09@HoDKH(OC1X+U3#-V9!S;QE{8(9AiNW&5V;`MTG~UzgDHps~aC~H%!J=Oapqa z0!xL90T7`OZOWbt#72zvfYBWb@xLV)wE~Nrvu?msQhky%25HY_w$MeR7hKj7aXi4< z6{5${Mzea&LDN8OvLrX;CV8=%xUp`HhcEC zxqfM-R<2)@!HlBptfVVQjbM{mDKo+o`BKm-f%%mU+Z;IzEx{%k)i#q6nj^n?D{omb zt&sU?>LkGsAyMa?HBNQ8P?gKu7`=j5pPV8|4(MflNI&bE1D z?WBH2;hwRTqXA)HW6i7xZL+&P!Ut&r_>24im@Rs}1kg5_Y zBiGrXSd;4@P*VAXjpbL*Clso#W~W6=o1Apf0n;+TepV`YSwPVusWKJ6lVw|zt=YeP zqv1SW;_@yYjk;mqvJGIS_HVc!?a}Q{zalsKTAGJwuL03#d7-55N_Mw5#T`~u$YYkK zWGXcNC~_PTZsA)25P@cH1HnR(Py7yPZUQ5V5Fh$P79=16G&;`6qWtfvU8J*)_m^+9%6_=vAYL&kykzpD!ll%#1bRZKG>T?}K43hG5Aee{to zEoID-8oDVMWvgX0^0c`p)?7UTvw#G9l)t0j9C5^HIvr9|CrYq zz3ypQTlfXvC9?8g>8A6W_30Ucl!z%g9}fF{SJZ>R(*@xfG!;}t$aFaIlYj+>x-fT> z*HL2{8%0&c*ewt;H_^%5^4J-OO$wK!TzF8M>hert3c1?%$L7lP%y@oRMmsIf<4nAU zp*tTan87>lVW>eWRkd$2mwb&4(ZDpOa@axB0Yy^fiwm@2cJ*ex$b&83G^mR?GuM|W zzlyobl{`n2wY$fAj$w_rITs-hW;ZM)KtF1@$p;+uwj zo_ieq4V&NL=baP(P~ZG4uJ zl*|1oeOI4P7C?6ISSZG-yK$Z_{8x4yrdzOS7evA%YTW2Cwx6OA(-sfm-l8gjnIqH9 zzXc)LEe3~^jS<&!Trq}5Q`o?5cSXHYqZU}2 z3igLw;z`b&<`Qa7IMSDMlJmqyqV?9f^tHv$o*JtkG+K!jrjP8UBxSUax=4jhd3Mw) zP7aDTARqKS+9k<9StU6Ex)&Yx!)0(y9J!?X(UQ-u_TkYUzybQ5%5T#>)!>Ug7iWTe zYc6$p*8?fSxHUwJax1apo(6j9R4fSJU|VBNb;w0tpEs)LiU3Q7A=Ji+U1TfW&wO1? zGAF7S&Eq~`^2WLxqtGy@9Wgyt6ok>B_{U6%{FOfBf2b-=MVANEJ^f-K+WN8{Tg=G4 zz-zQ(Q>~xIXrGj(dbHJ~x#?7@`^lY*0|z7oU~7T;bEPc-KL%SHgFp9>i^=8v;_clt z5?MRz4WgFW`uS%blEWlKeR$vC~_z^ z2FF%DVC&z+c_uPk$W|GLGf^8`ff%KBiWqf3Toh2fVup2Kp+heXjr_JiHNRFoF28U+ z�}JI*iy5B0Fj&yluaY3#6lVmp+QI0YTT3K6A} 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 OCif-(531pdRQ~|kLFG~a literal 0 HcmV?d00001 diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index 4f3f6c55..c90f4431 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -17,4 +17,4 @@ vault: termination: "edge" image: repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.12.0-ubi" + tag: "1.12.1-ubi" diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index 73417bc3..4ec079cb 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -62,7 +62,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -83,7 +83,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -113,7 +113,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -142,7 +142,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -184,7 +184,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -206,7 +206,6 @@ spec: terminationGracePeriodSeconds: 10 serviceAccountName: hashicorp-vault - volumes: - name: config @@ -218,7 +217,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -352,7 +351,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -379,7 +378,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index 73417bc3..4ec079cb 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -62,7 +62,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -83,7 +83,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -113,7 +113,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -142,7 +142,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -184,7 +184,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -206,7 +206,6 @@ spec: terminationGracePeriodSeconds: 10 serviceAccountName: hashicorp-vault - volumes: - name: config @@ -218,7 +217,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -352,7 +351,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -379,7 +378,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index 73417bc3..4ec079cb 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -62,7 +62,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -83,7 +83,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -113,7 +113,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -142,7 +142,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -184,7 +184,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -206,7 +206,6 @@ spec: terminationGracePeriodSeconds: 10 serviceAccountName: hashicorp-vault - volumes: - name: config @@ -218,7 +217,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -352,7 +351,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -379,7 +378,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index 9c473171..f681798f 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -62,7 +62,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -83,7 +83,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -113,7 +113,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -142,7 +142,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -184,7 +184,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -206,7 +206,6 @@ spec: terminationGracePeriodSeconds: 10 serviceAccountName: hashicorp-vault - volumes: - name: config @@ -218,7 +217,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -352,7 +351,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -379,7 +378,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index 73417bc3..4ec079cb 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -62,7 +62,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -83,7 +83,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -113,7 +113,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -142,7 +142,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -184,7 +184,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -206,7 +206,6 @@ spec: terminationGracePeriodSeconds: 10 serviceAccountName: hashicorp-vault - volumes: - name: config @@ -218,7 +217,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -352,7 +351,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.22.1 + helm.sh/chart: vault-0.23.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -379,7 +378,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.12.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR From 8a692eeb448440343bf184409f15430ecd8df730 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 29 Nov 2022 22:11:34 +0100 Subject: [PATCH 0692/1263] Filter out patches to test/ --- hashicorp-vault/update-helm-dependency.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hashicorp-vault/update-helm-dependency.sh b/hashicorp-vault/update-helm-dependency.sh index 64adef27..fafe3dde 100755 --- a/hashicorp-vault/update-helm-dependency.sh +++ b/hashicorp-vault/update-helm-dependency.sh @@ -20,7 +20,7 @@ rm -rf "${NAME}" tar xfz "${TAR}" pushd "${NAME}" for i in ../../local-patches/*.patch; do - patch -p1 < "${i}" + filterdiff "${i}" -p1 -x 'test/*' | patch -p1 done popd tar cvfz "${TAR}" "${NAME}" From 5a0510095129b553db727ef17477d4f4e11e03b8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 29 Nov 2022 22:13:18 +0100 Subject: [PATCH 0693/1263] Add patch to be able to use vault-ssl as a default We need a fix that has not landed upstream, which means we add per service annotations so different services do not fight over which one gets the certificate secret. --- hashicorp-vault/README.md | 7 + hashicorp-vault/charts/vault-0.23.0.tgz | Bin 52881 -> 56308 bytes .../0002-Allow-per-service-annotations.patch | 310 ++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch diff --git a/hashicorp-vault/README.md b/hashicorp-vault/README.md index a3d4249f..ec945cbd 100644 --- a/hashicorp-vault/README.md +++ b/hashicorp-vault/README.md @@ -1,9 +1,16 @@ # VP hashicorp-vault +## Patches + +# Issue 9136 **IMPORTANT**: Due to the fact that 'null' values do not work in helm charts ([GH#9136](https://github.com/helm/helm/issues/9136)), we need to patch the chart to skip setting the host. +# Issue 674 +In order to be able to use vault ssl we need to patch the helm chart to fix +upstream issue 674. + Make sure to run "./update-helm-dependency.sh" after you updated the subchart (by calling helm dependency update .) diff --git a/hashicorp-vault/charts/vault-0.23.0.tgz b/hashicorp-vault/charts/vault-0.23.0.tgz index b276c320303dda95c12b336946b32063d667e905..fddafdf9d1467c3eff46ddb47b14ee0556f8a36b 100644 GIT binary patch literal 56308 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


_S99I1q%)&SG)u1n?DA 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%0D_!|+5v;fk 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 z�}JI*iy5B0Fj&yluaY3#6lVmp+QI0YTT3K6A} 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 OCif-(531pdRQ~|kLFG~a diff --git a/hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch b/hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch new file mode 100644 index 00000000..2430976d --- /dev/null +++ b/hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch @@ -0,0 +1,310 @@ +From e8e2aafc583f494967f4a269e89323629161b5eb Mon Sep 17 00:00:00 2001 +From: Michele Baldessari +Date: Tue, 29 Nov 2022 20:06:09 +0100 +Subject: [PATCH] WIP Allow per-service annotations + +We add the 'annotations' field to the existing +vault.service.{active,standby} dictionaries which are relevant for the +active/standby vault ha services. We also add +vault.service.{main,internal}.annotations in order to allow per-service +annotations when using the non-ha variants. + +We chose 'main' as we cannot reuse the existing +vault.service.annotations key, because that gets still applied to all +services and we do not want to break existing installations. + +WIP as we need to add some more docs and maybe some more tests. +--- + templates/_helpers.tpl | 57 ++++++++++++++++++++++++ + templates/server-ha-active-service.yaml | 3 +- + templates/server-ha-standby-service.yaml | 1 + + templates/server-headless-service.yaml | 1 + + templates/server-service.yaml | 1 + + test/unit/server-ha-active-service.bats | 11 +++++ + test/unit/server-ha-standby-service.bats | 11 +++++ + test/unit/server-service.bats | 10 +++++ + values.schema.json | 34 ++++++++++++++ + values.yaml | 22 +++++++++ + 10 files changed, 150 insertions(+), 1 deletion(-) + +diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl +index 3897391..2a46856 100644 +--- a/templates/_helpers.tpl ++++ b/templates/_helpers.tpl +@@ -683,6 +683,63 @@ Sets extra vault server Service annotations + {{- end }} + {{- end -}} + ++{{/* ++Sets extra vault server Service active annotations ++*/}} ++{{- define "vault.service.active.annotations" -}} ++ {{- if .Values.server.service.active.annotations }} ++ {{- $tp := typeOf .Values.server.service.active.annotations }} ++ {{- if eq $tp "string" }} ++ {{- tpl .Values.server.service.active.annotations . | nindent 4 }} ++ {{- else }} ++ {{- toYaml .Values.server.service.active.annotations | nindent 4 }} ++ {{- end }} ++ {{- end }} ++{{- end -}} ++ ++{{/* ++Sets extra vault server Service standby annotations ++*/}} ++{{- define "vault.service.standby.annotations" -}} ++ {{- if .Values.server.service.standby.annotations }} ++ {{- $tp := typeOf .Values.server.service.standby.annotations }} ++ {{- if eq $tp "string" }} ++ {{- tpl .Values.server.service.standby.annotations . | nindent 4 }} ++ {{- else }} ++ {{- toYaml .Values.server.service.standby.annotations | nindent 4 }} ++ {{- end }} ++ {{- end }} ++{{- end -}} ++ ++{{/* ++Sets extra vault server Service internal annotations ++*/}} ++{{- define "vault.service.internal.annotations" -}} ++ {{- if .Values.server.service.internal.annotations }} ++ {{- $tp := typeOf .Values.server.service.internal.annotations }} ++ {{- if eq $tp "string" }} ++ {{- tpl .Values.server.service.internal.annotations . | nindent 4 }} ++ {{- else }} ++ {{- toYaml .Values.server.service.internal.annotations | nindent 4 }} ++ {{- end }} ++ {{- end }} ++{{- end -}} ++{{/* ++Sets extra vault server Service main annotations ++Note: We call it 'main' as we need to differentiate the "vault.service.annotations" which are ++ applied to all services ++*/}} ++{{- define "vault.service.main.annotations" -}} ++ {{- if .Values.server.service.main.annotations }} ++ {{- $tp := typeOf .Values.server.service.main.annotations }} ++ {{- if eq $tp "string" }} ++ {{- tpl .Values.server.service.main.annotations . | nindent 4 }} ++ {{- else }} ++ {{- toYaml .Values.server.service.main.annotations | nindent 4 }} ++ {{- end }} ++ {{- end }} ++{{- end -}} ++ + {{/* + Sets PodSecurityPolicy annotations + */}} +diff --git a/templates/server-ha-active-service.yaml b/templates/server-ha-active-service.yaml +index 7def2a0..649ffb8 100644 +--- a/templates/server-ha-active-service.yaml ++++ b/templates/server-ha-active-service.yaml +@@ -18,8 +18,9 @@ metadata: + vault-active: "true" + annotations: + {{ template "vault.service.annotations" .}} ++{{ template "vault.service.active.annotations" .}} + spec: +- {{- if .Values.server.service.type}} ++ {{- if .Values.server.service.type }} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} +diff --git a/templates/server-ha-standby-service.yaml b/templates/server-ha-standby-service.yaml +index 50fca4b..cdbfcad 100644 +--- a/templates/server-ha-standby-service.yaml ++++ b/templates/server-ha-standby-service.yaml +@@ -17,6 +17,7 @@ metadata: + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: + {{ template "vault.service.annotations" .}} ++{{ template "vault.service.standby.annotations" .}} + spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} +diff --git a/templates/server-headless-service.yaml b/templates/server-headless-service.yaml +index b03f491..25aaa8d 100644 +--- a/templates/server-headless-service.yaml ++++ b/templates/server-headless-service.yaml +@@ -16,6 +16,7 @@ metadata: + vault-internal: "true" + annotations: + {{ template "vault.service.annotations" .}} ++{{ template "vault.service.internal.annotations" .}} + spec: + clusterIP: None + publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} +diff --git a/templates/server-service.yaml b/templates/server-service.yaml +index 913b569..362c88e 100644 +--- a/templates/server-service.yaml ++++ b/templates/server-service.yaml +@@ -15,6 +15,7 @@ metadata: + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: + {{ template "vault.service.annotations" .}} ++{{ template "vault.service.main.annotations" .}} + spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} +diff --git a/test/unit/server-ha-active-service.bats b/test/unit/server-ha-active-service.bats +index d78f5d4..13b5271 100755 +--- a/test/unit/server-ha-active-service.bats ++++ b/test/unit/server-ha-active-service.bats +@@ -13,6 +13,17 @@ load _helpers + [ "${actual}" = "true" ] + } + ++@test "server/ha-active-Service: specific annotations" { ++ cd `chart_dir` ++ local actual=$(helm template \ ++ --show-only templates/server-ha-active-service.yaml \ ++ --set 'server.ha.enabled=true' \ ++ --set 'server.service.active.annotations=vaultIsAwesome: true' \ ++ . | tee /dev/stderr | ++ yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) ++ [ "${actual}" = "true" ] ++} ++ + @test "server/ha-active-Service: disable with ha.enabled false" { + cd `chart_dir` + local actual=$( (helm template \ +diff --git a/test/unit/server-ha-standby-service.bats b/test/unit/server-ha-standby-service.bats +index 6698314..6244565 100755 +--- a/test/unit/server-ha-standby-service.bats ++++ b/test/unit/server-ha-standby-service.bats +@@ -13,6 +13,17 @@ load _helpers + [ "${actual}" = "true" ] + } + ++@test "server/ha-standby-Service: specific annotations string" { ++ cd `chart_dir` ++ local actual=$(helm template \ ++ --show-only templates/server-ha-standby-service.yaml \ ++ --set 'server.ha.enabled=true' \ ++ --set 'server.service.standby.annotations=vaultIsAwesome: true' \ ++ . | tee /dev/stderr | ++ yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) ++ [ "${actual}" = "true" ] ++} ++ + @test "server/ha-standby-Service: generic annotations yaml" { + cd `chart_dir` + local actual=$(helm template \ +diff --git a/test/unit/server-service.bats b/test/unit/server-service.bats +index 70a5445..828877d 100755 +--- a/test/unit/server-service.bats ++++ b/test/unit/server-service.bats +@@ -153,6 +153,16 @@ load _helpers + [ "${actual}" = "true" ] + } + ++@test "server/Service: specific annotations" { ++ cd `chart_dir` ++ local actual=$(helm template \ ++ --show-only templates/server-service.yaml \ ++ --set 'server.service.main.annotations=vaultIsAwesome: true' \ ++ . | tee /dev/stderr | ++ yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) ++ [ "${actual}" = "true" ] ++} ++ + @test "server/Service: publish not ready" { + cd `chart_dir` + local actual=$(helm template \ +diff --git a/values.schema.json b/values.schema.json +index c183957..2bddf15 100644 +--- a/values.schema.json ++++ b/values.schema.json +@@ -854,11 +854,39 @@ + "active": { + "type": "object", + "properties": { ++ "annotations" : { ++ "type": [ ++ "object", ++ "string" ++ ] ++ }, + "enabled": { + "type": "boolean" + } + } + }, ++ "internal": { ++ "type": "object", ++ "properties": { ++ "annotations": { ++ "type": [ ++ "object", ++ "string" ++ ] ++ } ++ } ++ }, ++ "main": { ++ "type": "object", ++ "properties": { ++ "annotations": { ++ "type": [ ++ "object", ++ "string" ++ ] ++ } ++ } ++ }, + "annotations": { + "type": [ + "object", +@@ -890,6 +918,12 @@ + "properties": { + "enabled": { + "type": "boolean" ++ }, ++ "annotations": { ++ "type": [ ++ "object", ++ "string" ++ ] + } + } + }, +diff --git a/values.yaml b/values.yaml +index 2c3d9e2..97ddb16 100644 +--- a/values.yaml ++++ b/values.yaml +@@ -600,10 +600,32 @@ server: + # have labelled themselves as the cluster leader with `vault-active: "true"` + active: + enabled: true ++ # Extra annotations for the service definition. This can either be YAML or a ++ # YAML-formatted multi-line templated string map of the annotations to apply ++ # to the service. ++ annotations: {} ++ + # Enable or disable the vault-standby service, which selects Vault pods that + # have labelled themselves as a cluster follower with `vault-active: "false"` + standby: + enabled: true ++ # Extra annotations for the service definition. This can either be YAML or a ++ # YAML-formatted multi-line templated string map of the annotations to apply ++ # to the service. ++ annotations: {} ++ ++ main: ++ # Extra annotations for the service definition. This can either be YAML or a ++ # YAML-formatted multi-line templated string map of the annotations to apply ++ # to the service. ++ annotations: {} ++ ++ internal: ++ # Extra annotations for the service definition. This can either be YAML or a ++ # YAML-formatted multi-line templated string map of the annotations to apply ++ # to the service. ++ annotations: {} ++ + # If enabled, the service selectors will include `app.kubernetes.io/instance: {{ .Release.Name }}` + # When disabled, services may select Vault pods not deployed from the chart. + # Does not affect the headless vault-internal service with `ClusterIP: None` +-- +2.38.1 + From 0bff6196220b4b993943112cc0a0e9f828324d2c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 29 Nov 2022 22:15:17 +0100 Subject: [PATCH 0694/1263] Switch to vault-ssl as a default This avoids having vault running without tls on the hub. Tested by applying this change to MCG and doing a deploy of hub + cluster. Observed that: 1. the config-demo app and its related secrets worked correctly on both hub and regional cluster (i.e. ESO still managed to talk to the route and fetch the secrets) 2. Deployment succeeded and secrets were loaded correctly 3. Was able to connect to the UI with the root login token 4. Commands in the vault still work correctly: $ oc exec -it -n vault vault-0 -- bash bash-4.4$ vault kv list secret/hub Keys ---- config-demo --- Changes.md | 5 ++ hashicorp-vault/README.md | 6 ++- hashicorp-vault/charts/vault-0.23.0.tgz | Bin 56308 -> 56309 bytes .../0002-Allow-per-service-annotations.patch | 42 ++++++++-------- hashicorp-vault/values.yaml | 34 ++++++++++++- ...ault-industrial-edge-factory.expected.yaml | 45 +++++++++--------- ...rp-vault-industrial-edge-hub.expected.yaml | 45 +++++++++--------- ...-vault-medical-diagnosis-hub.expected.yaml | 45 +++++++++--------- tests/hashicorp-vault-naked.expected.yaml | 45 +++++++++--------- tests/hashicorp-vault-normal.expected.yaml | 45 +++++++++--------- 10 files changed, 173 insertions(+), 139 deletions(-) diff --git a/Changes.md b/Changes.md index 7da77d8b..177f3c45 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,10 @@ # Changes +## November 29, 2022 + +* Upgraded vault-helm to 0.23.0 +* Enable vault-ssl by default + ## November 22, 2022 * Implemented a new format for the values-secret.yaml. Example can be found in examples/ folder diff --git a/hashicorp-vault/README.md b/hashicorp-vault/README.md index ec945cbd..dbc0f016 100644 --- a/hashicorp-vault/README.md +++ b/hashicorp-vault/README.md @@ -2,12 +2,14 @@ ## Patches -# Issue 9136 +### Issue 9136 + **IMPORTANT**: Due to the fact that 'null' values do not work in helm charts ([GH#9136](https://github.com/helm/helm/issues/9136)), we need to patch the chart to skip setting the host. -# Issue 674 +### Issue 674 + In order to be able to use vault ssl we need to patch the helm chart to fix upstream issue 674. diff --git a/hashicorp-vault/charts/vault-0.23.0.tgz b/hashicorp-vault/charts/vault-0.23.0.tgz index fddafdf9d1467c3eff46ddb47b14ee0556f8a36b..7e8660a0c767be9b5bb3f0aaa8c7bea5a6bcc877 100644 GIT binary patch delta 54525 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 diff --git a/hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch b/hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch index 2430976d..ba73a467 100644 --- a/hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch +++ b/hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch @@ -1,15 +1,15 @@ -From e8e2aafc583f494967f4a269e89323629161b5eb Mon Sep 17 00:00:00 2001 +From f62623030374c55410624a00755e9a3c07a411da Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 29 Nov 2022 20:06:09 +0100 -Subject: [PATCH] WIP Allow per-service annotations +Subject: [PATCH] Allow per-service annotations We add the 'annotations' field to the existing vault.service.{active,standby} dictionaries which are relevant for the active/standby vault ha services. We also add -vault.service.{main,internal}.annotations in order to allow per-service -annotations when using the non-ha variants. +vault.service.{nonha,internal}.annotations in order to allow per-service +annotations when using the non-ha variant. -We chose 'main' as we cannot reuse the existing +We had to choose 'nonha' as we cannot reuse the existing vault.service.annotations key, because that gets still applied to all services and we do not want to break existing installations. @@ -28,7 +28,7 @@ WIP as we need to add some more docs and maybe some more tests. 10 files changed, 150 insertions(+), 1 deletion(-) diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl -index 3897391..2a46856 100644 +index 3897391..9e98c0b 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -683,6 +683,63 @@ Sets extra vault server Service annotations @@ -77,17 +77,17 @@ index 3897391..2a46856 100644 + {{- end }} +{{- end -}} +{{/* -+Sets extra vault server Service main annotations -+Note: We call it 'main' as we need to differentiate the "vault.service.annotations" which are ++Sets extra vault server Service nonha annotations ++Note: We call it 'nonha' as we need to differentiate the "vault.service.annotations" which are + applied to all services +*/}} -+{{- define "vault.service.main.annotations" -}} -+ {{- if .Values.server.service.main.annotations }} -+ {{- $tp := typeOf .Values.server.service.main.annotations }} ++{{- define "vault.service.nonha.annotations" -}} ++ {{- if .Values.server.service.nonha.annotations }} ++ {{- $tp := typeOf .Values.server.service.nonha.annotations }} + {{- if eq $tp "string" }} -+ {{- tpl .Values.server.service.main.annotations . | nindent 4 }} ++ {{- tpl .Values.server.service.nonha.annotations . | nindent 4 }} + {{- else }} -+ {{- toYaml .Values.server.service.main.annotations | nindent 4 }} ++ {{- toYaml .Values.server.service.nonha.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} @@ -135,14 +135,14 @@ index b03f491..25aaa8d 100644 clusterIP: None publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} diff --git a/templates/server-service.yaml b/templates/server-service.yaml -index 913b569..362c88e 100644 +index 913b569..02a1ccd 100644 --- a/templates/server-service.yaml +++ b/templates/server-service.yaml @@ -15,6 +15,7 @@ metadata: app.kubernetes.io/managed-by: {{ .Release.Service }} annotations: {{ template "vault.service.annotations" .}} -+{{ template "vault.service.main.annotations" .}} ++{{ template "vault.service.nonha.annotations" .}} spec: {{- if .Values.server.service.type}} type: {{ .Values.server.service.type }} @@ -191,7 +191,7 @@ index 6698314..6244565 100755 cd `chart_dir` local actual=$(helm template \ diff --git a/test/unit/server-service.bats b/test/unit/server-service.bats -index 70a5445..828877d 100755 +index 70a5445..cc66987 100755 --- a/test/unit/server-service.bats +++ b/test/unit/server-service.bats @@ -153,6 +153,16 @@ load _helpers @@ -202,7 +202,7 @@ index 70a5445..828877d 100755 + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-service.yaml \ -+ --set 'server.service.main.annotations=vaultIsAwesome: true' \ ++ --set 'server.service.nonha.annotations=vaultIsAwesome: true' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) + [ "${actual}" = "true" ] @@ -212,7 +212,7 @@ index 70a5445..828877d 100755 cd `chart_dir` local actual=$(helm template \ diff --git a/values.schema.json b/values.schema.json -index c183957..2bddf15 100644 +index c183957..d0dca34 100644 --- a/values.schema.json +++ b/values.schema.json @@ -854,11 +854,39 @@ @@ -241,7 +241,7 @@ index c183957..2bddf15 100644 + } + } + }, -+ "main": { ++ "nonha": { + "type": "object", + "properties": { + "annotations": { @@ -269,7 +269,7 @@ index c183957..2bddf15 100644 } }, diff --git a/values.yaml b/values.yaml -index 2c3d9e2..97ddb16 100644 +index 2c3d9e2..32d8ea1 100644 --- a/values.yaml +++ b/values.yaml @@ -600,10 +600,32 @@ server: @@ -290,7 +290,7 @@ index 2c3d9e2..97ddb16 100644 + # to the service. + annotations: {} + -+ main: ++ nonha: + # Extra annotations for the service definition. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the service. diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index c90f4431..a0e91908 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -10,11 +10,43 @@ vault: enabled: true serviceType: "LoadBalancer" server: + extraEnvironmentVars: + VAULT_CACERT: /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt + VAULT_ADDR: https://vault.vault.svc.cluster.local:8200 + standalone: + config: | + ui = true + listener "tcp" { + address = "[::]:8200" + cluster_address = "[::]:8201" + tls_cert_file = "/vault/userconfig/vault-secret/tls.crt" + tls_key_file = "/vault/userconfig/vault-secret/tls.key" + } + storage "file" { + path = "/vault/data" + } + + # These are automatically mounted in /vault/userconfig/ + extraVolumes: + - type: secret + name: vault-secret + + service: + enabled: true + nonha: + annotations: + service.beta.openshift.io/serving-cert-secret-name: vault-secret + internal: + annotations: + service.beta.openshift.io/serving-cert-secret-name: vault-secret-internal route: host: null enabled: true tls: - termination: "edge" + # We cannot use passthrough because you'd be talking to + # https://vault-vault.apps.mcg-hub.blueprints.rhecoeng.com but you'd + # get vault.vault.svc/vault.vault.svc.cluster.local + termination: "reencrypt" image: repository: "registry.connect.redhat.com/hashicorp/vault" tag: "1.12.1-ubi" diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index 4ec079cb..2f8643d4 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -26,35 +26,15 @@ data: extraconfig-from-values.hcl: |- disable_mlock = true ui = true - listener "tcp" { - tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" - # Enable unauthenticated metrics access (necessary for Prometheus Operator) - #telemetry { - # unauthenticated_metrics_access = "true" - #} + tls_cert_file = "/vault/userconfig/vault-secret/tls.crt" + tls_key_file = "/vault/userconfig/vault-secret/tls.key" } storage "file" { path = "/vault/data" } - - # Example configuration for using auto-unseal, using Google Cloud KMS. The - # GKMS keys must already exist, and the cluster must have a service account - # that is authorized to access GCP KMS. - #seal "gcpckms" { - # project = "vault-helm-dev" - # region = "global" - # key_ring = "vault-helm-unseal-kr" - # crypto_key = "vault-helm-unseal-key" - #} - - # Example configuration for enabling Prometheus metrics in your config. - #telemetry { - # prometheus_retention_time = "30s", - # disable_hostname = true - #} --- # Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -90,6 +70,8 @@ metadata: vault-internal: "true" annotations: + + service.beta.openshift.io/serving-cert-secret-name: vault-secret-internal spec: clusterIP: None publishNotReadyAddresses: true @@ -119,6 +101,8 @@ metadata: app.kubernetes.io/managed-by: Helm annotations: + + service.beta.openshift.io/serving-cert-secret-name: vault-secret spec: # We want the servers to become available even if they're not ready # since this DNS is also used for join operations. @@ -212,6 +196,10 @@ spec: configMap: name: hashicorp-vault-config + - name: userconfig-vault-secret + secret: + secretName: vault-secret + defaultMode: 420 - name: home emptyDir: {} containers: @@ -268,6 +256,10 @@ spec: value: "/home/vault" + - name: "VAULT_ADDR" + value: "https://vault.vault.svc.cluster.local:8200" + - name: "VAULT_CACERT" + value: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" volumeMounts: @@ -281,6 +273,9 @@ spec: - name: config mountPath: /vault/config + - name: userconfig-vault-secret + readOnly: true + mountPath: /vault/userconfig/vault-secret - name: home mountPath: /home/vault ports: @@ -364,7 +359,7 @@ spec: port: targetPort: 8200 tls: - termination: edge + termination: reencrypt --- # Source: hashicorp-vault/charts/vault/templates/tests/server-test.yaml apiVersion: v1 @@ -384,6 +379,10 @@ spec: - name: VAULT_ADDR value: http://hashicorp-vault.default.svc:8200 + - name: "VAULT_ADDR" + value: "https://vault.vault.svc.cluster.local:8200" + - name: "VAULT_CACERT" + value: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" command: - /bin/sh - -c diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index 4ec079cb..2f8643d4 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -26,35 +26,15 @@ data: extraconfig-from-values.hcl: |- disable_mlock = true ui = true - listener "tcp" { - tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" - # Enable unauthenticated metrics access (necessary for Prometheus Operator) - #telemetry { - # unauthenticated_metrics_access = "true" - #} + tls_cert_file = "/vault/userconfig/vault-secret/tls.crt" + tls_key_file = "/vault/userconfig/vault-secret/tls.key" } storage "file" { path = "/vault/data" } - - # Example configuration for using auto-unseal, using Google Cloud KMS. The - # GKMS keys must already exist, and the cluster must have a service account - # that is authorized to access GCP KMS. - #seal "gcpckms" { - # project = "vault-helm-dev" - # region = "global" - # key_ring = "vault-helm-unseal-kr" - # crypto_key = "vault-helm-unseal-key" - #} - - # Example configuration for enabling Prometheus metrics in your config. - #telemetry { - # prometheus_retention_time = "30s", - # disable_hostname = true - #} --- # Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -90,6 +70,8 @@ metadata: vault-internal: "true" annotations: + + service.beta.openshift.io/serving-cert-secret-name: vault-secret-internal spec: clusterIP: None publishNotReadyAddresses: true @@ -119,6 +101,8 @@ metadata: app.kubernetes.io/managed-by: Helm annotations: + + service.beta.openshift.io/serving-cert-secret-name: vault-secret spec: # We want the servers to become available even if they're not ready # since this DNS is also used for join operations. @@ -212,6 +196,10 @@ spec: configMap: name: hashicorp-vault-config + - name: userconfig-vault-secret + secret: + secretName: vault-secret + defaultMode: 420 - name: home emptyDir: {} containers: @@ -268,6 +256,10 @@ spec: value: "/home/vault" + - name: "VAULT_ADDR" + value: "https://vault.vault.svc.cluster.local:8200" + - name: "VAULT_CACERT" + value: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" volumeMounts: @@ -281,6 +273,9 @@ spec: - name: config mountPath: /vault/config + - name: userconfig-vault-secret + readOnly: true + mountPath: /vault/userconfig/vault-secret - name: home mountPath: /home/vault ports: @@ -364,7 +359,7 @@ spec: port: targetPort: 8200 tls: - termination: edge + termination: reencrypt --- # Source: hashicorp-vault/charts/vault/templates/tests/server-test.yaml apiVersion: v1 @@ -384,6 +379,10 @@ spec: - name: VAULT_ADDR value: http://hashicorp-vault.default.svc:8200 + - name: "VAULT_ADDR" + value: "https://vault.vault.svc.cluster.local:8200" + - name: "VAULT_CACERT" + value: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" command: - /bin/sh - -c diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index 4ec079cb..2f8643d4 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -26,35 +26,15 @@ data: extraconfig-from-values.hcl: |- disable_mlock = true ui = true - listener "tcp" { - tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" - # Enable unauthenticated metrics access (necessary for Prometheus Operator) - #telemetry { - # unauthenticated_metrics_access = "true" - #} + tls_cert_file = "/vault/userconfig/vault-secret/tls.crt" + tls_key_file = "/vault/userconfig/vault-secret/tls.key" } storage "file" { path = "/vault/data" } - - # Example configuration for using auto-unseal, using Google Cloud KMS. The - # GKMS keys must already exist, and the cluster must have a service account - # that is authorized to access GCP KMS. - #seal "gcpckms" { - # project = "vault-helm-dev" - # region = "global" - # key_ring = "vault-helm-unseal-kr" - # crypto_key = "vault-helm-unseal-key" - #} - - # Example configuration for enabling Prometheus metrics in your config. - #telemetry { - # prometheus_retention_time = "30s", - # disable_hostname = true - #} --- # Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -90,6 +70,8 @@ metadata: vault-internal: "true" annotations: + + service.beta.openshift.io/serving-cert-secret-name: vault-secret-internal spec: clusterIP: None publishNotReadyAddresses: true @@ -119,6 +101,8 @@ metadata: app.kubernetes.io/managed-by: Helm annotations: + + service.beta.openshift.io/serving-cert-secret-name: vault-secret spec: # We want the servers to become available even if they're not ready # since this DNS is also used for join operations. @@ -212,6 +196,10 @@ spec: configMap: name: hashicorp-vault-config + - name: userconfig-vault-secret + secret: + secretName: vault-secret + defaultMode: 420 - name: home emptyDir: {} containers: @@ -268,6 +256,10 @@ spec: value: "/home/vault" + - name: "VAULT_ADDR" + value: "https://vault.vault.svc.cluster.local:8200" + - name: "VAULT_CACERT" + value: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" volumeMounts: @@ -281,6 +273,9 @@ spec: - name: config mountPath: /vault/config + - name: userconfig-vault-secret + readOnly: true + mountPath: /vault/userconfig/vault-secret - name: home mountPath: /home/vault ports: @@ -364,7 +359,7 @@ spec: port: targetPort: 8200 tls: - termination: edge + termination: reencrypt --- # Source: hashicorp-vault/charts/vault/templates/tests/server-test.yaml apiVersion: v1 @@ -384,6 +379,10 @@ spec: - name: VAULT_ADDR value: http://hashicorp-vault.default.svc:8200 + - name: "VAULT_ADDR" + value: "https://vault.vault.svc.cluster.local:8200" + - name: "VAULT_CACERT" + value: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" command: - /bin/sh - -c diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index f681798f..d4b110b5 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -26,35 +26,15 @@ data: extraconfig-from-values.hcl: |- disable_mlock = true ui = true - listener "tcp" { - tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" - # Enable unauthenticated metrics access (necessary for Prometheus Operator) - #telemetry { - # unauthenticated_metrics_access = "true" - #} + tls_cert_file = "/vault/userconfig/vault-secret/tls.crt" + tls_key_file = "/vault/userconfig/vault-secret/tls.key" } storage "file" { path = "/vault/data" } - - # Example configuration for using auto-unseal, using Google Cloud KMS. The - # GKMS keys must already exist, and the cluster must have a service account - # that is authorized to access GCP KMS. - #seal "gcpckms" { - # project = "vault-helm-dev" - # region = "global" - # key_ring = "vault-helm-unseal-kr" - # crypto_key = "vault-helm-unseal-key" - #} - - # Example configuration for enabling Prometheus metrics in your config. - #telemetry { - # prometheus_retention_time = "30s", - # disable_hostname = true - #} --- # Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -90,6 +70,8 @@ metadata: vault-internal: "true" annotations: + + service.beta.openshift.io/serving-cert-secret-name: vault-secret-internal spec: clusterIP: None publishNotReadyAddresses: true @@ -119,6 +101,8 @@ metadata: app.kubernetes.io/managed-by: Helm annotations: + + service.beta.openshift.io/serving-cert-secret-name: vault-secret spec: # We want the servers to become available even if they're not ready # since this DNS is also used for join operations. @@ -212,6 +196,10 @@ spec: configMap: name: hashicorp-vault-config + - name: userconfig-vault-secret + secret: + secretName: vault-secret + defaultMode: 420 - name: home emptyDir: {} containers: @@ -268,6 +256,10 @@ spec: value: "/home/vault" + - name: "VAULT_ADDR" + value: "https://vault.vault.svc.cluster.local:8200" + - name: "VAULT_CACERT" + value: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" volumeMounts: @@ -281,6 +273,9 @@ spec: - name: config mountPath: /vault/config + - name: userconfig-vault-secret + readOnly: true + mountPath: /vault/userconfig/vault-secret - name: home mountPath: /home/vault ports: @@ -364,7 +359,7 @@ spec: port: targetPort: 8200 tls: - termination: edge + termination: reencrypt --- # Source: hashicorp-vault/charts/vault/templates/tests/server-test.yaml apiVersion: v1 @@ -384,6 +379,10 @@ spec: - name: VAULT_ADDR value: http://hashicorp-vault.default.svc:8200 + - name: "VAULT_ADDR" + value: "https://vault.vault.svc.cluster.local:8200" + - name: "VAULT_CACERT" + value: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" command: - /bin/sh - -c diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index 4ec079cb..2f8643d4 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -26,35 +26,15 @@ data: extraconfig-from-values.hcl: |- disable_mlock = true ui = true - listener "tcp" { - tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" - # Enable unauthenticated metrics access (necessary for Prometheus Operator) - #telemetry { - # unauthenticated_metrics_access = "true" - #} + tls_cert_file = "/vault/userconfig/vault-secret/tls.crt" + tls_key_file = "/vault/userconfig/vault-secret/tls.key" } storage "file" { path = "/vault/data" } - - # Example configuration for using auto-unseal, using Google Cloud KMS. The - # GKMS keys must already exist, and the cluster must have a service account - # that is authorized to access GCP KMS. - #seal "gcpckms" { - # project = "vault-helm-dev" - # region = "global" - # key_ring = "vault-helm-unseal-kr" - # crypto_key = "vault-helm-unseal-key" - #} - - # Example configuration for enabling Prometheus metrics in your config. - #telemetry { - # prometheus_retention_time = "30s", - # disable_hostname = true - #} --- # Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -90,6 +70,8 @@ metadata: vault-internal: "true" annotations: + + service.beta.openshift.io/serving-cert-secret-name: vault-secret-internal spec: clusterIP: None publishNotReadyAddresses: true @@ -119,6 +101,8 @@ metadata: app.kubernetes.io/managed-by: Helm annotations: + + service.beta.openshift.io/serving-cert-secret-name: vault-secret spec: # We want the servers to become available even if they're not ready # since this DNS is also used for join operations. @@ -212,6 +196,10 @@ spec: configMap: name: hashicorp-vault-config + - name: userconfig-vault-secret + secret: + secretName: vault-secret + defaultMode: 420 - name: home emptyDir: {} containers: @@ -268,6 +256,10 @@ spec: value: "/home/vault" + - name: "VAULT_ADDR" + value: "https://vault.vault.svc.cluster.local:8200" + - name: "VAULT_CACERT" + value: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" volumeMounts: @@ -281,6 +273,9 @@ spec: - name: config mountPath: /vault/config + - name: userconfig-vault-secret + readOnly: true + mountPath: /vault/userconfig/vault-secret - name: home mountPath: /home/vault ports: @@ -364,7 +359,7 @@ spec: port: targetPort: 8200 tls: - termination: edge + termination: reencrypt --- # Source: hashicorp-vault/charts/vault/templates/tests/server-test.yaml apiVersion: v1 @@ -384,6 +379,10 @@ spec: - name: VAULT_ADDR value: http://hashicorp-vault.default.svc:8200 + - name: "VAULT_ADDR" + value: "https://vault.vault.svc.cluster.local:8200" + - name: "VAULT_CACERT" + value: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" command: - /bin/sh - -c From 0a80bbf142ea47b76844805f45617fce9e0d4f33 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 30 Nov 2022 10:08:36 -0600 Subject: [PATCH 0695/1263] Remove stable default from channels --- clustergroup/templates/core/subscriptions.yaml | 8 ++++++-- tests/clustergroup-medical-diagnosis-hub.expected.yaml | 1 - tests/clustergroup-normal.expected.yaml | 1 - 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/clustergroup/templates/core/subscriptions.yaml b/clustergroup/templates/core/subscriptions.yaml index bdaeff84..f58f6c28 100644 --- a/clustergroup/templates/core/subscriptions.yaml +++ b/clustergroup/templates/core/subscriptions.yaml @@ -15,7 +15,9 @@ spec: name: {{ $subs.name }} source: {{ default "redhat-operators" $subs.source }} sourceNamespace: {{ default "openshift-marketplace" $subs.sourceNamespace }} - channel: {{ default "stable" $subs.channel }} + {{- if $subs.channel }} + channel: {{ $subs.channel }} + {{- end }} installPlanApproval: {{ coalesce $installPlanValue $.Values.global.options.installPlanApproval }} {{- if $subs.config }} {{- if $subs.config.env }} @@ -45,7 +47,9 @@ spec: name: {{ $subs.name }} source: {{ default "redhat-operators" $subs.source }} sourceNamespace: {{ default "openshift-marketplace" $subs.sourceNamespace }} - channel: {{ default "stable" $subs.channel }} + {{- if $subs.channel }} + channel: {{ $subs.channel }} + {{- end }} installPlanApproval: {{ coalesce $installPlanValue $.Values.global.options.installPlanApproval }} {{- if $subs.config }} {{- if $subs.config.env }} diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 5f0fa1e8..3b52ec6b 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -1513,7 +1513,6 @@ spec: name: opendatahub-operator source: community-operators sourceNamespace: openshift-marketplace - channel: stable installPlanApproval: Automatic startingCSV: --- diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 94ecfeca..3b378ca1 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -1012,6 +1012,5 @@ spec: name: openshift-pipelines-operator-rh source: redhat-operators sourceNamespace: openshift-marketplace - channel: stable installPlanApproval: Automatic startingCSV: redhat-openshift-pipelines.v1.5.2 From 90062787703ca43ee07996dcbacf5579094a17fd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 4 Dec 2022 22:20:56 +0100 Subject: [PATCH 0696/1263] Split secret counter in two variables The 'counter' variable is used to detect if it is the first vault kv command to be run (i.e. vault kv put as opposed to vault kv patch for subsequent runs), so we need to reset it for every top-level secret. We introduce an additional variable to count every secret entry uploaded. --- ansible/plugins/module_utils/load_secrets_v2.py | 6 ++++-- ansible/tests/unit/test_vault_load_secrets_v2.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 8ad5d1e2..50de6669 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -396,8 +396,9 @@ def inject_secrets(self): self.inject_vault_policies() secrets = self._get_secrets() - counter = 0 + total_secrets = 0 # Counter for all the secrets uploaded for s in secrets: + counter = 0 # This counter is to use kv put on first secret and kv patch on latter sname = s.get("name") fields = s.get("fields", []) mount = s.get("vaultMount", "secret") @@ -405,5 +406,6 @@ def inject_secrets(self): for i in fields: self._inject_field(sname, i, mount, vault_prefixes, counter == 0) counter += 1 + total_secrets += 1 - return counter + return total_secrets diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 0ea3af6f..a917a410 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -151,11 +151,11 @@ def test_ensure_no_vault_policies_is_ok(self, getpass): attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/region-two/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put -mount=secret secret/region-two/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), ] From d21b3dfaad077da89c1f31b4c862f24ab8f30580 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 4 Oct 2022 11:39:08 -0600 Subject: [PATCH 0697/1263] Validation Schema for common/clustergroup Having a schema for some of our values files will get us: - Minimize mistakes in the values files. - If there are mistakes we can discover them before deploying a validated pattern. - Having a pre-deploy target might be useful to validate the values files and give the user to continue or abort the deployment of the pattern. - Enforce structure in key values files used by the validated patterns framework. - The schema for the clustergroup chart will apply to any values-*.yaml file since it is the main chart for the Validated Patterns framework. The schema includes our global and main schemas to enable helm to validate these sections that we use in the values-global.yaml file. We think that this is a good solution to ensure that users do not add extraneous properties to the values files and it will, hopefully, help us with our documentation of the properties required in each of the values files for Validated Patterns. --- clustergroup/values.schema.json | 535 ++++++++++++++++++++++++++++++++ 1 file changed, 535 insertions(+) create mode 100644 clustergroup/values.schema.json diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json new file mode 100644 index 00000000..a369e97a --- /dev/null +++ b/clustergroup/values.schema.json @@ -0,0 +1,535 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$ref": "#/definitions/ValidatedPatterns", + "definitions": { + "ValidatedPatterns": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "string", + "enum": ["all", "core", "plumbing"] + }, + "secretStore": { + "$ref": "#/definitions/SecretStore" + }, + "main": { + "$ref": "#/definitions/Main" + }, + "global": { + "$ref": "#/definitions/Global" + }, + "clusterGroup": { + "$ref": "#/definitions/ClusterGroup" + } + }, + "required": [ + "clusterGroup" + ], + "title": "ValidatedPatterns" + }, + "SecretStore": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "kind": { + "type": "string" + } + }, + "required": [ + "name", + "kind" + ], + "title": "SecretsStore" + }, + "GlobalGit": { + "type": "object", + "additionalProperties": false, + "properties": { + "hostname": { + "type": "string" + }, + "account": { + "type": "string" + }, + "email": { + "type": "string" + }, + "dev_revision": { + "type": "string" + } + }, + "required": [ + "hostname", + "account", + "email" + ], + "title": "GlobalGit" + }, + "GitSecrets": { + "type": "object", + "additionalProperties": false, + "properties": { + "username": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "required": [ + "username", + "token" + ], + "title": "GitSecrets" + }, + "Main": { + "type": "object", + "additionalProperties": true, + "properties": { + "clusterGroupName": { + "type": "string" + }, + "hubClusterDomain": { + "type": "string" + }, + "localClusterDomain": { + "type": "string" + } + }, + "required": [ + "clusterGroupName" + ], + "title": "Main" + }, + "Global": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "localClusterDomain": { + "type": "string" + }, + "targetRevision": { + "type": "string" + }, + "hubClusterDomain": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "repoURL": { + "type": "string" + }, + "git": { + "$ref": "#/definitions/GlobalGit" + }, + "options": { + "$ref": "#/definitions/Options" + } + }, + "required": [ + "options", + "pattern" + ], + "title": "Global" + }, + "Options": { + "type": "object", + "additionalProperties": false, + "properties": { + "useCSV": { + "type": "boolean" + }, + "syncPolicy": { + "type": "string" + }, + "installPlanApproval": { + "type": "string" + } + }, + "required": [ + "installPlanApproval", + "syncPolicy", + "useCSV" + ], + "title": "Options" + }, + "ClusterGroup": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "targetCluster": { + "type": "string" + }, + "isHubCluster": { + "type": "boolean" + }, + "insecureUnsealVaultInsideCluster": { + "type": "boolean" + }, + "namespaces": { + "type": "array", + "items": { + "type": "string" + } + }, + "subscriptions": { + "type": "object", + "items": { + "$ref": "#/definitions/Subscription" + } + }, + "projects": { + "type": "array", + "items": { + "type": "string" + } + }, + "applications": { + "type": "object", + "items": { + "$ref": "#/definitions/Application" + } + }, + "imperative": { + "$ref": "#/definitions/Imperative" + }, + "managedClusterGroups": { + "anyOf": [ + { "type": "array" }, + { "type": "object" } + ], + "items": { + "$ref": "#/definitions/ManagedClusterGroup" + } + }, + "externalClusters": { + "type": "array" + } + }, + "required": [ + "applications", + "imperative", + "insecureUnsealVaultInsideCluster", + "isHubCluster", + "managedClusterGroups", + "name", + "namespaces", + "projects", + "subscriptions" + ], + "title": "ClusterGroup" + }, + "Application": { + "type": "object", + "additionalProperties": true, + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "project": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": [ + "name", + "namespace", + "path", + "project" + ], + "title": "Application" + }, + "Imperative": { + "type": "object", + "additionalProperties": false, + "properties": { + "jobs": { + "type": "array", + "items": { + "$ref": "#/definitions/Job" + } + }, + "image": { + "type": "string", + "default": "registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest" + }, + "namespace": { + "type": "string", + "default": "imperative", + "enum": ["imperative"] + }, + "serviceAccountCreate": { + "type": "boolean" + }, + "valuesConfigMap": { + "type": "string" + }, + "cronJobName": { + "type": "string" + }, + "jobName": { + "type": "string" + }, + "imagePullPolicy": { + "type": "string", + "default": "Always", + "enum": ["Always", "IfNotPresent", "Never"] + }, + "activeDeadlineSeconds": { + "type": "integer", + "default": 3600 + }, + "schedule": { + "type": "string", + "default": "*/10 * * * *" + }, + "insecureUnsealVaultInsideClusterSchedule": { + "type": "string", + "default": "*/5 * * * *" + }, + "verbosity": { + "type": "string", + "default": "", + "enum": ["","-v", "-vv", "-vvv", "-vvvv"] + }, + "serviceAccountName": { + "type": "string" + }, + "clusterRoleName": { + "type": "string" + }, + "clusterRoleYaml": { + "type": "string" + }, + "roleName": { + "type": "string" + }, + "roleYaml": { + "type": "string" + } + }, + "required": [ + "jobs", + "valuesConfigMap", + "cronJobName", + "jobName", + "activeDeadlineSeconds", + "schedule" + ], + "title": "Imperative" + }, + "Job": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "playbook": { + "type": "string" + }, + "timeout": { + "type": "integer" + }, + "verbosity": { + "type": "string" + } + }, + "required": [ + "name", + "playbook" + ], + "title": "Job" + }, + "ManagedClusterGroup": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "targetRevision": { + "type": "string" + }, + "acmlabels": { + "type": "array", + "items": { + "$ref": "#/definitions/ACMLabels" + } + }, + "hostedArgoSites": { + "type": "array", + "items": { + "$ref": "#/definitions/HostedArgoSites" + } + }, + "clusterPools": { + "type": "object", + "items": { + "$ref": "#/definitions/ClusterPools" + } + }, + "helmOverrides": { + "type": "array", + "items": { + "$ref": "#/definitions/HelmOverride" + } + } + }, + "required": [ + ], + "title": "ManagedClusterGroup" + }, + "ClusterPools": { + "type": "object", + "additionalProperties": false, + "properties": { + "size": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "openshiftVersion": { + "type": "string" + }, + "baseDomain": { + "type": "string" + }, + "platform": { + "type": "object", + "$ref": "#/definitions/ClusterPoolsPlatform" + }, + "clusters": { + "type": "array" + } + }, + "required": [ + "name", + "openshiftVersion", + "baseDomain", + "platform", + "clusters" + ], + "title": "ClusterPools" + }, + "ClusterPoolsPlatform": { + "type": "object", + "additionalProperties": false, + "properties": { + "baseDomainResourceGroupName": { + "type": "string" + }, + "region": { + "type": "string" + } + }, + "required": [ + "region" + ], + "title": "ClusterPoolsPlatform" + }, + "HelmOverride": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "name", + "value" + ], + "title": "HelmOverride" + }, + "ACMLabels": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "name", + "value" + ], + "title": "ACMLabels" + }, + "Subscription": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "source": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "csv": { + "type": "string" + }, + "disabled": { + "type": "boolean" + } + }, + "required": [ + "name", + "channel" + ], + "title": "Subscription" + }, + "HostedArgoSites": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "bearerKeyPath": { + "type": "string" + }, + "caKeyPath": { + "type": "string" + } + }, + "required": [ + "name", + "domain" + ], + "title": "HostedArgoSites" + } + } +} From 968fe0be0eb300c4e67c5d38251a4c656365607d Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 4 Oct 2022 14:00:35 -0600 Subject: [PATCH 0698/1263] - Fixed issues with schema validation in values.schema.json - Modified clustergroup/values.yaml and examples/values-example.yaml to have same sections and values. - Updated tests for tests/clustergroup-naked.expected.yaml and tests/clustergroup-normal.expected.yaml --- clustergroup/values.schema.json | 11 +- clustergroup/values.yaml | 189 +++--- examples/values-example.yaml | 20 + tests/clustergroup-naked.expected.yaml | 804 +++++++++++++++++++++++- tests/clustergroup-normal.expected.yaml | 110 ++-- tests/clustergroup.expected.diff | 302 +++++---- 6 files changed, 1117 insertions(+), 319 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index a369e97a..8790df09 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -112,6 +112,9 @@ "pattern": { "type": "string" }, + "clusterDomain": { + "type": "string" + }, "localClusterDomain": { "type": "string" }, @@ -219,7 +222,6 @@ }, "required": [ "applications", - "imperative", "insecureUnsealVaultInsideCluster", "isHubCluster", "managedClusterGroups", @@ -325,12 +327,7 @@ } }, "required": [ - "jobs", - "valuesConfigMap", - "cronJobName", - "jobName", - "activeDeadlineSeconds", - "schedule" + "jobs" ], "title": "Imperative" }, diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index fac5d56c..3b8b12b9 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -1,90 +1,125 @@ global: - pattern: common - targetRevision: main + pattern: example-pattern options: - useCSV: True + useCSV: False syncPolicy: Automatic installPlanApproval: Automatic -enabled: "all" +enabled: all -# Note that sometimes changing helm values might require a hard refresh (https://github.com/helm/helm/issues/3486) clusterGroup: name: example + insecureUnsealVaultInsideCluster: true isHubCluster: true - targetCluster: in-cluster - # Note: setting this to true stores the vault unseal keys inside a cluster secret and - # is fundamentally insecure - insecureUnsealVaultInsideCluster: false - imperative: - jobs: [] - # This image contains ansible + kubernetes.core by default and is used to run the jobs - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - namespace: "imperative" - # configmap name in the namespace that will contain all helm values - valuesConfigMap: "helm-values-configmap" - cronJobName: "imperative-cronjob" - jobName: "imperative-job" - imagePullPolicy: Always - # This is the maximum timeout of all the jobs (1h) - activeDeadlineSeconds: 3600 - # By default we run this every 10minutes - schedule: "*/10 * * * *" - # Schedule used to trigger the vault unsealing (if explicitely enabled) - # Set to run every 5 minutes in order for load-secrets to succeed within - # a reasonable amount of time (it waits up to 15 mins) - insecureUnsealVaultInsideClusterSchedule: "*/5 * * * *" - # Increase ansible verbosity with '-v' or '-vv..' - verbosity: "" - serviceAccountCreate: true - # service account to be used to run the cron pods - serviceAccountName: imperative-sa - clusterRoleName: imperative-cluster-role - clusterRoleYaml: "" - roleName: imperative-role - roleYaml: "" + namespaces: + - open-cluster-management + - application-ci + + subscriptions: + acm: + name: advanced-cluster-management + namespace: open-cluster-management + channel: release-2.4 + csv: advanced-cluster-management.v2.4.1 + + odh: + name: opendatahub-operator + source: community-operators + csv: opendatahub-operator.v1.1.0 + disabled: true -secretStore: - name: vault-backend - kind: ClusterSecretStore + pipelines: + name: openshift-pipelines-operator-rh + csv: redhat-openshift-pipelines.v1.5.2 -# Depends on the value of 'vault_hub' ansible variable used -# during the installation -secretsBase: - key: secret/data/hub + projects: + - datacenter + + applications: + acm: + name: acm + namespace: open-cluster-management + project: datacenter + path: common/acm + ignoreDifferences: + - group: internal.open-cluster-management.io + kind: ManagedClusterInfo + jsonPointers: + - /spec/loggingCA + pipe: + name: pipelines + namespace: application-ci + project: datacenter + path: charts/datacenter/pipelines + + imperative: + namespace: imperative + # NOTE: We *must* use lists and not hashes. As hashes lose ordering once parsed by helm + # The default schedule is every 10 minutes: imperative.schedule + # Total timeout of all jobs is 1h: imperative.activeDeadlineSeconds + # imagePullPolicy is set to always: imperative.imagePullPolicy + # For additional overrides that apply to the jobs, please refer to + # https://hybrid-cloud-patterns.io/imperative-actions/#additional-job-customizations + jobs: + - name: regional-ca + # ansible playbook to be run + playbook: ansible/playbooks/on-hub-get-regional-ca.yml + # per playbook timeout in seconds + timeout: 234 + # verbosity: "-v" -# managedClusterGroups: -# - name: factory -# # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git -# # Location of values-global.yaml, values-{name}.yaml, values-{app}.yaml -# targetRevision: main -# path: applications/factory -# helmOverrides: -# - name: clusterGroup.isHubCluster -# value: false -# clusterSelector: -# matchExpressions: -# - key: vendor -# operator: In -# values: -# - OpenShift -# -# namespaces: -# - open-cluster-management -# -# subscriptions: -# - name: advanced-cluster-management -# namespace: open-cluster-management -# source: redhat-operators -# channel: release-2.3 -# csv: v2.3.2 -# -# projects: -# - datacenter -# -# applications: -# - name: acm -# namespace: default -# project: datacenter -# path: applications/acm + managedClusterGroups: + - name: acm-edge + # Optional - Point to a different repo + # repoURL: https://github.com/hybrid-cloud-patterns/mySite.git + # Must contain values-{clustergroupname}.yaml at the top level + targetRevision: main + helmOverrides: + # Values must be strings! + - name: clusterGroup.isHubCluster + value: "false" + acmlabels: + - name: clusterGroup + value: acm-region + - name: acm-provision-edge + targetRevision: main + helmOverrides: + - name: clusterGroup.isHubCluster + value: "false" + clusterPools: + exampleAWSPool: + size: 3 + name: aws-ap + openshiftVersion: 4.10.18 + baseDomain: blueprints.rhecoeng.com + platform: + aws: + region: ap-southeast-2 + clusters: + - One + exampleAzurePool: + name: azure-us + openshiftVersion: 4.10.18 + baseDomain: blueprints.rhecoeng.com + platform: + azure: + baseDomainResourceGroupName: dojo-dns-zones + region: eastus + clusters: + - Two + - Three + acmlabels: + - name: clusterGroup + value: region + - name: argo-edge + hostedArgoSites: + - name: perth + domain: perth1.beekhof.net + bearerKeyPath: secret/data/hub/cluster_perth + caKeyPath: secret/data/hub/cluster_perth_ca + - name: sydney + domain: syd.beekhof.net + helmOverrides: + - name: clusterGroup.isHubCluster + value: "false" diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 54e0dd28..baf6b49a 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -4,8 +4,12 @@ global: syncPolicy: Automatic installPlanApproval: Automatic +enabled: all + clusterGroup: name: example + insecureUnsealVaultInsideCluster: false + isHubCluster: true namespaces: - open-cluster-management @@ -48,6 +52,22 @@ clusterGroup: project: datacenter path: charts/datacenter/pipelines + imperative: + namespace: imperative + # NOTE: We *must* use lists and not hashes. As hashes lose ordering once parsed by helm + # The default schedule is every 10 minutes: imperative.schedule + # Total timeout of all jobs is 1h: imperative.activeDeadlineSeconds + # imagePullPolicy is set to always: imperative.imagePullPolicy + # For additional overrides that apply to the jobs, please refer to + # https://hybrid-cloud-patterns.io/imperative-actions/#additional-job-customizations + jobs: + - name: regional-ca + # ansible playbook to be run + playbook: ansible/playbooks/on-hub-get-regional-ca.yml + # per playbook timeout in seconds + timeout: 234 + # verbosity: "-v" + managedClusterGroups: - name: acm-edge # Optional - Point to a different repo diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 4d5c5eee..cc9dc455 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -1,17 +1,186 @@ --- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: pattern + argocd.argoproj.io/managed-by: example-pattern-example + name: open-cluster-management +spec: +--- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: pattern + argocd.argoproj.io/managed-by: example-pattern-example + name: application-ci +spec: +--- +# Source: pattern-clustergroup/templates/imperative/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: imperative + argocd.argoproj.io/managed-by: example-pattern-example + name: imperative +--- # Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml apiVersion: v1 kind: Namespace metadata: labels: - name: common-example + name: example-pattern-example # The name here needs to be consistent with # - acm/templates/policies/application-policies.yaml # - clustergroup/templates/applications.yaml # - any references to secrets and route URLs in documentation - name: common-example + name: example-pattern-example spec: {} --- +# Source: pattern-clustergroup/templates/imperative/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: -example + namespace: imperative +data: + values.yaml: | + clusterGroup: + applications: + acm: + ignoreDifferences: + - group: internal.open-cluster-management.io + jsonPointers: + - /spec/loggingCA + kind: ManagedClusterInfo + name: acm + namespace: open-cluster-management + path: common/acm + project: datacenter + pipe: + name: pipelines + namespace: application-ci + path: charts/datacenter/pipelines + project: datacenter + imperative: + jobs: + - name: regional-ca + playbook: ansible/playbooks/on-hub-get-regional-ca.yml + timeout: 234 + namespace: imperative + insecureUnsealVaultInsideCluster: true + isHubCluster: true + managedClusterGroups: + - acmlabels: + - name: clusterGroup + value: acm-region + helmOverrides: + - name: clusterGroup.isHubCluster + value: "false" + name: acm-edge + targetRevision: main + - acmlabels: + - name: clusterGroup + value: region + clusterPools: + exampleAWSPool: + baseDomain: blueprints.rhecoeng.com + clusters: + - One + name: aws-ap + openshiftVersion: 4.10.18 + platform: + aws: + region: ap-southeast-2 + size: 3 + exampleAzurePool: + baseDomain: blueprints.rhecoeng.com + clusters: + - Two + - Three + name: azure-us + openshiftVersion: 4.10.18 + platform: + azure: + baseDomainResourceGroupName: dojo-dns-zones + region: eastus + helmOverrides: + - name: clusterGroup.isHubCluster + value: "false" + name: acm-provision-edge + targetRevision: main + - helmOverrides: + - name: clusterGroup.isHubCluster + value: "false" + hostedArgoSites: + - bearerKeyPath: secret/data/hub/cluster_perth + caKeyPath: secret/data/hub/cluster_perth_ca + domain: perth1.beekhof.net + name: perth + - domain: syd.beekhof.net + name: sydney + name: argo-edge + name: example + namespaces: + - open-cluster-management + - application-ci + projects: + - datacenter + subscriptions: + acm: + channel: release-2.4 + csv: advanced-cluster-management.v2.4.1 + name: advanced-cluster-management + namespace: open-cluster-management + odh: + csv: opendatahub-operator.v1.1.0 + disabled: true + name: opendatahub-operator + source: community-operators + pipelines: + csv: redhat-openshift-pipelines.v1.5.2 + name: openshift-pipelines-operator-rh + enabled: all + global: + options: + installPlanApproval: Automatic + syncPolicy: Automatic + useCSV: false + pattern: example-pattern +--- +# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - get + - list + - watch +--- +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: imperative-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: +subjects: + - kind: ServiceAccount + name: + namespace: imperative +--- # Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 @@ -36,7 +205,7 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: common-example-cluster-admin-rolebinding + name: example-pattern-example-cluster-admin-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -45,16 +214,583 @@ subjects: - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-application-controller name: example-gitops-argocd-application-controller - namespace: common-example + namespace: example-pattern-example # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-server name: example-gitops-argocd-server - namespace: common-example + namespace: example-pattern-example # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) - kind: ServiceAccount name: example-gitops-argocd-dex-server - namespace: common-example + namespace: example-pattern-example +--- +# Source: pattern-clustergroup/templates/imperative/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: + namespace: imperative +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: imperative-admin-rolebinding + namespace: imperative +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: +subjects: + - kind: ServiceAccount + name: + namespace: imperative +--- +# Source: pattern-clustergroup/templates/imperative/job.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: + namespace: imperative +spec: + schedule: + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: + template: + metadata: + name: + spec: + serviceAccountName: + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: + imagePullPolicy: + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" + - name: regional-ca + image: + imagePullPolicy: + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - "234" + - ansible-playbook + - -e + - "@/values/values.yaml" + - ansible/playbooks/on-hub-get-regional-ca.yml + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: "done" + image: + imagePullPolicy: + command: + - 'sh' + - '-c' + - 'echo' + - 'done' + - '\n' + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: -example + restartPolicy: Never +--- +# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: unsealvault-cronjob + namespace: imperative +spec: + schedule: + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: + template: + metadata: + name: unsealvault-job + spec: + serviceAccountName: + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: + imagePullPolicy: + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" + - name: unseal-playbook + image: + imagePullPolicy: + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - "600" + - ansible-playbook + - -e + - "@/values/values.yaml" + - -e + - '{"file_unseal": false}' + - -t + - 'vault_init,vault_unseal,vault_secrets_init' + - "common/ansible/playbooks/vault/vault.yaml" + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: "done" + image: + imagePullPolicy: + command: + - 'sh' + - '-c' + - 'echo' + - 'done' + - '\n' + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: -example + restartPolicy: Never +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +--- +--- +# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: argo-edge + namespace: openshift-gitops +spec: + description: "Cluster Group argo-edge" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- +# Source: pattern-clustergroup/templates/plumbing/projects.yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: datacenter + namespace: example-pattern-example +spec: + description: "Pattern datacenter" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: acm + namespace: example-pattern-example + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: + namespace: open-cluster-management + project: datacenter + source: + repoURL: + targetRevision: + path: common/acm + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-example.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: example-pattern + - name: global.clusterDomain + value: + - name: global.hubClusterDomain + value: + - name: global.localClusterDomain + value: + ignoreDifferences: [ + { + "group": "internal.open-cluster-management.io", + "jsonPointers": [ + "/spec/loggingCA" + ], + "kind": "ManagedClusterInfo" + } +] + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/applications.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: pipelines + namespace: example-pattern-example + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + destination: + name: + namespace: application-ci + project: datacenter + source: + repoURL: + targetRevision: + path: charts/datacenter/pipelines + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-example.yaml" + # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: example-pattern + - name: global.clusterDomain + value: + - name: global.hubClusterDomain + value: + - name: global.localClusterDomain + value: + syncPolicy: + automated: {} + # selfHeal: true +--- +# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: example-pattern-argo-edge-perth + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + project: argo-edge + source: + repoURL: + targetRevision: + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-argo-edge.yaml" + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: example-pattern + - name: global.hubClusterDomain + value: + - name: global.localClusterDomain + value: apps.perth1.beekhof.net + - name: global.clusterDomain + value: perth1.beekhof.net + - name: enabled + value: core + - name: clusterGroup.name + value: argo-edge + - name: clusterGroup.targetCluster + value: perth + - name: clusterGroup.hostedSite.bearerKeyPath + value: secret/data/hub/cluster_perth + - name: clusterGroup.hostedSite.caKeyPath + value: secret/data/hub/cluster_perth_ca + - name: clusterGroup.isHubCluster + value: "false" + destination: + name: perth + namespace: example-pattern-argo-edge + syncPolicy: + automated: + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +--- +# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: example-pattern-argo-edge-perth-plumbing + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + project: argo-edge + source: + repoURL: + targetRevision: + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-argo-edge.yaml" + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: example-pattern + - name: global.hubClusterDomain + value: + - name: global.localClusterDomain + value: apps.perth1.beekhof.net + - name: global.clusterDomain + value: perth1.beekhof.net + - name: enabled + value: plumbing + - name: clusterGroup.name + value: argo-edge + - name: clusterGroup.targetCluster + value: perth + - name: clusterGroup.hostedSite.bearerKeyPath + value: secret/data/hub/cluster_perth + - name: clusterGroup.hostedSite.caKeyPath + value: secret/data/hub/cluster_perth_ca + - name: clusterGroup.isHubCluster + value: "false" + destination: + name: in-cluster + namespace: openshift-gitops + syncPolicy: + automated: + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +--- +# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: example-pattern-argo-edge-sydney + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + project: argo-edge + source: + repoURL: + targetRevision: + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-argo-edge.yaml" + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: example-pattern + - name: global.hubClusterDomain + value: + - name: global.localClusterDomain + value: apps.syd.beekhof.net + - name: global.clusterDomain + value: syd.beekhof.net + - name: enabled + value: core + - name: clusterGroup.name + value: argo-edge + - name: clusterGroup.targetCluster + value: sydney + - name: clusterGroup.hostedSite.bearerKeyPath + value: secret/data/hub/cluster_sydney + - name: clusterGroup.hostedSite.caKeyPath + value: secret/data/hub/cluster_sydney_ca + - name: clusterGroup.isHubCluster + value: "false" + destination: + name: sydney + namespace: example-pattern-argo-edge + syncPolicy: + automated: + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +--- +# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: example-pattern-argo-edge-sydney-plumbing + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground +spec: + project: argo-edge + source: + repoURL: + targetRevision: + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-argo-edge.yaml" + parameters: + - name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL + - name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: example-pattern + - name: global.hubClusterDomain + value: + - name: global.localClusterDomain + value: apps.syd.beekhof.net + - name: global.clusterDomain + value: syd.beekhof.net + - name: enabled + value: plumbing + - name: clusterGroup.name + value: argo-edge + - name: clusterGroup.targetCluster + value: sydney + - name: clusterGroup.hostedSite.bearerKeyPath + value: secret/data/hub/cluster_sydney + - name: clusterGroup.hostedSite.caKeyPath + value: secret/data/hub/cluster_sydney_ca + - name: clusterGroup.isHubCluster + value: "false" + destination: + name: in-cluster + namespace: openshift-gitops + syncPolicy: + automated: + selfHeal: true + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 @@ -65,7 +801,7 @@ metadata: # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: example-gitops - namespace: common-example + namespace: example-pattern-example annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: @@ -94,7 +830,7 @@ spec: --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern=common + --set global.pattern=example-pattern --set global.clusterDomain= --set global.hubClusterDomain= --set global.localClusterDomain= @@ -174,11 +910,59 @@ apiVersion: console.openshift.io/v1 kind: ConsoleLink metadata: name: example-gitops-link - namespace: common-example + namespace: example-pattern-example spec: applicationMenu: section: OpenShift GitOps imageURL: data:image/png;base64,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 - href: 'https://example-gitops-server-common-example.' + href: 'https://example-gitops-server-example-pattern-example.' location: ApplicationMenu text: 'Example ArgoCD' +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: open-cluster-management-operator-group + namespace: open-cluster-management +spec: + targetNamespaces: + - open-cluster-management +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: application-ci-operator-group + namespace: application-ci +spec: + targetNamespaces: + - application-ci +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: advanced-cluster-management + namespace: open-cluster-management +spec: + name: advanced-cluster-management + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: release-2.4 + installPlanApproval: Automatic + startingCSV: advanced-cluster-management.v2.4.1 +--- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-pipelines-operator-rh + namespace: openshift-operators +spec: + name: openshift-pipelines-operator-rh + source: redhat-operators + sourceNamespace: openshift-marketplace + channel: stable + installPlanApproval: Automatic + startingCSV: redhat-openshift-pipelines.v1.5.2 diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 3b378ca1..69a64057 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -39,18 +39,11 @@ metadata: name: mypattern-example spec: {} --- -# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: imperative-sa - namespace: imperative ---- # Source: pattern-clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: - name: helm-values-configmap-example + name: -example namespace: imperative data: values.yaml: | @@ -72,25 +65,11 @@ data: path: charts/datacenter/pipelines project: datacenter imperative: - activeDeadlineSeconds: 3600 - clusterRoleName: imperative-cluster-role - clusterRoleYaml: "" - cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' - jobName: imperative-job jobs: - name: test playbook: ansible/test.yml + timeout: 234 namespace: imperative - roleName: imperative-role - roleYaml: "" - schedule: '*/10 * * * *' - serviceAccountCreate: true - serviceAccountName: imperative-sa - valuesConfigMap: helm-values-configmap - verbosity: "" insecureUnsealVaultInsideCluster: true isHubCluster: true managedClusterGroups: @@ -163,7 +142,6 @@ data: pipelines: csv: redhat-openshift-pipelines.v1.5.2 name: openshift-pipelines-operator-rh - targetCluster: in-cluster enabled: all global: clusterDomain: region.example.com @@ -181,23 +159,17 @@ data: useCSV: false pattern: mypattern repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main main: clusterGroupName: example git: repoURL: https://github.com/pattern-clone/mypattern revision: main - secretStore: - kind: ClusterSecretStore - name: vault-backend - secretsBase: - key: secret/data/hub --- # Source: pattern-clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: imperative-cluster-role + name: rules: - apiGroups: - '*' @@ -216,10 +188,10 @@ metadata: roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: imperative-cluster-role + name: subjects: - kind: ServiceAccount - name: imperative-sa + name: namespace: imperative --- # Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml @@ -270,7 +242,7 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: imperative-role + name: namespace: imperative rules: - apiGroups: @@ -289,49 +261,49 @@ metadata: roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: imperative-role + name: subjects: - kind: ServiceAccount - name: imperative-sa + name: namespace: imperative --- # Source: pattern-clustergroup/templates/imperative/job.yaml apiVersion: batch/v1 kind: CronJob metadata: - name: imperative-cronjob + name: namespace: imperative spec: - schedule: "*/10 * * * *" + schedule: # if previous Job is still running, skip execution of a new Job concurrencyPolicy: Forbid jobTemplate: spec: - activeDeadlineSeconds: 3600 + activeDeadlineSeconds: template: metadata: - name: imperative-job + name: spec: - serviceAccountName: imperative-sa + serviceAccountName: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always + image: + imagePullPolicy: env: - name: HOME value: /git/home command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" volumeMounts: - name: git mountPath: "/git" - name: test - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always + image: + imagePullPolicy: env: - name: HOME value: /git/home @@ -340,7 +312,7 @@ spec: # on a per-job basis command: - timeout - - "600" + - "234" - ansible-playbook - -e - "@/values/values.yaml" @@ -353,8 +325,8 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always + image: + imagePullPolicy: command: - 'sh' - '-c' @@ -366,7 +338,7 @@ spec: emptyDir: {} - name: values-volume configMap: - name: helm-values-configmap-example + name: -example restartPolicy: Never --- # Source: pattern-clustergroup/templates/imperative/unsealjob.yaml @@ -376,36 +348,36 @@ metadata: name: unsealvault-cronjob namespace: imperative spec: - schedule: "*/5 * * * *" + schedule: # if previous Job is still running, skip execution of a new Job concurrencyPolicy: Forbid jobTemplate: spec: - activeDeadlineSeconds: 3600 + activeDeadlineSeconds: template: metadata: name: unsealvault-job spec: - serviceAccountName: imperative-sa + serviceAccountName: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always + image: + imagePullPolicy: env: - name: HOME value: /git/home command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" volumeMounts: - name: git mountPath: "/git" - name: unseal-playbook - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always + image: + imagePullPolicy: env: - name: HOME value: /git/home @@ -431,8 +403,8 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always + image: + imagePullPolicy: command: - 'sh' - '-c' @@ -444,7 +416,7 @@ spec: emptyDir: {} - name: values-volume configMap: - name: helm-values-configmap-example + name: -example restartPolicy: Never --- # Source: pattern-clustergroup/templates/core/subscriptions.yaml @@ -502,12 +474,12 @@ metadata: - resources-finalizer.argocd.argoproj.io/foreground spec: destination: - name: in-cluster + name: namespace: open-cluster-management project: datacenter source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main + targetRevision: path: common/acm helm: ignoreMissingValueFiles: true @@ -557,12 +529,12 @@ metadata: - resources-finalizer.argocd.argoproj.io/foreground spec: destination: - name: in-cluster + name: namespace: application-ci project: datacenter source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main + targetRevision: path: charts/datacenter/pipelines helm: ignoreMissingValueFiles: true @@ -605,7 +577,7 @@ spec: project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main + targetRevision: path: common/clustergroup helm: ignoreMissingValueFiles: true @@ -667,7 +639,7 @@ spec: project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main + targetRevision: path: common/clustergroup helm: ignoreMissingValueFiles: true @@ -729,7 +701,7 @@ spec: project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main + targetRevision: path: common/clustergroup helm: ignoreMissingValueFiles: true @@ -791,7 +763,7 @@ spec: project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main + targetRevision: path: common/clustergroup helm: ignoreMissingValueFiles: true diff --git a/tests/clustergroup.expected.diff b/tests/clustergroup.expected.diff index 7f04814c..35a5b6b4 100644 --- a/tests/clustergroup.expected.diff +++ b/tests/clustergroup.expected.diff @@ -17,75 +17,30 @@ +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-example -+ name: application-ci -+spec: -+--- -+# Source: pattern-clustergroup/templates/imperative/namespace.yaml -+apiVersion: v1 -+kind: Namespace -+metadata: -+ labels: -+ name: imperative -+ argocd.argoproj.io/managed-by: mypattern-example -+ name: imperative -+--- + name: imperative + --- # Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml - apiVersion: v1 +@@ -33,12 +33,12 @@ kind: Namespace metadata: labels: -- name: common-example +- name: example-pattern-example + name: mypattern-example # The name here needs to be consistent with # - acm/templates/policies/application-policies.yaml # - clustergroup/templates/applications.yaml # - any references to secrets and route URLs in documentation -- name: common-example +- name: example-pattern-example + name: mypattern-example spec: {} --- -+# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml -+apiVersion: v1 -+kind: ServiceAccount -+metadata: -+ name: imperative-sa -+ namespace: imperative -+--- -+# Source: pattern-clustergroup/templates/imperative/configmap.yaml -+apiVersion: v1 -+kind: ConfigMap -+metadata: -+ name: helm-values-configmap-example -+ namespace: imperative -+data: -+ values.yaml: | -+ clusterGroup: -+ applications: -+ acm: -+ ignoreDifferences: -+ - group: internal.open-cluster-management.io -+ jsonPointers: -+ - /spec/loggingCA -+ kind: ManagedClusterInfo -+ name: acm -+ namespace: open-cluster-management -+ path: common/acm -+ project: datacenter -+ pipe: -+ name: pipelines -+ namespace: application-ci -+ path: charts/datacenter/pipelines -+ project: datacenter -+ imperative: -+ activeDeadlineSeconds: 3600 -+ clusterRoleName: imperative-cluster-role -+ clusterRoleYaml: "" -+ cronJobName: imperative-cronjob -+ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest -+ imagePullPolicy: Always -+ insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' -+ jobName: imperative-job -+ jobs: + # Source: pattern-clustergroup/templates/imperative/configmap.yaml +@@ -68,8 +68,8 @@ + project: datacenter + imperative: + jobs: +- - name: regional-ca +- playbook: ansible/playbooks/on-hub-get-regional-ca.yml + - name: test + playbook: ansible/test.yml + namespace: imperative @@ -180,61 +135,26 @@ + hubClusterDomain: apps.hub.example.com + localClusterDomain: apps.region.example.com + namespace: pattern-namespace -+ options: -+ installPlanApproval: Automatic -+ syncPolicy: Automatic -+ useCSV: false + options: + installPlanApproval: Automatic + syncPolicy: Automatic + useCSV: false +- pattern: example-pattern + pattern: mypattern + repoURL: https://github.com/pattern-clone/mypattern -+ targetRevision: main + main: + clusterGroupName: example + git: + repoURL: https://github.com/pattern-clone/mypattern + revision: main -+ secretStore: -+ kind: ClusterSecretStore -+ name: vault-backend -+ secretsBase: -+ key: secret/data/hub -+--- -+# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml -+apiVersion: rbac.authorization.k8s.io/v1 -+kind: ClusterRole -+metadata: -+ name: imperative-cluster-role -+rules: -+ - apiGroups: -+ - '*' -+ resources: -+ - '*' -+ verbs: -+ - get -+ - list -+ - watch -+--- -+# Source: pattern-clustergroup/templates/imperative/rbac.yaml -+apiVersion: rbac.authorization.k8s.io/v1 -+kind: ClusterRoleBinding -+metadata: -+ name: imperative-cluster-admin-rolebinding -+roleRef: -+ apiGroup: rbac.authorization.k8s.io -+ kind: ClusterRole -+ name: imperative-cluster-role -+subjects: -+ - kind: ServiceAccount -+ name: imperative-sa -+ namespace: imperative -+--- - # Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml - # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS + --- + # Source: pattern-clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -36,7 +246,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: -- name: common-example-cluster-admin-rolebinding +- name: example-pattern-example-cluster-admin-rolebinding + name: mypattern-example-cluster-admin-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io @@ -243,18 +163,18 @@ - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-application-controller name: example-gitops-argocd-application-controller -- namespace: common-example +- namespace: example-pattern-example + namespace: mypattern-example # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-server name: example-gitops-argocd-server -- namespace: common-example +- namespace: example-pattern-example + namespace: mypattern-example # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) - kind: ServiceAccount name: example-gitops-argocd-dex-server -- namespace: common-example +- namespace: example-pattern-example + namespace: mypattern-example +--- +# Source: pattern-clustergroup/templates/imperative/role.yaml @@ -831,14 +751,131 @@ + kind: Route + jsonPointers: + - /status +======= +>>>>>>> bfaedd3 (- Fixed issues with schema validation in values.schema.json) --- - # Source: pattern-clustergroup/templates/plumbing/argocd.yaml + # Source: pattern-clustergroup/templates/imperative/role.yaml + apiVersion: rbac.authorization.k8s.io/v1 +@@ -284,11 +299,11 @@ + command: + - 'sh' + - '-c' +- - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- /git/repo;chmod 0770 /git/{repo,home}" ++ - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" +- - name: regional-ca ++ - name: test + image: + imagePullPolicy: + env: +@@ -303,7 +318,7 @@ + - ansible-playbook + - -e + - "@/values/values.yaml" +- - ansible/playbooks/on-hub-get-regional-ca.yml ++ - ansible/test.yml + volumeMounts: + - name: git + mountPath: "/git" +@@ -358,7 +373,7 @@ + command: + - 'sh' + - '-c' +- - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- /git/repo;chmod 0770 /git/{repo,home}" ++ - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" +@@ -435,7 +450,7 @@ + kind: AppProject + metadata: + name: datacenter +- namespace: example-pattern-example ++ namespace: mypattern-example + spec: + description: "Pattern datacenter" + destinations: +@@ -456,7 +471,7 @@ + kind: Application + metadata: + name: acm +- namespace: example-pattern-example ++ namespace: mypattern-example + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground + spec: +@@ -465,7 +480,7 @@ + namespace: open-cluster-management + project: datacenter + source: +- repoURL: ++ repoURL: https://github.com/pattern-clone/mypattern + targetRevision: + path: common/acm + helm: +@@ -482,13 +497,13 @@ + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern +- value: example-pattern ++ value: mypattern + - name: global.clusterDomain +- value: ++ value: region.example.com + - name: global.hubClusterDomain +- value: ++ value: apps.hub.example.com + - name: global.localClusterDomain +- value: ++ value: apps.region.example.com + ignoreDifferences: [ + { + "group": "internal.open-cluster-management.io", +@@ -507,7 +522,7 @@ + kind: Application + metadata: + name: pipelines +- namespace: example-pattern-example ++ namespace: mypattern-example + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground + spec: +@@ -516,7 +531,7 @@ + namespace: application-ci + project: datacenter + source: +- repoURL: ++ repoURL: https://github.com/pattern-clone/mypattern + targetRevision: + path: charts/datacenter/pipelines + helm: +@@ -533,13 +548,13 @@ + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern +- value: example-pattern ++ value: mypattern + - name: global.clusterDomain +- value: ++ value: region.example.com + - name: global.hubClusterDomain +- value: ++ value: apps.hub.example.com + - name: global.localClusterDomain +- value: ++ value: apps.region.example.com + syncPolicy: + automated: {} + # selfHeal: true +@@ -548,14 +563,14 @@ apiVersion: argoproj.io/v1alpha1 @@ -65,7 +850,7 @@ # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: example-gitops -- namespace: common-example +- namespace: example-pattern-example + namespace: mypattern-example annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous @@ -847,7 +884,7 @@ --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE -- --set global.pattern=common +- --set global.pattern=example-pattern - --set global.clusterDomain= - --set global.hubClusterDomain= - --set global.localClusterDomain= @@ -862,61 +899,14 @@ kind: ConsoleLink metadata: name: example-gitops-link -- namespace: common-example +- namespace: example-pattern-example + namespace: mypattern-example spec: applicationMenu: section: OpenShift GitOps imageURL: data:image/png;base64,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 -- href: 'https://example-gitops-server-common-example.' +- href: 'https://example-gitops-server-example-pattern-example.' + href: 'https://example-gitops-server-mypattern-example.apps.region.example.com' location: ApplicationMenu text: 'Example ArgoCD' -+--- -+# Source: pattern-clustergroup/templates/core/operatorgroup.yaml -+apiVersion: operators.coreos.com/v1 -+kind: OperatorGroup -+metadata: -+ name: open-cluster-management-operator-group -+ namespace: open-cluster-management -+spec: -+ targetNamespaces: -+ - open-cluster-management -+--- -+# Source: pattern-clustergroup/templates/core/operatorgroup.yaml -+apiVersion: operators.coreos.com/v1 -+kind: OperatorGroup -+metadata: -+ name: application-ci-operator-group -+ namespace: application-ci -+spec: -+ targetNamespaces: -+ - application-ci -+--- -+# Source: pattern-clustergroup/templates/core/subscriptions.yaml -+apiVersion: operators.coreos.com/v1alpha1 -+kind: Subscription -+metadata: -+ name: advanced-cluster-management -+ namespace: open-cluster-management -+spec: -+ name: advanced-cluster-management -+ source: redhat-operators -+ sourceNamespace: openshift-marketplace -+ channel: release-2.4 -+ installPlanApproval: Automatic -+ startingCSV: advanced-cluster-management.v2.4.1 -+--- -+# Source: pattern-clustergroup/templates/core/subscriptions.yaml -+apiVersion: operators.coreos.com/v1alpha1 -+kind: Subscription -+metadata: -+ name: openshift-pipelines-operator-rh -+ namespace: openshift-operators -+spec: -+ name: openshift-pipelines-operator-rh -+ source: redhat-operators -+ sourceNamespace: openshift-marketplace -+ channel: stable -+ installPlanApproval: Automatic -+ startingCSV: redhat-openshift-pipelines.v1.5.2 + --- From 78224c46c94555bab7256bfba46841084f5d2730 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 4 Oct 2022 15:35:40 -0600 Subject: [PATCH 0699/1263] Fixed issues with kubeconform in clustergroup/values.yaml. Reran make test to generate proper files --- clustergroup/values.yaml | 26 +++++++- tests/clustergroup-naked.expected.yaml | 80 ++++++++++++++++--------- tests/clustergroup-normal.expected.yaml | 80 ++++++++++++++++--------- tests/clustergroup.expected.diff | 34 +++++------ 4 files changed, 144 insertions(+), 76 deletions(-) diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 3b8b12b9..b35fb8ac 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -54,13 +54,37 @@ clusterGroup: path: charts/datacenter/pipelines imperative: - namespace: imperative # NOTE: We *must* use lists and not hashes. As hashes lose ordering once parsed by helm # The default schedule is every 10 minutes: imperative.schedule # Total timeout of all jobs is 1h: imperative.activeDeadlineSeconds # imagePullPolicy is set to always: imperative.imagePullPolicy # For additional overrides that apply to the jobs, please refer to # https://hybrid-cloud-patterns.io/imperative-actions/#additional-job-customizations + # This image contains ansible + kubernetes.core by default and is used to run the jobs + image: "registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest" + namespace: "imperative" + # configmap name in the namespace that will contain all helm values + valuesConfigMap: "helm-values-configmap" + cronJobName: "imperative-cronjob" + jobName: "imperative-job" + imagePullPolicy: Always + # This is the maximum timeout of all the jobs (1h) + activeDeadlineSeconds: 3600 + # By default we run this every 10minutes + schedule: "*/10 * * * *" + # Schedule used to trigger the vault unsealing (if explicitely enabled) + # Set to run every 5 minutes in order for load-secrets to succeed within + # a reasonable amount of time (it waits up to 15 mins) + insecureUnsealVaultInsideClusterSchedule: "*/5 * * * *" + # Increase ansible verbosity with '-v' or '-vv..' + verbosity: "" + serviceAccountCreate: true + # service account to be used to run the cron pods + serviceAccountName: imperative-sa + clusterRoleName: imperative-cluster-role + clusterRoleYaml: "" + roleName: imperative-role + roleYaml: "" jobs: - name: regional-ca # ansible playbook to be run diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index cc9dc455..da0f637b 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -41,11 +41,18 @@ metadata: name: example-pattern-example spec: {} --- +# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: imperative-sa + namespace: imperative +--- # Source: pattern-clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: - name: -example + name: helm-values-configmap-example namespace: imperative data: values.yaml: | @@ -67,11 +74,26 @@ data: path: charts/datacenter/pipelines project: datacenter imperative: + activeDeadlineSeconds: 3600 + clusterRoleName: imperative-cluster-role + clusterRoleYaml: "" + cronJobName: imperative-cronjob + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' + jobName: imperative-job jobs: - name: regional-ca playbook: ansible/playbooks/on-hub-get-regional-ca.yml timeout: 234 namespace: imperative + roleName: imperative-role + roleYaml: "" + schedule: '*/10 * * * *' + serviceAccountCreate: true + serviceAccountName: imperative-sa + valuesConfigMap: helm-values-configmap + verbosity: "" insecureUnsealVaultInsideCluster: true isHubCluster: true managedClusterGroups: @@ -156,7 +178,7 @@ data: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: + name: imperative-cluster-role rules: - apiGroups: - '*' @@ -175,10 +197,10 @@ metadata: roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: + name: imperative-cluster-role subjects: - kind: ServiceAccount - name: + name: imperative-sa namespace: imperative --- # Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml @@ -229,7 +251,7 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: + name: imperative-role namespace: imperative rules: - apiGroups: @@ -248,36 +270,36 @@ metadata: roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: + name: imperative-role subjects: - kind: ServiceAccount - name: + name: imperative-sa namespace: imperative --- # Source: pattern-clustergroup/templates/imperative/job.yaml apiVersion: batch/v1 kind: CronJob metadata: - name: + name: imperative-cronjob namespace: imperative spec: - schedule: + schedule: "*/10 * * * *" # if previous Job is still running, skip execution of a new Job concurrencyPolicy: Forbid jobTemplate: spec: - activeDeadlineSeconds: + activeDeadlineSeconds: 3600 template: metadata: - name: + name: imperative-job spec: - serviceAccountName: + serviceAccountName: imperative-sa initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always env: - name: HOME value: /git/home @@ -289,8 +311,8 @@ spec: - name: git mountPath: "/git" - name: regional-ca - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always env: - name: HOME value: /git/home @@ -312,8 +334,8 @@ spec: subPath: values.yaml containers: - name: "done" - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always command: - 'sh' - '-c' @@ -325,7 +347,7 @@ spec: emptyDir: {} - name: values-volume configMap: - name: -example + name: helm-values-configmap-example restartPolicy: Never --- # Source: pattern-clustergroup/templates/imperative/unsealjob.yaml @@ -335,23 +357,23 @@ metadata: name: unsealvault-cronjob namespace: imperative spec: - schedule: + schedule: "*/5 * * * *" # if previous Job is still running, skip execution of a new Job concurrencyPolicy: Forbid jobTemplate: spec: - activeDeadlineSeconds: + activeDeadlineSeconds: 3600 template: metadata: name: unsealvault-job spec: - serviceAccountName: + serviceAccountName: imperative-sa initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always env: - name: HOME value: /git/home @@ -363,8 +385,8 @@ spec: - name: git mountPath: "/git" - name: unseal-playbook - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always env: - name: HOME value: /git/home @@ -390,8 +412,8 @@ spec: subPath: values.yaml containers: - name: "done" - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always command: - 'sh' - '-c' @@ -403,7 +425,7 @@ spec: emptyDir: {} - name: values-volume configMap: - name: -example + name: helm-values-configmap-example restartPolicy: Never --- # Source: pattern-clustergroup/templates/core/subscriptions.yaml diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 69a64057..d78287a2 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -39,11 +39,18 @@ metadata: name: mypattern-example spec: {} --- +# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: imperative-sa + namespace: imperative +--- # Source: pattern-clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: - name: -example + name: helm-values-configmap-example namespace: imperative data: values.yaml: | @@ -65,11 +72,26 @@ data: path: charts/datacenter/pipelines project: datacenter imperative: + activeDeadlineSeconds: 3600 + clusterRoleName: imperative-cluster-role + clusterRoleYaml: "" + cronJobName: imperative-cronjob + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' + jobName: imperative-job jobs: - name: test playbook: ansible/test.yml timeout: 234 namespace: imperative + roleName: imperative-role + roleYaml: "" + schedule: '*/10 * * * *' + serviceAccountCreate: true + serviceAccountName: imperative-sa + valuesConfigMap: helm-values-configmap + verbosity: "" insecureUnsealVaultInsideCluster: true isHubCluster: true managedClusterGroups: @@ -169,7 +191,7 @@ data: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: + name: imperative-cluster-role rules: - apiGroups: - '*' @@ -188,10 +210,10 @@ metadata: roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: + name: imperative-cluster-role subjects: - kind: ServiceAccount - name: + name: imperative-sa namespace: imperative --- # Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml @@ -242,7 +264,7 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: + name: imperative-role namespace: imperative rules: - apiGroups: @@ -261,36 +283,36 @@ metadata: roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: + name: imperative-role subjects: - kind: ServiceAccount - name: + name: imperative-sa namespace: imperative --- # Source: pattern-clustergroup/templates/imperative/job.yaml apiVersion: batch/v1 kind: CronJob metadata: - name: + name: imperative-cronjob namespace: imperative spec: - schedule: + schedule: "*/10 * * * *" # if previous Job is still running, skip execution of a new Job concurrencyPolicy: Forbid jobTemplate: spec: - activeDeadlineSeconds: + activeDeadlineSeconds: 3600 template: metadata: - name: + name: imperative-job spec: - serviceAccountName: + serviceAccountName: imperative-sa initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always env: - name: HOME value: /git/home @@ -302,8 +324,8 @@ spec: - name: git mountPath: "/git" - name: test - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always env: - name: HOME value: /git/home @@ -325,8 +347,8 @@ spec: subPath: values.yaml containers: - name: "done" - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always command: - 'sh' - '-c' @@ -338,7 +360,7 @@ spec: emptyDir: {} - name: values-volume configMap: - name: -example + name: helm-values-configmap-example restartPolicy: Never --- # Source: pattern-clustergroup/templates/imperative/unsealjob.yaml @@ -348,23 +370,23 @@ metadata: name: unsealvault-cronjob namespace: imperative spec: - schedule: + schedule: "*/5 * * * *" # if previous Job is still running, skip execution of a new Job concurrencyPolicy: Forbid jobTemplate: spec: - activeDeadlineSeconds: + activeDeadlineSeconds: 3600 template: metadata: name: unsealvault-job spec: - serviceAccountName: + serviceAccountName: imperative-sa initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always env: - name: HOME value: /git/home @@ -376,8 +398,8 @@ spec: - name: git mountPath: "/git" - name: unseal-playbook - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always env: - name: HOME value: /git/home @@ -403,8 +425,8 @@ spec: subPath: values.yaml containers: - name: "done" - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always command: - 'sh' - '-c' @@ -416,7 +438,7 @@ spec: emptyDir: {} - name: values-volume configMap: - name: -example + name: helm-values-configmap-example restartPolicy: Never --- # Source: pattern-clustergroup/templates/core/subscriptions.yaml diff --git a/tests/clustergroup.expected.diff b/tests/clustergroup.expected.diff index 35a5b6b4..19430a6a 100644 --- a/tests/clustergroup.expected.diff +++ b/tests/clustergroup.expected.diff @@ -34,10 +34,10 @@ + name: mypattern-example spec: {} --- - # Source: pattern-clustergroup/templates/imperative/configmap.yaml -@@ -68,8 +68,8 @@ - project: datacenter - imperative: + # Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +@@ -83,8 +83,8 @@ + insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' + jobName: imperative-job jobs: - - name: regional-ca - playbook: ansible/playbooks/on-hub-get-regional-ca.yml @@ -756,7 +756,7 @@ --- # Source: pattern-clustergroup/templates/imperative/role.yaml apiVersion: rbac.authorization.k8s.io/v1 -@@ -284,11 +299,11 @@ +@@ -306,11 +321,11 @@ command: - 'sh' - '-c' @@ -767,10 +767,10 @@ mountPath: "/git" - - name: regional-ca + - name: test - image: - imagePullPolicy: + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always env: -@@ -303,7 +318,7 @@ +@@ -325,7 +340,7 @@ - ansible-playbook - -e - "@/values/values.yaml" @@ -779,7 +779,7 @@ volumeMounts: - name: git mountPath: "/git" -@@ -358,7 +373,7 @@ +@@ -380,7 +395,7 @@ command: - 'sh' - '-c' @@ -788,7 +788,7 @@ volumeMounts: - name: git mountPath: "/git" -@@ -435,7 +450,7 @@ +@@ -457,7 +472,7 @@ kind: AppProject metadata: name: datacenter @@ -797,7 +797,7 @@ spec: description: "Pattern datacenter" destinations: -@@ -456,7 +471,7 @@ +@@ -478,7 +493,7 @@ kind: Application metadata: name: acm @@ -806,7 +806,7 @@ finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: -@@ -465,7 +480,7 @@ +@@ -487,7 +502,7 @@ namespace: open-cluster-management project: datacenter source: @@ -815,7 +815,7 @@ targetRevision: path: common/acm helm: -@@ -482,13 +497,13 @@ +@@ -504,13 +519,13 @@ - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -833,7 +833,7 @@ ignoreDifferences: [ { "group": "internal.open-cluster-management.io", -@@ -507,7 +522,7 @@ +@@ -529,7 +544,7 @@ kind: Application metadata: name: pipelines @@ -842,7 +842,7 @@ finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: -@@ -516,7 +531,7 @@ +@@ -538,7 +553,7 @@ namespace: application-ci project: datacenter source: @@ -851,7 +851,7 @@ targetRevision: path: charts/datacenter/pipelines helm: -@@ -533,13 +548,13 @@ +@@ -555,13 +570,13 @@ - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -869,7 +869,7 @@ syncPolicy: automated: {} # selfHeal: true -@@ -548,14 +563,14 @@ +@@ -570,14 +585,14 @@ apiVersion: argoproj.io/v1alpha1 @@ -65,7 +850,7 @@ # Changing the name affects the ClusterRoleBinding, the generated secret, From d137495bdd49ad75f3f08dc518ddb3580d160cd0 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 7 Oct 2022 12:12:47 -0600 Subject: [PATCH 0700/1263] - Updated schema to include all elements in subscriptions - Updated tests associated with clustergroup - Updated values-example.yaml --- clustergroup/values.schema.json | 94 +++++++++++++++++++++++-- examples/values-example.yaml | 3 +- tests/clustergroup-normal.expected.yaml | 1 + tests/clustergroup.expected.diff | 23 +++--- 4 files changed, 103 insertions(+), 18 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 8790df09..c23a8cb0 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -8,6 +8,7 @@ "properties": { "enabled": { "type": "string", + "description": "", "enum": ["all", "core", "plumbing"] }, "secretStore": { @@ -107,8 +108,11 @@ }, "Global": { "type": "object", - "additionalProperties": false, + "additionalProperties": true, "properties": { + "useCSV": { + "type": "boolean" + }, "pattern": { "type": "string" }, @@ -201,7 +205,7 @@ "applications": { "type": "object", "items": { - "$ref": "#/definitions/Application" + "$ref": "#/definitions/Applications" } }, "imperative": { @@ -232,13 +236,61 @@ ], "title": "ClusterGroup" }, - "Application": { + "Applications": { "type": "object", "additionalProperties": true, "properties": { "name": { "type": "string" }, + "repoURL": { + "type": "string" + }, + "targetRevision": { + "type": "string" + }, + "chart": { + "type": "string" + }, + "kustomize": { + "type": "boolean" + }, + "plugin": { + "type": "object" + }, + "extraValueFiles": { + "type": "array" + }, + "extraHubClusterDomainFields": { + "type": "array" + }, + "extraLocalClusterDomainFields": { + "type": "array" + }, + "extraRepoURLFields": { + "type": "array" + }, + "extraTargetRevisionFields": { + "type": "array" + }, + "extraNamespaceFields": { + "type": "array" + }, + "extraPatternNameFields": { + "type": "array" + }, + "overrides": { + "type": "object" + }, + "fileParameters": { + "type": "array" + }, + "ignoreDifferences": { + "type": "array" + }, + "syncPolicy": { + "type": "object" + }, "namespace": { "type": "string" }, @@ -255,7 +307,7 @@ "path", "project" ], - "title": "Application" + "title": "Applications" }, "Imperative": { "type": "object", @@ -483,9 +535,15 @@ "name": { "type": "string" }, + "namespaces": { + "type": "array" + }, "namespace": { "type": "string" }, + "sourceNamespace": { + "type": "string" + }, "source": { "type": "string" }, @@ -495,15 +553,39 @@ "csv": { "type": "string" }, + "installPlanApproval": { + "type": "string", + "enum": ["Manual", "Automatic"] + }, + "config": { + "type": "object", + "$ref": "#/definitions/SubscriptionsConfigEnv" + }, "disabled": { "type": "boolean" } }, "required": [ - "name", - "channel" + "name" ], "title": "Subscription" + }, + "SubscriptionsConfigEnv": { + "type": "array", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "name", + "value" + ], + "title": "SubscriptionsConfigEnv" }, "HostedArgoSites": { "type": "object", diff --git a/examples/values-example.yaml b/examples/values-example.yaml index baf6b49a..45e6c092 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -3,8 +3,9 @@ global: useCSV: False syncPolicy: Automatic installPlanApproval: Automatic + multiClusterTarget: all -enabled: all +#enabled: all clusterGroup: name: example diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index d78287a2..663aa7d4 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -174,6 +174,7 @@ data: hostname: github.com hubClusterDomain: apps.hub.example.com localClusterDomain: apps.region.example.com + multiClusterTarget: all namespace: pattern-namespace options: installPlanApproval: Automatic diff --git a/tests/clustergroup.expected.diff b/tests/clustergroup.expected.diff index 19430a6a..72137dec 100644 --- a/tests/clustergroup.expected.diff +++ b/tests/clustergroup.expected.diff @@ -134,6 +134,7 @@ + hostname: github.com + hubClusterDomain: apps.hub.example.com + localClusterDomain: apps.region.example.com ++ multiClusterTarget: all + namespace: pattern-namespace options: installPlanApproval: Automatic @@ -756,7 +757,7 @@ --- # Source: pattern-clustergroup/templates/imperative/role.yaml apiVersion: rbac.authorization.k8s.io/v1 -@@ -306,11 +321,11 @@ +@@ -306,11 +322,11 @@ command: - 'sh' - '-c' @@ -770,7 +771,7 @@ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest imagePullPolicy: Always env: -@@ -325,7 +340,7 @@ +@@ -325,7 +341,7 @@ - ansible-playbook - -e - "@/values/values.yaml" @@ -779,7 +780,7 @@ volumeMounts: - name: git mountPath: "/git" -@@ -380,7 +395,7 @@ +@@ -380,7 +396,7 @@ command: - 'sh' - '-c' @@ -788,7 +789,7 @@ volumeMounts: - name: git mountPath: "/git" -@@ -457,7 +472,7 @@ +@@ -457,7 +473,7 @@ kind: AppProject metadata: name: datacenter @@ -797,7 +798,7 @@ spec: description: "Pattern datacenter" destinations: -@@ -478,7 +493,7 @@ +@@ -478,7 +494,7 @@ kind: Application metadata: name: acm @@ -806,7 +807,7 @@ finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: -@@ -487,7 +502,7 @@ +@@ -487,7 +503,7 @@ namespace: open-cluster-management project: datacenter source: @@ -815,7 +816,7 @@ targetRevision: path: common/acm helm: -@@ -504,13 +519,13 @@ +@@ -504,13 +520,13 @@ - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -833,7 +834,7 @@ ignoreDifferences: [ { "group": "internal.open-cluster-management.io", -@@ -529,7 +544,7 @@ +@@ -529,7 +545,7 @@ kind: Application metadata: name: pipelines @@ -842,7 +843,7 @@ finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: -@@ -538,7 +553,7 @@ +@@ -538,7 +554,7 @@ namespace: application-ci project: datacenter source: @@ -851,7 +852,7 @@ targetRevision: path: charts/datacenter/pipelines helm: -@@ -555,13 +570,13 @@ +@@ -555,13 +571,13 @@ - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -869,7 +870,7 @@ syncPolicy: automated: {} # selfHeal: true -@@ -570,14 +585,14 @@ +@@ -570,14 +586,14 @@ apiVersion: argoproj.io/v1alpha1 @@ -65,7 +850,7 @@ # Changing the name affects the ClusterRoleBinding, the generated secret, From 39a8afc37529de7554cecec7e8bca94c8aee7043 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Mon, 17 Oct 2022 10:12:32 -0600 Subject: [PATCH 0701/1263] applications and subscriptions can be obj and array --- clustergroup/values.schema.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index c23a8cb0..e271b0d9 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -191,7 +191,10 @@ } }, "subscriptions": { - "type": "object", + "anyOf": [ + { "type": "array" }, + { "type": "object" } + ], "items": { "$ref": "#/definitions/Subscription" } @@ -203,7 +206,10 @@ } }, "applications": { - "type": "object", + "anyOf": [ + { "type": "array" }, + { "type": "object" } + ], "items": { "$ref": "#/definitions/Applications" } From e8c441d134938c2917e5d8d93ecd8bf93b93e1ca Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Mon, 17 Oct 2022 10:12:50 -0600 Subject: [PATCH 0702/1263] restore values.yaml file to a saner commented default --- clustergroup/values.yaml | 167 +++++++++++++-------------------------- 1 file changed, 54 insertions(+), 113 deletions(-) diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index b35fb8ac..59763bbc 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -1,67 +1,26 @@ global: - pattern: example-pattern + pattern: common + targetRevision: main options: - useCSV: False + useCSV: True syncPolicy: Automatic installPlanApproval: Automatic -enabled: all +enabled: "all" +# Note that sometimes changing helm values might require a hard refresh (https://github.com/helm/helm/issues/3486) clusterGroup: name: example - insecureUnsealVaultInsideCluster: true isHubCluster: true - - namespaces: - - open-cluster-management - - application-ci - - subscriptions: - acm: - name: advanced-cluster-management - namespace: open-cluster-management - channel: release-2.4 - csv: advanced-cluster-management.v2.4.1 - - odh: - name: opendatahub-operator - source: community-operators - csv: opendatahub-operator.v1.1.0 - disabled: true - - pipelines: - name: openshift-pipelines-operator-rh - csv: redhat-openshift-pipelines.v1.5.2 - - projects: - - datacenter - - applications: - acm: - name: acm - namespace: open-cluster-management - project: datacenter - path: common/acm - ignoreDifferences: - - group: internal.open-cluster-management.io - kind: ManagedClusterInfo - jsonPointers: - - /spec/loggingCA - pipe: - name: pipelines - namespace: application-ci - project: datacenter - path: charts/datacenter/pipelines + targetCluster: in-cluster + # Note: setting this to true stores the vault unseal keys inside a cluster secret and + # is fundamentally insecure + insecureUnsealVaultInsideCluster: false imperative: - # NOTE: We *must* use lists and not hashes. As hashes lose ordering once parsed by helm - # The default schedule is every 10 minutes: imperative.schedule - # Total timeout of all jobs is 1h: imperative.activeDeadlineSeconds - # imagePullPolicy is set to always: imperative.imagePullPolicy - # For additional overrides that apply to the jobs, please refer to - # https://hybrid-cloud-patterns.io/imperative-actions/#additional-job-customizations + jobs: [] # This image contains ansible + kubernetes.core by default and is used to run the jobs - image: "registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest" + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest namespace: "imperative" # configmap name in the namespace that will contain all helm values valuesConfigMap: "helm-values-configmap" @@ -85,65 +44,47 @@ clusterGroup: clusterRoleYaml: "" roleName: imperative-role roleYaml: "" - jobs: - - name: regional-ca - # ansible playbook to be run - playbook: ansible/playbooks/on-hub-get-regional-ca.yml - # per playbook timeout in seconds - timeout: 234 - # verbosity: "-v" + managedClusterGroups: [] + namespaces: [] +# - name: factory +# # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git +# # Location of values-global.yaml, values-{name}.yaml, values-{app}.yaml +# targetRevision: main +# path: applications/factory +# helmOverrides: +# - name: clusterGroup.isHubCluster +# value: false +# clusterSelector: +# matchExpressions: +# - key: vendor +# operator: In +# values: +# - OpenShift +# +# - open-cluster-management +# + subscriptions: [] +# - name: advanced-cluster-management +# namespace: open-cluster-management +# source: redhat-operators +# channel: release-2.3 +# csv: v2.3.2 +# + projects: [] +# - datacenter +# + applications: [] +# - name: acm +# namespace: default +# project: datacenter +# path: applications/acm + +secretStore: + name: vault-backend + kind: ClusterSecretStore + +# Depends on the value of 'vault_hub' ansible variable used +# during the installation +#secretsBase: +# key: secret/data/hub - managedClusterGroups: - - name: acm-edge - # Optional - Point to a different repo - # repoURL: https://github.com/hybrid-cloud-patterns/mySite.git - # Must contain values-{clustergroupname}.yaml at the top level - targetRevision: main - helmOverrides: - # Values must be strings! - - name: clusterGroup.isHubCluster - value: "false" - acmlabels: - - name: clusterGroup - value: acm-region - - name: acm-provision-edge - targetRevision: main - helmOverrides: - - name: clusterGroup.isHubCluster - value: "false" - clusterPools: - exampleAWSPool: - size: 3 - name: aws-ap - openshiftVersion: 4.10.18 - baseDomain: blueprints.rhecoeng.com - platform: - aws: - region: ap-southeast-2 - clusters: - - One - exampleAzurePool: - name: azure-us - openshiftVersion: 4.10.18 - baseDomain: blueprints.rhecoeng.com - platform: - azure: - baseDomainResourceGroupName: dojo-dns-zones - region: eastus - clusters: - - Two - - Three - acmlabels: - - name: clusterGroup - value: region - - name: argo-edge - hostedArgoSites: - - name: perth - domain: perth1.beekhof.net - bearerKeyPath: secret/data/hub/cluster_perth - caKeyPath: secret/data/hub/cluster_perth_ca - - name: sydney - domain: syd.beekhof.net - helmOverrides: - - name: clusterGroup.isHubCluster - value: "false" From d8bcda8a70d650426efc4d3792be4010d6dc4a7c Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 19 Oct 2022 08:29:08 -0600 Subject: [PATCH 0703/1263] Ran values.schema.json through jq to fix indentation issues --- clustergroup/values.schema.json | 1256 ++++++++++++++++--------------- 1 file changed, 643 insertions(+), 613 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index e271b0d9..8ff1ed28 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -1,620 +1,650 @@ { - "$schema": "http://json-schema.org/draft-06/schema#", - "$ref": "#/definitions/ValidatedPatterns", - "definitions": { - "ValidatedPatterns": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "string", - "description": "", - "enum": ["all", "core", "plumbing"] - }, - "secretStore": { - "$ref": "#/definitions/SecretStore" - }, - "main": { - "$ref": "#/definitions/Main" - }, - "global": { - "$ref": "#/definitions/Global" - }, - "clusterGroup": { - "$ref": "#/definitions/ClusterGroup" - } - }, - "required": [ - "clusterGroup" - ], - "title": "ValidatedPatterns" - }, - "SecretStore": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "kind": { - "type": "string" - } - }, - "required": [ - "name", - "kind" - ], - "title": "SecretsStore" - }, - "GlobalGit": { - "type": "object", - "additionalProperties": false, - "properties": { - "hostname": { - "type": "string" - }, - "account": { - "type": "string" - }, - "email": { - "type": "string" - }, - "dev_revision": { - "type": "string" - } - }, - "required": [ - "hostname", - "account", - "email" - ], - "title": "GlobalGit" - }, - "GitSecrets": { - "type": "object", - "additionalProperties": false, - "properties": { - "username": { - "type": "string" - }, - "token": { - "type": "string" - } - }, - "required": [ - "username", - "token" - ], - "title": "GitSecrets" - }, - "Main": { - "type": "object", - "additionalProperties": true, - "properties": { - "clusterGroupName": { - "type": "string" - }, - "hubClusterDomain": { - "type": "string" - }, - "localClusterDomain": { - "type": "string" - } - }, - "required": [ - "clusterGroupName" - ], - "title": "Main" - }, - "Global": { - "type": "object", - "additionalProperties": true, - "properties": { - "useCSV": { - "type": "boolean" - }, - "pattern": { - "type": "string" - }, - "clusterDomain": { - "type": "string" - }, - "localClusterDomain": { - "type": "string" - }, - "targetRevision": { - "type": "string" - }, - "hubClusterDomain": { - "type": "string" - }, - "namespace": { - "type": "string" - }, - "repoURL": { - "type": "string" - }, - "git": { - "$ref": "#/definitions/GlobalGit" - }, - "options": { - "$ref": "#/definitions/Options" - } - }, - "required": [ - "options", - "pattern" - ], - "title": "Global" - }, - "Options": { - "type": "object", - "additionalProperties": false, - "properties": { - "useCSV": { - "type": "boolean" - }, - "syncPolicy": { - "type": "string" - }, - "installPlanApproval": { - "type": "string" - } - }, - "required": [ - "installPlanApproval", - "syncPolicy", - "useCSV" - ], - "title": "Options" - }, - "ClusterGroup": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "targetCluster": { - "type": "string" - }, - "isHubCluster": { - "type": "boolean" - }, - "insecureUnsealVaultInsideCluster": { - "type": "boolean" - }, - "namespaces": { - "type": "array", - "items": { - "type": "string" - } - }, - "subscriptions": { - "anyOf": [ - { "type": "array" }, - { "type": "object" } - ], - "items": { - "$ref": "#/definitions/Subscription" - } - }, - "projects": { - "type": "array", - "items": { - "type": "string" - } - }, - "applications": { - "anyOf": [ - { "type": "array" }, - { "type": "object" } - ], - "items": { - "$ref": "#/definitions/Applications" - } - }, - "imperative": { - "$ref": "#/definitions/Imperative" - }, - "managedClusterGroups": { - "anyOf": [ - { "type": "array" }, - { "type": "object" } - ], - "items": { - "$ref": "#/definitions/ManagedClusterGroup" - } - }, - "externalClusters": { - "type": "array" - } - }, - "required": [ - "applications", - "insecureUnsealVaultInsideCluster", - "isHubCluster", - "managedClusterGroups", - "name", - "namespaces", - "projects", - "subscriptions" - ], - "title": "ClusterGroup" - }, - "Applications": { - "type": "object", - "additionalProperties": true, - "properties": { - "name": { - "type": "string" - }, - "repoURL": { - "type": "string" - }, - "targetRevision": { - "type": "string" - }, - "chart": { - "type": "string" - }, - "kustomize": { - "type": "boolean" - }, - "plugin": { - "type": "object" - }, - "extraValueFiles": { - "type": "array" - }, - "extraHubClusterDomainFields": { - "type": "array" - }, - "extraLocalClusterDomainFields": { - "type": "array" - }, - "extraRepoURLFields": { - "type": "array" - }, - "extraTargetRevisionFields": { - "type": "array" - }, - "extraNamespaceFields": { - "type": "array" - }, - "extraPatternNameFields": { - "type": "array" - }, - "overrides": { - "type": "object" - }, - "fileParameters": { - "type": "array" - }, - "ignoreDifferences": { - "type": "array" - }, - "syncPolicy": { - "type": "object" - }, - "namespace": { - "type": "string" - }, - "project": { - "type": "string" - }, - "path": { - "type": "string" - } - }, - "required": [ - "name", - "namespace", - "path", - "project" - ], - "title": "Applications" - }, - "Imperative": { - "type": "object", - "additionalProperties": false, - "properties": { - "jobs": { - "type": "array", - "items": { - "$ref": "#/definitions/Job" - } - }, - "image": { - "type": "string", - "default": "registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest" - }, - "namespace": { - "type": "string", - "default": "imperative", - "enum": ["imperative"] - }, - "serviceAccountCreate": { - "type": "boolean" - }, - "valuesConfigMap": { - "type": "string" - }, - "cronJobName": { - "type": "string" - }, - "jobName": { - "type": "string" - }, - "imagePullPolicy": { - "type": "string", - "default": "Always", - "enum": ["Always", "IfNotPresent", "Never"] - }, - "activeDeadlineSeconds": { - "type": "integer", - "default": 3600 - }, - "schedule": { - "type": "string", - "default": "*/10 * * * *" - }, - "insecureUnsealVaultInsideClusterSchedule": { - "type": "string", - "default": "*/5 * * * *" - }, - "verbosity": { - "type": "string", - "default": "", - "enum": ["","-v", "-vv", "-vvv", "-vvvv"] - }, - "serviceAccountName": { - "type": "string" - }, - "clusterRoleName": { - "type": "string" - }, - "clusterRoleYaml": { - "type": "string" - }, - "roleName": { - "type": "string" - }, - "roleYaml": { - "type": "string" - } - }, - "required": [ - "jobs" - ], - "title": "Imperative" - }, - "Job": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "playbook": { - "type": "string" - }, - "timeout": { - "type": "integer" - }, - "verbosity": { - "type": "string" - } - }, - "required": [ - "name", - "playbook" - ], - "title": "Job" - }, - "ManagedClusterGroup": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "targetRevision": { - "type": "string" - }, - "acmlabels": { - "type": "array", - "items": { - "$ref": "#/definitions/ACMLabels" - } - }, - "hostedArgoSites": { - "type": "array", - "items": { - "$ref": "#/definitions/HostedArgoSites" - } - }, - "clusterPools": { - "type": "object", - "items": { - "$ref": "#/definitions/ClusterPools" - } - }, - "helmOverrides": { - "type": "array", - "items": { - "$ref": "#/definitions/HelmOverride" - } - } - }, - "required": [ - ], - "title": "ManagedClusterGroup" - }, - "ClusterPools": { - "type": "object", - "additionalProperties": false, - "properties": { - "size": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "openshiftVersion": { - "type": "string" - }, - "baseDomain": { - "type": "string" - }, - "platform": { - "type": "object", - "$ref": "#/definitions/ClusterPoolsPlatform" - }, - "clusters": { - "type": "array" - } - }, - "required": [ - "name", - "openshiftVersion", - "baseDomain", - "platform", - "clusters" - ], - "title": "ClusterPools" - }, - "ClusterPoolsPlatform": { - "type": "object", - "additionalProperties": false, - "properties": { - "baseDomainResourceGroupName": { - "type": "string" - }, - "region": { - "type": "string" - } - }, - "required": [ - "region" - ], - "title": "ClusterPoolsPlatform" - }, - "HelmOverride": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "required": [ - "name", - "value" - ], - "title": "HelmOverride" - }, - "ACMLabels": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "required": [ - "name", - "value" - ], - "title": "ACMLabels" - }, - "Subscription": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "namespaces": { - "type": "array" - }, - "namespace": { - "type": "string" - }, - "sourceNamespace": { - "type": "string" - }, - "source": { - "type": "string" - }, - "channel": { - "type": "string" - }, - "csv": { - "type": "string" - }, - "installPlanApproval": { - "type": "string", - "enum": ["Manual", "Automatic"] - }, - "config": { - "type": "object", - "$ref": "#/definitions/SubscriptionsConfigEnv" - }, - "disabled": { - "type": "boolean" - } + "$schema": "http://json-schema.org/draft-06/schema#", + "$ref": "#/definitions/ValidatedPatterns", + "definitions": { + "ValidatedPatterns": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "string", + "description": "", + "enum": [ + "all", + "core", + "plumbing" + ] + }, + "secretStore": { + "$ref": "#/definitions/SecretStore" + }, + "main": { + "$ref": "#/definitions/Main" + }, + "global": { + "$ref": "#/definitions/Global" + }, + "clusterGroup": { + "$ref": "#/definitions/ClusterGroup" + } + }, + "required": [ + "clusterGroup" + ], + "title": "ValidatedPatterns" + }, + "SecretStore": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "kind": { + "type": "string" + } + }, + "required": [ + "name", + "kind" + ], + "title": "SecretsStore" + }, + "GlobalGit": { + "type": "object", + "additionalProperties": false, + "properties": { + "hostname": { + "type": "string" + }, + "account": { + "type": "string" + }, + "email": { + "type": "string" + }, + "dev_revision": { + "type": "string" + } + }, + "required": [ + "hostname", + "account", + "email" + ], + "title": "GlobalGit" + }, + "GitSecrets": { + "type": "object", + "additionalProperties": false, + "properties": { + "username": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "required": [ + "username", + "token" + ], + "title": "GitSecrets" + }, + "Main": { + "type": "object", + "additionalProperties": true, + "properties": { + "clusterGroupName": { + "type": "string" + }, + "hubClusterDomain": { + "type": "string" + }, + "localClusterDomain": { + "type": "string" + } + }, + "required": [ + "clusterGroupName" + ], + "title": "Main" + }, + "Global": { + "type": "object", + "additionalProperties": true, + "properties": { + "useCSV": { + "type": "boolean" + }, + "pattern": { + "type": "string" + }, + "clusterDomain": { + "type": "string" + }, + "localClusterDomain": { + "type": "string" + }, + "targetRevision": { + "type": "string" + }, + "hubClusterDomain": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "repoURL": { + "type": "string" + }, + "git": { + "$ref": "#/definitions/GlobalGit" + }, + "options": { + "$ref": "#/definitions/Options" + } + }, + "required": [ + "options", + "pattern" + ], + "title": "Global" + }, + "Options": { + "type": "object", + "additionalProperties": false, + "properties": { + "useCSV": { + "type": "boolean" + }, + "syncPolicy": { + "type": "string" + }, + "installPlanApproval": { + "type": "string" + } + }, + "required": [ + "installPlanApproval", + "syncPolicy", + "useCSV" + ], + "title": "Options" + }, + "ClusterGroup": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "targetCluster": { + "type": "string" + }, + "isHubCluster": { + "type": "boolean" + }, + "insecureUnsealVaultInsideCluster": { + "type": "boolean" + }, + "namespaces": { + "type": "array", + "items": { + "type": "string" + } + }, + "subscriptions": { + "anyOf": [ + { + "type": "array" }, - "required": [ - "name" - ], - "title": "Subscription" - }, - "SubscriptionsConfigEnv": { - "type": "array", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - } + { + "type": "object" + } + ], + "items": { + "$ref": "#/definitions/Subscription" + } + }, + "projects": { + "type": "array", + "items": { + "type": "string" + } + }, + "applications": { + "anyOf": [ + { + "type": "array" }, - "required": [ - "name", - "value" - ], - "title": "SubscriptionsConfigEnv" - }, - "HostedArgoSites": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "domain": { - "type": "string" - }, - "bearerKeyPath": { - "type": "string" - }, - "caKeyPath": { - "type": "string" - } + { + "type": "object" + } + ], + "items": { + "$ref": "#/definitions/Applications" + } + }, + "imperative": { + "$ref": "#/definitions/Imperative" + }, + "managedClusterGroups": { + "anyOf": [ + { + "type": "array" }, - "required": [ - "name", - "domain" - ], - "title": "HostedArgoSites" + { + "type": "object" + } + ], + "items": { + "$ref": "#/definitions/ManagedClusterGroup" + } + }, + "externalClusters": { + "type": "array" + } + }, + "required": [ + "applications", + "insecureUnsealVaultInsideCluster", + "isHubCluster", + "managedClusterGroups", + "name", + "namespaces", + "projects", + "subscriptions" + ], + "title": "ClusterGroup" + }, + "Applications": { + "type": "object", + "additionalProperties": true, + "properties": { + "name": { + "type": "string" + }, + "repoURL": { + "type": "string" + }, + "targetRevision": { + "type": "string" + }, + "chart": { + "type": "string" + }, + "kustomize": { + "type": "boolean" + }, + "plugin": { + "type": "object" + }, + "extraValueFiles": { + "type": "array" + }, + "extraHubClusterDomainFields": { + "type": "array" + }, + "extraLocalClusterDomainFields": { + "type": "array" + }, + "extraRepoURLFields": { + "type": "array" + }, + "extraTargetRevisionFields": { + "type": "array" + }, + "extraNamespaceFields": { + "type": "array" + }, + "extraPatternNameFields": { + "type": "array" + }, + "overrides": { + "type": "object" + }, + "fileParameters": { + "type": "array" + }, + "ignoreDifferences": { + "type": "array" + }, + "syncPolicy": { + "type": "object" + }, + "namespace": { + "type": "string" + }, + "project": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": [ + "name", + "namespace", + "path", + "project" + ], + "title": "Applications" + }, + "Imperative": { + "type": "object", + "additionalProperties": false, + "properties": { + "jobs": { + "type": "array", + "items": { + "$ref": "#/definitions/Job" + } + }, + "image": { + "type": "string", + "default": "registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest" + }, + "namespace": { + "type": "string", + "default": "imperative", + "enum": [ + "imperative" + ] + }, + "serviceAccountCreate": { + "type": "boolean" + }, + "valuesConfigMap": { + "type": "string" + }, + "cronJobName": { + "type": "string" + }, + "jobName": { + "type": "string" + }, + "imagePullPolicy": { + "type": "string", + "default": "Always", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ] + }, + "activeDeadlineSeconds": { + "type": "integer", + "default": 3600 + }, + "schedule": { + "type": "string", + "default": "*/10 * * * *" + }, + "insecureUnsealVaultInsideClusterSchedule": { + "type": "string", + "default": "*/5 * * * *" + }, + "verbosity": { + "type": "string", + "default": "", + "enum": [ + "", + "-v", + "-vv", + "-vvv", + "-vvvv" + ] + }, + "serviceAccountName": { + "type": "string" + }, + "clusterRoleName": { + "type": "string" + }, + "clusterRoleYaml": { + "type": "string" + }, + "roleName": { + "type": "string" + }, + "roleYaml": { + "type": "string" + } + }, + "required": [ + "jobs" + ], + "title": "Imperative" + }, + "Job": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "playbook": { + "type": "string" + }, + "timeout": { + "type": "integer" + }, + "verbosity": { + "type": "string" + } + }, + "required": [ + "name", + "playbook" + ], + "title": "Job" + }, + "ManagedClusterGroup": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "targetRevision": { + "type": "string" + }, + "acmlabels": { + "type": "array", + "items": { + "$ref": "#/definitions/ACMLabels" + } + }, + "hostedArgoSites": { + "type": "array", + "items": { + "$ref": "#/definitions/HostedArgoSites" + } + }, + "clusterPools": { + "type": "object", + "items": { + "$ref": "#/definitions/ClusterPools" + } + }, + "helmOverrides": { + "type": "array", + "items": { + "$ref": "#/definitions/HelmOverride" + } + } + }, + "required": [], + "title": "ManagedClusterGroup" + }, + "ClusterPools": { + "type": "object", + "additionalProperties": false, + "properties": { + "size": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "openshiftVersion": { + "type": "string" + }, + "baseDomain": { + "type": "string" + }, + "platform": { + "type": "object", + "$ref": "#/definitions/ClusterPoolsPlatform" + }, + "clusters": { + "type": "array" + } + }, + "required": [ + "name", + "openshiftVersion", + "baseDomain", + "platform", + "clusters" + ], + "title": "ClusterPools" + }, + "ClusterPoolsPlatform": { + "type": "object", + "additionalProperties": false, + "properties": { + "baseDomainResourceGroupName": { + "type": "string" + }, + "region": { + "type": "string" + } + }, + "required": [ + "region" + ], + "title": "ClusterPoolsPlatform" + }, + "HelmOverride": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "name", + "value" + ], + "title": "HelmOverride" + }, + "ACMLabels": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "name", + "value" + ], + "title": "ACMLabels" + }, + "Subscription": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "namespaces": { + "type": "array" + }, + "namespace": { + "type": "string" + }, + "sourceNamespace": { + "type": "string" + }, + "source": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "csv": { + "type": "string" + }, + "installPlanApproval": { + "type": "string", + "enum": [ + "Manual", + "Automatic" + ] + }, + "config": { + "type": "object", + "$ref": "#/definitions/SubscriptionsConfigEnv" + }, + "disabled": { + "type": "boolean" + } + }, + "required": [ + "name" + ], + "title": "Subscription" + }, + "SubscriptionsConfigEnv": { + "type": "array", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "name", + "value" + ], + "title": "SubscriptionsConfigEnv" + }, + "HostedArgoSites": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "bearerKeyPath": { + "type": "string" + }, + "caKeyPath": { + "type": "string" } + }, + "required": [ + "name", + "domain" + ], + "title": "HostedArgoSites" } + } } From 57e2ac150945596ce113eea23b3b6033f84a29e3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 6 Dec 2022 18:09:43 +0100 Subject: [PATCH 0704/1263] Fix up tests --- tests/clustergroup-naked.expected.yaml | 826 +------------------------ 1 file changed, 10 insertions(+), 816 deletions(-) diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index da0f637b..4d5c5eee 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -1,208 +1,17 @@ --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml -apiVersion: v1 -kind: Namespace -metadata: - labels: - name: pattern - argocd.argoproj.io/managed-by: example-pattern-example - name: open-cluster-management -spec: ---- -# Source: pattern-clustergroup/templates/core/namespaces.yaml -apiVersion: v1 -kind: Namespace -metadata: - labels: - name: pattern - argocd.argoproj.io/managed-by: example-pattern-example - name: application-ci -spec: ---- -# Source: pattern-clustergroup/templates/imperative/namespace.yaml -apiVersion: v1 -kind: Namespace -metadata: - labels: - name: imperative - argocd.argoproj.io/managed-by: example-pattern-example - name: imperative ---- # Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml apiVersion: v1 kind: Namespace metadata: labels: - name: example-pattern-example + name: common-example # The name here needs to be consistent with # - acm/templates/policies/application-policies.yaml # - clustergroup/templates/applications.yaml # - any references to secrets and route URLs in documentation - name: example-pattern-example + name: common-example spec: {} --- -# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: imperative-sa - namespace: imperative ---- -# Source: pattern-clustergroup/templates/imperative/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: helm-values-configmap-example - namespace: imperative -data: - values.yaml: | - clusterGroup: - applications: - acm: - ignoreDifferences: - - group: internal.open-cluster-management.io - jsonPointers: - - /spec/loggingCA - kind: ManagedClusterInfo - name: acm - namespace: open-cluster-management - path: common/acm - project: datacenter - pipe: - name: pipelines - namespace: application-ci - path: charts/datacenter/pipelines - project: datacenter - imperative: - activeDeadlineSeconds: 3600 - clusterRoleName: imperative-cluster-role - clusterRoleYaml: "" - cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' - jobName: imperative-job - jobs: - - name: regional-ca - playbook: ansible/playbooks/on-hub-get-regional-ca.yml - timeout: 234 - namespace: imperative - roleName: imperative-role - roleYaml: "" - schedule: '*/10 * * * *' - serviceAccountCreate: true - serviceAccountName: imperative-sa - valuesConfigMap: helm-values-configmap - verbosity: "" - insecureUnsealVaultInsideCluster: true - isHubCluster: true - managedClusterGroups: - - acmlabels: - - name: clusterGroup - value: acm-region - helmOverrides: - - name: clusterGroup.isHubCluster - value: "false" - name: acm-edge - targetRevision: main - - acmlabels: - - name: clusterGroup - value: region - clusterPools: - exampleAWSPool: - baseDomain: blueprints.rhecoeng.com - clusters: - - One - name: aws-ap - openshiftVersion: 4.10.18 - platform: - aws: - region: ap-southeast-2 - size: 3 - exampleAzurePool: - baseDomain: blueprints.rhecoeng.com - clusters: - - Two - - Three - name: azure-us - openshiftVersion: 4.10.18 - platform: - azure: - baseDomainResourceGroupName: dojo-dns-zones - region: eastus - helmOverrides: - - name: clusterGroup.isHubCluster - value: "false" - name: acm-provision-edge - targetRevision: main - - helmOverrides: - - name: clusterGroup.isHubCluster - value: "false" - hostedArgoSites: - - bearerKeyPath: secret/data/hub/cluster_perth - caKeyPath: secret/data/hub/cluster_perth_ca - domain: perth1.beekhof.net - name: perth - - domain: syd.beekhof.net - name: sydney - name: argo-edge - name: example - namespaces: - - open-cluster-management - - application-ci - projects: - - datacenter - subscriptions: - acm: - channel: release-2.4 - csv: advanced-cluster-management.v2.4.1 - name: advanced-cluster-management - namespace: open-cluster-management - odh: - csv: opendatahub-operator.v1.1.0 - disabled: true - name: opendatahub-operator - source: community-operators - pipelines: - csv: redhat-openshift-pipelines.v1.5.2 - name: openshift-pipelines-operator-rh - enabled: all - global: - options: - installPlanApproval: Automatic - syncPolicy: Automatic - useCSV: false - pattern: example-pattern ---- -# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: imperative-cluster-role -rules: - - apiGroups: - - '*' - resources: - - '*' - verbs: - - get - - list - - watch ---- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: imperative-cluster-admin-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: imperative-cluster-role -subjects: - - kind: ServiceAccount - name: imperative-sa - namespace: imperative ---- # Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 @@ -227,7 +36,7 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: example-pattern-example-cluster-admin-rolebinding + name: common-example-cluster-admin-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -236,583 +45,16 @@ subjects: - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-application-controller name: example-gitops-argocd-application-controller - namespace: example-pattern-example + namespace: common-example # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-server name: example-gitops-argocd-server - namespace: example-pattern-example + namespace: common-example # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) - kind: ServiceAccount name: example-gitops-argocd-dex-server - namespace: example-pattern-example ---- -# Source: pattern-clustergroup/templates/imperative/role.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: imperative-role - namespace: imperative -rules: - - apiGroups: - - '*' - resources: - - '*' - verbs: - - '*' ---- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: imperative-admin-rolebinding - namespace: imperative -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: imperative-role -subjects: - - kind: ServiceAccount - name: imperative-sa - namespace: imperative ---- -# Source: pattern-clustergroup/templates/imperative/job.yaml -apiVersion: batch/v1 -kind: CronJob -metadata: - name: imperative-cronjob - namespace: imperative -spec: - schedule: "*/10 * * * *" - # if previous Job is still running, skip execution of a new Job - concurrencyPolicy: Forbid - jobTemplate: - spec: - activeDeadlineSeconds: 3600 - template: - metadata: - name: imperative-job - spec: - serviceAccountName: imperative-sa - initContainers: - # git init happens in /git/repo so that we can set the folder to 0770 permissions - # reason for that is ansible refuses to create temporary folders in there - - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - env: - - name: HOME - value: /git/home - command: - - 'sh' - - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- /git/repo;chmod 0770 /git/{repo,home}" - volumeMounts: - - name: git - mountPath: "/git" - - name: regional-ca - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - env: - - name: HOME - value: /git/home - workingDir: /git/repo - # We have a default timeout of 600s for each playbook. Can be overridden - # on a per-job basis - command: - - timeout - - "234" - - ansible-playbook - - -e - - "@/values/values.yaml" - - ansible/playbooks/on-hub-get-regional-ca.yml - volumeMounts: - - name: git - mountPath: "/git" - - name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml - containers: - - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - command: - - 'sh' - - '-c' - - 'echo' - - 'done' - - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap-example - restartPolicy: Never ---- -# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml -apiVersion: batch/v1 -kind: CronJob -metadata: - name: unsealvault-cronjob - namespace: imperative -spec: - schedule: "*/5 * * * *" - # if previous Job is still running, skip execution of a new Job - concurrencyPolicy: Forbid - jobTemplate: - spec: - activeDeadlineSeconds: 3600 - template: - metadata: - name: unsealvault-job - spec: - serviceAccountName: imperative-sa - initContainers: - # git init happens in /git/repo so that we can set the folder to 0770 permissions - # reason for that is ansible refuses to create temporary folders in there - - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - env: - - name: HOME - value: /git/home - command: - - 'sh' - - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- /git/repo;chmod 0770 /git/{repo,home}" - volumeMounts: - - name: git - mountPath: "/git" - - name: unseal-playbook - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - env: - - name: HOME - value: /git/home - workingDir: /git/repo - # We have a default timeout of 600s for each playbook. Can be overridden - # on a per-job basis - command: - - timeout - - "600" - - ansible-playbook - - -e - - "@/values/values.yaml" - - -e - - '{"file_unseal": false}' - - -t - - 'vault_init,vault_unseal,vault_secrets_init' - - "common/ansible/playbooks/vault/vault.yaml" - volumeMounts: - - name: git - mountPath: "/git" - - name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml - containers: - - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - command: - - 'sh' - - '-c' - - 'echo' - - 'done' - - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap-example - restartPolicy: Never ---- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml ---- ---- -# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml -apiVersion: argoproj.io/v1alpha1 -kind: AppProject -metadata: - name: argo-edge - namespace: openshift-gitops -spec: - description: "Cluster Group argo-edge" - destinations: - - namespace: '*' - server: '*' - clusterResourceWhitelist: - - group: '*' - kind: '*' - namespaceResourceWhitelist: - - group: '*' - kind: '*' - sourceRepos: - - '*' -status: {} ---- -# Source: pattern-clustergroup/templates/plumbing/projects.yaml -apiVersion: argoproj.io/v1alpha1 -kind: AppProject -metadata: - name: datacenter - namespace: example-pattern-example -spec: - description: "Pattern datacenter" - destinations: - - namespace: '*' - server: '*' - clusterResourceWhitelist: - - group: '*' - kind: '*' - namespaceResourceWhitelist: - - group: '*' - kind: '*' - sourceRepos: - - '*' -status: {} ---- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: acm - namespace: example-pattern-example - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: - namespace: open-cluster-management - project: datacenter - source: - repoURL: - targetRevision: - path: common/acm - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-example.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: example-pattern - - name: global.clusterDomain - value: - - name: global.hubClusterDomain - value: - - name: global.localClusterDomain - value: - ignoreDifferences: [ - { - "group": "internal.open-cluster-management.io", - "jsonPointers": [ - "/spec/loggingCA" - ], - "kind": "ManagedClusterInfo" - } -] - syncPolicy: - automated: {} - # selfHeal: true ---- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: pipelines - namespace: example-pattern-example - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: - namespace: application-ci - project: datacenter - source: - repoURL: - targetRevision: - path: charts/datacenter/pipelines - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-example.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: example-pattern - - name: global.clusterDomain - value: - - name: global.hubClusterDomain - value: - - name: global.localClusterDomain - value: - syncPolicy: - automated: {} - # selfHeal: true ---- -# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: example-pattern-argo-edge-perth - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - project: argo-edge - source: - repoURL: - targetRevision: - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-argo-edge.yaml" - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: example-pattern - - name: global.hubClusterDomain - value: - - name: global.localClusterDomain - value: apps.perth1.beekhof.net - - name: global.clusterDomain - value: perth1.beekhof.net - - name: enabled - value: core - - name: clusterGroup.name - value: argo-edge - - name: clusterGroup.targetCluster - value: perth - - name: clusterGroup.hostedSite.bearerKeyPath - value: secret/data/hub/cluster_perth - - name: clusterGroup.hostedSite.caKeyPath - value: secret/data/hub/cluster_perth_ca - - name: clusterGroup.isHubCluster - value: "false" - destination: - name: perth - namespace: example-pattern-argo-edge - syncPolicy: - automated: - selfHeal: true - ignoreDifferences: - - group: apps - kind: Deployment - jsonPointers: - - /spec/replicas - - group: route.openshift.io - kind: Route - jsonPointers: - - /status ---- -# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: example-pattern-argo-edge-perth-plumbing - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - project: argo-edge - source: - repoURL: - targetRevision: - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-argo-edge.yaml" - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: example-pattern - - name: global.hubClusterDomain - value: - - name: global.localClusterDomain - value: apps.perth1.beekhof.net - - name: global.clusterDomain - value: perth1.beekhof.net - - name: enabled - value: plumbing - - name: clusterGroup.name - value: argo-edge - - name: clusterGroup.targetCluster - value: perth - - name: clusterGroup.hostedSite.bearerKeyPath - value: secret/data/hub/cluster_perth - - name: clusterGroup.hostedSite.caKeyPath - value: secret/data/hub/cluster_perth_ca - - name: clusterGroup.isHubCluster - value: "false" - destination: - name: in-cluster - namespace: openshift-gitops - syncPolicy: - automated: - selfHeal: true - ignoreDifferences: - - group: apps - kind: Deployment - jsonPointers: - - /spec/replicas - - group: route.openshift.io - kind: Route - jsonPointers: - - /status ---- -# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: example-pattern-argo-edge-sydney - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - project: argo-edge - source: - repoURL: - targetRevision: - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-argo-edge.yaml" - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: example-pattern - - name: global.hubClusterDomain - value: - - name: global.localClusterDomain - value: apps.syd.beekhof.net - - name: global.clusterDomain - value: syd.beekhof.net - - name: enabled - value: core - - name: clusterGroup.name - value: argo-edge - - name: clusterGroup.targetCluster - value: sydney - - name: clusterGroup.hostedSite.bearerKeyPath - value: secret/data/hub/cluster_sydney - - name: clusterGroup.hostedSite.caKeyPath - value: secret/data/hub/cluster_sydney_ca - - name: clusterGroup.isHubCluster - value: "false" - destination: - name: sydney - namespace: example-pattern-argo-edge - syncPolicy: - automated: - selfHeal: true - ignoreDifferences: - - group: apps - kind: Deployment - jsonPointers: - - /spec/replicas - - group: route.openshift.io - kind: Route - jsonPointers: - - /status ---- -# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: example-pattern-argo-edge-sydney-plumbing - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - project: argo-edge - source: - repoURL: - targetRevision: - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-argo-edge.yaml" - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: example-pattern - - name: global.hubClusterDomain - value: - - name: global.localClusterDomain - value: apps.syd.beekhof.net - - name: global.clusterDomain - value: syd.beekhof.net - - name: enabled - value: plumbing - - name: clusterGroup.name - value: argo-edge - - name: clusterGroup.targetCluster - value: sydney - - name: clusterGroup.hostedSite.bearerKeyPath - value: secret/data/hub/cluster_sydney - - name: clusterGroup.hostedSite.caKeyPath - value: secret/data/hub/cluster_sydney_ca - - name: clusterGroup.isHubCluster - value: "false" - destination: - name: in-cluster - namespace: openshift-gitops - syncPolicy: - automated: - selfHeal: true - ignoreDifferences: - - group: apps - kind: Deployment - jsonPointers: - - /spec/replicas - - group: route.openshift.io - kind: Route - jsonPointers: - - /status + namespace: common-example --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 @@ -823,7 +65,7 @@ metadata: # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: example-gitops - namespace: example-pattern-example + namespace: common-example annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: @@ -852,7 +94,7 @@ spec: --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern=example-pattern + --set global.pattern=common --set global.clusterDomain= --set global.hubClusterDomain= --set global.localClusterDomain= @@ -932,59 +174,11 @@ apiVersion: console.openshift.io/v1 kind: ConsoleLink metadata: name: example-gitops-link - namespace: example-pattern-example + namespace: common-example spec: applicationMenu: section: OpenShift GitOps imageURL: data:image/png;base64,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 - href: 'https://example-gitops-server-example-pattern-example.' + href: 'https://example-gitops-server-common-example.' location: ApplicationMenu text: 'Example ArgoCD' ---- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup -metadata: - name: open-cluster-management-operator-group - namespace: open-cluster-management -spec: - targetNamespaces: - - open-cluster-management ---- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup -metadata: - name: application-ci-operator-group - namespace: application-ci -spec: - targetNamespaces: - - application-ci ---- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: advanced-cluster-management - namespace: open-cluster-management -spec: - name: advanced-cluster-management - source: redhat-operators - sourceNamespace: openshift-marketplace - channel: release-2.4 - installPlanApproval: Automatic - startingCSV: advanced-cluster-management.v2.4.1 ---- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: openshift-pipelines-operator-rh - namespace: openshift-operators -spec: - name: openshift-pipelines-operator-rh - source: redhat-operators - sourceNamespace: openshift-marketplace - channel: stable - installPlanApproval: Automatic - startingCSV: redhat-openshift-pipelines.v1.5.2 From 6c3eae444bec3a235c0c4ce1cc5f55e09293e64c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 6 Dec 2022 18:15:57 +0100 Subject: [PATCH 0705/1263] Move to draft 0.7 of the schema spec --- clustergroup/values.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 8ff1ed28..5783a811 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-06/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "$ref": "#/definitions/ValidatedPatterns", "definitions": { "ValidatedPatterns": { From a125039e199100ab4f9e9b42aa6489874a99e8f1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 6 Dec 2022 18:39:14 +0100 Subject: [PATCH 0706/1263] Add operatorgroupExcludes which is in examples/industrial-edge-factory.yaml --- clustergroup/values.schema.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 5783a811..f787b699 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -194,6 +194,12 @@ "type": "string" } }, + "operatorgroupExcludes": { + "type": "array", + "items": { + "type": "string" + } + }, "subscriptions": { "anyOf": [ { From 7f4058fe940c19c48e90affd0591584527c98432 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 6 Dec 2022 18:55:34 +0100 Subject: [PATCH 0707/1263] namespace is not mandatory in an application examples/industrial-edge-factory.yaml has an example where namespace is not specified. --- clustergroup/values.schema.json | 1 - 1 file changed, 1 deletion(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index f787b699..62fb4886 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -331,7 +331,6 @@ }, "required": [ "name", - "namespace", "path", "project" ], From 0ad50de7f97f6d4b8e8ef2c75d0522a3b32fa354 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 6 Dec 2022 18:59:28 +0100 Subject: [PATCH 0708/1263] Fix up some more tests --- ...roup-industrial-edge-factory.expected.yaml | 3 +-- ...tergroup-industrial-edge-hub.expected.yaml | 2 -- ...rgroup-medical-diagnosis-hub.expected.yaml | 2 -- tests/clustergroup-normal.expected.yaml | 25 +++++++++++-------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 31178ed1..428892b8 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -105,6 +105,7 @@ data: verbosity: "" insecureUnsealVaultInsideCluster: true isHubCluster: false + managedClusterGroups: [] name: factory namespaces: - manuela-stormshift-line-dashboard @@ -159,8 +160,6 @@ data: secretStore: kind: ClusterSecretStore name: vault-backend - secretsBase: - key: secret/data/hub --- # Source: pattern-clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index d6f0f520..77887144 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -321,8 +321,6 @@ data: secretStore: kind: ClusterSecretStore name: vault-backend - secretsBase: - key: secret/data/hub --- # Source: pattern-clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 3b52ec6b..0000ed5c 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -308,8 +308,6 @@ data: secretStore: kind: ClusterSecretStore name: vault-backend - secretsBase: - key: secret/data/hub --- # Source: pattern-clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 663aa7d4..730a905c 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -164,6 +164,7 @@ data: pipelines: csv: redhat-openshift-pipelines.v1.5.2 name: openshift-pipelines-operator-rh + targetCluster: in-cluster enabled: all global: clusterDomain: region.example.com @@ -182,11 +183,15 @@ data: useCSV: false pattern: mypattern repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main main: clusterGroupName: example git: repoURL: https://github.com/pattern-clone/mypattern revision: main + secretStore: + kind: ClusterSecretStore + name: vault-backend --- # Source: pattern-clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -320,7 +325,7 @@ spec: command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" volumeMounts: - name: git mountPath: "/git" @@ -394,7 +399,7 @@ spec: command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" volumeMounts: - name: git mountPath: "/git" @@ -497,12 +502,12 @@ metadata: - resources-finalizer.argocd.argoproj.io/foreground spec: destination: - name: + name: in-cluster namespace: open-cluster-management project: datacenter source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: + targetRevision: main path: common/acm helm: ignoreMissingValueFiles: true @@ -552,12 +557,12 @@ metadata: - resources-finalizer.argocd.argoproj.io/foreground spec: destination: - name: + name: in-cluster namespace: application-ci project: datacenter source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: + targetRevision: main path: charts/datacenter/pipelines helm: ignoreMissingValueFiles: true @@ -600,7 +605,7 @@ spec: project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: + targetRevision: main path: common/clustergroup helm: ignoreMissingValueFiles: true @@ -662,7 +667,7 @@ spec: project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: + targetRevision: main path: common/clustergroup helm: ignoreMissingValueFiles: true @@ -724,7 +729,7 @@ spec: project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: + targetRevision: main path: common/clustergroup helm: ignoreMissingValueFiles: true @@ -786,7 +791,7 @@ spec: project: argo-edge source: repoURL: https://github.com/pattern-clone/mypattern - targetRevision: + targetRevision: main path: common/clustergroup helm: ignoreMissingValueFiles: true From 9068b228a16840408f22ae4a574d372e2503e35d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 6 Dec 2022 21:55:17 +0100 Subject: [PATCH 0709/1263] add clusterSelector in managedClusterGroups --- clustergroup/values.schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 62fb4886..2ce8c655 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -473,6 +473,10 @@ "$ref": "#/definitions/ClusterPools" } }, + "clusterSelector": { + "type": "object", + "additionalProperties": true + }, "helmOverrides": { "type": "array", "items": { From e8153b461a455bae93a19ad7a5473c0e19774f7b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 7 Dec 2022 16:59:37 +0100 Subject: [PATCH 0710/1263] Allow clusterRoleYaml to be an array and timeout to be a string --- clustergroup/values.schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 2ce8c655..05dd9690 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -408,7 +408,7 @@ "type": "string" }, "clusterRoleYaml": { - "type": "string" + "type": ["string", "array"] }, "roleName": { "type": "string" @@ -433,7 +433,7 @@ "type": "string" }, "timeout": { - "type": "integer" + "type": ["integer", "string"] }, "verbosity": { "type": "string" From 4ed3f8d9afd5014d966599cf9db0b42e4ddcd382 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 7 Dec 2022 17:03:49 +0100 Subject: [PATCH 0711/1263] Add image, tags, extravars to imperative job object --- clustergroup/values.schema.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 05dd9690..663f649c 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -435,6 +435,16 @@ "timeout": { "type": ["integer", "string"] }, + "image": { + "type": "string", + "default": "registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest" + }, + "tags": { + "type": "string" + }, + "extravars": { + "type": "array" + }, "verbosity": { "type": "string" } From eddb7bcccdfabf23a686b450b397f7a7aa83c20c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 8 Dec 2022 12:17:36 +0100 Subject: [PATCH 0712/1263] Add descriptions for SecretStore properties --- clustergroup/values.schema.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 663f649c..c3ad7f78 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -8,7 +8,6 @@ "properties": { "enabled": { "type": "string", - "description": "", "enum": [ "all", "core", @@ -38,10 +37,14 @@ "additionalProperties": false, "properties": { "name": { - "type": "string" + "type": "string", + "description": "Name of the external secret backend", + "default": "vault-backend" }, "kind": { "type": "string" + "description": "Type of the external secret backend", + "default": "ClusterSecretStore" } }, "required": [ From ae6efb85c7e50216168773f8e6decd7103fe32c3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 8 Dec 2022 12:18:16 +0100 Subject: [PATCH 0713/1263] move GlobalGit and GitSecret after Main --- clustergroup/values.schema.json | 82 ++++++++++++++++----------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index c3ad7f78..3f5f57fa 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -53,47 +53,6 @@ ], "title": "SecretsStore" }, - "GlobalGit": { - "type": "object", - "additionalProperties": false, - "properties": { - "hostname": { - "type": "string" - }, - "account": { - "type": "string" - }, - "email": { - "type": "string" - }, - "dev_revision": { - "type": "string" - } - }, - "required": [ - "hostname", - "account", - "email" - ], - "title": "GlobalGit" - }, - "GitSecrets": { - "type": "object", - "additionalProperties": false, - "properties": { - "username": { - "type": "string" - }, - "token": { - "type": "string" - } - }, - "required": [ - "username", - "token" - ], - "title": "GitSecrets" - }, "Main": { "type": "object", "additionalProperties": true, @@ -154,6 +113,47 @@ ], "title": "Global" }, + "GlobalGit": { + "type": "object", + "additionalProperties": false, + "properties": { + "hostname": { + "type": "string" + }, + "account": { + "type": "string" + }, + "email": { + "type": "string" + }, + "dev_revision": { + "type": "string" + } + }, + "required": [ + "hostname", + "account", + "email" + ], + "title": "GlobalGit" + }, + "GitSecrets": { + "type": "object", + "additionalProperties": false, + "properties": { + "username": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "required": [ + "username", + "token" + ], + "title": "GitSecrets" + }, "Options": { "type": "object", "additionalProperties": false, From 0042074dfdbca6b2ce4e4a29a3a9660ce05c0202 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 8 Dec 2022 12:22:22 +0100 Subject: [PATCH 0714/1263] Forgot , --- clustergroup/values.schema.json | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 3f5f57fa..bf419752 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -42,7 +42,7 @@ "default": "vault-backend" }, "kind": { - "type": "string" + "type": "string", "description": "Type of the external secret backend", "default": "ClusterSecretStore" } @@ -137,23 +137,6 @@ ], "title": "GlobalGit" }, - "GitSecrets": { - "type": "object", - "additionalProperties": false, - "properties": { - "username": { - "type": "string" - }, - "token": { - "type": "string" - } - }, - "required": [ - "username", - "token" - ], - "title": "GitSecrets" - }, "Options": { "type": "object", "additionalProperties": false, From 9a3445d1353ac03834e660b008dd8c8bf8cde7a3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 6 Nov 2022 18:35:06 +0100 Subject: [PATCH 0715/1263] Move testing targets after the installation ones This leaves the more important targets on top for easier readability --- Makefile | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 5415703d..cbd9a129 100644 --- a/Makefile +++ b/Makefile @@ -32,28 +32,6 @@ help: ## This help message show: ## show the starting template without installing it helm template common/install/ --name-template $(NAME) $(HELM_OPTS) -CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | grep -v examples | sed -e 's/.\///') -test: ## run helm tests - @for t in $(CHARTS); do common/scripts/test.sh $$t all "$(TEST_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done - -helmlint: ## run helm lint - @for t in $(CHARTS); do common/scripts/lint.sh $$t $(TEST_OPTS); if [ $$? != 0 ]; then exit 1; fi; done - -API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.10/ -KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition' -# We need to skip 'CustomResourceDefinition' as openapi2jsonschema seems to be unable to generate them ATM -kubeconform: ## run helm kubeconform - @for t in $(CHARTS); do helm template $(TEST_OPTS) $(PATTERN_OPTS) $$t | kubeconform -strict $(KUBECONFORM_SKIP) -verbose -schema-location $(API_URL); if [ $$? != 0 ]; then exit 1; fi; done - -validate-prereq: ## verify pre-requisites - @for t in $(EXECUTABLES); do if ! which $$t > /dev/null 2>&1; then echo "No $$t in PATH"; exit 1; fi; done - @echo "Prerequisites checked '$(EXECUTABLES)': OK" - @ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1 - @echo "Python kubernetes module: OK" - @echo -n "Check for kubernetes.core collection: " - @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi - @echo "OK" - # We only check the remote ssh git branch's existance if we're not running inside a container # as getting ssh auth working inside a container seems a bit brittle validate-origin: ## verify the git origin is available @@ -92,6 +70,28 @@ vault-unseal: ## unseals the vault load-secrets: ## loads the secrets into the vault common/scripts/vault-utils.sh push_secrets common/pattern-vault.init $(NAME) +CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | grep -v examples | sed -e 's/.\///') +test: ## run helm tests + @for t in $(CHARTS); do common/scripts/test.sh $$t all "$(TEST_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done + +helmlint: ## run helm lint + @for t in $(CHARTS); do common/scripts/lint.sh $$t $(TEST_OPTS); if [ $$? != 0 ]; then exit 1; fi; done + +API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.10/ +KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition' +# We need to skip 'CustomResourceDefinition' as openapi2jsonschema seems to be unable to generate them ATM +kubeconform: ## run helm kubeconform + @for t in $(CHARTS); do helm template $(TEST_OPTS) $(PATTERN_OPTS) $$t | kubeconform -strict $(KUBECONFORM_SKIP) -verbose -schema-location $(API_URL); if [ $$? != 0 ]; then exit 1; fi; done + +validate-prereq: ## verify pre-requisites + @for t in $(EXECUTABLES); do if ! which $$t > /dev/null 2>&1; then echo "No $$t in PATH"; exit 1; fi; done + @echo "Prerequisites checked '$(EXECUTABLES)': OK" + @ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1 + @echo "Python kubernetes module: OK" + @echo -n "Check for kubernetes.core collection: " + @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi + @echo "OK" + super-linter: ## Runs super linter locally rm -rf .mypy_cache podman run -e RUN_LOCAL=true -e USE_FIND_ALGORITHM=true \ From 1401e860b737b83ef45e9495e4a40f2f810dce25 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 6 Nov 2022 18:41:01 +0100 Subject: [PATCH 0716/1263] Move some of the oc calls into the legacy target Namely HUBCLUSTER_APPS_DOMAIN and HUBCLUSTER_VERSION do not need to be calculated at every command. So let's move them into the legacy target. We do not need them in the operator-based deploy as those are calculated by the operator anyways. Tested both with 'make install' and 'make legacy-install' in MCG. --- Makefile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index cbd9a129..b87231bb 100644 --- a/Makefile +++ b/Makefile @@ -9,12 +9,9 @@ TARGET_ORIGIN ?= origin TARGET_REPO=$(shell git remote show $(TARGET_ORIGIN) | grep Push | sed -e 's/.*URL:[[:space:]]*//' -e 's%^git@%%' -e 's%^https://%%' -e 's%:%/%' -e 's%^%https://%') # git branch --show-current is also available as of git 2.22, but we will use this for compatibility TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) -HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spec.domain}) -HUBCLUSTER_VERSION=$(shell oc get OpenShiftControllerManager/cluster -o jsonpath='{.status.version}' | sed -n -E 's/([0-9]+).([0-9]+).*/\1.\2/p') # --set values always take precedence over the contents of -f -HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) \ - --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) --set global.clusterVersion="$(HUBCLUSTER_VERSION)" $(TARGET_SITE_OPT) +HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) TEST_OPTS= -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" \ --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" \ --set global.namespace="pattern-namespace" --set global.hubClusterDomain=apps.hub.example.com --set global.localClusterDomain=apps.region.example.com --set global.clusterDomain=region.example.com\ @@ -49,7 +46,11 @@ validate-origin: ## verify the git origin is available # pointing to one place or another, and don't need to change when they do (provide they use either legacy- or operator- # targets) deploy upgrade legacy-deploy legacy-upgrade: validate-prereq validate-origin ## deploys the pattern - helm upgrade --install $(NAME) common/install/ $(HELM_OPTS) + $(eval HUBCLUSTER_APPS_DOMAIN := $(shell oc get ingresses.config/cluster -o jsonpath={.spec.domain})) + $(eval HUBCLUSTER_VERSION := $(shell oc get OpenShiftControllerManager/cluster -o jsonpath='{.status.version}' | sed -n -E 's/([0-9]+).([0-9]+).*/\1.\2/p')) + $(eval LEGACY_INSTALL_HELM_OPTS := -f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) \ + --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) --set global.clusterVersion="$(HUBCLUSTER_VERSION)" $(TARGET_SITE_OPT)) + helm upgrade --install $(NAME) common/install/ $(LEGACY_INSTALL_HELM_OPTS) operator-deploy operator-upgrade: validate-origin ## runs helm install helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) From c0d48a39c6afc2f2fb9b9dd027df33d010dd2d98 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 6 Nov 2022 19:01:47 +0100 Subject: [PATCH 0717/1263] Simplify getting the remote git URL associated to the target origin Avoids a not-needed grep. This requires at least git >= 1.17.5. which has been released about ~12 years ago --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b87231bb..0abedf0b 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ ifneq ($(origin TARGET_SITE), undefined) endif TARGET_ORIGIN ?= origin -TARGET_REPO=$(shell git remote show $(TARGET_ORIGIN) | grep Push | sed -e 's/.*URL:[[:space:]]*//' -e 's%^git@%%' -e 's%^https://%%' -e 's%:%/%' -e 's%^%https://%') +TARGET_REPO=$(shell git ls-remote --get-url --symref $(TARGET_ORIGIN) | sed -e 's/.*URL:[[:space:]]*//' -e 's%^git@%%' -e 's%^https://%%' -e 's%:%/%' -e 's%^%https://%') # git branch --show-current is also available as of git 2.22, but we will use this for compatibility TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) From 1391079b9bb703eea42473957407df4790ebd1c0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 6 Nov 2022 19:10:00 +0100 Subject: [PATCH 0718/1263] Drop legacy-install from common We now only support the installation via the operator-based templates. This way we avoid duplication and get a certain amount of flexibility as the operator is in a better place to fetch information from the cluster (clusterVersion, DNS, etc.) As an added bonus we can drop most 'oc' calls that were only really needed in the legacy install form. When the old target is invoked, let's print an error message. --- Makefile | 16 +- install/.helmignore | 1 - install/Chart.yaml | 6 - install/crds/applications.argoproj.io.yaml | 2312 -------------------- install/templates/argocd/application.yaml | 42 - install/templates/argocd/namespace.yaml | 6 - install/templates/argocd/subscription.yaml | 20 - install/values.yaml | 25 - tests/install-naked.expected.yaml | 66 - tests/install-normal.expected.yaml | 66 - tests/install.expected.diff | 43 - 11 files changed, 4 insertions(+), 2599 deletions(-) delete mode 100644 install/.helmignore delete mode 100644 install/Chart.yaml delete mode 100644 install/crds/applications.argoproj.io.yaml delete mode 100644 install/templates/argocd/application.yaml delete mode 100644 install/templates/argocd/namespace.yaml delete mode 100644 install/templates/argocd/subscription.yaml delete mode 100644 install/values.yaml delete mode 100644 tests/install-naked.expected.yaml delete mode 100644 tests/install-normal.expected.yaml delete mode 100644 tests/install.expected.diff diff --git a/Makefile b/Makefile index 0abedf0b..4a902c6f 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ help: ## This help message # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show show: ## show the starting template without installing it - helm template common/install/ --name-template $(NAME) $(HELM_OPTS) + helm template common/operator-install/ --name-template $(NAME) $(HELM_OPTS) # We only check the remote ssh git branch's existance if we're not running inside a container # as getting ssh auth working inside a container seems a bit brittle @@ -41,20 +41,12 @@ validate-origin: ## verify the git origin is available echo "Running inside a container: Skipping git ssh checks";\ fi -# Default targets are "deploy" and "upgrade"; they can "move" to whichever install mechanism should be default. -# legacy-deploy and legacy-upgrade should be present so that patterns don't need to depend on "deploy" and "upgrade" -# pointing to one place or another, and don't need to change when they do (provide they use either legacy- or operator- -# targets) -deploy upgrade legacy-deploy legacy-upgrade: validate-prereq validate-origin ## deploys the pattern - $(eval HUBCLUSTER_APPS_DOMAIN := $(shell oc get ingresses.config/cluster -o jsonpath={.spec.domain})) - $(eval HUBCLUSTER_VERSION := $(shell oc get OpenShiftControllerManager/cluster -o jsonpath='{.status.version}' | sed -n -E 's/([0-9]+).([0-9]+).*/\1.\2/p')) - $(eval LEGACY_INSTALL_HELM_OPTS := -f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) \ - --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) --set global.clusterVersion="$(HUBCLUSTER_VERSION)" $(TARGET_SITE_OPT)) - helm upgrade --install $(NAME) common/install/ $(LEGACY_INSTALL_HELM_OPTS) - operator-deploy operator-upgrade: validate-origin ## runs helm install helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) +deploy upgrade legacy-deploy legacy-upgrade: ## does nothing anymore. use operator-deploy + @echo "UNSUPPORTED TARGET: please switch to 'operator-deploy'" + uninstall: ## runs helm uninstall $(eval CSV := $(shell oc get subscriptions -n openshift-operators openshift-gitops-operator -ojsonpath={.status.currentCSV})) helm uninstall $(NAME) diff --git a/install/.helmignore b/install/.helmignore deleted file mode 100644 index b25c15b8..00000000 --- a/install/.helmignore +++ /dev/null @@ -1 +0,0 @@ -*~ diff --git a/install/Chart.yaml b/install/Chart.yaml deleted file mode 100644 index d1c80ab3..00000000 --- a/install/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -description: A Helm chart to build and deploy a Cloud Pattern -keywords: -- pattern -name: pattern-install -version: 0.0.1 diff --git a/install/crds/applications.argoproj.io.yaml b/install/crds/applications.argoproj.io.yaml deleted file mode 100644 index c9866d38..00000000 --- a/install/crds/applications.argoproj.io.yaml +++ /dev/null @@ -1,2312 +0,0 @@ -# oc get crd/applications.argoproj.io -o yaml > applications.argoproj.io.yaml -# Remove annotation, timestamps and other cluster-specific cruft -# Also remove any status leftovers at the end -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - generation: 1 - labels: - app.kubernetes.io/name: applications.argoproj.io - app.kubernetes.io/part-of: argocd - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" - name: applications.argoproj.io -spec: - conversion: - strategy: None - group: argoproj.io - names: - kind: Application - listKind: ApplicationList - plural: applications - shortNames: - - app - - apps - singular: application - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.sync.status - name: Sync Status - type: string - - jsonPath: .status.health.status - name: Health Status - type: string - - jsonPath: .status.sync.revision - name: Revision - priority: 10 - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: Application is a definition of Application resource. - 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 - operation: - description: Operation contains information about a requested or running - operation - properties: - info: - description: Info is a list of informational items for this operation - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - initiatedBy: - description: InitiatedBy contains information about who initiated - the operations - properties: - automated: - description: Automated is set to true if operation was initiated - automatically by the application controller. - type: boolean - username: - description: Username contains the name of a user who started - operation - type: string - type: object - retry: - description: Retry controls the strategy to apply if a sync fails - properties: - backoff: - description: Backoff controls how to backoff on subsequent retries - of failed syncs - properties: - duration: - description: Duration is the amount to back off. Default unit - is seconds, but could also be a duration (e.g. "2m", "1h") - type: string - factor: - description: Factor is a factor to multiply the base duration - after each failed retry - format: int64 - type: integer - maxDuration: - description: MaxDuration is the maximum amount of time allowed - for the backoff strategy - type: string - type: object - limit: - description: Limit is the maximum number of attempts for retrying - a failed sync. If set to 0, no retries will be performed. - format: int64 - type: integer - type: object - sync: - description: Sync contains parameters for the operation - properties: - dryRun: - description: DryRun specifies to perform a `kubectl apply --dry-run` - without actually performing the sync - type: boolean - manifests: - description: Manifests is an optional field that overrides sync - source with a local directory for development - items: - type: string - type: array - prune: - description: Prune specifies to delete resources from the cluster - that are no longer tracked in git - type: boolean - resources: - description: Resources describes which resources shall be part - of the sync - items: - description: SyncOperationResource contains resources to sync. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - type: array - revision: - description: Revision is the revision (Git) or chart version (Helm) - which to sync the application to If omitted, will use the revision - specified in app spec. - type: string - source: - description: Source overrides the source definition set in the - application. This is typically set in a Rollback operation and - is nil during a Sync operation - properties: - chart: - description: Chart is a Helm chart name, and must be specified - for applications sourced from a Helm repo. - type: string - directory: - description: Directory holds path/directory specific options - properties: - exclude: - description: Exclude contains a glob pattern to match - paths against that should be explicitly excluded from - being used during manifest generation - type: string - include: - description: Include contains a glob pattern to match - paths against that should be explicitly included during - manifest generation - type: string - jsonnet: - description: Jsonnet holds options specific to Jsonnet - properties: - extVars: - description: ExtVars is a list of Jsonnet External - Variables - items: - description: JsonnetVar represents a variable to - be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - description: Additional library search dirs - items: - type: string - type: array - tlas: - description: TLAS is a list of Jsonnet Top-level Arguments - items: - description: JsonnetVar represents a variable to - be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - description: Recurse specifies whether to scan a directory - recursively for manifests - type: boolean - type: object - helm: - description: Helm holds helm specific options - properties: - fileParameters: - description: FileParameters are file parameters to the - helm template - items: - description: HelmFileParameter is a file parameter that's - passed to helm template during manifest generation - properties: - name: - description: Name is the name of the Helm parameter - type: string - path: - description: Path is the path to the file containing - the values for the Helm parameter - type: string - type: object - type: array - ignoreMissingValueFiles: - description: IgnoreMissingValueFiles prevents helm template - from failing when valueFiles do not exist locally by - not appending them to helm template --values - type: boolean - parameters: - description: Parameters is a list of Helm parameters which - are passed to the helm template command upon manifest - generation - items: - description: HelmParameter is a parameter that's passed - to helm template during manifest generation - properties: - forceString: - description: ForceString determines whether to tell - Helm to interpret booleans and numbers as strings - type: boolean - name: - description: Name is the name of the Helm parameter - type: string - value: - description: Value is the value for the Helm parameter - type: string - type: object - type: array - passCredentials: - description: PassCredentials pass credentials to all domains - (Helm's --pass-credentials) - type: boolean - releaseName: - description: ReleaseName is the Helm release name to use. - If omitted it will use the application name - type: string - skipCrds: - description: SkipCrds skips custom resource definition - installation step (Helm's --skip-crds) - type: boolean - valueFiles: - description: ValuesFiles is a list of Helm value files - to use when generating a template - items: - type: string - type: array - values: - description: Values specifies Helm values to be passed - to helm template, typically defined as a block - type: string - version: - description: Version is the Helm version to use for templating - (either "2" or "3") - type: string - type: object - ksonnet: - description: Ksonnet holds ksonnet specific options - properties: - environment: - description: Environment is a ksonnet application environment - name - type: string - parameters: - description: Parameters are a list of ksonnet component - parameter override values - items: - description: KsonnetParameter is a ksonnet component - parameter - properties: - component: - type: string - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - kustomize: - description: Kustomize holds kustomize specific options - properties: - commonAnnotations: - additionalProperties: - type: string - description: CommonAnnotations is a list of additional - annotations to add to rendered manifests - type: object - commonLabels: - additionalProperties: - type: string - description: CommonLabels is a list of additional labels - to add to rendered manifests - type: object - forceCommonAnnotations: - description: ForceCommonAnnotations specifies whether - to force applying common annotations to resources for - Kustomize apps - type: boolean - forceCommonLabels: - description: ForceCommonLabels specifies whether to force - applying common labels to resources for Kustomize apps - type: boolean - images: - description: Images is a list of Kustomize image override - specifications - items: - description: KustomizeImage represents a Kustomize image - definition in the format [old_image_name=]: - type: string - type: array - namePrefix: - description: NamePrefix is a prefix appended to resources - for Kustomize apps - type: string - nameSuffix: - description: NameSuffix is a suffix appended to resources - for Kustomize apps - type: string - version: - description: Version controls which version of Kustomize - to use for rendering manifests - type: string - type: object - path: - description: Path is a directory path within the Git repository, - and is only valid for applications sourced from Git. - type: string - plugin: - description: ConfigManagementPlugin holds config management - plugin specific options - properties: - env: - description: Env is a list of environment variable entries - items: - description: EnvEntry represents an entry in the application's - environment - properties: - name: - description: Name is the name of the variable, usually - expressed in uppercase - type: string - value: - description: Value is the value of the variable - type: string - required: - - name - - value - type: object - type: array - name: - type: string - type: object - repoURL: - description: RepoURL is the URL to the repository (Git or - Helm) that contains the application manifests - type: string - targetRevision: - description: TargetRevision defines the revision of the source - to sync the application to. In case of Git, this can be - commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. - type: string - required: - - repoURL - type: object - syncOptions: - description: SyncOptions provide per-sync sync-options, e.g. Validate=false - items: - type: string - type: array - syncStrategy: - description: SyncStrategy describes how to perform the sync - properties: - apply: - description: Apply will perform a `kubectl apply` to perform - the sync. - properties: - force: - description: Force indicates whether or not to supply - the --force flag to `kubectl apply`. The --force flag - deletes and re-create the resource, when PATCH encounters - conflict and has retried for 5 times. - type: boolean - type: object - hook: - description: Hook will submit any referenced resources to - perform the sync. This is the default strategy - properties: - force: - description: Force indicates whether or not to supply - the --force flag to `kubectl apply`. The --force flag - deletes and re-create the resource, when PATCH encounters - conflict and has retried for 5 times. - type: boolean - type: object - type: object - type: object - type: object - spec: - description: ApplicationSpec represents desired application state. Contains - link to repository with application definition and additional parameters - link definition revision. - properties: - destination: - description: Destination is a reference to the target Kubernetes server - and namespace - properties: - name: - description: Name is an alternate way of specifying the target - cluster by its symbolic name - type: string - namespace: - description: Namespace specifies the target namespace for the - application's resources. The namespace will only be set for - namespace-scoped resources that have not set a value for .metadata.namespace - type: string - server: - description: Server specifies the URL of the target cluster and - must be set to the Kubernetes control plane API - type: string - type: object - ignoreDifferences: - description: IgnoreDifferences is a list of resources and their fields - which should be ignored during comparison - items: - description: ResourceIgnoreDifferences contains resource filter - and list of json paths which should be ignored during comparison - with live state. - properties: - group: - type: string - jqPathExpressions: - items: - type: string - type: array - jsonPointers: - items: - type: string - type: array - kind: - type: string - managedFieldsManagers: - description: ManagedFieldsManagers is a list of trusted managers. - Fields mutated by those managers will take precedence over - the desired state defined in the SCM and won't be displayed - in diffs - items: - type: string - type: array - name: - type: string - namespace: - type: string - required: - - kind - type: object - type: array - info: - description: Info contains a list of information (URLs, email addresses, - and plain text) that relates to the application - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - project: - description: Project is a reference to the project this application - belongs to. The empty string means that application belongs to the - 'default' project. - type: string - revisionHistoryLimit: - description: RevisionHistoryLimit limits the number of items kept - in the application's revision history, which is used for informational - purposes as well as for rollbacks to previous versions. This should - only be changed in exceptional circumstances. Setting to zero will - store no history. This will reduce storage used. Increasing will - increase the space used to store the history, so we do not recommend - increasing it. Default is 10. - format: int64 - type: integer - source: - description: Source is a reference to the location of the application's - manifests or chart - properties: - chart: - description: Chart is a Helm chart name, and must be specified - for applications sourced from a Helm repo. - type: string - directory: - description: Directory holds path/directory specific options - properties: - exclude: - description: Exclude contains a glob pattern to match paths - against that should be explicitly excluded from being used - during manifest generation - type: string - include: - description: Include contains a glob pattern to match paths - against that should be explicitly included during manifest - generation - type: string - jsonnet: - description: Jsonnet holds options specific to Jsonnet - properties: - extVars: - description: ExtVars is a list of Jsonnet External Variables - items: - description: JsonnetVar represents a variable to be - passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - description: Additional library search dirs - items: - type: string - type: array - tlas: - description: TLAS is a list of Jsonnet Top-level Arguments - items: - description: JsonnetVar represents a variable to be - passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - description: Recurse specifies whether to scan a directory - recursively for manifests - type: boolean - type: object - helm: - description: Helm holds helm specific options - properties: - fileParameters: - description: FileParameters are file parameters to the helm - template - items: - description: HelmFileParameter is a file parameter that's - passed to helm template during manifest generation - properties: - name: - description: Name is the name of the Helm parameter - type: string - path: - description: Path is the path to the file containing - the values for the Helm parameter - type: string - type: object - type: array - ignoreMissingValueFiles: - description: IgnoreMissingValueFiles prevents helm template - from failing when valueFiles do not exist locally by not - appending them to helm template --values - type: boolean - parameters: - description: Parameters is a list of Helm parameters which - are passed to the helm template command upon manifest generation - items: - description: HelmParameter is a parameter that's passed - to helm template during manifest generation - properties: - forceString: - description: ForceString determines whether to tell - Helm to interpret booleans and numbers as strings - type: boolean - name: - description: Name is the name of the Helm parameter - type: string - value: - description: Value is the value for the Helm parameter - type: string - type: object - type: array - passCredentials: - description: PassCredentials pass credentials to all domains - (Helm's --pass-credentials) - type: boolean - releaseName: - description: ReleaseName is the Helm release name to use. - If omitted it will use the application name - type: string - skipCrds: - description: SkipCrds skips custom resource definition installation - step (Helm's --skip-crds) - type: boolean - valueFiles: - description: ValuesFiles is a list of Helm value files to - use when generating a template - items: - type: string - type: array - values: - description: Values specifies Helm values to be passed to - helm template, typically defined as a block - type: string - version: - description: Version is the Helm version to use for templating - (either "2" or "3") - type: string - type: object - ksonnet: - description: Ksonnet holds ksonnet specific options - properties: - environment: - description: Environment is a ksonnet application environment - name - type: string - parameters: - description: Parameters are a list of ksonnet component parameter - override values - items: - description: KsonnetParameter is a ksonnet component parameter - properties: - component: - type: string - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - kustomize: - description: Kustomize holds kustomize specific options - properties: - commonAnnotations: - additionalProperties: - type: string - description: CommonAnnotations is a list of additional annotations - to add to rendered manifests - type: object - commonLabels: - additionalProperties: - type: string - description: CommonLabels is a list of additional labels to - add to rendered manifests - type: object - forceCommonAnnotations: - description: ForceCommonAnnotations specifies whether to force - applying common annotations to resources for Kustomize apps - type: boolean - forceCommonLabels: - description: ForceCommonLabels specifies whether to force - applying common labels to resources for Kustomize apps - type: boolean - images: - description: Images is a list of Kustomize image override - specifications - items: - description: KustomizeImage represents a Kustomize image - definition in the format [old_image_name=]: - type: string - type: array - namePrefix: - description: NamePrefix is a prefix appended to resources - for Kustomize apps - type: string - nameSuffix: - description: NameSuffix is a suffix appended to resources - for Kustomize apps - type: string - version: - description: Version controls which version of Kustomize to - use for rendering manifests - type: string - type: object - path: - description: Path is a directory path within the Git repository, - and is only valid for applications sourced from Git. - type: string - plugin: - description: ConfigManagementPlugin holds config management plugin - specific options - properties: - env: - description: Env is a list of environment variable entries - items: - description: EnvEntry represents an entry in the application's - environment - properties: - name: - description: Name is the name of the variable, usually - expressed in uppercase - type: string - value: - description: Value is the value of the variable - type: string - required: - - name - - value - type: object - type: array - name: - type: string - type: object - repoURL: - description: RepoURL is the URL to the repository (Git or Helm) - that contains the application manifests - type: string - targetRevision: - description: TargetRevision defines the revision of the source - to sync the application to. In case of Git, this can be commit, - tag, or branch. If omitted, will equal to HEAD. In case of Helm, - this is a semver tag for the Chart's version. - type: string - required: - - repoURL - type: object - syncPolicy: - description: SyncPolicy controls when and how a sync will be performed - properties: - automated: - description: Automated will keep an application synced to the - target revision - properties: - allowEmpty: - description: 'AllowEmpty allows apps have zero live resources - (default: false)' - type: boolean - prune: - description: 'Prune specifies whether to delete resources - from the cluster that are not found in the sources anymore - as part of automated sync (default: false)' - type: boolean - selfHeal: - description: 'SelfHeal specifes whether to revert resources - back to their desired state upon modification in the cluster - (default: false)' - type: boolean - type: object - retry: - description: Retry controls failed sync retry behavior - properties: - backoff: - description: Backoff controls how to backoff on subsequent - retries of failed syncs - properties: - duration: - description: Duration is the amount to back off. Default - unit is seconds, but could also be a duration (e.g. - "2m", "1h") - type: string - factor: - description: Factor is a factor to multiply the base duration - after each failed retry - format: int64 - type: integer - maxDuration: - description: MaxDuration is the maximum amount of time - allowed for the backoff strategy - type: string - type: object - limit: - description: Limit is the maximum number of attempts for retrying - a failed sync. If set to 0, no retries will be performed. - format: int64 - type: integer - type: object - syncOptions: - description: Options allow you to specify whole app sync-options - items: - type: string - type: array - type: object - required: - - destination - - project - - source - type: object - status: - description: ApplicationStatus contains status information for the application - properties: - conditions: - description: Conditions is a list of currently observed application - conditions - items: - description: ApplicationCondition contains details about an application - condition, which is usally an error or warning - properties: - lastTransitionTime: - description: LastTransitionTime is the time the condition was - last observed - format: date-time - type: string - message: - description: Message contains human-readable message indicating - details about condition - type: string - type: - description: Type is an application condition type - type: string - required: - - message - - type - type: object - type: array - health: - description: Health contains information about the application's current - health status - properties: - message: - description: Message is a human-readable informational message - describing the health status - type: string - status: - description: Status holds the status code of the application or - resource - type: string - type: object - history: - description: History contains information about the application's - sync history - items: - description: RevisionHistory contains history information about - a previous sync - properties: - deployStartedAt: - description: DeployStartedAt holds the time the sync operation - started - format: date-time - type: string - deployedAt: - description: DeployedAt holds the time the sync operation completed - format: date-time - type: string - id: - description: ID is an auto incrementing identifier of the RevisionHistory - format: int64 - type: integer - revision: - description: Revision holds the revision the sync was performed - against - type: string - source: - description: Source is a reference to the application source - used for the sync operation - properties: - chart: - description: Chart is a Helm chart name, and must be specified - for applications sourced from a Helm repo. - type: string - directory: - description: Directory holds path/directory specific options - properties: - exclude: - description: Exclude contains a glob pattern to match - paths against that should be explicitly excluded from - being used during manifest generation - type: string - include: - description: Include contains a glob pattern to match - paths against that should be explicitly included during - manifest generation - type: string - jsonnet: - description: Jsonnet holds options specific to Jsonnet - properties: - extVars: - description: ExtVars is a list of Jsonnet External - Variables - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - description: Additional library search dirs - items: - type: string - type: array - tlas: - description: TLAS is a list of Jsonnet Top-level - Arguments - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - description: Recurse specifies whether to scan a directory - recursively for manifests - type: boolean - type: object - helm: - description: Helm holds helm specific options - properties: - fileParameters: - description: FileParameters are file parameters to the - helm template - items: - description: HelmFileParameter is a file parameter - that's passed to helm template during manifest generation - properties: - name: - description: Name is the name of the Helm parameter - type: string - path: - description: Path is the path to the file containing - the values for the Helm parameter - type: string - type: object - type: array - ignoreMissingValueFiles: - description: IgnoreMissingValueFiles prevents helm template - from failing when valueFiles do not exist locally - by not appending them to helm template --values - type: boolean - parameters: - description: Parameters is a list of Helm parameters - which are passed to the helm template command upon - manifest generation - items: - description: HelmParameter is a parameter that's passed - to helm template during manifest generation - properties: - forceString: - description: ForceString determines whether to - tell Helm to interpret booleans and numbers - as strings - type: boolean - name: - description: Name is the name of the Helm parameter - type: string - value: - description: Value is the value for the Helm parameter - type: string - type: object - type: array - passCredentials: - description: PassCredentials pass credentials to all - domains (Helm's --pass-credentials) - type: boolean - releaseName: - description: ReleaseName is the Helm release name to - use. If omitted it will use the application name - type: string - skipCrds: - description: SkipCrds skips custom resource definition - installation step (Helm's --skip-crds) - type: boolean - valueFiles: - description: ValuesFiles is a list of Helm value files - to use when generating a template - items: - type: string - type: array - values: - description: Values specifies Helm values to be passed - to helm template, typically defined as a block - type: string - version: - description: Version is the Helm version to use for - templating (either "2" or "3") - type: string - type: object - ksonnet: - description: Ksonnet holds ksonnet specific options - properties: - environment: - description: Environment is a ksonnet application environment - name - type: string - parameters: - description: Parameters are a list of ksonnet component - parameter override values - items: - description: KsonnetParameter is a ksonnet component - parameter - properties: - component: - type: string - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - kustomize: - description: Kustomize holds kustomize specific options - properties: - commonAnnotations: - additionalProperties: - type: string - description: CommonAnnotations is a list of additional - annotations to add to rendered manifests - type: object - commonLabels: - additionalProperties: - type: string - description: CommonLabels is a list of additional labels - to add to rendered manifests - type: object - forceCommonAnnotations: - description: ForceCommonAnnotations specifies whether - to force applying common annotations to resources - for Kustomize apps - type: boolean - forceCommonLabels: - description: ForceCommonLabels specifies whether to - force applying common labels to resources for Kustomize - apps - type: boolean - images: - description: Images is a list of Kustomize image override - specifications - items: - description: KustomizeImage represents a Kustomize - image definition in the format [old_image_name=]: - type: string - type: array - namePrefix: - description: NamePrefix is a prefix appended to resources - for Kustomize apps - type: string - nameSuffix: - description: NameSuffix is a suffix appended to resources - for Kustomize apps - type: string - version: - description: Version controls which version of Kustomize - to use for rendering manifests - type: string - type: object - path: - description: Path is a directory path within the Git repository, - and is only valid for applications sourced from Git. - type: string - plugin: - description: ConfigManagementPlugin holds config management - plugin specific options - properties: - env: - description: Env is a list of environment variable entries - items: - description: EnvEntry represents an entry in the application's - environment - properties: - name: - description: Name is the name of the variable, - usually expressed in uppercase - type: string - value: - description: Value is the value of the variable - type: string - required: - - name - - value - type: object - type: array - name: - type: string - type: object - repoURL: - description: RepoURL is the URL to the repository (Git or - Helm) that contains the application manifests - type: string - targetRevision: - description: TargetRevision defines the revision of the - source to sync the application to. In case of Git, this - can be commit, tag, or branch. If omitted, will equal - to HEAD. In case of Helm, this is a semver tag for the - Chart's version. - type: string - required: - - repoURL - type: object - required: - - deployedAt - - id - - revision - type: object - type: array - observedAt: - description: 'ObservedAt indicates when the application state was - updated without querying latest git state Deprecated: controller - no longer updates ObservedAt field' - format: date-time - type: string - operationState: - description: OperationState contains information about any ongoing - operations, such as a sync - properties: - finishedAt: - description: FinishedAt contains time of operation completion - format: date-time - type: string - message: - description: Message holds any pertinent messages when attempting - to perform operation (typically errors). - type: string - operation: - description: Operation is the original requested operation - properties: - info: - description: Info is a list of informational items for this - operation - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - initiatedBy: - description: InitiatedBy contains information about who initiated - the operations - properties: - automated: - description: Automated is set to true if operation was - initiated automatically by the application controller. - type: boolean - username: - description: Username contains the name of a user who - started operation - type: string - type: object - retry: - description: Retry controls the strategy to apply if a sync - fails - properties: - backoff: - description: Backoff controls how to backoff on subsequent - retries of failed syncs - properties: - duration: - description: Duration is the amount to back off. Default - unit is seconds, but could also be a duration (e.g. - "2m", "1h") - type: string - factor: - description: Factor is a factor to multiply the base - duration after each failed retry - format: int64 - type: integer - maxDuration: - description: MaxDuration is the maximum amount of - time allowed for the backoff strategy - type: string - type: object - limit: - description: Limit is the maximum number of attempts for - retrying a failed sync. If set to 0, no retries will - be performed. - format: int64 - type: integer - type: object - sync: - description: Sync contains parameters for the operation - properties: - dryRun: - description: DryRun specifies to perform a `kubectl apply - --dry-run` without actually performing the sync - type: boolean - manifests: - description: Manifests is an optional field that overrides - sync source with a local directory for development - items: - type: string - type: array - prune: - description: Prune specifies to delete resources from - the cluster that are no longer tracked in git - type: boolean - resources: - description: Resources describes which resources shall - be part of the sync - items: - description: SyncOperationResource contains resources - to sync. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - type: array - revision: - description: Revision is the revision (Git) or chart version - (Helm) which to sync the application to If omitted, - will use the revision specified in app spec. - type: string - source: - description: Source overrides the source definition set - in the application. This is typically set in a Rollback - operation and is nil during a Sync operation - properties: - chart: - description: Chart is a Helm chart name, and must - be specified for applications sourced from a Helm - repo. - type: string - directory: - description: Directory holds path/directory specific - options - properties: - exclude: - description: Exclude contains a glob pattern to - match paths against that should be explicitly - excluded from being used during manifest generation - type: string - include: - description: Include contains a glob pattern to - match paths against that should be explicitly - included during manifest generation - type: string - jsonnet: - description: Jsonnet holds options specific to - Jsonnet - properties: - extVars: - description: ExtVars is a list of Jsonnet - External Variables - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest - generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - description: Additional library search dirs - items: - type: string - type: array - tlas: - description: TLAS is a list of Jsonnet Top-level - Arguments - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest - generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - description: Recurse specifies whether to scan - a directory recursively for manifests - type: boolean - type: object - helm: - description: Helm holds helm specific options - properties: - fileParameters: - description: FileParameters are file parameters - to the helm template - items: - description: HelmFileParameter is a file parameter - that's passed to helm template during manifest - generation - properties: - name: - description: Name is the name of the Helm - parameter - type: string - path: - description: Path is the path to the file - containing the values for the Helm parameter - type: string - type: object - type: array - ignoreMissingValueFiles: - description: IgnoreMissingValueFiles prevents - helm template from failing when valueFiles do - not exist locally by not appending them to helm - template --values - type: boolean - parameters: - description: Parameters is a list of Helm parameters - which are passed to the helm template command - upon manifest generation - items: - description: HelmParameter is a parameter that's - passed to helm template during manifest generation - properties: - forceString: - description: ForceString determines whether - to tell Helm to interpret booleans and - numbers as strings - type: boolean - name: - description: Name is the name of the Helm - parameter - type: string - value: - description: Value is the value for the - Helm parameter - type: string - type: object - type: array - passCredentials: - description: PassCredentials pass credentials - to all domains (Helm's --pass-credentials) - type: boolean - releaseName: - description: ReleaseName is the Helm release name - to use. If omitted it will use the application - name - type: string - skipCrds: - description: SkipCrds skips custom resource definition - installation step (Helm's --skip-crds) - type: boolean - valueFiles: - description: ValuesFiles is a list of Helm value - files to use when generating a template - items: - type: string - type: array - values: - description: Values specifies Helm values to be - passed to helm template, typically defined as - a block - type: string - version: - description: Version is the Helm version to use - for templating (either "2" or "3") - type: string - type: object - ksonnet: - description: Ksonnet holds ksonnet specific options - properties: - environment: - description: Environment is a ksonnet application - environment name - type: string - parameters: - description: Parameters are a list of ksonnet - component parameter override values - items: - description: KsonnetParameter is a ksonnet component - parameter - properties: - component: - type: string - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - kustomize: - description: Kustomize holds kustomize specific options - properties: - commonAnnotations: - additionalProperties: - type: string - description: CommonAnnotations is a list of additional - annotations to add to rendered manifests - type: object - commonLabels: - additionalProperties: - type: string - description: CommonLabels is a list of additional - labels to add to rendered manifests - type: object - forceCommonAnnotations: - description: ForceCommonAnnotations specifies - whether to force applying common annotations - to resources for Kustomize apps - type: boolean - forceCommonLabels: - description: ForceCommonLabels specifies whether - to force applying common labels to resources - for Kustomize apps - type: boolean - images: - description: Images is a list of Kustomize image - override specifications - items: - description: KustomizeImage represents a Kustomize - image definition in the format [old_image_name=]: - type: string - type: array - namePrefix: - description: NamePrefix is a prefix appended to - resources for Kustomize apps - type: string - nameSuffix: - description: NameSuffix is a suffix appended to - resources for Kustomize apps - type: string - version: - description: Version controls which version of - Kustomize to use for rendering manifests - type: string - type: object - path: - description: Path is a directory path within the Git - repository, and is only valid for applications sourced - from Git. - type: string - plugin: - description: ConfigManagementPlugin holds config management - plugin specific options - properties: - env: - description: Env is a list of environment variable - entries - items: - description: EnvEntry represents an entry in - the application's environment - properties: - name: - description: Name is the name of the variable, - usually expressed in uppercase - type: string - value: - description: Value is the value of the variable - type: string - required: - - name - - value - type: object - type: array - name: - type: string - type: object - repoURL: - description: RepoURL is the URL to the repository - (Git or Helm) that contains the application manifests - type: string - targetRevision: - description: TargetRevision defines the revision of - the source to sync the application to. In case of - Git, this can be commit, tag, or branch. If omitted, - will equal to HEAD. In case of Helm, this is a semver - tag for the Chart's version. - type: string - required: - - repoURL - type: object - syncOptions: - description: SyncOptions provide per-sync sync-options, - e.g. Validate=false - items: - type: string - type: array - syncStrategy: - description: SyncStrategy describes how to perform the - sync - properties: - apply: - description: Apply will perform a `kubectl apply` - to perform the sync. - properties: - force: - description: Force indicates whether or not to - supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, - when PATCH encounters conflict and has retried - for 5 times. - type: boolean - type: object - hook: - description: Hook will submit any referenced resources - to perform the sync. This is the default strategy - properties: - force: - description: Force indicates whether or not to - supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, - when PATCH encounters conflict and has retried - for 5 times. - type: boolean - type: object - type: object - type: object - type: object - phase: - description: Phase is the current phase of the operation - type: string - retryCount: - description: RetryCount contains time of operation retries - format: int64 - type: integer - startedAt: - description: StartedAt contains time of operation start - format: date-time - type: string - syncResult: - description: SyncResult is the result of a Sync operation - properties: - resources: - description: Resources contains a list of sync result items - for each individual resource in a sync operation - items: - description: ResourceResult holds the operation result details - of a specific resource - properties: - group: - description: Group specifies the API group of the resource - type: string - hookPhase: - description: HookPhase contains the state of any operation - associated with this resource OR hook This can also - contain values for non-hook resources. - type: string - hookType: - description: HookType specifies the type of the hook. - Empty for non-hook resources - type: string - kind: - description: Kind specifies the API kind of the resource - type: string - message: - description: Message contains an informational or error - message for the last sync OR operation - type: string - name: - description: Name specifies the name of the resource - type: string - namespace: - description: Namespace specifies the target namespace - of the resource - type: string - status: - description: Status holds the final result of the sync. - Will be empty if the resources is yet to be applied/pruned - and is always zero-value for hooks - type: string - syncPhase: - description: SyncPhase indicates the particular phase - of the sync that this result was acquired in - type: string - version: - description: Version specifies the API version of the - resource - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - type: array - revision: - description: Revision holds the revision this sync operation - was performed to - type: string - source: - description: Source records the application source information - of the sync, used for comparing auto-sync - properties: - chart: - description: Chart is a Helm chart name, and must be specified - for applications sourced from a Helm repo. - type: string - directory: - description: Directory holds path/directory specific options - properties: - exclude: - description: Exclude contains a glob pattern to match - paths against that should be explicitly excluded - from being used during manifest generation - type: string - include: - description: Include contains a glob pattern to match - paths against that should be explicitly included - during manifest generation - type: string - jsonnet: - description: Jsonnet holds options specific to Jsonnet - properties: - extVars: - description: ExtVars is a list of Jsonnet External - Variables - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - description: Additional library search dirs - items: - type: string - type: array - tlas: - description: TLAS is a list of Jsonnet Top-level - Arguments - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - description: Recurse specifies whether to scan a directory - recursively for manifests - type: boolean - type: object - helm: - description: Helm holds helm specific options - properties: - fileParameters: - description: FileParameters are file parameters to - the helm template - items: - description: HelmFileParameter is a file parameter - that's passed to helm template during manifest - generation - properties: - name: - description: Name is the name of the Helm parameter - type: string - path: - description: Path is the path to the file containing - the values for the Helm parameter - type: string - type: object - type: array - ignoreMissingValueFiles: - description: IgnoreMissingValueFiles prevents helm - template from failing when valueFiles do not exist - locally by not appending them to helm template --values - type: boolean - parameters: - description: Parameters is a list of Helm parameters - which are passed to the helm template command upon - manifest generation - items: - description: HelmParameter is a parameter that's - passed to helm template during manifest generation - properties: - forceString: - description: ForceString determines whether - to tell Helm to interpret booleans and numbers - as strings - type: boolean - name: - description: Name is the name of the Helm parameter - type: string - value: - description: Value is the value for the Helm - parameter - type: string - type: object - type: array - passCredentials: - description: PassCredentials pass credentials to all - domains (Helm's --pass-credentials) - type: boolean - releaseName: - description: ReleaseName is the Helm release name - to use. If omitted it will use the application name - type: string - skipCrds: - description: SkipCrds skips custom resource definition - installation step (Helm's --skip-crds) - type: boolean - valueFiles: - description: ValuesFiles is a list of Helm value files - to use when generating a template - items: - type: string - type: array - values: - description: Values specifies Helm values to be passed - to helm template, typically defined as a block - type: string - version: - description: Version is the Helm version to use for - templating (either "2" or "3") - type: string - type: object - ksonnet: - description: Ksonnet holds ksonnet specific options - properties: - environment: - description: Environment is a ksonnet application - environment name - type: string - parameters: - description: Parameters are a list of ksonnet component - parameter override values - items: - description: KsonnetParameter is a ksonnet component - parameter - properties: - component: - type: string - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - kustomize: - description: Kustomize holds kustomize specific options - properties: - commonAnnotations: - additionalProperties: - type: string - description: CommonAnnotations is a list of additional - annotations to add to rendered manifests - type: object - commonLabels: - additionalProperties: - type: string - description: CommonLabels is a list of additional - labels to add to rendered manifests - type: object - forceCommonAnnotations: - description: ForceCommonAnnotations specifies whether - to force applying common annotations to resources - for Kustomize apps - type: boolean - forceCommonLabels: - description: ForceCommonLabels specifies whether to - force applying common labels to resources for Kustomize - apps - type: boolean - images: - description: Images is a list of Kustomize image override - specifications - items: - description: KustomizeImage represents a Kustomize - image definition in the format [old_image_name=]: - type: string - type: array - namePrefix: - description: NamePrefix is a prefix appended to resources - for Kustomize apps - type: string - nameSuffix: - description: NameSuffix is a suffix appended to resources - for Kustomize apps - type: string - version: - description: Version controls which version of Kustomize - to use for rendering manifests - type: string - type: object - path: - description: Path is a directory path within the Git repository, - and is only valid for applications sourced from Git. - type: string - plugin: - description: ConfigManagementPlugin holds config management - plugin specific options - properties: - env: - description: Env is a list of environment variable - entries - items: - description: EnvEntry represents an entry in the - application's environment - properties: - name: - description: Name is the name of the variable, - usually expressed in uppercase - type: string - value: - description: Value is the value of the variable - type: string - required: - - name - - value - type: object - type: array - name: - type: string - type: object - repoURL: - description: RepoURL is the URL to the repository (Git - or Helm) that contains the application manifests - type: string - targetRevision: - description: TargetRevision defines the revision of the - source to sync the application to. In case of Git, this - can be commit, tag, or branch. If omitted, will equal - to HEAD. In case of Helm, this is a semver tag for the - Chart's version. - type: string - required: - - repoURL - type: object - required: - - revision - type: object - required: - - operation - - phase - - startedAt - type: object - reconciledAt: - description: ReconciledAt indicates when the application state was - reconciled using the latest git version - format: date-time - type: string - resources: - description: Resources is a list of Kubernetes resources managed by - this application - items: - description: 'ResourceStatus holds the current sync and health status - of a resource TODO: describe members of this type' - properties: - group: - type: string - health: - description: HealthStatus contains information about the currently - observed health state of an application or resource - properties: - message: - description: Message is a human-readable informational message - describing the health status - type: string - status: - description: Status holds the status code of the application - or resource - type: string - type: object - hook: - type: boolean - kind: - type: string - name: - type: string - namespace: - type: string - requiresPruning: - type: boolean - status: - description: SyncStatusCode is a type which represents possible - comparison results - type: string - version: - type: string - type: object - type: array - sourceType: - description: SourceType specifies the type of this application - type: string - summary: - description: Summary contains a list of URLs and container images - used by this application - properties: - externalURLs: - description: ExternalURLs holds all external URLs of application - child resources. - items: - type: string - type: array - images: - description: Images holds all images of application child resources. - items: - type: string - type: array - type: object - sync: - description: Sync contains information about the application's current - sync status - properties: - comparedTo: - description: ComparedTo contains information about what has been - compared - properties: - destination: - description: Destination is a reference to the application's - destination used for comparison - properties: - name: - description: Name is an alternate way of specifying the - target cluster by its symbolic name - type: string - namespace: - description: Namespace specifies the target namespace - for the application's resources. The namespace will - only be set for namespace-scoped resources that have - not set a value for .metadata.namespace - type: string - server: - description: Server specifies the URL of the target cluster - and must be set to the Kubernetes control plane API - type: string - type: object - source: - description: Source is a reference to the application's source - used for comparison - properties: - chart: - description: Chart is a Helm chart name, and must be specified - for applications sourced from a Helm repo. - type: string - directory: - description: Directory holds path/directory specific options - properties: - exclude: - description: Exclude contains a glob pattern to match - paths against that should be explicitly excluded - from being used during manifest generation - type: string - include: - description: Include contains a glob pattern to match - paths against that should be explicitly included - during manifest generation - type: string - jsonnet: - description: Jsonnet holds options specific to Jsonnet - properties: - extVars: - description: ExtVars is a list of Jsonnet External - Variables - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - description: Additional library search dirs - items: - type: string - type: array - tlas: - description: TLAS is a list of Jsonnet Top-level - Arguments - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - description: Recurse specifies whether to scan a directory - recursively for manifests - type: boolean - type: object - helm: - description: Helm holds helm specific options - properties: - fileParameters: - description: FileParameters are file parameters to - the helm template - items: - description: HelmFileParameter is a file parameter - that's passed to helm template during manifest - generation - properties: - name: - description: Name is the name of the Helm parameter - type: string - path: - description: Path is the path to the file containing - the values for the Helm parameter - type: string - type: object - type: array - ignoreMissingValueFiles: - description: IgnoreMissingValueFiles prevents helm - template from failing when valueFiles do not exist - locally by not appending them to helm template --values - type: boolean - parameters: - description: Parameters is a list of Helm parameters - which are passed to the helm template command upon - manifest generation - items: - description: HelmParameter is a parameter that's - passed to helm template during manifest generation - properties: - forceString: - description: ForceString determines whether - to tell Helm to interpret booleans and numbers - as strings - type: boolean - name: - description: Name is the name of the Helm parameter - type: string - value: - description: Value is the value for the Helm - parameter - type: string - type: object - type: array - passCredentials: - description: PassCredentials pass credentials to all - domains (Helm's --pass-credentials) - type: boolean - releaseName: - description: ReleaseName is the Helm release name - to use. If omitted it will use the application name - type: string - skipCrds: - description: SkipCrds skips custom resource definition - installation step (Helm's --skip-crds) - type: boolean - valueFiles: - description: ValuesFiles is a list of Helm value files - to use when generating a template - items: - type: string - type: array - values: - description: Values specifies Helm values to be passed - to helm template, typically defined as a block - type: string - version: - description: Version is the Helm version to use for - templating (either "2" or "3") - type: string - type: object - ksonnet: - description: Ksonnet holds ksonnet specific options - properties: - environment: - description: Environment is a ksonnet application - environment name - type: string - parameters: - description: Parameters are a list of ksonnet component - parameter override values - items: - description: KsonnetParameter is a ksonnet component - parameter - properties: - component: - type: string - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - kustomize: - description: Kustomize holds kustomize specific options - properties: - commonAnnotations: - additionalProperties: - type: string - description: CommonAnnotations is a list of additional - annotations to add to rendered manifests - type: object - commonLabels: - additionalProperties: - type: string - description: CommonLabels is a list of additional - labels to add to rendered manifests - type: object - forceCommonAnnotations: - description: ForceCommonAnnotations specifies whether - to force applying common annotations to resources - for Kustomize apps - type: boolean - forceCommonLabels: - description: ForceCommonLabels specifies whether to - force applying common labels to resources for Kustomize - apps - type: boolean - images: - description: Images is a list of Kustomize image override - specifications - items: - description: KustomizeImage represents a Kustomize - image definition in the format [old_image_name=]: - type: string - type: array - namePrefix: - description: NamePrefix is a prefix appended to resources - for Kustomize apps - type: string - nameSuffix: - description: NameSuffix is a suffix appended to resources - for Kustomize apps - type: string - version: - description: Version controls which version of Kustomize - to use for rendering manifests - type: string - type: object - path: - description: Path is a directory path within the Git repository, - and is only valid for applications sourced from Git. - type: string - plugin: - description: ConfigManagementPlugin holds config management - plugin specific options - properties: - env: - description: Env is a list of environment variable - entries - items: - description: EnvEntry represents an entry in the - application's environment - properties: - name: - description: Name is the name of the variable, - usually expressed in uppercase - type: string - value: - description: Value is the value of the variable - type: string - required: - - name - - value - type: object - type: array - name: - type: string - type: object - repoURL: - description: RepoURL is the URL to the repository (Git - or Helm) that contains the application manifests - type: string - targetRevision: - description: TargetRevision defines the revision of the - source to sync the application to. In case of Git, this - can be commit, tag, or branch. If omitted, will equal - to HEAD. In case of Helm, this is a semver tag for the - Chart's version. - type: string - required: - - repoURL - type: object - required: - - destination - - source - type: object - revision: - description: Revision contains information about the revision - the comparison has been performed to - type: string - status: - description: Status is the sync state of the comparison - type: string - required: - - status - type: object - type: object - required: - - metadata - - spec - type: object - served: true - storage: true - subresources: {} diff --git a/install/templates/argocd/application.yaml b/install/templates/argocd/application.yaml deleted file mode 100644 index f1a6a84d..00000000 --- a/install/templates/argocd/application.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: {{ .Release.Name }}-{{ .Values.main.clusterGroupName }} - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: in-cluster - namespace: {{ .Release.Name }}-{{ .Values.main.clusterGroupName }} - project: default - source: - repoURL: {{ .Values.main.git.repoURL }} - targetRevision: {{ .Values.main.git.revision }} - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-{{ .Values.main.clusterGroupName }}.yaml" - {{- if .Values.global.clusterVersion }} - - "/values-{{ .Values.global.clusterVersion }}-{{ .Values.main.clusterGroupName }}.yaml" - {{- end }} - # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: {{ .Release.Name }} - - name: global.hubClusterDomain - value: {{ .Values.global.hubClusterDomain }} - - name: global.clusterVersion - value: "{{ .Values.global.clusterVersion }}" -{{- if eq .Values.main.options.syncPolicy "Automatic" }} - syncPolicy: - automated: {} -{{- end }} diff --git a/install/templates/argocd/namespace.yaml b/install/templates/argocd/namespace.yaml deleted file mode 100644 index 9e9fafb2..00000000 --- a/install/templates/argocd/namespace.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# Pre-create so we can create our argo app for keeping subscriptions in sync -# Do it here so that we don't try to sync it in the future -apiVersion: v1 -kind: Namespace -metadata: - name: openshift-gitops diff --git a/install/templates/argocd/subscription.yaml b/install/templates/argocd/subscription.yaml deleted file mode 100644 index 22837b9a..00000000 --- a/install/templates/argocd/subscription.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: openshift-gitops-operator - namespace: openshift-operators - labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" -spec: - channel: {{ .Values.main.gitops.channel }} - installPlanApproval: {{ .Values.main.options.installPlanApproval }} - name: openshift-gitops-operator - source: redhat-operators - sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: {{ .Release.Name }}-{{ .Values.main.clusterGroupName }},openshift-gitops -{{- if .Values.main.options.useCSV }} - startingCSV: openshift-gitops-operator.{{ .Values.main.gitops.csv }} -{{- end }} diff --git a/install/values.yaml b/install/values.yaml deleted file mode 100644 index 370e6b15..00000000 --- a/install/values.yaml +++ /dev/null @@ -1,25 +0,0 @@ -main: - git: - repoURL: https://github.com/pattern-clone/mypattern - revision: main - - options: - syncPolicy: Automatic - installPlanApproval: Automatic - useCSV: False - - gitops: - channel: stable - source: redhat-operators - csv: v1.3.0 - - clusterGroupName: default - -global: - imageregistry: - type: quay - - git: - hostname: github.com - # Account is the user or organization under which the pattern repo lives - account: hybrid-cloud-patterns diff --git a/tests/install-naked.expected.yaml b/tests/install-naked.expected.yaml deleted file mode 100644 index 4bd38238..00000000 --- a/tests/install-naked.expected.yaml +++ /dev/null @@ -1,66 +0,0 @@ ---- -# Source: pattern-install/templates/argocd/namespace.yaml -# Pre-create so we can create our argo app for keeping subscriptions in sync -# Do it here so that we don't try to sync it in the future -apiVersion: v1 -kind: Namespace -metadata: - name: openshift-gitops ---- -# Source: pattern-install/templates/argocd/application.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: install-default - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: in-cluster - namespace: install-default - project: default - source: - repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-default.yaml" - # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: install - - name: global.hubClusterDomain - value: - - name: global.clusterVersion - value: "" - syncPolicy: - automated: {} ---- -# Source: pattern-install/templates/argocd/subscription.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: openshift-gitops-operator - namespace: openshift-operators - labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" -spec: - channel: stable - installPlanApproval: Automatic - name: openshift-gitops-operator - source: redhat-operators - sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: install-default,openshift-gitops diff --git a/tests/install-normal.expected.yaml b/tests/install-normal.expected.yaml deleted file mode 100644 index 0dfd0d84..00000000 --- a/tests/install-normal.expected.yaml +++ /dev/null @@ -1,66 +0,0 @@ ---- -# Source: pattern-install/templates/argocd/namespace.yaml -# Pre-create so we can create our argo app for keeping subscriptions in sync -# Do it here so that we don't try to sync it in the future -apiVersion: v1 -kind: Namespace -metadata: - name: openshift-gitops ---- -# Source: pattern-install/templates/argocd/application.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: install-example - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: in-cluster - namespace: install-example - project: default - source: - repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-example.yaml" - # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: install - - name: global.hubClusterDomain - value: apps.hub.example.com - - name: global.clusterVersion - value: "" - syncPolicy: - automated: {} ---- -# Source: pattern-install/templates/argocd/subscription.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: openshift-gitops-operator - namespace: openshift-operators - labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" -spec: - channel: stable - installPlanApproval: Automatic - name: openshift-gitops-operator - source: redhat-operators - sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: install-example,openshift-gitops diff --git a/tests/install.expected.diff b/tests/install.expected.diff deleted file mode 100644 index 605728b4..00000000 --- a/tests/install.expected.diff +++ /dev/null @@ -1,43 +0,0 @@ ---- tests/install-naked.expected.yaml -+++ tests/install-normal.expected.yaml -@@ -11,14 +11,14 @@ - apiVersion: argoproj.io/v1alpha1 - kind: Application - metadata: -- name: install-default -+ name: install-example - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground - spec: - destination: - name: in-cluster -- namespace: install-default -+ namespace: install-example - project: default - source: - repoURL: https://github.com/pattern-clone/mypattern -@@ -28,7 +28,7 @@ - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" -- - "/values-default.yaml" -+ - "/values-example.yaml" - # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 - parameters: - - name: global.repoURL -@@ -40,7 +40,7 @@ - - name: global.pattern - value: install - - name: global.hubClusterDomain -- value: -+ value: apps.hub.example.com - - name: global.clusterVersion - value: "" - syncPolicy: -@@ -63,4 +63,4 @@ - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES -- value: install-default,openshift-gitops -+ value: install-example,openshift-gitops From 423872b7bad40772e2febad8328fa7b21a0eee31 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 6 Nov 2022 19:10:48 +0100 Subject: [PATCH 0719/1263] Move comments in the right place --- Changes.md | 6 ++++++ Makefile | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Changes.md b/Changes.md index 177f3c45..d6fe8276 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,11 @@ # Changes +## December 8, 2022 + +* Removed the legacy installation targets: + `deploy upgrade legacy-deploy legacy-upgrade` + Patterns must now use the operator-based installation + ## November 29, 2022 * Upgraded vault-helm to 0.23.0 diff --git a/Makefile b/Makefile index 4a902c6f..c0859ba6 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ NAME=$(shell basename "`pwd`") -# This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL -# This is because we expect to use tokens for repo authentication as opposed to SSH keys ifneq ($(origin TARGET_SITE), undefined) TARGET_SITE_OPT=--set main.clusterGroupName=$(TARGET_SITE) endif TARGET_ORIGIN ?= origin +# This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL +# This is because we expect to use tokens for repo authentication as opposed to SSH keys TARGET_REPO=$(shell git ls-remote --get-url --symref $(TARGET_ORIGIN) | sed -e 's/.*URL:[[:space:]]*//' -e 's%^git@%%' -e 's%^https://%%' -e 's%:%/%' -e 's%^%https://%') # git branch --show-current is also available as of git 2.22, but we will use this for compatibility TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) From 382697840bb9092bc5637fa32bd6d8b44bd7b03a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 6 Nov 2022 19:14:31 +0100 Subject: [PATCH 0720/1263] Clean up PHONY targets in Makefile Let's follow http://clarkgrubb.com/makefile-style-guide#phony-targets that way we're consistent with all target and spurious file named like the target won't trip up things. --- Makefile | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c0859ba6..29f3ad19 100644 --- a/Makefile +++ b/Makefile @@ -26,11 +26,13 @@ help: ## This help message # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show +.PHONY: show show: ## show the starting template without installing it helm template common/operator-install/ --name-template $(NAME) $(HELM_OPTS) # We only check the remote ssh git branch's existance if we're not running inside a container # as getting ssh auth working inside a container seems a bit brittle +.PHONY: validate-origin validate-origin: ## verify the git origin is available @echo Checking repo $(TARGET_REPO) - branch $(TARGET_BRANCH) @if [ ! -f /run/.containerenv ]; then\ @@ -41,41 +43,51 @@ validate-origin: ## verify the git origin is available echo "Running inside a container: Skipping git ssh checks";\ fi +.PHONY: operator-deploy operator-upgrade operator-deploy operator-upgrade: validate-origin ## runs helm install helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) +.PHONY: deploy upgrade legacy-deploy legacy-upgrade deploy upgrade legacy-deploy legacy-upgrade: ## does nothing anymore. use operator-deploy @echo "UNSUPPORTED TARGET: please switch to 'operator-deploy'" +.PHONY: uninstall uninstall: ## runs helm uninstall $(eval CSV := $(shell oc get subscriptions -n openshift-operators openshift-gitops-operator -ojsonpath={.status.currentCSV})) helm uninstall $(NAME) @oc delete csv -n openshift-operators $(CSV) +.PHONY: vault-init vault-init: ## inits, unseals and configured the vault common/scripts/vault-utils.sh vault_init common/pattern-vault.init common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init common/scripts/vault-utils.sh vault_secrets_init common/pattern-vault.init +.PHONY: vault-unseal vault-unseal: ## unseals the vault common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init +.PHONY: load-secrets load-secrets: ## loads the secrets into the vault common/scripts/vault-utils.sh push_secrets common/pattern-vault.init $(NAME) CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | grep -v examples | sed -e 's/.\///') +.PHONY: test test: ## run helm tests @for t in $(CHARTS); do common/scripts/test.sh $$t all "$(TEST_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done +.PHONY: helmlint helmlint: ## run helm lint @for t in $(CHARTS); do common/scripts/lint.sh $$t $(TEST_OPTS); if [ $$? != 0 ]; then exit 1; fi; done API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.10/ KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition' # We need to skip 'CustomResourceDefinition' as openapi2jsonschema seems to be unable to generate them ATM +.PHONY: kubeconform kubeconform: ## run helm kubeconform @for t in $(CHARTS); do helm template $(TEST_OPTS) $(PATTERN_OPTS) $$t | kubeconform -strict $(KUBECONFORM_SKIP) -verbose -schema-location $(API_URL); if [ $$? != 0 ]; then exit 1; fi; done +.PHONY: validate-prereq validate-prereq: ## verify pre-requisites @for t in $(EXECUTABLES); do if ! which $$t > /dev/null 2>&1; then echo "No $$t in PATH"; exit 1; fi; done @echo "Prerequisites checked '$(EXECUTABLES)': OK" @@ -85,6 +97,7 @@ validate-prereq: ## verify pre-requisites @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi @echo "OK" +.PHONY: super-linter super-linter: ## Runs super linter locally rm -rf .mypy_cache podman run -e RUN_LOCAL=true -e USE_FIND_ALGORITHM=true \ @@ -98,10 +111,10 @@ super-linter: ## Runs super linter locally $(DISABLE_LINTERS) \ -v $(PWD):/tmp/lint:rw,z docker.io/github/super-linter:slim-v4 +.PHONY: ansible-lint ansible-lint: ## run ansible lint on ansible/ folder podman run -it -v $(PWD):/workspace:rw,z --workdir /workspace --entrypoint "/usr/local/bin/ansible-lint" quay.io/ansible/creator-ee:latest "-vvv" "ansible/" +.PHONY: ansible-unittest ansible-unittest: ## run ansible unit tests pytest -r a --fulltrace --color yes ansible/tests/unit/test_*.py - -.phony: install test From 4ae8fef78983a7c9cc579325f0983f9fff2a2b70 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 6 Nov 2022 19:19:09 +0100 Subject: [PATCH 0721/1263] Move all testing-related variables closer to the tests target Makes the Makefile a bit more readable --- Makefile | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 29f3ad19..98818398 100644 --- a/Makefile +++ b/Makefile @@ -12,14 +12,6 @@ TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) # --set values always take precedence over the contents of -f HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) -TEST_OPTS= -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" \ - --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" \ - --set global.namespace="pattern-namespace" --set global.hubClusterDomain=apps.hub.example.com --set global.localClusterDomain=apps.region.example.com --set global.clusterDomain=region.example.com\ - --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" \ - --set clusterGroup.insecureUnsealVaultInsideCluster=true -PATTERN_OPTS=-f common/examples/values-example.yaml -EXECUTABLES=git helm oc ansible - .PHONY: help help: ## This help message @printf "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)\n" @@ -72,6 +64,15 @@ load-secrets: ## loads the secrets into the vault common/scripts/vault-utils.sh push_secrets common/pattern-vault.init $(NAME) CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | grep -v examples | sed -e 's/.\///') +# Section related to tests and linting +TEST_OPTS= -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" \ + --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" \ + --set global.namespace="pattern-namespace" --set global.hubClusterDomain=apps.hub.example.com --set global.localClusterDomain=apps.region.example.com --set global.clusterDomain=region.example.com\ + --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" \ + --set clusterGroup.insecureUnsealVaultInsideCluster=true +PATTERN_OPTS=-f common/examples/values-example.yaml +EXECUTABLES=git helm oc ansible + .PHONY: test test: ## run helm tests @for t in $(CHARTS); do common/scripts/test.sh $$t all "$(TEST_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done From b78dc9631e7222fb3f72497cbd5db4ed25ec7ccd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 6 Nov 2022 19:54:17 +0100 Subject: [PATCH 0722/1263] Make sure we error out after the legacy removal Otherwise any post target might continue to run which would become confusing for the user. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 98818398..1714cdab 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ operator-deploy operator-upgrade: validate-origin ## runs helm install .PHONY: deploy upgrade legacy-deploy legacy-upgrade deploy upgrade legacy-deploy legacy-upgrade: ## does nothing anymore. use operator-deploy - @echo "UNSUPPORTED TARGET: please switch to 'operator-deploy'" + @echo "UNSUPPORTED TARGET: please switch to 'operator-deploy'"; exit 1 .PHONY: uninstall uninstall: ## runs helm uninstall From 060bb94cabf88463e229d2c27504bea7a221d139 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 8 Dec 2022 14:59:40 +0100 Subject: [PATCH 0723/1263] Fix up the main section a bit --- clustergroup/values.schema.json | 40 ++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index bf419752..79878154 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -55,22 +55,42 @@ }, "Main": { "type": "object", - "additionalProperties": true, + "additionalProperties": false, + "required": [ + "clusterGroupName" + ], + "title": "Main", + "description": "This section contains the 'main' variables which are used by the install chart only and are passed to helm via the Makefile", "properties": { "clusterGroupName": { "type": "string" }, - "hubClusterDomain": { - "type": "string" + "git": { + "type": "object", + "additionalProperties": false, + "required": [ + "repoURL", + "revision" + ], + "properties": { + "repoURL": { + "type": "string" + }, + "revision": { + "type": "string" + } + } }, - "localClusterDomain": { - "type": "string" + "gitops": { + "type": "object", + "additionalProperties": false, + "properties": { + "channel": { + "type": "string" + } + } } - }, - "required": [ - "clusterGroupName" - ], - "title": "Main" + } }, "Global": { "type": "object", From a40234350fc2071e9876dd0b3fee8d03db47f81b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 8 Dec 2022 15:02:10 +0100 Subject: [PATCH 0724/1263] Make subscriptions not mandatory Not all patterns require subscriptions necessarily --- clustergroup/values.schema.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 79878154..cc4e101d 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -265,8 +265,7 @@ "managedClusterGroups", "name", "namespaces", - "projects", - "subscriptions" + "projects" ], "title": "ClusterGroup" }, From ae14334c2793a1fd0e9d71ef5d298f3ee91ef468 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 8 Dec 2022 18:23:18 +0100 Subject: [PATCH 0725/1263] Add description to main.git fields --- clustergroup/values.schema.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index cc4e101d..cfed4ecc 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -74,10 +74,12 @@ ], "properties": { "repoURL": { - "type": "string" + "type": "string", + "description": "URL of the pattern's git repository" }, "revision": { - "type": "string" + "type": "string", + "description": "revision (branch/commit/ref) to use on the pattern's git repository" } } }, From bee0d32b093660d2252dc4607528ff1b3f40fd65 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 8 Dec 2022 18:24:00 +0100 Subject: [PATCH 0726/1263] Drop useCSV from global It exists only under global.options --- clustergroup/values.schema.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index cfed4ecc..0b9aedef 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -98,9 +98,6 @@ "type": "object", "additionalProperties": true, "properties": { - "useCSV": { - "type": "boolean" - }, "pattern": { "type": "string" }, From 982239433059361fd7568fb77df19605ec3e11ae Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 8 Dec 2022 18:39:37 +0100 Subject: [PATCH 0727/1263] Add description for main.gitops.channel --- clustergroup/values.schema.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 0b9aedef..13e95636 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -88,7 +88,9 @@ "additionalProperties": false, "properties": { "channel": { - "type": "string" + "type": "string", + "description": "The channel from which to install the gitops operator", + "default": "stable" } } } From 0d9a38c4f60915138961e1ae6e653ab6756d026f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 8 Dec 2022 19:48:15 +0100 Subject: [PATCH 0728/1263] Add some descriptions --- clustergroup/values.schema.json | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 13e95636..436a3d8e 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -101,25 +101,39 @@ "additionalProperties": true, "properties": { "pattern": { - "type": "string" + "type": "string", + "readOnly": "true", + "description": "The name of the pattern being installed. The default is the name of the repository's folder and is automatically set by the Makefile" }, "clusterDomain": { - "type": "string" + "type": "string", + "readOnly": "true", + "description": "The FQDN domain of the cluster without the 'apps.' component. For example: mcg-hub.blueprints.rhecoeng.com. Gets set automatically by the framework" }, "localClusterDomain": { - "type": "string" + "type": "string", + "readOnly": "true", + "description": "The FQDN domain of the cluster including the 'apps.' component. For example: apps.mcg-hub.blueprints.rhecoeng.com. Gets set automatically by the framework" }, "targetRevision": { - "type": "string" + "type": "string", + "readOnly": "true", + "description": "revision (branch/commit/ref) to use on the pattern's git repository, it is set automatically by the pattern's operator" + }, + "repoURL": { + "type": "string", + "readOnly": "true", + "description": "URL of the pattern's git repository, it is set automatically by the pattern's operator" }, "hubClusterDomain": { - "type": "string" + "type": "string", + "readOnly": "true", + "description": "The FQDN domain of the hub cluster including the 'apps.' component. For example: apps.mcg-hub.blueprints.rhecoeng.com. Gets set automatically by the framework. Only makes sense when using ACM" }, "namespace": { - "type": "string" - }, - "repoURL": { - "type": "string" + "type": "string", + "readOnly": "true", + "description": "The namespace in which the ArgoCD instance is running. Automatically set to either 'openshift-operators' or '$ARGOCD_APP_NAMESPACE'" }, "git": { "$ref": "#/definitions/GlobalGit" From 5c3d5a8f286f14896e7c12c6835a95668faada68 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 8 Dec 2022 20:02:41 +0100 Subject: [PATCH 0729/1263] Mark useCSV as deprecated --- clustergroup/values.schema.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 436a3d8e..9205b156 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -177,7 +177,8 @@ "additionalProperties": false, "properties": { "useCSV": { - "type": "boolean" + "type": "boolean", + "deprecated": true }, "syncPolicy": { "type": "string" From 3d07e45a5316dceb83908328fe7e91ece2056706 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 9 Dec 2022 00:14:57 -0700 Subject: [PATCH 0730/1263] - Added descriptions for namespaces, subscriptions, managedClusterGroup, applications, and projects --- clustergroup/values.schema.json | 92 +++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 27 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 9205b156..4977b9af 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -151,18 +151,24 @@ "GlobalGit": { "type": "object", "additionalProperties": false, + "description": "The git configuration used to support Tekton pipeline tasks.", "properties": { "hostname": { - "type": "string" + "type": "string", + "description": "The hostname for the Git provider being used. e.g. github.com or gitlab.com" }, "account": { - "type": "string" + "type": "string", + "description": "The account for the Git provider. Accounts allow you to organize and control access to that code. There are three types of accounts on GitHub. Personal accounts Organization accounts Enterprise accounts e.g. hybrid-cloud-patterns or claudiol" }, "email": { - "type": "string" + "type": "string", + "description": "The contact email for the Git account. e.g. account@gmail.com" }, "dev_revision": { - "type": "string" + "type": "string", + "deprecated": true, + "description": "This is used by the pipelines as the branch for the development repository. e.g. v2.0. This is marked as deprecated" } }, "required": [ @@ -181,10 +187,13 @@ "deprecated": true }, "syncPolicy": { - "type": "string" + "type": "string", + "description": "This is the sync policy for the ArgoCD applications. When set to Automatic ArgoCD will automatically sync an application when it detects differences between the desired manifests in Git." }, "installPlanApproval": { - "type": "string" + "type": "string", + "deprecated": true, + "description": "This is used to approval strategy for the subscriptions of OpenShift Operators being installed. You can choose Automatic or Manual updates. NOTE: This setting is now available in the subcriptions description in the values file." } }, "required": [ @@ -199,25 +208,30 @@ "additionalProperties": false, "properties": { "name": { - "type": "string" + "type": "string", + "description": "The name of the cluster group." }, "targetCluster": { "type": "string" }, "isHubCluster": { - "type": "boolean" + "type": "boolean", + "description": "If set to true the values is used to identify whether this is the hub cluster or an edge/spoke cluster configuration." }, "insecureUnsealVaultInsideCluster": { - "type": "boolean" + "type": "boolean", + "description": "If set to true the value is used stores the vault unseal keys inside a cluster secret. This is fundamentally insecure." }, "namespaces": { "type": "array", + "description": "This is the array of namespaces that the VP framework will create. In addition, operator groups will also be created for each namespace.", "items": { "type": "string" } }, "operatorgroupExcludes": { "type": "array", + "description": "List of namespaces to exclude the creation of operator groups.", "items": { "type": "string" } @@ -231,12 +245,14 @@ "type": "object" } ], + "description": "Description of the subscriptions that the VP Framework will install in the cluster. Two ways of defining subscriptions: Using a list or using a dictionary.", "items": { "$ref": "#/definitions/Subscription" } }, "projects": { "type": "array", + "description": "The list of projects that will be created in the ArgoCD instances.", "items": { "type": "string" } @@ -250,6 +266,7 @@ "type": "object" } ], + "description": "Description of the applications that will be created in the ArgoCD instances. Two ways of defining applications: Using a list or using a dictionary.", "items": { "$ref": "#/definitions/Applications" } @@ -266,6 +283,7 @@ "type": "object" } ], + "description": "Description of the managed clusters that ACM will be able to manage. Two ways of defining managed clusters: Using a list or using a dictionary.", "items": { "$ref": "#/definitions/ManagedClusterGroup" } @@ -287,67 +305,87 @@ }, "Applications": { "type": "object", + "description": "Description of the applications that will be created in the ArgoCD instances. The Application CRD is the Kubernetes resource object representing a deployed application instance in an environment. Two ways of defining applications: Using a list or using a dictionary.", "additionalProperties": true, "properties": { "name": { - "type": "string" + "type": "string", + "description": "Name of the application in ArgoCD." }, "repoURL": { - "type": "string" + "type": "string", + "description": "RepoURL is the URL to the repository (Git or Helm) that contains the application manifests." }, "targetRevision": { - "type": "string" + "type": "string", + "description": "TargetRevision defines the revision of the source to sync the application to. In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. In case of Helm, this is a semver tag for the Chart's version." }, "chart": { "type": "string" + "description": "Chart is a Helm chart name, and must be specified for applications sourced from a Helm repo." }, "kustomize": { - "type": "boolean" + "type": "boolean", + "description": "If set to true it will tell ArgoCD to use kustomize to deploy the application." }, "plugin": { - "type": "object" + "type": "object", + "description": "Plugin holds config management plugin specific options" }, "extraValueFiles": { - "type": "array" + "type": "array", + "description": "List of extra values files that will be passed to ArgoCD." }, "extraHubClusterDomainFields": { - "type": "array" + "type": "array", + "description": "List of extra fields that will be passed to ArgoCD." }, "extraLocalClusterDomainFields": { - "type": "array" + "type": "array", + "description": "List of extra fields that will be passed to ArgoCD." }, "extraRepoURLFields": { - "type": "array" + "type": "array", + "description": "List of extra fields that will be passed to ArgoCD." }, "extraTargetRevisionFields": { - "type": "array" + "type": "array", + "description": "List of extra fields that will be passed to ArgoCD." }, "extraNamespaceFields": { - "type": "array" + "type": "array", + "description": "List of extra fields that will be passed to ArgoCD." }, "extraPatternNameFields": { - "type": "array" + "type": "array", + "description": "List of extra fields that will be passed to ArgoCD." }, "overrides": { "type": "object" }, "fileParameters": { - "type": "array" + "type": "array", + "description": "FileParameters are file parameters to the helm template" }, "ignoreDifferences": { - "type": "array" + "type": "array", + "description": "IgnoreDifferences is a list of resources and their fields which should be ignored during comparison" }, "syncPolicy": { - "type": "object" + "type": "object", + "description": "SyncPolicy controls when and how a sync will be performed" }, "namespace": { - "type": "string" + "type": "string", + "description": "Namespace specifies the target namespace for the application's resources. The namespace will only be set for namespace-scoped resources that have not set a value for .metadata.namespace" }, "project": { - "type": "string" + "type": "string", + "description": "Project is a reference to the project this application belongs to. The empty string means that application belongs to the 'default' project." }, "path": { - "type": "string" + "type": "string", + "description": "Path is a directory path within the Git repository, and is only valid for applications sourced from Git." } }, "required": [ From 181d082eeffa006f40118e4e527175ff7bb57adf Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 9 Dec 2022 00:22:04 -0700 Subject: [PATCH 0731/1263] - Fixed issue with description definition --- clustergroup/values.schema.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 4977b9af..94ac1a2b 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -89,8 +89,7 @@ "properties": { "channel": { "type": "string", - "description": "The channel from which to install the gitops operator", - "default": "stable" + "description": "The channel from which to install the gitops operator" } } } @@ -321,7 +320,7 @@ "description": "TargetRevision defines the revision of the source to sync the application to. In case of Git, this can be commit, tag, or branch. If omitted, will equal to HEAD. In case of Helm, this is a semver tag for the Chart's version." }, "chart": { - "type": "string" + "type": "string", "description": "Chart is a Helm chart name, and must be specified for applications sourced from a Helm repo." }, "kustomize": { From 172f62fa3940da9fa10d58a0746f274a02e1caf4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 9 Nov 2022 18:47:58 +0100 Subject: [PATCH 0732/1263] drop insecureUnsealVaultInsideCluster entirely We always just unseal the vault from the cluster. We simply drop support of running these commands manually entirely. The advantage is that the code is a lot simpler and we have a single avenue to do things. Tested as following: 1. Deployed MCG and observed loading of secrets happening correctly 2. Removed the imperative/vaultkeys secret 3. Deleted the vault-0 pod 4. Observed that the vault-0 pod was restarted and that the vault was sealed 5. Observed that the unsealvault cronjob did nothing 6. Restored the vaultkeys secret 7. Observed that the unsealvault cronjob unsealed the vault correctly again --- Changes.md | 6 + Makefile | 13 +- ansible/roles/vault_utils/README.md | 7 +- ansible/roles/vault_utils/defaults/main.yml | 4 - .../roles/vault_utils/tasks/vault_init.yaml | 42 --- .../roles/vault_utils/tasks/vault_unseal.yaml | 52 +--- .../templates/imperative/clusterrole.yaml | 5 +- .../templates/imperative/configmap.yaml | 5 +- .../templates/imperative/namespace.yaml | 5 +- clustergroup/templates/imperative/rbac.yaml | 5 +- clustergroup/templates/imperative/role.yaml | 5 +- .../templates/imperative/serviceaccount.yaml | 5 +- .../templates/imperative/unsealjob.yaml | 5 +- clustergroup/test.yaml | 3 - clustergroup/values.yaml | 3 - examples/industrial-edge-hub.yaml | 3 - examples/medical-diagnosis-hub.yaml | 3 - ...roup-industrial-edge-factory.expected.yaml | 1 - ...tergroup-industrial-edge-hub.expected.yaml | 3 - ...rgroup-medical-diagnosis-hub.expected.yaml | 3 - tests/clustergroup-naked.expected.yaml | 194 ++++++++++++ tests/clustergroup-normal.expected.yaml | 3 - tests/clustergroup.expected.diff | 277 +++++------------- 23 files changed, 295 insertions(+), 357 deletions(-) diff --git a/Changes.md b/Changes.md index d6fe8276..713ba367 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,11 @@ # Changes +## December 9, 2022 + +* Dropped insecureUnsealVaultInsideCluster (and file_unseal) entirely. Now + vault is always unsealed via a cronjob in the cluster. It is recommended to + store the imperative/vaultkeys secret offline securely and then delete it. + ## December 8, 2022 * Removed the legacy installation targets: diff --git a/Makefile b/Makefile index 1714cdab..85b047a1 100644 --- a/Makefile +++ b/Makefile @@ -49,16 +49,6 @@ uninstall: ## runs helm uninstall helm uninstall $(NAME) @oc delete csv -n openshift-operators $(CSV) -.PHONY: vault-init -vault-init: ## inits, unseals and configured the vault - common/scripts/vault-utils.sh vault_init common/pattern-vault.init - common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init - common/scripts/vault-utils.sh vault_secrets_init common/pattern-vault.init - -.PHONY: vault-unseal -vault-unseal: ## unseals the vault - common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init - .PHONY: load-secrets load-secrets: ## loads the secrets into the vault common/scripts/vault-utils.sh push_secrets common/pattern-vault.init $(NAME) @@ -68,8 +58,7 @@ CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | grep TEST_OPTS= -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" \ --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" \ --set global.namespace="pattern-namespace" --set global.hubClusterDomain=apps.hub.example.com --set global.localClusterDomain=apps.region.example.com --set global.clusterDomain=region.example.com\ - --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" \ - --set clusterGroup.insecureUnsealVaultInsideCluster=true + --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" PATTERN_OPTS=-f common/examples/values-example.yaml EXECUTABLES=git helm oc ansible diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index 8ed3a3a8..340c4567 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -39,7 +39,6 @@ If set to false they will be stored inside a secret defined by `unseal_secret` in the `imperative` namespace: ```yaml -file_unseal: true # token inside a secret in the cluster. # *Note* that this is fundamentally unsafe output_file: "common/pattern-vault.init" @@ -218,11 +217,9 @@ Internals Here is the rough high-level algorithm used to unseal the vault: 1. Check vault status. If vault is not initialized go to 2. If initialized go to 3. -2. Initialize vault and store unseal keys + login token either on a local file - or inside a secret in k8s (file_unseal var controls this) +2. Initialize vault and store unseal keys + login token inside a secret in k8s 3. Check vault status. If vault is unsealed go to 5. else to to 4. -4. Unseal the vault using the secrets read from the file or the secret - (file_unseal controls this) +4. Unseal the vault using the secrets read from the k8s secret 5. Configure the vault (should be idempotent) ## License diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 04fccf7b..35326a5b 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -18,9 +18,5 @@ output_file: "common/pattern-vault.init" external_secrets_ns: golang-external-secrets external_secrets_sa: golang-external-secrets external_secrets_secret: golang-external-secrets -# Setting this to false makes the role store the vault unseal keys and root login -# token inside a secret in the cluster. -# *Note* that this is fundamentally unsafe -file_unseal: true unseal_secret: "vaultkeys" unseal_namespace: "imperative" diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index b339a257..16ce73df 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -9,33 +9,6 @@ ansible.builtin.set_fact: vault_initialized: "{{ vault_status['initialized'] | bool }}" -# Note that the 'realpath' filter explicitely only resolves on the ansible/local box -# which is fine in our case -- name: Set absolute path for output_file - ansible.builtin.set_fact: - output_file_abs: "{{ output_file | realpath }}" - when: - - not vault_initialized - - file_unseal - -- name: Check for existence of "{{ output_file_abs }}" - ansible.builtin.stat: - path: "{{ output_file_abs }}" - register: result - when: - - not vault_initialized - - file_unseal - -- name: Rename "{{ output_file_abs }} if it exists" - ansible.builtin.copy: - src: "{{ output_file_abs }}" - dest: "{{ output_file_abs }}.bak" - mode: '0600' - when: - - not vault_initialized - - file_unseal - - result.stat.exists - # We need to retry here because the vault service might be starting # and can return a 500 internal server until its state is fully ready - name: Init vault operator @@ -56,20 +29,6 @@ vault_init_json: "{{ vault_init_json_out.stdout | from_json }}" when: not vault_initialized -# We store the the operator unseal keys and root token to a file when -# the vault was not already initialized *and* when unseal_from_cluster -# is set to false -- name: Save vault operator output (local file) - no_log: true - ansible.builtin.copy: - follow: true - dest: "{{ output_file_abs }}" - content: "{{ vault_init_json | to_nice_json }}" - mode: '0600' - when: - - not vault_initialized - - file_unseal - # We store the the operator unseal keys and root token to a secret inside # the cluster when the vault was not already initialized *and* when # unseal_from_cluster is set to true @@ -88,4 +47,3 @@ vault_data_json: "{{ vault_init_json | to_nice_json | b64encode }}" when: - not vault_initialized - - not file_unseal diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index 9bd1bd3d..2254959e 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -9,43 +9,8 @@ ansible.builtin.set_fact: vault_sealed: "{{ vault_status['sealed'] | bool }}" -# Note that the 'realpath' filter explicitely only resolves on the ansible/local box -# which is fine in our case -- name: Set absolute path for output_file - ansible.builtin.set_fact: - output_file_abs: "{{ output_file | realpath }}" - when: - - vault_sealed - - file_unseal - -- name: Check for existence of "{{ output_file_abs }}" - ansible.builtin.stat: - path: "{{ output_file_abs }}" - register: result - when: - - vault_sealed - - file_unseal - -- name: Fail if "{{ output_file_abs }}" does not exists - ansible.builtin.fail: - msg: "{{ output_file_abs }} does not exist. Stopping here" - failed_when: not result.stat.exists - when: - - vault_sealed - - file_unseal - -# We reparse the json vault init file in case unseal was called without operator init before -# and if file_unseal is true -- name: Parse "{{ output_file_abs }}" - ansible.builtin.set_fact: - vault_init_json: "{{ lookup('file', output_file_abs) | from_json }}" - when: - - vault_sealed - - file_unseal - # We reparse the json vault init secret in case unseal was called without operator init before -# and if file_unseal is false -- name: Parse "{{ output_file_abs }}" +- name: Parse vaultkeys kubernetes.core.k8s_info: kind: Secret namespace: "{{ unseal_namespace }}" @@ -54,14 +19,21 @@ register: vault_init_data when: - vault_sealed - - not file_unseal -- name: Set vault init json +- name: Does the vaultkeys secret exist? ansible.builtin.set_fact: - vault_init_json: "{{ vault_init_data.resources[0].data.vault_data_json | b64decode | from_json }}" + vaultkeys_exists: "{{ vault_init_data.resources | length > 0 }}" + +- name: Vaultkeys does not exist and the vault is sealed, so exit + ansible.builtin.meta: end_play when: - vault_sealed - - not file_unseal + - not vaultkeys_exists + +- name: Set vault init json + ansible.builtin.set_fact: + vault_init_json: "{{ vault_init_data.resources[0].data.vault_data_json | b64decode | from_json }}" + when: vault_sealed - name: Set root token and unseal_keys ansible.builtin.set_fact: diff --git a/clustergroup/templates/imperative/clusterrole.yaml b/clustergroup/templates/imperative/clusterrole.yaml index b893d0e2..e3646917 100644 --- a/clustergroup/templates/imperative/clusterrole.yaml +++ b/clustergroup/templates/imperative/clusterrole.yaml @@ -1,7 +1,5 @@ {{- if not (eq .Values.enabled "plumbing") }} -{{/* Define this if needed (jobs defined or insecure unseal configured) */}} -{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) - (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} +{{/* This is always defined as we always unseal the cluster with an imperative job */}} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -21,4 +19,3 @@ rules: - watch {{- end }} {{- end }} -{{- end }} diff --git a/clustergroup/templates/imperative/configmap.yaml b/clustergroup/templates/imperative/configmap.yaml index 5abb473b..8ca5a176 100644 --- a/clustergroup/templates/imperative/configmap.yaml +++ b/clustergroup/templates/imperative/configmap.yaml @@ -1,7 +1,5 @@ {{- if not (eq .Values.enabled "plumbing") }} -{{/* Define this if needed (jobs defined or insecure unseal configured) */}} -{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) - (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} +{{/* This is always defined as we always unseal the cluster with an imperative job */}} {{- $valuesyaml := toYaml $.Values -}} apiVersion: v1 kind: ConfigMap @@ -12,4 +10,3 @@ data: values.yaml: | {{ tpl $valuesyaml . | indent 4 }} {{- end }} -{{- end }} \ No newline at end of file diff --git a/clustergroup/templates/imperative/namespace.yaml b/clustergroup/templates/imperative/namespace.yaml index fd4569c6..ee7b8adb 100644 --- a/clustergroup/templates/imperative/namespace.yaml +++ b/clustergroup/templates/imperative/namespace.yaml @@ -1,7 +1,5 @@ {{- if not (eq .Values.enabled "plumbing") }} -{{/* Define this if needed (jobs defined or insecure unseal configured) */}} -{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) - (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} +{{/* This is always defined as we always unseal the cluster with an imperative job */}} apiVersion: v1 kind: Namespace metadata: @@ -10,4 +8,3 @@ metadata: argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} name: {{ $.Values.clusterGroup.imperative.namespace }} {{- end }} -{{- end }} \ No newline at end of file diff --git a/clustergroup/templates/imperative/rbac.yaml b/clustergroup/templates/imperative/rbac.yaml index 1b73ca3e..1a4b3e2b 100644 --- a/clustergroup/templates/imperative/rbac.yaml +++ b/clustergroup/templates/imperative/rbac.yaml @@ -1,7 +1,5 @@ {{- if not (eq .Values.enabled "plumbing") }} -{{/* Define this if needed (jobs defined or insecure unseal configured) */}} -{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) - (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} +{{/* This is always defined as we always unseal the cluster with an imperative job */}} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -30,4 +28,3 @@ subjects: name: {{ $.Values.clusterGroup.imperative.serviceAccountName }} namespace: {{ $.Values.clusterGroup.imperative.namespace }} {{- end }} -{{- end }} \ No newline at end of file diff --git a/clustergroup/templates/imperative/role.yaml b/clustergroup/templates/imperative/role.yaml index 79b7b7a7..63ad37d1 100644 --- a/clustergroup/templates/imperative/role.yaml +++ b/clustergroup/templates/imperative/role.yaml @@ -1,7 +1,5 @@ {{- if not (eq .Values.enabled "plumbing") }} -{{/* Define this if needed (jobs defined or insecure unseal configured) */}} -{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) - (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} +{{/* This is always defined as we always unseal the cluster with an imperative job */}} --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -20,4 +18,3 @@ rules: - '*' {{- end }} {{- end }} -{{- end }} diff --git a/clustergroup/templates/imperative/serviceaccount.yaml b/clustergroup/templates/imperative/serviceaccount.yaml index b90ac2a4..ac051348 100644 --- a/clustergroup/templates/imperative/serviceaccount.yaml +++ b/clustergroup/templates/imperative/serviceaccount.yaml @@ -1,7 +1,5 @@ {{- if not (eq .Values.enabled "plumbing") }} -{{/* Define this if needed (jobs defined or insecure unseal configured) */}} -{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) - (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} +{{/* This is always defined as we always unseal the cluster with an imperative job */}} {{- if $.Values.clusterGroup.imperative.serviceAccountCreate -}} apiVersion: v1 kind: ServiceAccount @@ -10,4 +8,3 @@ metadata: namespace: {{ $.Values.clusterGroup.imperative.namespace }} {{- end }} {{- end }} -{{- end }} diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index 76fbd135..baa078cb 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -1,6 +1,5 @@ {{- if not (eq .Values.enabled "plumbing") }} -{{/* Only define this if the values.insecureUnsealVaultInsideCluster is set to tre and we're on the cluster */}} -{{- if and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster }} +{{- if $.Values.clusterGroup.isHubCluster }} --- apiVersion: batch/v1 kind: CronJob @@ -41,8 +40,6 @@ spec: {{- end }} - -e - "@/values/values.yaml" - - -e - - '{"file_unseal": false}' - -t - 'vault_init,vault_unseal,vault_secrets_init' - "common/ansible/playbooks/vault/vault.yaml" diff --git a/clustergroup/test.yaml b/clustergroup/test.yaml index 5db2e4a6..5f758643 100644 --- a/clustergroup/test.yaml +++ b/clustergroup/test.yaml @@ -1,9 +1,6 @@ clusterGroup: name: hub isHubCluster: true - # Note: setting this to true stores the vault unseal keys inside a cluster secret and - # is fundamentally insecure - insecureUnsealVaultInsideCluster: false namespaces: - open-cluster-management diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index fac5d56c..1953b25c 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -13,9 +13,6 @@ clusterGroup: name: example isHubCluster: true targetCluster: in-cluster - # Note: setting this to true stores the vault unseal keys inside a cluster secret and - # is fundamentally insecure - insecureUnsealVaultInsideCluster: false imperative: jobs: [] diff --git a/examples/industrial-edge-hub.yaml b/examples/industrial-edge-hub.yaml index 662ac85d..3dfd2fc8 100644 --- a/examples/industrial-edge-hub.yaml +++ b/examples/industrial-edge-hub.yaml @@ -1,9 +1,6 @@ clusterGroup: name: datacenter isHubCluster: true - # Note: setting this to true stores the vault unseal keys inside a cluster secret and - # is fundamentally insecure - insecureUnsealVaultInsideCluster: true namespaces: - golang-external-secrets diff --git a/examples/medical-diagnosis-hub.yaml b/examples/medical-diagnosis-hub.yaml index e303b93f..8bde30d0 100644 --- a/examples/medical-diagnosis-hub.yaml +++ b/examples/medical-diagnosis-hub.yaml @@ -1,9 +1,6 @@ clusterGroup: name: hub isHubCluster: true - # Note: setting this to true stores the vault unseal keys inside a cluster secret and - # is fundamentally insecure - insecureUnsealVaultInsideCluster: true namespaces: - open-cluster-management diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 31178ed1..9c3fd773 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -103,7 +103,6 @@ data: serviceAccountName: imperative-sa valuesConfigMap: helm-values-configmap verbosity: "" - insecureUnsealVaultInsideCluster: true isHubCluster: false name: factory namespaces: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index d6f0f520..6f9d0162 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -224,7 +224,6 @@ data: serviceAccountName: imperative-sa valuesConfigMap: helm-values-configmap verbosity: "" - insecureUnsealVaultInsideCluster: true isHubCluster: true managedClusterGroups: factory: @@ -549,8 +548,6 @@ spec: - ansible-playbook - -e - "@/values/values.yaml" - - -e - - '{"file_unseal": false}' - -t - 'vault_init,vault_unseal,vault_secrets_init' - "common/ansible/playbooks/vault/vault.yaml" diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 3b52ec6b..7c2f02ce 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -236,7 +236,6 @@ data: serviceAccountName: imperative-sa valuesConfigMap: helm-values-configmap verbosity: "" - insecureUnsealVaultInsideCluster: true isHubCluster: true managedClusterGroups: region-one: @@ -536,8 +535,6 @@ spec: - ansible-playbook - -e - "@/values/values.yaml" - - -e - - '{"file_unseal": false}' - -t - 'vault_init,vault_unseal,vault_secrets_init' - "common/ansible/playbooks/vault/vault.yaml" diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 4d5c5eee..81484019 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -1,4 +1,13 @@ --- +# Source: pattern-clustergroup/templates/imperative/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + name: imperative + argocd.argoproj.io/managed-by: common-example + name: imperative +--- # Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml apiVersion: v1 kind: Namespace @@ -12,6 +21,86 @@ metadata: name: common-example spec: {} --- +# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/imperative/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: helm-values-configmap-example + namespace: imperative +data: + values.yaml: | + clusterGroup: + imperative: + activeDeadlineSeconds: 3600 + clusterRoleName: imperative-cluster-role + clusterRoleYaml: "" + cronJobName: imperative-cronjob + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' + jobName: imperative-job + jobs: [] + namespace: imperative + roleName: imperative-role + roleYaml: "" + schedule: '*/10 * * * *' + serviceAccountCreate: true + serviceAccountName: imperative-sa + valuesConfigMap: helm-values-configmap + verbosity: "" + isHubCluster: true + name: example + targetCluster: in-cluster + enabled: all + global: + options: + installPlanApproval: Automatic + syncPolicy: Automatic + useCSV: true + pattern: common + targetRevision: main + secretStore: + kind: ClusterSecretStore + name: vault-backend + secretsBase: + key: secret/data/hub +--- +# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: imperative-cluster-role +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - get + - list + - watch +--- +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: imperative-cluster-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: imperative-cluster-role +subjects: + - kind: ServiceAccount + name: imperative-sa + namespace: imperative +--- # Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 @@ -56,6 +145,111 @@ subjects: name: example-gitops-argocd-dex-server namespace: common-example --- +# Source: pattern-clustergroup/templates/imperative/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: imperative-role + namespace: imperative +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- +# Source: pattern-clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: imperative-admin-rolebinding + namespace: imperative +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: imperative-role +subjects: + - kind: ServiceAccount + name: imperative-sa + namespace: imperative +--- +# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: unsealvault-cronjob + namespace: imperative +spec: + schedule: "*/5 * * * *" + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: 3600 + template: + metadata: + name: unsealvault-job + spec: + serviceAccountName: imperative-sa + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" + - name: unseal-playbook + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - "600" + - ansible-playbook + - -e + - "@/values/values.yaml" + - -t + - 'vault_init,vault_unseal,vault_secrets_init' + - "common/ansible/playbooks/vault/vault.yaml" + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: "done" + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + command: + - 'sh' + - '-c' + - 'echo' + - 'done' + - '\n' + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-example + restartPolicy: Never +--- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 kind: ArgoCD diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 3b378ca1..835aa13a 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -91,7 +91,6 @@ data: serviceAccountName: imperative-sa valuesConfigMap: helm-values-configmap verbosity: "" - insecureUnsealVaultInsideCluster: true isHubCluster: true managedClusterGroups: - acmlabels: @@ -418,8 +417,6 @@ spec: - ansible-playbook - -e - "@/values/values.yaml" - - -e - - '{"file_unseal": false}' - -t - 'vault_init,vault_unseal,vault_secrets_init' - "common/ansible/playbooks/vault/vault.yaml" diff --git a/tests/clustergroup.expected.diff b/tests/clustergroup.expected.diff index 7f04814c..23e2e77b 100644 --- a/tests/clustergroup.expected.diff +++ b/tests/clustergroup.expected.diff @@ -1,6 +1,6 @@ --- tests/clustergroup-naked.expected.yaml +++ tests/clustergroup-normal.expected.yaml -@@ -1,17 +1,227 @@ +@@ -1,11 +1,29 @@ --- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 @@ -20,17 +20,18 @@ + name: application-ci +spec: +--- -+# Source: pattern-clustergroup/templates/imperative/namespace.yaml -+apiVersion: v1 -+kind: Namespace -+metadata: -+ labels: -+ name: imperative + # Source: pattern-clustergroup/templates/imperative/namespace.yaml + apiVersion: v1 + kind: Namespace + metadata: + labels: + name: imperative +- argocd.argoproj.io/managed-by: common-example + argocd.argoproj.io/managed-by: mypattern-example -+ name: imperative -+--- + name: imperative + --- # Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml - apiVersion: v1 +@@ -13,12 +31,12 @@ kind: Namespace metadata: labels: @@ -44,22 +45,11 @@ + name: mypattern-example spec: {} --- -+# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml -+apiVersion: v1 -+kind: ServiceAccount -+metadata: -+ name: imperative-sa -+ namespace: imperative -+--- -+# Source: pattern-clustergroup/templates/imperative/configmap.yaml -+apiVersion: v1 -+kind: ConfigMap -+metadata: -+ name: helm-values-configmap-example -+ namespace: imperative -+data: -+ values.yaml: | -+ clusterGroup: + # Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +@@ -37,6 +55,22 @@ + data: + values.yaml: | + clusterGroup: + applications: + acm: + ignoreDifferences: @@ -76,28 +66,24 @@ + namespace: application-ci + path: charts/datacenter/pipelines + project: datacenter -+ imperative: -+ activeDeadlineSeconds: 3600 -+ clusterRoleName: imperative-cluster-role -+ clusterRoleYaml: "" -+ cronJobName: imperative-cronjob -+ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest -+ imagePullPolicy: Always -+ insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' -+ jobName: imperative-job + imperative: + activeDeadlineSeconds: 3600 + clusterRoleName: imperative-cluster-role +@@ -46,7 +80,9 @@ + imagePullPolicy: Always + insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' + jobName: imperative-job +- jobs: [] + jobs: + - name: test + playbook: ansible/test.yml -+ namespace: imperative -+ roleName: imperative-role -+ roleYaml: "" -+ schedule: '*/10 * * * *' -+ serviceAccountCreate: true -+ serviceAccountName: imperative-sa -+ valuesConfigMap: helm-values-configmap -+ verbosity: "" -+ insecureUnsealVaultInsideCluster: true -+ isHubCluster: true + namespace: imperative + roleName: imperative-role + roleYaml: "" +@@ -57,16 +93,100 @@ + verbosity: "" + insecureUnsealVaultInsideCluster: true + isHubCluster: true + managedClusterGroups: + - acmlabels: + - name: clusterGroup @@ -148,7 +134,7 @@ + - domain: syd.beekhof.net + name: sydney + name: argo-edge -+ name: example + name: example + namespaces: + - open-cluster-management + - application-ci @@ -168,9 +154,9 @@ + pipelines: + csv: redhat-openshift-pipelines.v1.5.2 + name: openshift-pipelines-operator-rh -+ targetCluster: in-cluster -+ enabled: all -+ global: + targetCluster: in-cluster + enabled: all + global: + clusterDomain: region.example.com + git: + account: hybrid-cloud-patterns @@ -180,57 +166,24 @@ + hubClusterDomain: apps.hub.example.com + localClusterDomain: apps.region.example.com + namespace: pattern-namespace -+ options: -+ installPlanApproval: Automatic -+ syncPolicy: Automatic + options: + installPlanApproval: Automatic + syncPolicy: Automatic +- useCSV: true +- pattern: common + useCSV: false + pattern: mypattern + repoURL: https://github.com/pattern-clone/mypattern -+ targetRevision: main + targetRevision: main + main: + clusterGroupName: example + git: + repoURL: https://github.com/pattern-clone/mypattern + revision: main -+ secretStore: -+ kind: ClusterSecretStore -+ name: vault-backend -+ secretsBase: -+ key: secret/data/hub -+--- -+# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml -+apiVersion: rbac.authorization.k8s.io/v1 -+kind: ClusterRole -+metadata: -+ name: imperative-cluster-role -+rules: -+ - apiGroups: -+ - '*' -+ resources: -+ - '*' -+ verbs: -+ - get -+ - list -+ - watch -+--- -+# Source: pattern-clustergroup/templates/imperative/rbac.yaml -+apiVersion: rbac.authorization.k8s.io/v1 -+kind: ClusterRoleBinding -+metadata: -+ name: imperative-cluster-admin-rolebinding -+roleRef: -+ apiGroup: rbac.authorization.k8s.io -+ kind: ClusterRole -+ name: imperative-cluster-role -+subjects: -+ - kind: ServiceAccount -+ name: imperative-sa -+ namespace: imperative -+--- - # Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml - # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS - apiVersion: rbac.authorization.k8s.io/v1 -@@ -36,7 +246,7 @@ + secretStore: + kind: ClusterSecretStore + name: vault-backend +@@ -126,7 +246,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -239,7 +192,7 @@ roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole -@@ -45,16 +255,591 @@ +@@ -135,16 +255,16 @@ - kind: ServiceAccount # This is the {ArgoCD.name}-argocd-application-controller name: example-gitops-argocd-application-controller @@ -256,36 +209,13 @@ name: example-gitops-argocd-dex-server - namespace: common-example + namespace: mypattern-example -+--- -+# Source: pattern-clustergroup/templates/imperative/role.yaml -+apiVersion: rbac.authorization.k8s.io/v1 -+kind: Role -+metadata: -+ name: imperative-role -+ namespace: imperative -+rules: -+ - apiGroups: -+ - '*' -+ resources: -+ - '*' -+ verbs: -+ - '*' -+--- -+# Source: pattern-clustergroup/templates/imperative/rbac.yaml -+apiVersion: rbac.authorization.k8s.io/v1 -+kind: RoleBinding -+metadata: -+ name: imperative-admin-rolebinding -+ namespace: imperative -+roleRef: -+ apiGroup: rbac.authorization.k8s.io -+ kind: Role -+ name: imperative-role -+subjects: -+ - kind: ServiceAccount -+ name: imperative-sa -+ namespace: imperative -+--- + --- + # Source: pattern-clustergroup/templates/imperative/role.yaml + apiVersion: rbac.authorization.k8s.io/v1 +@@ -175,6 +295,80 @@ + name: imperative-sa + namespace: imperative + --- +# Source: pattern-clustergroup/templates/imperative/job.yaml +apiVersion: batch/v1 +kind: CronJob @@ -360,84 +290,22 @@ + name: helm-values-configmap-example + restartPolicy: Never +--- -+# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml -+apiVersion: batch/v1 -+kind: CronJob -+metadata: -+ name: unsealvault-cronjob -+ namespace: imperative -+spec: -+ schedule: "*/5 * * * *" -+ # if previous Job is still running, skip execution of a new Job -+ concurrencyPolicy: Forbid -+ jobTemplate: -+ spec: -+ activeDeadlineSeconds: 3600 -+ template: -+ metadata: -+ name: unsealvault-job -+ spec: -+ serviceAccountName: imperative-sa -+ initContainers: -+ # git init happens in /git/repo so that we can set the folder to 0770 permissions -+ # reason for that is ansible refuses to create temporary folders in there -+ - name: git-init -+ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest -+ imagePullPolicy: Always -+ env: -+ - name: HOME -+ value: /git/home -+ command: -+ - 'sh' -+ - '-c' + # Source: pattern-clustergroup/templates/imperative/unsealjob.yaml + apiVersion: batch/v1 + kind: CronJob +@@ -205,7 +399,7 @@ + command: + - 'sh' + - '-c' +- - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- /git/repo;chmod 0770 /git/{repo,home}" + - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" -+ volumeMounts: -+ - name: git -+ mountPath: "/git" -+ - name: unseal-playbook -+ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest -+ imagePullPolicy: Always -+ env: -+ - name: HOME -+ value: /git/home -+ workingDir: /git/repo -+ # We have a default timeout of 600s for each playbook. Can be overridden -+ # on a per-job basis -+ command: -+ - timeout -+ - "600" -+ - ansible-playbook -+ - -e -+ - "@/values/values.yaml" -+ - -e -+ - '{"file_unseal": false}' -+ - -t -+ - 'vault_init,vault_unseal,vault_secrets_init' -+ - "common/ansible/playbooks/vault/vault.yaml" -+ volumeMounts: -+ - name: git -+ mountPath: "/git" -+ - name: values-volume -+ mountPath: /values/values.yaml -+ subPath: values.yaml -+ containers: -+ - name: "done" -+ image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest -+ imagePullPolicy: Always -+ command: -+ - 'sh' -+ - '-c' -+ - 'echo' -+ - 'done' -+ - '\n' -+ volumes: -+ - name: git -+ emptyDir: {} -+ - name: values-volume -+ configMap: -+ name: helm-values-configmap-example -+ restartPolicy: Never -+--- + volumeMounts: + - name: git + mountPath: "/git" +@@ -253,6 +447,400 @@ + name: helm-values-configmap-example + restartPolicy: Never + --- +# Source: pattern-clustergroup/templates/core/subscriptions.yaml +--- +--- @@ -831,10 +699,11 @@ + kind: Route + jsonPointers: + - /status - --- ++--- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 -@@ -65,7 +850,7 @@ + kind: ArgoCD +@@ -262,7 +850,7 @@ # Changing the name affects the ClusterRoleBinding, the generated secret, # route URL, and argocd.argoproj.io/managed-by annotations name: example-gitops @@ -843,7 +712,7 @@ annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: -@@ -94,10 +879,10 @@ +@@ -291,10 +879,10 @@ --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION --set global.namespace=$ARGOCD_APP_NAMESPACE @@ -858,7 +727,7 @@ --set clusterGroup.name=example --post-renderer ./kustomize"] applicationSet: -@@ -174,11 +959,59 @@ +@@ -371,11 +959,59 @@ kind: ConsoleLink metadata: name: example-gitops-link From d426621d716c598d1243aba7f9b07e4656e178a0 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 13 Dec 2022 09:31:40 -0700 Subject: [PATCH 0733/1263] - Removed insecure boolean from schema file and from values-example.yaml --- clustergroup/values.schema.json | 5 ----- examples/values-example.yaml | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 94ac1a2b..c02d0bd0 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -217,10 +217,6 @@ "type": "boolean", "description": "If set to true the values is used to identify whether this is the hub cluster or an edge/spoke cluster configuration." }, - "insecureUnsealVaultInsideCluster": { - "type": "boolean", - "description": "If set to true the value is used stores the vault unseal keys inside a cluster secret. This is fundamentally insecure." - }, "namespaces": { "type": "array", "description": "This is the array of namespaces that the VP framework will create. In addition, operator groups will also be created for each namespace.", @@ -293,7 +289,6 @@ }, "required": [ "applications", - "insecureUnsealVaultInsideCluster", "isHubCluster", "managedClusterGroups", "name", diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 45e6c092..30ac93cf 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -9,7 +9,7 @@ global: clusterGroup: name: example - insecureUnsealVaultInsideCluster: false + #insecureUnsealVaultInsideCluster: false isHubCluster: true namespaces: From 3a14d797adf51918ff5030255e596c318e23c39b Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 13 Dec 2022 09:33:46 -0700 Subject: [PATCH 0734/1263] Re-ran make tests --- tests/clustergroup-naked.expected.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 81484019..c2fba541 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -37,6 +37,7 @@ metadata: data: values.yaml: | clusterGroup: + applications: [] imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role @@ -56,7 +57,11 @@ data: valuesConfigMap: helm-values-configmap verbosity: "" isHubCluster: true + managedClusterGroups: [] name: example + namespaces: [] + projects: [] + subscriptions: [] targetCluster: in-cluster enabled: all global: @@ -69,8 +74,6 @@ data: secretStore: kind: ClusterSecretStore name: vault-backend - secretsBase: - key: secret/data/hub --- # Source: pattern-clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 From 82fcce824cbaa3611b030b521ada634928715492 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 14 Dec 2022 18:03:55 +0100 Subject: [PATCH 0735/1263] Drop any remains of output_file This was only used when we had support for storing the unseal keys in files. Since we dropped this support we can remove any traces of that. --- Makefile | 2 +- ansible/roles/vault_utils/README.md | 10 ---------- ansible/roles/vault_utils/defaults/main.yml | 1 - scripts/vault-utils.sh | 5 ++--- 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index 85b047a1..d95b889e 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ uninstall: ## runs helm uninstall .PHONY: load-secrets load-secrets: ## loads the secrets into the vault - common/scripts/vault-utils.sh push_secrets common/pattern-vault.init $(NAME) + common/scripts/vault-utils.sh push_secrets $(NAME) CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | grep -v examples | sed -e 's/.\///') # Section related to tests and linting diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index 340c4567..4c8da9af 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -32,16 +32,6 @@ vault_hub_ttl: "15m" vault_pki_max_lease_ttl: "8760h" external_secrets_ns: golang-external-secrets external_secrets_sa: golang-external-secrets -``` - -Use the local file system (output_file variable) to store the vault's unseal keys. -If set to false they will be stored inside a secret defined by `unseal_secret` -in the `imperative` namespace: - -```yaml -# token inside a secret in the cluster. -# *Note* that this is fundamentally unsafe -output_file: "common/pattern-vault.init" unseal_secret: "vaultkeys" unseal_namespace: "imperative" ``` diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 35326a5b..220dd6e3 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -14,7 +14,6 @@ vault_base_path: "secret" vault_path: "{{ vault_base_path }}/{{ vault_hub }}" vault_hub_ttl: "15m" vault_pki_max_lease_ttl: "8760h" -output_file: "common/pattern-vault.init" external_secrets_ns: golang-external-secrets external_secrets_sa: golang-external-secrets external_secrets_secret: golang-external-secrets diff --git a/scripts/vault-utils.sh b/scripts/vault-utils.sh index 3aabcb8c..310d76d6 100755 --- a/scripts/vault-utils.sh +++ b/scripts/vault-utils.sh @@ -21,12 +21,11 @@ if [ $# -lt 1 ]; then fi TASK="${1}" -OUTFILE=${2:-"$COMMONPATH"/pattern-vault.init} -PATTERN_NAME=${3:-$(basename "`pwd`")} +PATTERN_NAME=${2:-$(basename "`pwd`")} if [ -z ${TASK} ]; then echo "Task is unset" exit 1 fi -ansible-playbook -t "${TASK}" -e pattern_name="${PATTERN_NAME}" -e pattern_dir="${PATTERNPATH}" -e output_file="${OUTFILE}" "${PLAYBOOKPATH}/vault/vault.yaml" +ansible-playbook -t "${TASK}" -e pattern_name="${PATTERN_NAME}" -e pattern_dir="${PATTERNPATH}" "${PLAYBOOKPATH}/vault/vault.yaml" From 73b63dfad1366ee56f47c58e4a37ef5b44f0cf47 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Thu, 15 Dec 2022 08:58:18 -0700 Subject: [PATCH 0736/1263] Updated python-version for json schema --- .github/workflows/jsonschema.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jsonschema.yaml b/.github/workflows/jsonschema.yaml index a89f6ab8..3b7ff11d 100644 --- a/.github/workflows/jsonschema.yaml +++ b/.github/workflows/jsonschema.yaml @@ -20,7 +20,7 @@ jobs: name: Json Schema tests strategy: matrix: - python-version: [3.9.15] + python-version: [3.11.1] # Set the agent to run on runs-on: ubuntu-latest From 50b4731769e2d018a2d11d0a4c93ffd8778ac686 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 15 Dec 2022 17:05:04 +0100 Subject: [PATCH 0737/1263] Update python version for ansible-unittest --- .github/workflows/ansible-unittest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ansible-unittest.yml b/.github/workflows/ansible-unittest.yml index df28b96f..b6a56049 100644 --- a/.github/workflows/ansible-unittest.yml +++ b/.github/workflows/ansible-unittest.yml @@ -20,7 +20,7 @@ jobs: name: Ansible unit tests strategy: matrix: - python-version: [3.9.15] + python-version: [3.10.9] # Set the agent to run on runs-on: ubuntu-latest From 8f432b73d118e28dcc1063119db85016063f0909 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 18 Dec 2022 16:05:33 +0100 Subject: [PATCH 0738/1263] Add filter to parse ACM secrets This filter takes a bunch of acm secrets that represent the remote clusters (Usually it is all secrets that are labeled with: "apps.open-cluster-management.io/secret-type=acm-cluster") These secrets are usually in the form of: data: config: ewogIC... name: bWNnLW9uZQ== server: aHR0cHM6Ly9hcGkubWNnLW9uZS5ibHVlcHJpbnRzLnJoZWNvZW5nLmNvbTo2NDQz The filter parses the secret (name, server, config) and returns a dictionary of secrets in the following form: : name: cluster_fqdn: server_api: https://api.:6443 bearerToken: tlsClientConfig: ... --- ansible/plugins/filter/parse_acm_secrets.py | 69 +++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 ansible/plugins/filter/parse_acm_secrets.py diff --git a/ansible/plugins/filter/parse_acm_secrets.py b/ansible/plugins/filter/parse_acm_secrets.py new file mode 100644 index 00000000..028f6145 --- /dev/null +++ b/ansible/plugins/filter/parse_acm_secrets.py @@ -0,0 +1,69 @@ +# This filter takes a bunch of acm secrets that represent the remote clusters +# (Usually it is all secrets that are labeled with: +# "apps.open-cluster-management.io/secret-type=acm-cluster") + +# These secrets are usually in the form of: +# data: +# config: ewogIC... +# name: bWNnLW9uZQ== +# server: aHR0cHM6Ly9hcGkubWNnLW9uZS5ibHVlcHJpbnRzLnJoZWNvZW5nLmNvbTo2NDQz + +# The filter parses the secret (name, server, config) and returns a dictionary of secrets in the +# following form: +# : +# name: +# cluster_fqdn: +# server_api: https://api.:6443 +# bearerToken: +# tlsClientConfig: + +import json +from base64 import b64decode + + +# These are the labels of an acm secret +# labels: +# apps.open-cluster-management.io/cluster-name: local-cluster +# apps.open-cluster-management.io/cluster-server: api.mcg-hub.blueprints.rhecoeng.com +# apps.open-cluster-management.io/secret-type: acm-cluster +def get_cluster_name(d): + if "metadata" in d and "labels" in d["metadata"]: + return d["metadata"]["labels"].get( + "apps.open-cluster-management.io/cluster-name", None + ) + return None + + +def get_cluster_fqdn(d): + if "metadata" in d and "labels" in d["metadata"]: + server = d["metadata"]["labels"].get( + "apps.open-cluster-management.io/cluster-server", None + ) + # It is rather hard to override this in an OCP deployment so we are okay in just dropping 'api.' + return server.removeprefix("api.") + return None + + +def parse_acm_secrets(secrets): + ret = {} + for secret in secrets: + cluster = get_cluster_name(secret) + if cluster is None: + continue + + ret[cluster] = {} + ret[cluster]["name"] = b64decode(secret["data"]["name"]) + ret[cluster]["server_api"] = b64decode(secret["data"]["server"]) + ret[cluster]["cluster_fqdn"] = get_cluster_fqdn(secret) + + config = b64decode(secret["data"]["config"]) + parsed_config = json.loads(config) + ret[cluster]["bearerToken"] = parsed_config["bearerToken"] + ret[cluster]["tlsClientConfig"] = parsed_config["tlsClientConfig"] + + return ret + + +class FilterModule(object): + def filters(self): + return {"parse_acm_secrets": parse_acm_secrets} From f946d8c1bb8d8ca4ec4f760b9dc6ae65ca6860ba Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 2 Jan 2023 11:38:01 +0100 Subject: [PATCH 0739/1263] Update ESO to 0.7.0 Tested in MCG on hub + regional cluster --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.6.1.tgz | Bin 44651 -> 0 bytes .../charts/external-secrets-0.7.0.tgz | Bin 0 -> 50386 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 1036 ++++++++++++++++- ...-secrets-industrial-edge-hub.expected.yaml | 1036 ++++++++++++++++- ...ecrets-medical-diagnosis-hub.expected.yaml | 1036 ++++++++++++++++- ...olang-external-secrets-naked.expected.yaml | 1036 ++++++++++++++++- ...lang-external-secrets-normal.expected.yaml | 1036 ++++++++++++++++- 9 files changed, 4944 insertions(+), 244 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.6.1.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.7.0.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 71b867f8..19bf435a 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.6.1" + version: "0.7.0" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.6.1.tgz b/golang-external-secrets/charts/external-secrets-0.6.1.tgz deleted file mode 100644 index 82520667c03fb93b475298065117f428c124f335..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44651 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 LZ~%1-5B0wQ0{Ubg diff --git a/golang-external-secrets/charts/external-secrets-0.7.0.tgz b/golang-external-secrets/charts/external-secrets-0.7.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..04249d7e2d00f87a1d6f67b6bc61eb8d408f1fa3 GIT binary patch literal 50386 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

^%Z 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 z z;}!LB;QA5H87)>>k*900l?sg&oR(k-nYQ;?Y)Kj;HF?T_sGR2UmuDw|VwUdDyX!cG z6!p(i?K3djMAVrITo9guqF!Q(Ko#jXt*!}TWShuT<2DD>YKZ-KcR@Exj7%lxcy!tq zwGvxZuLf|}v0qPQ2Hm1XObDZ?`S+))BaTQi(Z3iZX};8_va1xGwTeBK_)skg!h>Zm z|DIBp=Oe2iCxS&zHX6JS!%g0k5s^>43YSRYB?p{4MY@(!YPIu3K4!4G@(`I#sBlG& z$n%_jX0$X-CZmtu3>gr|95UX{znedmP6}f@YQ&d`s)Mso=fP*unNvYWltLK9gSFup z?4}DKvLGPj1qwKZ<;fY2ea>l0X(?rHx~Ue6sPpa;CPNo1HbophrRz=jV>n2(MT#%R zWQOt^qpI^5oa>$chQr;7f84-M?utRTeW3r-swk-9D9`#;(5obhrnTk<^5nf4aboo2v69=)C~c=u80la-WwN?aC0NPg z-rP%OpK`H{?Vc-$CS?_rfXd2JhU`q}lDU=*_U`M8Q`5k&(u<|4BD7>(J84rIz#A_L zz4f(kR{b8&o(g#F8qX!1yUd)(=OB6mJ5mP3i3_zgQ55|dxwq0G-{Ezt&~VuKbGTG- zIf{XQ>;rAM`JhS!U{hwn5b!SK`0hyqT%DdBkN`I9a)iFIg8*?(>l6_*?%!f;Z?cBH zc{@OA#>*9&5NZ_o`fQ^Rr@Zh`0+q%j*fC&NJg_(y^}^+I%^Cb(0b&51|5p_5)4WYpsln`iuB@P{H$Gr8kK4(u`{Qu$c;{yGEc0A<& q+0CO0$L#@-!1;3^c7C{h!!ta?Gd$nJ^Zy3`0RR7wd^=(Qz6SsiM9GN& diff --git a/golang-external-secrets/charts/external-secrets-0.7.2.tgz b/golang-external-secrets/charts/external-secrets-0.7.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..cf3b1b21c5b0b31fcef5c6fa6fbeb52c2d62f73f GIT binary patch literal 50796 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 E0Qm-?AOHXW literal 0 HcmV?d00001 diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index 9bd9ee22..231d96e9 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -13,10 +13,10 @@ clusterGroup: external-secrets: image: - tag: v0.7.0-ubi + tag: v0.7.2-ubi webhook: image: - tag: v0.7.0-ubi + tag: v0.7.2-ubi certController: image: - tag: v0.7.0-ubi + tag: v0.7.2-ubi diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index b74699af..13b41d47 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: acraccesstokens.generators.external-secrets.io spec: @@ -209,7 +209,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: clusterexternalsecrets.external-secrets.io spec: @@ -514,8 +514,6 @@ spec: type: object templateFrom: items: - maxProperties: 1 - minProperties: 1 properties: configMap: properties: @@ -524,6 +522,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -534,6 +535,8 @@ spec: - items - name type: object + literal: + type: string secret: properties: items: @@ -541,6 +544,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -551,6 +557,9 @@ spec: - items - name type: object + target: + default: Data + type: string type: object type: array type: @@ -655,7 +664,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: clustersecretstores.external-secrets.io spec: @@ -1980,6 +1989,11 @@ spec: aws: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: + additionalRoles: + description: AdditionalRoles is a chained list of Role ARNs which the SecretManager provider will sequentially assume before assuming Role + items: + type: string + type: array auth: description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' properties: @@ -3112,7 +3126,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: ecrauthorizationtokens.generators.external-secrets.io spec: @@ -3241,7 +3255,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: externalsecrets.external-secrets.io spec: @@ -3756,8 +3770,6 @@ spec: type: object templateFrom: items: - maxProperties: 1 - minProperties: 1 properties: configMap: properties: @@ -3766,6 +3778,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -3776,6 +3791,8 @@ spec: - items - name type: object + literal: + type: string secret: properties: items: @@ -3783,6 +3800,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -3793,6 +3813,9 @@ spec: - items - name type: object + target: + default: Data + type: string type: object type: array type: @@ -3851,7 +3874,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: fakes.generators.external-secrets.io spec: @@ -3910,7 +3933,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: gcraccesstokens.generators.external-secrets.io spec: @@ -4019,7 +4042,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: passwords.generators.external-secrets.io spec: @@ -4098,7 +4121,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: pushsecrets.external-secrets.io spec: @@ -4313,7 +4336,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: secretstores.external-secrets.io spec: @@ -5638,6 +5661,11 @@ spec: aws: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: + additionalRoles: + description: AdditionalRoles is a chained list of Role ARNs which the SecretManager provider will sequentially assume before assuming Role + items: + type: string + type: array auth: description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' properties: @@ -6771,10 +6799,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6829,10 +6857,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6937,10 +6965,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -6964,10 +6992,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -6992,10 +7020,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7012,10 +7040,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7048,10 +7076,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7087,10 +7115,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7108,10 +7136,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -7132,10 +7160,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7150,9 +7178,10 @@ spec: app.kubernetes.io/instance: golang-external-secrets spec: serviceAccountName: external-secrets-cert-controller + automountServiceAccountToken: true containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -7161,6 +7190,7 @@ spec: - --service-namespace=default - --secret-name=golang-external-secrets-webhook - --secret-namespace=default + ports: - containerPort: 8080 protocol: TCP @@ -7179,10 +7209,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7197,9 +7227,10 @@ spec: app.kubernetes.io/instance: golang-external-secrets spec: serviceAccountName: golang-external-secrets + automountServiceAccountToken: true containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -7215,10 +7246,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7234,9 +7265,10 @@ spec: spec: hostNetwork: false serviceAccountName: external-secrets-webhook + automountServiceAccountToken: true containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index 8cb910d3..75ab0308 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: acraccesstokens.generators.external-secrets.io spec: @@ -209,7 +209,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: clusterexternalsecrets.external-secrets.io spec: @@ -514,8 +514,6 @@ spec: type: object templateFrom: items: - maxProperties: 1 - minProperties: 1 properties: configMap: properties: @@ -524,6 +522,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -534,6 +535,8 @@ spec: - items - name type: object + literal: + type: string secret: properties: items: @@ -541,6 +544,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -551,6 +557,9 @@ spec: - items - name type: object + target: + default: Data + type: string type: object type: array type: @@ -655,7 +664,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: clustersecretstores.external-secrets.io spec: @@ -1980,6 +1989,11 @@ spec: aws: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: + additionalRoles: + description: AdditionalRoles is a chained list of Role ARNs which the SecretManager provider will sequentially assume before assuming Role + items: + type: string + type: array auth: description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' properties: @@ -3112,7 +3126,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: ecrauthorizationtokens.generators.external-secrets.io spec: @@ -3241,7 +3255,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: externalsecrets.external-secrets.io spec: @@ -3756,8 +3770,6 @@ spec: type: object templateFrom: items: - maxProperties: 1 - minProperties: 1 properties: configMap: properties: @@ -3766,6 +3778,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -3776,6 +3791,8 @@ spec: - items - name type: object + literal: + type: string secret: properties: items: @@ -3783,6 +3800,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -3793,6 +3813,9 @@ spec: - items - name type: object + target: + default: Data + type: string type: object type: array type: @@ -3851,7 +3874,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: fakes.generators.external-secrets.io spec: @@ -3910,7 +3933,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: gcraccesstokens.generators.external-secrets.io spec: @@ -4019,7 +4042,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: passwords.generators.external-secrets.io spec: @@ -4098,7 +4121,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: pushsecrets.external-secrets.io spec: @@ -4313,7 +4336,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: secretstores.external-secrets.io spec: @@ -5638,6 +5661,11 @@ spec: aws: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: + additionalRoles: + description: AdditionalRoles is a chained list of Role ARNs which the SecretManager provider will sequentially assume before assuming Role + items: + type: string + type: array auth: description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' properties: @@ -6771,10 +6799,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6829,10 +6857,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6937,10 +6965,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -6964,10 +6992,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -6992,10 +7020,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7012,10 +7040,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7048,10 +7076,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7087,10 +7115,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7108,10 +7136,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -7132,10 +7160,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7150,9 +7178,10 @@ spec: app.kubernetes.io/instance: golang-external-secrets spec: serviceAccountName: external-secrets-cert-controller + automountServiceAccountToken: true containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -7161,6 +7190,7 @@ spec: - --service-namespace=default - --secret-name=golang-external-secrets-webhook - --secret-namespace=default + ports: - containerPort: 8080 protocol: TCP @@ -7179,10 +7209,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7197,9 +7227,10 @@ spec: app.kubernetes.io/instance: golang-external-secrets spec: serviceAccountName: golang-external-secrets + automountServiceAccountToken: true containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -7215,10 +7246,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7234,9 +7265,10 @@ spec: spec: hostNetwork: false serviceAccountName: external-secrets-webhook + automountServiceAccountToken: true containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index 8cb910d3..75ab0308 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: acraccesstokens.generators.external-secrets.io spec: @@ -209,7 +209,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: clusterexternalsecrets.external-secrets.io spec: @@ -514,8 +514,6 @@ spec: type: object templateFrom: items: - maxProperties: 1 - minProperties: 1 properties: configMap: properties: @@ -524,6 +522,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -534,6 +535,8 @@ spec: - items - name type: object + literal: + type: string secret: properties: items: @@ -541,6 +544,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -551,6 +557,9 @@ spec: - items - name type: object + target: + default: Data + type: string type: object type: array type: @@ -655,7 +664,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: clustersecretstores.external-secrets.io spec: @@ -1980,6 +1989,11 @@ spec: aws: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: + additionalRoles: + description: AdditionalRoles is a chained list of Role ARNs which the SecretManager provider will sequentially assume before assuming Role + items: + type: string + type: array auth: description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' properties: @@ -3112,7 +3126,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: ecrauthorizationtokens.generators.external-secrets.io spec: @@ -3241,7 +3255,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: externalsecrets.external-secrets.io spec: @@ -3756,8 +3770,6 @@ spec: type: object templateFrom: items: - maxProperties: 1 - minProperties: 1 properties: configMap: properties: @@ -3766,6 +3778,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -3776,6 +3791,8 @@ spec: - items - name type: object + literal: + type: string secret: properties: items: @@ -3783,6 +3800,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -3793,6 +3813,9 @@ spec: - items - name type: object + target: + default: Data + type: string type: object type: array type: @@ -3851,7 +3874,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: fakes.generators.external-secrets.io spec: @@ -3910,7 +3933,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: gcraccesstokens.generators.external-secrets.io spec: @@ -4019,7 +4042,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: passwords.generators.external-secrets.io spec: @@ -4098,7 +4121,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: pushsecrets.external-secrets.io spec: @@ -4313,7 +4336,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: secretstores.external-secrets.io spec: @@ -5638,6 +5661,11 @@ spec: aws: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: + additionalRoles: + description: AdditionalRoles is a chained list of Role ARNs which the SecretManager provider will sequentially assume before assuming Role + items: + type: string + type: array auth: description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' properties: @@ -6771,10 +6799,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6829,10 +6857,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6937,10 +6965,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -6964,10 +6992,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -6992,10 +7020,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7012,10 +7040,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7048,10 +7076,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7087,10 +7115,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7108,10 +7136,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -7132,10 +7160,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7150,9 +7178,10 @@ spec: app.kubernetes.io/instance: golang-external-secrets spec: serviceAccountName: external-secrets-cert-controller + automountServiceAccountToken: true containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -7161,6 +7190,7 @@ spec: - --service-namespace=default - --secret-name=golang-external-secrets-webhook - --secret-namespace=default + ports: - containerPort: 8080 protocol: TCP @@ -7179,10 +7209,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7197,9 +7227,10 @@ spec: app.kubernetes.io/instance: golang-external-secrets spec: serviceAccountName: golang-external-secrets + automountServiceAccountToken: true containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -7215,10 +7246,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7234,9 +7265,10 @@ spec: spec: hostNetwork: false serviceAccountName: external-secrets-webhook + automountServiceAccountToken: true containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index ab78d185..68eeb525 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: acraccesstokens.generators.external-secrets.io spec: @@ -209,7 +209,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: clusterexternalsecrets.external-secrets.io spec: @@ -514,8 +514,6 @@ spec: type: object templateFrom: items: - maxProperties: 1 - minProperties: 1 properties: configMap: properties: @@ -524,6 +522,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -534,6 +535,8 @@ spec: - items - name type: object + literal: + type: string secret: properties: items: @@ -541,6 +544,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -551,6 +557,9 @@ spec: - items - name type: object + target: + default: Data + type: string type: object type: array type: @@ -655,7 +664,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: clustersecretstores.external-secrets.io spec: @@ -1980,6 +1989,11 @@ spec: aws: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: + additionalRoles: + description: AdditionalRoles is a chained list of Role ARNs which the SecretManager provider will sequentially assume before assuming Role + items: + type: string + type: array auth: description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' properties: @@ -3112,7 +3126,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: ecrauthorizationtokens.generators.external-secrets.io spec: @@ -3241,7 +3255,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: externalsecrets.external-secrets.io spec: @@ -3756,8 +3770,6 @@ spec: type: object templateFrom: items: - maxProperties: 1 - minProperties: 1 properties: configMap: properties: @@ -3766,6 +3778,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -3776,6 +3791,8 @@ spec: - items - name type: object + literal: + type: string secret: properties: items: @@ -3783,6 +3800,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -3793,6 +3813,9 @@ spec: - items - name type: object + target: + default: Data + type: string type: object type: array type: @@ -3851,7 +3874,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: fakes.generators.external-secrets.io spec: @@ -3910,7 +3933,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: gcraccesstokens.generators.external-secrets.io spec: @@ -4019,7 +4042,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: passwords.generators.external-secrets.io spec: @@ -4098,7 +4121,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: pushsecrets.external-secrets.io spec: @@ -4313,7 +4336,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: secretstores.external-secrets.io spec: @@ -5638,6 +5661,11 @@ spec: aws: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: + additionalRoles: + description: AdditionalRoles is a chained list of Role ARNs which the SecretManager provider will sequentially assume before assuming Role + items: + type: string + type: array auth: description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' properties: @@ -6771,10 +6799,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6829,10 +6857,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6937,10 +6965,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -6964,10 +6992,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -6992,10 +7020,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7012,10 +7040,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7048,10 +7076,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7087,10 +7115,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7108,10 +7136,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -7132,10 +7160,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7150,9 +7178,10 @@ spec: app.kubernetes.io/instance: golang-external-secrets spec: serviceAccountName: external-secrets-cert-controller + automountServiceAccountToken: true containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -7161,6 +7190,7 @@ spec: - --service-namespace=default - --secret-name=golang-external-secrets-webhook - --secret-namespace=default + ports: - containerPort: 8080 protocol: TCP @@ -7179,10 +7209,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7197,9 +7227,10 @@ spec: app.kubernetes.io/instance: golang-external-secrets spec: serviceAccountName: golang-external-secrets + automountServiceAccountToken: true containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -7215,10 +7246,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7234,9 +7265,10 @@ spec: spec: hostNetwork: false serviceAccountName: external-secrets-webhook + automountServiceAccountToken: true containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 8cb910d3..75ab0308 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: acraccesstokens.generators.external-secrets.io spec: @@ -209,7 +209,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: clusterexternalsecrets.external-secrets.io spec: @@ -514,8 +514,6 @@ spec: type: object templateFrom: items: - maxProperties: 1 - minProperties: 1 properties: configMap: properties: @@ -524,6 +522,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -534,6 +535,8 @@ spec: - items - name type: object + literal: + type: string secret: properties: items: @@ -541,6 +544,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -551,6 +557,9 @@ spec: - items - name type: object + target: + default: Data + type: string type: object type: array type: @@ -655,7 +664,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: clustersecretstores.external-secrets.io spec: @@ -1980,6 +1989,11 @@ spec: aws: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: + additionalRoles: + description: AdditionalRoles is a chained list of Role ARNs which the SecretManager provider will sequentially assume before assuming Role + items: + type: string + type: array auth: description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' properties: @@ -3112,7 +3126,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: ecrauthorizationtokens.generators.external-secrets.io spec: @@ -3241,7 +3255,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: externalsecrets.external-secrets.io spec: @@ -3756,8 +3770,6 @@ spec: type: object templateFrom: items: - maxProperties: 1 - minProperties: 1 properties: configMap: properties: @@ -3766,6 +3778,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -3776,6 +3791,8 @@ spec: - items - name type: object + literal: + type: string secret: properties: items: @@ -3783,6 +3800,9 @@ spec: properties: key: type: string + templateAs: + default: Values + type: string required: - key type: object @@ -3793,6 +3813,9 @@ spec: - items - name type: object + target: + default: Data + type: string type: object type: array type: @@ -3851,7 +3874,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: fakes.generators.external-secrets.io spec: @@ -3910,7 +3933,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: gcraccesstokens.generators.external-secrets.io spec: @@ -4019,7 +4042,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: passwords.generators.external-secrets.io spec: @@ -4098,7 +4121,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: pushsecrets.external-secrets.io spec: @@ -4313,7 +4336,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.11.1 creationTimestamp: null name: secretstores.external-secrets.io spec: @@ -5638,6 +5661,11 @@ spec: aws: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: + additionalRoles: + description: AdditionalRoles is a chained list of Role ARNs which the SecretManager provider will sequentially assume before assuming Role + items: + type: string + type: array auth: description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' properties: @@ -6771,10 +6799,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6829,10 +6857,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6937,10 +6965,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -6964,10 +6992,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -6992,10 +7020,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7012,10 +7040,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7048,10 +7076,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7087,10 +7115,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7108,10 +7136,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -7132,10 +7160,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7150,9 +7178,10 @@ spec: app.kubernetes.io/instance: golang-external-secrets spec: serviceAccountName: external-secrets-cert-controller + automountServiceAccountToken: true containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -7161,6 +7190,7 @@ spec: - --service-namespace=default - --secret-name=golang-external-secrets-webhook - --secret-namespace=default + ports: - containerPort: 8080 protocol: TCP @@ -7179,10 +7209,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7197,9 +7227,10 @@ spec: app.kubernetes.io/instance: golang-external-secrets spec: serviceAccountName: golang-external-secrets + automountServiceAccountToken: true containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -7215,10 +7246,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.7.0 + helm.sh/chart: external-secrets-0.7.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.7.0" + app.kubernetes.io/version: "v0.7.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7234,9 +7265,10 @@ spec: spec: hostNetwork: false serviceAccountName: external-secrets-webhook + automountServiceAccountToken: true containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.2-ubi" imagePullPolicy: IfNotPresent args: - webhook From b7dc09b7c117d1df97a3d19ba2513db6ff6071cc Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 29 Jan 2023 16:10:30 +0100 Subject: [PATCH 0779/1263] Add acm policy to push HUB's CA to managed clusters This avoids the need to use imperative jobs running on the imported clusters and is more declarative anyways. As an added bonus the ESO pods will be a lot faster in being able to connect to the vault running on the hub --- Changes.md | 5 ++ acm/templates/policies/acm-hub-ca-policy.yaml | 69 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 acm/templates/policies/acm-hub-ca-policy.yaml diff --git a/Changes.md b/Changes.md index ce2b1a99..fef551dd 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,10 @@ # Changes +## January 29, 2023 + +* Stop extracting the HUB's CA via an imperative job running on the imported cluster. + Just use ACM to push the HUB's CA out to the managed clusters. + ## January 23, 2023 * Add initial support for running ESO on ACM-imported clusters diff --git a/acm/templates/policies/acm-hub-ca-policy.yaml b/acm/templates/policies/acm-hub-ca-policy.yaml new file mode 100644 index 00000000..6747b92f --- /dev/null +++ b/acm/templates/policies/acm-hub-ca-policy.yaml @@ -0,0 +1,69 @@ +# This pushes out the HUB's Certificate Authorities on to the imported clusters +{{ if .Values.clusterGroup.isHubCluster }} +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: acm-hub-ca-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: acm-hub-ca-config-policy + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: Secret + apiVersion: v1 + type: Opaque + metadata: + name: hub-ca + namespace: imperative + data: + hub-kube-root-ca.crt: '{{ `{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}` }}' + hub-openshift-service-ca.crt: '{{ `{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | base64enc hub}}` }}' +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: acm-hub-ca-policy-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: acm-hub-ca-policy-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: acm-hub-ca-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: acm-hub-ca-policy-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +{{ end }} + From 94e9cd7642794570ba0fcad8c08bac41c806eb79 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 29 Jan 2023 16:22:06 +0100 Subject: [PATCH 0780/1263] Point golang-external-secrets to the secret injected by ACM --- .../templates/golang-external-secrets-hub-secretstore.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml index 4171df44..0245ebf7 100644 --- a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +++ b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml @@ -19,8 +19,8 @@ spec: {{ else }} caProvider: type: Secret - name: hub - key: caBundle + name: hub-ca + key: hub-kube-root-ca.crt namespace: imperative {{ end }} auth: From 0c22f2dd0b950792ce7d484557d67ba2513728ab Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 29 Jan 2023 16:24:31 +0100 Subject: [PATCH 0781/1263] Drop imperative job to fetch ACM HUB CA as we use ACM to push that out to clusters --- .../templates/imperative/unsealjob.yaml | 53 ------------------- 1 file changed, 53 deletions(-) diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index 41b47ad7..d0dbc3c7 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -54,58 +54,5 @@ spec: configMap: name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} restartPolicy: Never -{{- else }} ---- -apiVersion: batch/v1 -kind: CronJob -metadata: - name: acmhub-ca-cronjob - namespace: {{ $.Values.clusterGroup.imperative.namespace}} -spec: - schedule: {{ $.Values.clusterGroup.imperative.insecureUnsealVaultInsideClusterSchedule | quote }} - # if previous Job is still running, skip execution of a new Job - concurrencyPolicy: Forbid - jobTemplate: - spec: - activeDeadlineSeconds: {{ $.Values.clusterGroup.imperative.activeDeadlineSeconds }} - template: - metadata: - name: acmhub-ca-job - spec: - serviceAccountName: {{ $.Values.clusterGroup.imperative.serviceAccountName }} - initContainers: - # git init happens in /git/repo so that we can set the folder to 0770 permissions - # reason for that is ansible refuses to create temporary folders in there - {{- include "imperative.initcontainers.gitinit" . | indent 12 }} - - name: acmhub-ca-playbook - image: {{ $.Values.clusterGroup.imperative.image }} - imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} - env: - - name: HOME - value: /git/home - workingDir: /git/repo - # We have a default timeout of 600s for each playbook. Can be overridden - # on a per-job basis - command: - - timeout - - {{ .timeout | default "600" | quote }} - - ansible-playbook - {{- if $.Values.clusterGroup.imperative.verbosity }} - - {{ $.Values.clusterGroup.imperative.verbosity }} - {{- end }} - - -e - - "@/values/values.yaml" - - "common/ansible/playbooks/acm/acmhub-get-ca.yaml" - volumeMounts: - {{- include "imperative.volumemounts" . | indent 16 }} - containers: - {{- include "imperative.containers.done" . | indent 12 }} - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} - restartPolicy: Never {{- end }} {{- end }} From 4eb20526aced86c1127c402ee41d92244af3072f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 29 Jan 2023 17:23:43 +0100 Subject: [PATCH 0782/1263] Fix up tests --- .../acm-industrial-edge-factory.expected.yaml | 3 + tests/acm-industrial-edge-hub.expected.yaml | 71 ++++++++++++++++++ tests/acm-medical-diagnosis-hub.expected.yaml | 71 ++++++++++++++++++ tests/acm-naked.expected.yaml | 3 + tests/acm-normal.expected.yaml | 71 ++++++++++++++++++ ...roup-industrial-edge-factory.expected.yaml | 74 ------------------- ...rets-industrial-edge-factory.expected.yaml | 4 +- 7 files changed, 221 insertions(+), 76 deletions(-) diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index e94c6a51..eecf05fc 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -1,4 +1,7 @@ --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +# This pushes out the HUB's Certificate Authorities on to the imported clusters +--- # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io --- diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 008e0a39..074bf864 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -1,4 +1,7 @@ --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +# This pushes out the HUB's Certificate Authorities on to the imported clusters +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub @@ -9,6 +12,22 @@ metadata: argocd.argoproj.io/sync-wave: "-1" spec: {} --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: acm-hub-ca-policy-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: acm-hub-ca-policy-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: acm-hub-ca-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- # Source: acm/templates/policies/application-policies.yaml apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding @@ -41,6 +60,22 @@ subjects: kind: Policy apiGroup: policy.open-cluster-management.io --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: acm-hub-ca-policy-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +--- # Source: acm/templates/policies/application-policies.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule @@ -81,6 +116,42 @@ spec: values: - OpenShift --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: acm-hub-ca-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: acm-hub-ca-config-policy + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: Secret + apiVersion: v1 + type: Opaque + metadata: + name: hub-ca + namespace: imperative + data: + hub-kube-root-ca.crt: '{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}' + hub-openshift-service-ca.crt: '{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | base64enc hub}}' +--- # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io apiVersion: policy.open-cluster-management.io/v1 diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 7f4791e8..50cecece 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -1,4 +1,7 @@ --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +# This pushes out the HUB's Certificate Authorities on to the imported clusters +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub @@ -9,6 +12,22 @@ metadata: argocd.argoproj.io/sync-wave: "-1" spec: {} --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: acm-hub-ca-policy-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: acm-hub-ca-policy-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: acm-hub-ca-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- # Source: acm/templates/policies/application-policies.yaml apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding @@ -41,6 +60,22 @@ subjects: kind: Policy apiGroup: policy.open-cluster-management.io --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: acm-hub-ca-policy-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +--- # Source: acm/templates/policies/application-policies.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule @@ -72,6 +107,42 @@ spec: values: - OpenShift --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: acm-hub-ca-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: acm-hub-ca-config-policy + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: Secret + apiVersion: v1 + type: Opaque + metadata: + name: hub-ca + namespace: imperative + data: + hub-kube-root-ca.crt: '{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}' + hub-openshift-service-ca.crt: '{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | base64enc hub}}' +--- # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io apiVersion: policy.open-cluster-management.io/v1 diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index e94c6a51..eecf05fc 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -1,4 +1,7 @@ --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +# This pushes out the HUB's Certificate Authorities on to the imported clusters +--- # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io --- diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index e2e974ed..5281e747 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -19,6 +19,9 @@ data: install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXp1cmUtdXMnIApiYXNlRG9tYWluOiBibHVlcHJpbnRzLnJoZWNvZW5nLmNvbQpjb250cm9sUGxhbmU6CiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgbmFtZTogY29udHJvbFBsYW5lCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF6dXJlOgogICAgICB0eXBlOiBTdGFuZGFyZF9EOHNfdjMKY29tcHV0ZToKLSBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBuYW1lOiAnd29ya2VyJwogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhenVyZToKICAgICAgdHlwZTogU3RhbmRhcmRfRDhzX3YzCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT3BlblNoaWZ0U0ROCiAgc2VydmljZU5ldHdvcms6CiAgLSAxNzIuMzAuMC4wLzE2CnBsYXRmb3JtOiB7CiAgImF6dXJlIjogewogICAgImJhc2VEb21haW5SZXNvdXJjZUdyb3VwTmFtZSI6ICJkb2pvLWRucy16b25lcyIsCiAgICAicmVnaW9uIjogImVhc3R1cyIKICB9Cn0KcHVsbFNlY3JldDogIiIgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cwpzc2hLZXk6ICIiICAgICAjIHNraXAsIGhpdmUgd2lsbCBpbmplY3QgYmFzZWQgb24gaXQncyBzZWNyZXRz type: Opaque --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +# This pushes out the HUB's Certificate Authorities on to the imported clusters +--- # Source: acm/templates/provision/clusterpool.yaml apiVersion: hive.openshift.io/v1 kind: ClusterClaim @@ -398,6 +401,22 @@ metadata: argocd.argoproj.io/sync-wave: "-1" spec: {} --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: acm-hub-ca-policy-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: acm-hub-ca-policy-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: acm-hub-ca-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- # Source: acm/templates/policies/application-policies.yaml apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding @@ -446,6 +465,22 @@ subjects: kind: Policy apiGroup: policy.open-cluster-management.io --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: acm-hub-ca-policy-placement +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +--- # Source: acm/templates/policies/application-policies.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule @@ -488,6 +523,42 @@ spec: values: - OpenShift --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: acm-hub-ca-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: acm-hub-ca-config-policy + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: Secret + apiVersion: v1 + type: Opaque + metadata: + name: hub-ca + namespace: imperative + data: + hub-kube-root-ca.crt: '{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}' + hub-openshift-service-ca.crt: '{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | base64enc hub}}' +--- # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io apiVersion: policy.open-cluster-management.io/v1 diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 335a2682..c1aef7c3 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -336,80 +336,6 @@ spec: name: helm-values-configmap-factory restartPolicy: Never --- -# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml -apiVersion: batch/v1 -kind: CronJob -metadata: - name: acmhub-ca-cronjob - namespace: imperative -spec: - schedule: "*/5 * * * *" - # if previous Job is still running, skip execution of a new Job - concurrencyPolicy: Forbid - jobTemplate: - spec: - activeDeadlineSeconds: 3600 - template: - metadata: - name: acmhub-ca-job - spec: - serviceAccountName: imperative-sa - initContainers: - # git init happens in /git/repo so that we can set the folder to 0770 permissions - # reason for that is ansible refuses to create temporary folders in there - - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - env: - - name: HOME - value: /git/home - command: - - 'sh' - - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" - volumeMounts: - - name: git - mountPath: "/git" - - name: acmhub-ca-playbook - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - env: - - name: HOME - value: /git/home - workingDir: /git/repo - # We have a default timeout of 600s for each playbook. Can be overridden - # on a per-job basis - command: - - timeout - - "600" - - ansible-playbook - - -e - - "@/values/values.yaml" - - "common/ansible/playbooks/acm/acmhub-get-ca.yaml" - volumeMounts: - - name: git - mountPath: "/git" - - name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml - containers: - - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - command: - - 'sh' - - '-c' - - 'echo' - - 'done' - - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap-factory - restartPolicy: Never ---- # Source: pattern-clustergroup/templates/core/subscriptions.yaml --- --- diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index 13b41d47..f81f1558 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -7316,8 +7316,8 @@ spec: caProvider: type: Secret - name: hub - key: caBundle + name: hub-ca + key: hub-kube-root-ca.crt namespace: imperative auth: From 61dd9b6b365a479aadf2ba82fa237d27f249b1e5 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 31 Jan 2023 10:16:26 -0600 Subject: [PATCH 0783/1263] Loosen grep regex to catch compound/alias Makefile targets --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d737ea14..b6fda968 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) .PHONY: help help: ## This help message - @printf "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)\n" + @printf "$$(grep -hE '^\S.*:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)\n" # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show From 256002c9dad086d6dbbfea605ef0962968ba826a Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 31 Jan 2023 11:14:34 -0600 Subject: [PATCH 0784/1263] Add pattern-home mount to allow resolution of ~ in files --- scripts/pattern-util.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 47b64a8f..585f65db 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -25,6 +25,7 @@ podman run -it \ ${KUBECONF_ENV} \ ${SSH_SOCK_MOUNTS} \ -v ${HOME}:${HOME} \ + -v ${HOME}:/pattern-home \ -v ${HOME}:/root \ -w $(pwd) \ "$PATTERN_UTILITY_CONTAINER" \ From 2dfcfaf1d2d4795c02592f64da5ddfbd6d3d1e75 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 2 Feb 2023 15:17:07 +0100 Subject: [PATCH 0785/1263] Better help output This way the output is split up in multiple paragraph and things are a bit clearer. Now the output is as follows: Usage: make Pattern Common Tasks help This help message show show the starting template without installing it operator-deploy runs helm install uninstall runs helm uninstall load-secrets loads the secrets into the vault Validation Tasks validate-origin verify the git origin is available validate-schema validates values files against schema in common/clustergroup validate-prereq verify pre-requisites Test and Linters Tasks test run helm tests helmlint run helm lint kubeconform run helm kubeconform super-linter Runs super linter locally ansible-lint run ansible lint on ansible/ folder ansible-unittest run ansible unit tests Note that we are intentionally not adding support yet for multiple targets on the same line as they mess up the output. We rather drop the multiple definitions. --- Makefile | 68 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/Makefile b/Makefile index b6fda968..181c275f 100644 --- a/Makefile +++ b/Makefile @@ -12,9 +12,12 @@ TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) # --set values always take precedence over the contents of -f HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) + +##@ Pattern Common Tasks + .PHONY: help help: ## This help message - @printf "$$(grep -hE '^\S.*:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)\n" + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^(\s|[a-zA-Z_0-9-])+:.*?##/ { printf " \033[36m%-35s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) # Makefiles in the individual patterns should call these targets explicitly # e.g. from industrial-edge: make -f common/Makefile show @@ -22,6 +25,23 @@ help: ## This help message show: ## show the starting template without installing it helm template common/operator-install/ --name-template $(NAME) $(HELM_OPTS) +.PHONY: operator-deploy +operator-deploy operator-upgrade: validate-prereq validate-origin ## runs helm install + @echo "Running helm:" + helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) + +.PHONY: uninstall +uninstall: ## runs helm uninstall + $(eval CSV := $(shell oc get subscriptions -n openshift-operators openshift-gitops-operator -ojsonpath={.status.currentCSV})) + helm uninstall $(NAME) + @oc delete csv -n openshift-operators $(CSV) + +.PHONY: load-secrets +load-secrets: ## loads the secrets into the vault + common/scripts/vault-utils.sh push_secrets $(NAME) + +##@ Validation Tasks + # We only check the remote ssh git branch's existance if we're not running inside a container # as getting ssh auth working inside a container seems a bit brittle .PHONY: validate-origin @@ -43,24 +63,19 @@ validate-schema: ## validates values files against schema in common/clustergroup @set -e; for i in $(VAL_PARAMS); do echo -n " $$i"; helm template common/clustergroup $(HELM_OPTS) -f "$${i}" >/dev/null; done @echo -.PHONY: operator-deploy operator-upgrade -operator-deploy operator-upgrade: validate-prereq validate-origin ## runs helm install - @echo "Running helm:" - helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) - -.PHONY: deploy upgrade legacy-deploy legacy-upgrade -deploy upgrade legacy-deploy legacy-upgrade: ## does nothing anymore. use operator-deploy - @echo "UNSUPPORTED TARGET: please switch to 'operator-deploy'"; exit 1 - -.PHONY: uninstall -uninstall: ## runs helm uninstall - $(eval CSV := $(shell oc get subscriptions -n openshift-operators openshift-gitops-operator -ojsonpath={.status.currentCSV})) - helm uninstall $(NAME) - @oc delete csv -n openshift-operators $(CSV) +.PHONY: validate-prereq +validate-prereq: ## verify pre-requisites + @echo "Checking prerequisites:" + @for t in $(EXECUTABLES); do if ! which $$t > /dev/null 2>&1; then echo "No $$t in PATH"; exit 1; fi; done + @echo " Check for '$(EXECUTABLES)': OK" + @echo -n " Check for python-kubernetes: " + @if ! ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1; then echo "Not found"; exit 1; fi + @echo "OK" + @echo -n " Check for kubernetes.core collection: " + @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi + @echo "OK" -.PHONY: load-secrets -load-secrets: ## loads the secrets into the vault - common/scripts/vault-utils.sh push_secrets $(NAME) +##@ Test and Linters Tasks CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | grep -v examples | sed -e 's/.\///') # Section related to tests and linting @@ -86,18 +101,6 @@ KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition' kubeconform: ## run helm kubeconform @for t in $(CHARTS); do helm template $(TEST_OPTS) $(PATTERN_OPTS) $$t | kubeconform -strict $(KUBECONFORM_SKIP) -verbose -schema-location $(API_URL); if [ $$? != 0 ]; then exit 1; fi; done -.PHONY: validate-prereq -validate-prereq: ## verify pre-requisites - @echo "Checking prerequisites:" - @for t in $(EXECUTABLES); do if ! which $$t > /dev/null 2>&1; then echo "No $$t in PATH"; exit 1; fi; done - @echo " Check for '$(EXECUTABLES)': OK" - @echo -n " Check for python-kubernetes: " - @if ! ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1; then echo "Not found"; exit 1; fi - @echo "OK" - @echo -n " Check for kubernetes.core collection: " - @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi - @echo "OK" - .PHONY: super-linter super-linter: ## Runs super linter locally rm -rf .mypy_cache @@ -119,3 +122,8 @@ ansible-lint: ## run ansible lint on ansible/ folder .PHONY: ansible-unittest ansible-unittest: ## run ansible unit tests pytest -r a --fulltrace --color yes ansible/tests/unit/test_*.py + +.PHONY: deploy upgrade legacy-deploy legacy-upgrade +deploy upgrade legacy-deploy legacy-upgrade: ## does nothing anymore. use operator-deploy + @echo "UNSUPPORTED TARGET: please switch to 'operator-deploy'"; exit 1 + From 5f69dcb30500626d8e223cba42116807b8222a55 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 2 Feb 2023 17:31:23 +0100 Subject: [PATCH 0786/1263] Stop outputting help for deprecated commands --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 181c275f..8679d7ce 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,6 @@ ansible-unittest: ## run ansible unit tests pytest -r a --fulltrace --color yes ansible/tests/unit/test_*.py .PHONY: deploy upgrade legacy-deploy legacy-upgrade -deploy upgrade legacy-deploy legacy-upgrade: ## does nothing anymore. use operator-deploy +deploy upgrade legacy-deploy legacy-upgrade: @echo "UNSUPPORTED TARGET: please switch to 'operator-deploy'"; exit 1 From 3f32c0f2d7e63048b93f73db0e1e19ce5042659a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 2 Feb 2023 20:27:57 +0100 Subject: [PATCH 0787/1263] Allow additional properties for GlobalGit --- clustergroup/values.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 6f4cfab0..29447988 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -148,7 +148,7 @@ }, "GlobalGit": { "type": "object", - "additionalProperties": false, + "additionalProperties": true, "description": "The git configuration used to support Tekton pipeline tasks.", "properties": { "hostname": { From 2cbeb74fc59bb4b00c05031b17b6895bcc93aa03 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 8 Feb 2023 09:10:48 +0100 Subject: [PATCH 0788/1263] Print which files we are using to parse values secrets --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index d5bb8d15..b2cb28fa 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -77,6 +77,10 @@ register: encrypted failed_when: (encrypted.rc not in [0, 1]) +- name: Is found values secret file encrypted + ansible.builtin.debug: + msg: "Using {{ found_file }} to parse secrets" + - name: Set encryption bool fact no_log: true ansible.builtin.set_fact: From 5948f0a4ced0b75ab68a1278381be0c69617d4a0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 9 Feb 2023 08:17:04 +0100 Subject: [PATCH 0789/1263] Drop SSH_SOCK_MOUNT from pattern-util.sh As reported by Jonny this might be problematic on some Mac OSX versions. While the investigation still needs to be done, we can safely drop this additional mount point because in the 'validate-origin' target we are currently skipping the 'git ls-remote' call when running inside the container. We can reassess this at a later time and debug all these corner case, but it will only make sense if we run the git ls-remote check in the container as well. --- scripts/pattern-util.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 585f65db..a2159305 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -7,11 +7,6 @@ fi # Copy Kubeconfig from current environment. The utilities will pick up ~/.kube/config if set so it's not mandatory # $HOME is mounted as itself for any files that are referenced with absolute paths # $HOME is mounted to /root because the UID in the container is 0 and that's where SSH looks for credentials -# We bind mount the SSH_AUTH_SOCK socket if it is set, so ssh works without user prompting -SSH_SOCK_MOUNTS="" -if [ -n "$SSH_AUTH_SOCK" ]; then - SSH_SOCK_MOUNTS="-v ${SSH_AUTH_SOCK}:${SSH_AUTH_SOCK} -e SSH_AUTH_SOCK=${SSH_AUTH_SOCK}" -fi # We must pass -e KUBECONFIG *only* if it is set, otherwise we end up passing # KUBECONFIG="" which then will confuse ansible From ea35c666a56d8dc4d591a3fbce2ea4515938ae73 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 9 Feb 2023 08:54:37 +0100 Subject: [PATCH 0790/1263] Add /values-.yaml and /values--.yaml support Tested this with MCG and got the following: * On the cluster-wide instance on the hub: /values-global.yaml, /values-hub.yaml, /values-AWS.yaml, /values-AWS-4.11.yaml, /values-AWS-hub.y aml, /values-4.11-hub.yaml, /values-mcg-hub.yaml * On the namespaced instance on the hub: /values-global.yaml, /values-hub.yaml, /values-AWS.yaml, /values-AWS-4.11.yaml, /values-AWS-hub.y aml, /values-4.11-hub.yaml * On the imported cluster on the cluster-wide argo instance: /values-global.yaml, /values-group-one.yaml, /values-AWS.yaml, /values-AWS-4.11.yaml, /values-AWS -group-one.yaml, /values-4.11-group-one.yaml * On the imported cluster on the namespaced argo instance: /values-global.yaml, /values-group-one.yaml, /values-AWS.yaml, /values-AWS-4.11.yaml, /values-AWS -group-one.yaml, /values-4.11-group-one.yaml This seems exactly what we want to cover for some of the more exotic corner cases and it is consistent across all argo instances on all clusters. --- Changes.md | 4 ++++ acm/templates/policies/application-policies.yaml | 2 ++ clustergroup/templates/plumbing/applications.yaml | 4 ++++ tests/acm-industrial-edge-hub.expected.yaml | 2 ++ tests/acm-medical-diagnosis-hub.expected.yaml | 2 ++ tests/acm-normal.expected.yaml | 4 ++++ 6 files changed, 18 insertions(+) diff --git a/Changes.md b/Changes.md index fef551dd..79c70b7f 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## February 9, 2023 + +* Add support for /values-.yaml and for /values--.yaml + ## January 29, 2023 * Stop extracting the HUB's CA via an imperative job running on the imported cluster. diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index c62fd68e..4eff7c62 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -45,6 +45,8 @@ spec: valueFiles: - "/values-global.yaml" - "/values-{{ .name }}.yaml" + - '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}.yaml' + - '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}-{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}.yaml' - '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}-{{ .name }}.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 57c1cad8..2593942f 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -152,6 +152,10 @@ spec: - "/values-global.yaml" - "/values-{{ $.Values.clusterGroup.name }}.yaml" {{- if $.Values.global.clusterPlatform }} + - "/values-{{ $.Values.global.clusterPlatform }}.yaml" + {{- if $.Values.global.clusterVersion }} + - "/values-{{ $.Values.global.clusterPlatform }}-{{ $.Values.global.clusterVersion }}.yaml" + {{- end }} - "/values-{{ $.Values.global.clusterPlatform }}-{{ $.Values.clusterGroup.name }}.yaml" {{- end }} {{- if $.Values.global.clusterVersion }} diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 074bf864..5b369a19 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -197,6 +197,8 @@ spec: valueFiles: - "/values-global.yaml" - "/values-factory.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-factory.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 50cecece..eeb5c575 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -188,6 +188,8 @@ spec: valueFiles: - "/values-global.yaml" - "/values-region-one.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-region-one.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 5281e747..e6fadf6e 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -604,6 +604,8 @@ spec: valueFiles: - "/values-global.yaml" - "/values-acm-edge.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-edge.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version @@ -694,6 +696,8 @@ spec: valueFiles: - "/values-global.yaml" - "/values-acm-provision-edge.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-edge.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version From 86724bc15ff79c789b7643a382933255da17d949 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 9 Feb 2023 19:18:25 +0100 Subject: [PATCH 0791/1263] Bail out on unsupported podman versions Tested on Fedora and on Mac OSX. We use the grep -E on the '\b' word boundary to be a bit more resilient in our grep. --- scripts/pattern-util.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index a2159305..48e407c8 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -4,6 +4,16 @@ if [ -z "$PATTERN_UTILITY_CONTAINER" ]; then PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/utility-container" fi +UNSUPPORTED_PODMAN_VERSIONS="1.6 1.5" +for i in ${UNSUPPORTED_PODMAN_VERSIONS}; do + # We add a space + if podman --version | grep -q -E "\b${i}"; then + echo "Unsupported podman version. We recommend >= 4.2.0" + podman --version + exit 1 + fi +done + # Copy Kubeconfig from current environment. The utilities will pick up ~/.kube/config if set so it's not mandatory # $HOME is mounted as itself for any files that are referenced with absolute paths # $HOME is mounted to /root because the UID in the container is 0 and that's where SSH looks for credentials From 801e90680c5d178a404cd03ef644f2c861a16ea0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 22 Feb 2023 10:38:43 +0100 Subject: [PATCH 0792/1263] Use more recent python versions to unreak CI --- .github/workflows/ansible-unittest.yml | 2 +- .github/workflows/jsonschema.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ansible-unittest.yml b/.github/workflows/ansible-unittest.yml index b6a56049..3c8b5c46 100644 --- a/.github/workflows/ansible-unittest.yml +++ b/.github/workflows/ansible-unittest.yml @@ -20,7 +20,7 @@ jobs: name: Ansible unit tests strategy: matrix: - python-version: [3.10.9] + python-version: [3.10.10] # Set the agent to run on runs-on: ubuntu-latest diff --git a/.github/workflows/jsonschema.yaml b/.github/workflows/jsonschema.yaml index d0c19ad8..00232f0b 100644 --- a/.github/workflows/jsonschema.yaml +++ b/.github/workflows/jsonschema.yaml @@ -20,7 +20,7 @@ jobs: name: Json Schema tests strategy: matrix: - python-version: [3.11.1] + python-version: [3.11.2] # Set the agent to run on runs-on: ubuntu-latest From 0a1b8292205fc5def02bfd552eab2854386efcee Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 27 Feb 2023 23:23:57 +0100 Subject: [PATCH 0793/1263] Escape all the variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes sure that any paths with spaces in them does not break things. Tested as follows: * Before ❯ export KUBECONFIG=/tmp/working && ./pattern.sh sh sh-5.1# echo $KUBECONFIG /tmp/working sh-5.1# exit exit ❯ export KUBECONFIG=/tmp/kube\ broken && ./pattern.sh sh ? Please select an image: ▸ registry.fedoraproject.org/broken:latest registry.access.redhat.com/broken:latest docker.io/library/broken:latest quay.io/broken:latest * After ❯ export KUBECONFIG=/tmp/kube\ broken && ./pattern.sh sh sh-5.1# echo $KUBECONFIG /tmp/kube broken sh-5.1# exit exit ❯ export KUBECONFIG=/tmp/working && ./pattern.sh sh sh-5.1# echo $KUBECONFIG /tmp/working --- scripts/pattern-util.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 48e407c8..ca75053e 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -27,11 +27,10 @@ fi podman run -it \ --security-opt label=disable \ - ${KUBECONF_ENV} \ - ${SSH_SOCK_MOUNTS} \ - -v ${HOME}:${HOME} \ - -v ${HOME}:/pattern-home \ - -v ${HOME}:/root \ - -w $(pwd) \ + "${KUBECONF_ENV}" \ + -v "${HOME}":"${HOME}" \ + -v "${HOME}":/pattern-home \ + -v "${HOME}":/root \ + -w "$(pwd)" \ "$PATTERN_UTILITY_CONTAINER" \ $@ From f73409100ac6595f2d7dbdf535975e1aff2ccb75 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 17 Feb 2023 02:14:52 +0000 Subject: [PATCH 0794/1263] Add index image support to the schema --- clustergroup/values.schema.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 29447988..0c6ba8c0 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -223,6 +223,13 @@ "type": "string" } }, + "indexImages": { + "type": "array", + "description": "List of index images for overriding default catalog sources.", + "items": { + "$ref": "#/definitions/IndexImages" + } + }, "operatorgroupExcludes": { "type": "array", "description": "List of namespaces to exclude the creation of operator groups.", @@ -396,6 +403,21 @@ ], "title": "Applications" }, + "IndexImages": { + "type": "object", + "description": "Details for overriding default catalog sources", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "Name for the custom catalog source." + }, + "image": { + "type": "string", + "description": "Location of the index image." + } + } + }, "HostedSite": { "type": "object", "additionalProperties": false, From f35e2f7a7bcc900a22d1a969891b79f8221e4882 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 17 Feb 2023 02:15:34 +0000 Subject: [PATCH 0795/1263] Consume versioned index images, and provide a default name --- clustergroup/templates/core/catalog-sources.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/core/catalog-sources.yaml b/clustergroup/templates/core/catalog-sources.yaml index 2f0c2a95..73c2e949 100644 --- a/clustergroup/templates/core/catalog-sources.yaml +++ b/clustergroup/templates/core/catalog-sources.yaml @@ -1,13 +1,14 @@ {{- if not (eq .Values.enabled "plumbing") }} {{- range .Values.clusterGroup.indexImages }} +{{- $name := mustRegexReplaceAll "[^/]*/(.*):.*" .image "${1}" | replace "/" "-" }} apiVersion: operators.coreos.com/v1alpha1 kind: CatalogSource metadata: - name: {{ .name }} + name: {{ coalesce .name $name }} namespace: openshift-marketplace spec: sourceType: grpc - image: {{ .image }}:{{ .version }} + image: {{ .image }} --- {{- end -}} {{- end -}} From b51775501921797cde38bbf3c6eec83b760a926a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 17 Feb 2023 02:33:26 +0000 Subject: [PATCH 0796/1263] Allow index images to be a map to avoid accidental overrides --- clustergroup/values.schema.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 0c6ba8c0..2377e31b 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -224,7 +224,14 @@ } }, "indexImages": { - "type": "array", + "anyOf": [ + { + "type": "array" + }, + { + "type": "object" + } + ], "description": "List of index images for overriding default catalog sources.", "items": { "$ref": "#/definitions/IndexImages" From 516b74181663e65a2d121390f97fc11f069f0e8a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 17 Feb 2023 02:47:05 +0000 Subject: [PATCH 0797/1263] Support passing index images as extra parameters --- Makefile | 6 +++++- operator-install/templates/pattern.yaml | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8679d7ce..d1618130 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,10 @@ ifneq ($(origin TARGET_SITE), undefined) TARGET_SITE_OPT=--set main.clusterGroupName=$(TARGET_SITE) endif +# INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:394248 +INDEX_IMAGES ?= +INDEX_OPTIONS=$(shell echo $(INDEX_IMAGES) | tr ',' '\n' | awk -F: 'match($$1,"/"){print "--set main.extraParameters."NR".name=clusterGroup.indexImages."NR".image --set main.extraParameters."NR".value="$$1":"$$2}') + TARGET_ORIGIN ?= origin # This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL # This is because we expect to use tokens for repo authentication as opposed to SSH keys @@ -11,7 +15,7 @@ TARGET_REPO=$(shell git ls-remote --get-url --symref $(TARGET_ORIGIN) | sed -e ' TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) # --set values always take precedence over the contents of -f -HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) +HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) $(INDEX_OPTIONS) ##@ Pattern Common Tasks diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index 2ac7b52a..d5a6c543 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -10,3 +10,10 @@ spec: targetRevision: {{ .Values.main.git.revision }} gitOpsSpec: operatorChannel: {{ default "stable" .Values.main.gitops.channel }} +{{- if .Values.main.extraParameters }} + extraParameters: +{{- range .Values.main.extraParameters }} + - name: {{ .name }} + value: {{ .value }} +{{- end }} +{{- end }} \ No newline at end of file From 6103d501a23a228313e736239c25cc34a325b126 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 7 Mar 2023 04:23:30 +0000 Subject: [PATCH 0798/1263] Update index image example --- clustergroup/test.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clustergroup/test.yaml b/clustergroup/test.yaml index 5f758643..3c0afc6f 100644 --- a/clustergroup/test.yaml +++ b/clustergroup/test.yaml @@ -10,8 +10,7 @@ clusterGroup: indexImages: - name: snr - image: quay.io/mshitrit/self-node-remediation-manager-index - version: 0.0.104 + image: quay.io/mshitrit/self-node-remediation-manager-index:0.0.104 subscriptions: acm: From 6caea07bb62e2c7fb4cccf4f27e9f94adcf9a959 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 8 Mar 2023 17:41:27 +0100 Subject: [PATCH 0799/1263] Add SkipDryRunOnMissingResource=true to all PlacementRules All other objects under common/acm already have this sync-option. Now the reason this is mandatory is because during sync-wave: -1 we create the "kind: MultiClusterHub" which actually installs the operator which creates all the ACM objects we then use (PlacementRules, PlacementBindings, Policies). Then during the default sync-wave ("0") we create all other objects. The most astute reader will wonder why this is needed at all, since on paper, on wave "-1" MCH gets created/installed and later during wave "0" all the objects dependent on MCG should be installed. Well, buckle up, my friend: Argo will just straight out refuse to proceed syncing any object (MCH included) unless those objects have the SkipDryRunOnMissingResource=true annotation set. In this case sync-waves do not matter *at all*. The logs in the gitops server will be something along these lines: time="2023-03-08T11:28:02Z" level=info msg="Adding resource result, status: 'SyncFailed', phase: '', message: 'the server could not find the requested resource'" application=acm kind=PlacementRule What makes this even more "fun" is that this has always worked for us up until ACM 2.7 when MCH suddenly stopped being installed. Likely it all has worked by pure accident until now and the new ACM/MCH combo triggers a slight different ordering which sets us up for the current bag of fail. Tested this with both ACM 2.7 and ACM 2.6 and ACM + MCH got installed correctly. Closes: MBP-458 --- acm/templates/policies/acm-hub-ca-policy.yaml | 2 ++ acm/templates/policies/application-policies.yaml | 2 ++ acm/templates/policies/ocp-gitops-policy.yaml | 2 ++ tests/acm-industrial-edge-factory.expected.yaml | 2 ++ tests/acm-industrial-edge-hub.expected.yaml | 6 ++++++ tests/acm-medical-diagnosis-hub.expected.yaml | 6 ++++++ tests/acm-naked.expected.yaml | 2 ++ tests/acm-normal.expected.yaml | 8 ++++++++ 8 files changed, 30 insertions(+) diff --git a/acm/templates/policies/acm-hub-ca-policy.yaml b/acm/templates/policies/acm-hub-ca-policy.yaml index 6747b92f..890e6bae 100644 --- a/acm/templates/policies/acm-hub-ca-policy.yaml +++ b/acm/templates/policies/acm-hub-ca-policy.yaml @@ -55,6 +55,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: acm-hub-ca-policy-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 4eff7c62..0cb10ae0 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -124,6 +124,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: {{ .name }}-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index 7ca61b0f..d2ead9d3 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -64,6 +64,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: openshift-gitops-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index eecf05fc..d3da852a 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -36,6 +36,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: openshift-gitops-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 5b369a19..e468cb4f 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -65,6 +65,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: acm-hub-ca-policy-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' @@ -81,6 +83,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: factory-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' @@ -105,6 +109,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: openshift-gitops-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index eeb5c575..5b3300b4 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -65,6 +65,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: acm-hub-ca-policy-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' @@ -81,6 +83,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: region-one-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' @@ -96,6 +100,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: openshift-gitops-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index eecf05fc..d3da852a 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -36,6 +36,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: openshift-gitops-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index e6fadf6e..c6e589ae 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -470,6 +470,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: acm-hub-ca-policy-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' @@ -486,6 +488,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: acm-edge-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' @@ -499,6 +503,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: acm-provision-edge-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' @@ -512,6 +518,8 @@ apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: name: openshift-gitops-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: clusterConditions: - status: 'True' From d9efbf2e06955ba677407aced2f395ee71781883 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 9 Mar 2023 14:56:56 +0100 Subject: [PATCH 0800/1263] Do not quote KUBECONF_ENV It is already quoted above, so there is no need for the additional quoting which is actually harmful when it is already unset as we are going to pass down an empty '' to podman which will be confused. --- scripts/pattern-util.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index ca75053e..82416a2d 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -25,9 +25,11 @@ if [ -n "$KUBECONFIG" ]; then KUBECONF_ENV="-e KUBECONFIG=${KUBECONFIG}" fi +# Do not quote the ${KUBECONF_ENV} below, otherwise we will pass '' to podman +# which will be confused podman run -it \ --security-opt label=disable \ - "${KUBECONF_ENV}" \ + ${KUBECONF_ENV} \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ -v "${HOME}":/root \ From 588740aec4e2cd3aa93cb8aec17aee8132b0fa70 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 9 Mar 2023 16:59:41 +0100 Subject: [PATCH 0801/1263] Add support for ~/.config/hybrid-cloud-patterns folder to store secrets One point of feedback received lately, was that having ~/values-secrets*yaml files straight in the ~ folder can clutter the home folder. Add support for storing them inside the ~/.config/hybrid-cloud-patterns folder. Overriding the location completely via VALUES_SECRET is still supported. --- ansible/roles/vault_utils/README.md | 5 +++-- ansible/roles/vault_utils/tasks/push_secrets.yaml | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index 38ae5891..55babb03 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -44,8 +44,9 @@ This relies on [kubernetes.core](https://docs.ansible.com/ansible/latest/collect Currently this role supports two formats: version 1.0 (which is the assumed default when not specified) and version 2.0. The latter is more fatureful and supports generating secrets directly into the vault and also prompting the user for a secret. -By default, the first file that will looked up is `~/values-secret-.yaml` and should that not exist it will look -for `~/values-secret.yaml`. The paths can be overridden by setting the environment variable `VALUES_SECRET` to the path of the +By default, the first file that will looked up is `~/.config/hybrid-cloud-patterns/values-secret-.yaml`, then +`~/values-secret-.yaml` and should that not exist it will look for `~/values-secret.yaml`. +The paths can be overridden by setting the environment variable `VALUES_SECRET` to the path of the secret file. The values secret yaml files can be encrypted with `ansible-vault`. If the role detects they are encrypted, the password to diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index b2cb28fa..422878fb 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -63,6 +63,7 @@ found_file: "{{ lookup('ansible.builtin.first_found', findme) }}" vars: findme: + - "~/.config/hybrid-cloud-patterns/values-secret-{{ pattern_name }}.yaml" - "~/values-secret-{{ pattern_name }}.yaml" - "~/values-secret.yaml" - "{{ pattern_dir }}/values-secret.yaml.template" From 213e54ed31ba1182cf9ac4d621c438b10daba809 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 10 Mar 2023 11:52:59 +0100 Subject: [PATCH 0802/1263] Add a comment about removing older search paths --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 422878fb..0fdaa631 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -58,6 +58,8 @@ - custom_env_values_secret | default('') | length > 0 - custom_file_values_secret.stat.exists +# FIXME(bandini): Eventually around end of 2023(?) we should drop +# ~/values-secret-{{ pattern_name }}.yaml and ~/values-secret.yaml - name: Find first existing values-secret yaml file ansible.builtin.set_fact: found_file: "{{ lookup('ansible.builtin.first_found', findme) }}" From b2462dc273bc21d4b3ad275f50272aaa28d04c12 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 14 Mar 2023 09:28:29 +0100 Subject: [PATCH 0803/1263] Add more logic to `install` target So this commit aims to tweak the `install` make target so that it can be invoked whenever we need to tweak things like git branch or git repo via CLI but the VP pattern has been installed via the OperatorHub. Currently if you just run `make install` from the repo after the operator has been installed via the Web UI, there will be an error like: ``` Error: rendered manifests contain a resource that already exists. Unable to continue with install: Pattern "multicloud-gitops" in namespace "openshift-operators" exists and cannot be imported into the current release: invalid ownership metadata; label validation error: missing key "app.kubernetes.io/managed-by": must be set to "Helm"; annotation validation error: missing key "meta.helm.sh/release-name": must be set to "multicloud-gitops"; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "default" ``` The problem is that the pattern object and the patterns operator subscription are missing some annotations and labels and that confuses helm install later on as it sees the object it needs to create but craps itself as it has no knowledge of it. A few approaches were investigated to solve this: 1. Tweak the operator to add annotations. This, besides being a bit fragile and convoluted, is not a viable path, because the operator would also have to tweak the subscription that was used to install itself, which seems grossly out of bounds. 2. Tweak `make install` to always use `helm template` and pipe it through `oc apply`. This quickly would become rather complex and fragile because of the way CRDs are managed in helm: we would have to first install the CRD, wait for its successful creation and only then do another `helm template ... | oc apply -f-`. This quickly becomes rather involved and fragile. Also it is really reimplementing all of the `helm install` logic. 3. Add a post-renderer script to strip the standard helm annotations (https://helm.sh/docs/chart_best_practices/labels/#standard-labels). Seemingly helm does not pass those annotations to the post renderer but adds them afterwards, because a --post-renderer pointing to a script that does `yq e 'del(.metadata.annotations)'` did not work Given the above limits, I decided to tweak `make install` so that if the CRD for the pattern is missing, it would just run the usual helm install. If the CRD is present then either it has been installed via the operator's UI or via `make install`. Either way, we just just run helm template and pipe it to oc apply, so that we won't force the helm standard annotations. We have two invocations of helm template (one piped to oc apply set-last-applied) so we avoid the following warning during the first reapply call: Warning: resource patterns/multicloud-gitops is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by oc apply. oc apply should only b e used on resources created declaratively by either oc create --save-config or oc apply. The missing annotation will be patched automatically. pattern.gitops.hybrid-cloud-patterns.io/multicloud-gitops configured Warning: resource subscriptions/patterns-operator is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by oc apply. oc apply should o nly be used on resources created declaratively by either oc create --save-config or oc apply. The missing annotation will be patched automatically. subscription.operators.coreos.com/patterns-operator configured Tested by installing a pattern via UI of the VP Operator, then locally ran `make install` and observed the new git branch being applied in the operator: Reapplying helm chart: pattern.gitops.hybrid-cloud-patterns.io/multicloud-gitops configured subscription.operators.coreos.com/patterns-operator configured pattern.gitops.hybrid-cloud-patterns.io/multicloud-gitops configured subscription.operators.coreos.com/patterns-operator configured make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Tested also with a clean `make install` from scratch: Running helm: Release "multicloud-gitops" does not exist. Installing it now. NAME: multicloud-gitops LAST DEPLOYED: Wed Mar 15 12:14:49 2023 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None --- Makefile | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index d1618130..9f660b09 100644 --- a/Makefile +++ b/Makefile @@ -29,10 +29,21 @@ help: ## This help message show: ## show the starting template without installing it helm template common/operator-install/ --name-template $(NAME) $(HELM_OPTS) +# Only call helm install if the CRD is missing. If it already exists just +# push the templated files. +# The reason we have two helm template calls in the else branch is to avoid +# warnings when the chart gets applied the first time, but the resources were +# created first via the VP operator's UI .PHONY: operator-deploy operator-deploy operator-upgrade: validate-prereq validate-origin ## runs helm install - @echo "Running helm:" - helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) + @set -e; if ! oc get crds patterns.gitops.hybrid-cloud-patterns.io >/dev/null 2>&1; then \ + echo "Running helm:"; \ + helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS); \ + else \ + echo "Reapplying helm chart:"; \ + helm template --name-template $(NAME) common/operator-install/ $(HELM_OPTS) | oc apply set-last-applied --create-annotation -f-; \ + helm template --name-template $(NAME) common/operator-install/ $(HELM_OPTS) | oc apply -f-; \ + fi .PHONY: uninstall uninstall: ## runs helm uninstall @@ -130,4 +141,3 @@ ansible-unittest: ## run ansible unit tests .PHONY: deploy upgrade legacy-deploy legacy-upgrade deploy upgrade legacy-deploy legacy-upgrade: @echo "UNSUPPORTED TARGET: please switch to 'operator-deploy'"; exit 1 - From 9a55253ac354490e45fe00f70e775e43748ff94f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 15 Mar 2023 11:31:32 +0100 Subject: [PATCH 0804/1263] Fix .git missing warnings when running super-linter locally See https://github.com/github/super-linter/issues/3786#issuecomment-1444923003 --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d1618130..1763eaf7 100644 --- a/Makefile +++ b/Makefile @@ -117,7 +117,9 @@ super-linter: ## Runs super linter locally -e VALIDATE_DOCKERFILE_HADOLINT=false \ -e VALIDATE_TEKTON=false \ $(DISABLE_LINTERS) \ - -v $(PWD):/tmp/lint:rw,z docker.io/github/super-linter:slim-v4 + -v $(PWD):/tmp/lint:rw,z \ + -w /tmp/lint \ + docker.io/github/super-linter:slim-v4 .PHONY: ansible-lint ansible-lint: ## run ansible lint on ansible/ folder From 7c4f28ad171040e6d57d642e2dfaa946b4f0459f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 20 Mar 2023 11:25:44 +0100 Subject: [PATCH 0805/1263] Switch to ansible-automation-platform-23/ee-supported-rhel8:latest image by default We want to stay up-to-date as to what image containing ansible we use. https://catalog.redhat.com/software/containers/ansible-automation-platform-23/ee-supported-rhel8/62c7060bc1d77e67d5893a78 This image contains ansible 2.14.3 and kubernetes.cor 2.3.2: $ podman run -it --rm --net=host --entrypoint /bin/bash registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest -c "ansible-galaxy collection list|grep kubern" kubernetes.core 2.3.2 Tested on an MCG deployment --- clustergroup/values.yaml | 2 +- ...stergroup-industrial-edge-factory.expected.yaml | 8 ++++---- .../clustergroup-industrial-edge-hub.expected.yaml | 14 +++++++------- ...lustergroup-medical-diagnosis-hub.expected.yaml | 14 +++++++------- tests/clustergroup-naked.expected.yaml | 8 ++++---- tests/clustergroup-normal.expected.yaml | 14 +++++++------- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 414b00cb..18212e4b 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -17,7 +17,7 @@ clusterGroup: imperative: jobs: [] # This image contains ansible + kubernetes.core by default and is used to run the jobs - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest namespace: "imperative" # configmap name in the namespace that will contain all helm values valuesConfigMap: "helm-values-configmap" diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index c1aef7c3..dfb795d4 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -88,7 +88,7 @@ data: clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' jobName: imperative-job @@ -284,7 +284,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -297,7 +297,7 @@ spec: - name: git mountPath: "/git" - name: test - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -320,7 +320,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always command: - 'sh' diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 03db6b16..9c43c8cc 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -209,7 +209,7 @@ data: clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' jobName: imperative-job @@ -445,7 +445,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -458,7 +458,7 @@ spec: - name: git mountPath: "/git" - name: test - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -481,7 +481,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always command: - 'sh' @@ -519,7 +519,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -532,7 +532,7 @@ spec: - name: git mountPath: "/git" - name: unseal-playbook - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -557,7 +557,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always command: - 'sh' diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 0fb20ab7..89ba22d6 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -220,7 +220,7 @@ data: clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' jobName: imperative-job @@ -432,7 +432,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -445,7 +445,7 @@ spec: - name: git mountPath: "/git" - name: test - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -468,7 +468,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always command: - 'sh' @@ -506,7 +506,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -519,7 +519,7 @@ spec: - name: git mountPath: "/git" - name: unseal-playbook - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -544,7 +544,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always command: - 'sh' diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index b85f2bf0..f0c12937 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -43,7 +43,7 @@ data: clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' jobName: imperative-job @@ -199,7 +199,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -212,7 +212,7 @@ spec: - name: git mountPath: "/git" - name: unseal-playbook - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -237,7 +237,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always command: - 'sh' diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 8e29ef47..f7e6d84d 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -76,7 +76,7 @@ data: clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' jobName: imperative-job @@ -314,7 +314,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -327,7 +327,7 @@ spec: - name: git mountPath: "/git" - name: test - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -350,7 +350,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always command: - 'sh' @@ -388,7 +388,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -401,7 +401,7 @@ spec: - name: git mountPath: "/git" - name: unseal-playbook - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always env: - name: HOME @@ -426,7 +426,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest imagePullPolicy: Always command: - 'sh' From e249096dd59ddce8c21a9fec90624e6bcac3d173 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 20 Mar 2023 20:16:05 +0100 Subject: [PATCH 0806/1263] Switch to ESO 0.8.2 Tested on MCG hub + spoke and everything worked correctly on both clusters. --- Changes.md | 4 + golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.7.2.tgz | Bin 50796 -> 0 bytes .../charts/external-secrets-0.8.1.tgz | Bin 0 -> 72021 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 687 ++++++++++++++++-- ...-secrets-industrial-edge-hub.expected.yaml | 687 ++++++++++++++++-- ...ecrets-medical-diagnosis-hub.expected.yaml | 687 ++++++++++++++++-- ...olang-external-secrets-naked.expected.yaml | 687 ++++++++++++++++-- ...lang-external-secrets-normal.expected.yaml | 687 ++++++++++++++++-- 10 files changed, 3203 insertions(+), 244 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.7.2.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.8.1.tgz diff --git a/Changes.md b/Changes.md index 79c70b7f..15dcdf94 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## March 20, 2023 + +* Upgraded ESO to 0.8.1 + ## February 9, 2023 * Add support for /values-.yaml and for /values--.yaml diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index ba782b1f..a0e1afe0 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.7.2" + version: "0.8.1" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.7.2.tgz b/golang-external-secrets/charts/external-secrets-0.7.2.tgz deleted file mode 100644 index cf3b1b21c5b0b31fcef5c6fa6fbeb52c2d62f73f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50796 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 E0Qm-?AOHXW diff --git a/golang-external-secrets/charts/external-secrets-0.8.1.tgz b/golang-external-secrets/charts/external-secrets-0.8.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..73fc4a59ef1b4fa97ea5b661208f14704efa7940 GIT binary patch literal 72021 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 pH+HAAk={W4- zpE0;AP%=%`w_xN}R@~IEr_6@u(katYIIiT^2a2zC$5cjUaT*K6KNv$7{H(r_>&aq&-URaa-gzoVWyxPy7y%;Owazu4^C z(_9|wFHWm^f^8J16Iwsv{Z2eEDhfZYRt*K!kF zP5mSz0!^TD{{@6}(px=TN08R`s>RjZ-q)h$aU(?F%lBr4z(mNFg1|6|m>)`WlmeKH z9J>#!{`2)1)C=1g37OZ3Q7AH+O>)8jM*c~wSf+9KL|uY`vkM@$bX}A4qTt+A1G2i} zmJ)~mxP|B?4Vy;{Li!Ih!MdznGR6x4nQXzxcV+}e%mhB7Dv{yj=#_pFN+d>^y&2pH zP|3w%cY2xLpr=(BkvV@M#-kj3ygCfrvk44!u{Y5>sW=6y1}e7_IsO98F7yG_P%3r6 zB_JRW2zZkHdVYCnz1hCb&EnU}Qz+yBq#)b9w_DjKqj>8F!7`g&i5p_07yQUx3!Pd7kFAZ zXLhNWe3s#d1W8*E1Wl44kpWCXs*s~~230pb3AVH7I+ZzKI+!*a(5nV`g8{&Oq^QK3 zEXPeH8X1ZVQCi)SaWZl&d$tU)HR@?dAoX^AR4zYg371iMP)9p zs5jwJvB3b8Lz(z*8vhOx^dKQdRLL#Otg?2ZBO6UnYed<^V=Rp~p+Wgrs*@+flFUeu zKtJzmTS+fYhz7O0c#fx9+IL&>wNc(LBlqVYKWKbRHXkblg|VP`~8c~e#SMtZah zU2_S1Rq9exKpVi^XzTtTb7LW4iQaGQ|1mcfyKpZDZAwFwMH?wgjg%1* zU~U8%7M6zkKjy}Yr~hSc6m8bcmyTFo9P`Tn>w0^M_x1gwI+&%Obq+1g_jU4Ed44*? zps}J-PFP*f!9LE(&RDNE+WkjtXvAdnFhFfYb@9EB%Pdfl+5huQ`#DkL@#LN}1dL!J zdU)9~3mJ-nFGbogCsA?ZOMP90hFoSil@xVW3#2Jj8$~|TJ!nqt7|MpxMb)hzlXLKD z2?$!JtTJP=*9_&4iIKs)4&Pdxdulbv=KWOeo`qp7gAQ}Zflt-=iV3i}WK^jWtgY4` z;XY*KNezirLfdBJu70hrG-2`REcb4Nfp~^q*OJmqlD3B+3i%b`&V>Vi#%Uth;FGYFz86pWF^t! zEJv?Yvze_w1~v1WDI@dQ^NMx#8?d}1IFX5nRq^T48??2q8kOqDjmbdI#MG9kFh|Z@ zt+d?!h`_s#uGu319tG9zlG~n_>NneUB@=YNe2q&#bOjOb0XjpJJME$5fX}0GxceRY z)}9nP+}KzWM3zqU1I1Yop#zXKkswY^$aRF2}wkR&TF_I&YG+j+6F zRB@Jv{{!v&Mf}NDems2Sb|%+IzEkW1o8KrRwG<^!_ac>1$PdMxIe|6=60~Ho_Qi@O`1otwlK&!&zIxDVW`Dl6;-W^^UMX-k)lVePp zMhWC$9Q`#JBe$)-Uz0v#8J!H97SA808h*umk1scIG_Ip)Yd5fM!aa=eE|FI425rJJ z2Mr9T?TNb=E$hupY}iv^GaH-wPwRH`wbfM(#j;e;V%KEv2DN03AiYl+{d_tDxZT@l z5@5L`2sqCx$)wrh|Nq&`)6$_o%yRVY6h2;RfzAd_u2NTe}fYf7>is}P}Bhk8dlQ~Rgza@n~DTDkhLjZ6?1%5L{&OMQ%wowU&MQU7%RV^YCG9*V?PZCkl`!(iRi}392 z*Yew^tGO&yt+9~5ItJviNVRf7sBQ4Lto|o?@EJMAB3-GAU8(buoj9c=Wdj)Pe_{H5 z&bjjuHw*)ex6!9yJ+KqP3X6>rpWA|J?`awI8O>kHkMGb1>OU>1R`wdMmMSTK*0p#( zk^ihqHB50H9PE@ELTE|)#he~m`0aIn_h9Uxek)Hc%LLpps;5SB8`nB$@t)WOe}rtd zvarGC{9@d;zOm<_=gT+>!8Suk!20+}(=*n22r81mqZIbsp1|yBYN7X~?zIC`>U-@T ziw`(BU@yIkrQO$I8W{sdotH9dS844vc{Dc7LPy?3=nztgoh_i-j=|!eqp6=#t!kPi zb;vjE>QCCmLmkL|*GPPAEUVf5uG>WFZ+17b=06sZ(K)dsgPS@CZ&=U;Q>M4`&1qFm zIV88^7SOjSjo!OtA*%6!&^g^emvZdGBBj3k({8~i#9g6v4g?Hl5mCV=Na2nz&jP;{ zba(h>n)W=Jc%DkqQkrBQjjL@Uzo9-Z@W1Yts=0}TExZAAE1*yj#5H3w!g33NM3^zV zy1Mbug#&;N@~EtM*-#tQS>shM{h>MW-Bj%CmW@oL;<|1muwf^~dJPx=UH`r% z2u9sM;uJ=XzIXWa zs$hoUs2TbP*Pp2ZS0D4$CVL&nk*vN~1xXEGI>HSU+V;ADp@fg;Q>aXR(j*X~v(VZ{ zkjVz(W@$-_E~hEg3yacWDeR}!i)JMx5#6vyBlF5529f@iZLIixlNb-a09bwI=G*tW zqrVPZHWU2M{gPWw`8?ldK3VOgt5HkSy2*BU$9DVcqwlO67B*hVlL~D~GM}U>H>_Z7 zHe?h81%@wqw=uRv*c7vT=EjwOB=@kGUqb2+szvVsdHk$I1!&s29y|$FvP!+M;n0UA zlyv*Ej!Nr`Vzosl1S=Knw~)${Qr5ZlQy!4mt~zj-V;de*X?&-o%wljM8Y**s#6Sv1 zP+Az`ePB(zArKoVeAa&XO~8;D1R`uo=mF=8JUK=9T8?G3WPjg1p=}*xW=ylX zAi*o!9!{9kUav{nnxkPmZjX4!)9Bm7T<8^RpG7?L_5MI`>>FqpDFy!K5Oose2rfN` zf6esCxD8N<$rbQR82RRV-F+Qf0RDbPR0xMAPRlbe{-vf93KZi7{`x+D6^lFSU;*-& zM9mJ5eO0q5M_8X1KJcEPyvr zIizZA7CW;13kO|e?FdePw@9&AS5S?uMzVDyjUd#CSNTk^yr(uSYQu zmu|{Jc3@sKqLeiq4&}{6y&C8?Yx_q>^tHruP9^s5iliR6bsycc2AFp3H(33?AMKKb zC(@A)&DndI+2(ZD2(LAtRVg%oRbWsLD3XmG(B^|XT<$MrZM%F8B3T&FAURjFloIl0 z!g*pgBD0S~d7KYs%|@{BGPCH2WJsB)9fEX!d+9mg_-xFcCsG_T9JSj&kH9bjAnh;G z6PQEIS*V-iTn6b{lvbc`n+A$grJUh9B-gJG7uAo5zrQe8`}@dSnk%~gcGG%b&-@~M z9930}=+xMgY+pcKeb(R0W}HbSL^Ppjni^lDaqpoMW|Bs!L{s zk(b#MzCZ66N-df|TbAYP6OVeeX~?Vp5=?Sz(KRufsXZUidNum2xfvVBZZa^5 zqeXn_Va_@JrmlL$5C}phD$|nFD_m?7_==MY7WhWZuB29LQMqsO&>WnPWipczTH>Cz zZ-W`XlQo+>0W2jZt?76ZH$DH72e#n|0Ir##hrV8PYZHbV0Cs36JrwOBKKH#3t9tK1 zlG$#|$n$N3{-H#W>)zaidjWx6O!J`b$ny)nDw|_yefS}-xmcdB@@ysj&?jPvM&Jt7 z5XWws@}Eqsi%6Y9Cf*#%ETbs=#UIK&p#R>@1>QIwy|K5*q0d9i_FK`{iY zGBy-xL(hPmq>mP6Yd6%fSj2wvZHyh7)}XqkVs6#?m;8C9r|IlFMj3L6&9RF+kbJv z_}lC4p(yVA_TY7)6!cSIR3(qec`6wRG71LFpgY8>z6kc4-iwz4=3p6{)XhtG9+J;- z$TV9`*-Eg!C4&6Yz1p-r0)oe?8yZ7J?sDTYsj!kySB*A0>4O=Zakv&s&LmU1ZUeSN zI$sCTXJi;g&bsqu^rLJ^A^BQ%{mraSe)F93aN_?X>z$%BYod0`v~AlqDs9`gD^cmJ zv~AnAZKKk*ZTmg>{pWO#?s0DR&A!?(_J|d+X2g6p?QMq4wKy@T>cvQ>8htk-WGtFK zxGvt+UcDt@j*OkULZ`p;)?B?j1&oWLl%N^CApb&vTe;7a~v$~P%@Z{*aat3iN_AuC^(_Z&3 zQzM+KzOvK)vJf@<;((G~$Q~~8o>NLG4Wg?5emXF(_|vlO|v zF%6UIRBP)k_|W!EGygK_8X40rJ1J&!xPC3y>>*^_ysDd1D-=cqjpu6{Tu>ud0_d|7 zj9NQeh0k1Fe4v6XDft*SmtjK(H>pGU34Hs>hD_(Yq#c7H!Z{i5=Ao=!p&A2m)ook% zeyLVl4l=5=3@Ey3ljThKL3%6mWmr4UUz}gnj-Tp%&|uOW2)SU6_=&?u z*20-ckJ&A={F_FwOqA>h>)e3W{x>fwNDa&UXQlg^)zM#DtjavaAl818&vNXndnZCq z${@z)ig%w^se%lpkCDHe)0%w<|LT)%)i;W9b($iN;{)(;q7Zo}Fe>oLwCam+XF9!x zzU?=1Z?A84!+$}pd4O$LUQ2<XPxXoCQI8_`$m8?Q>sfT4TwnX87rV54%Hr4z#r( zcwaTCqEp{UEvHTlakXn_=vf|R=GkMZdBL~ZV|x$BG9NH1z)L&i%g^oe{538JK)gc( zmA_ZgX3qf<1F?VYjdAYrN9+QXHo%@q&V)aM&x!^AT@r%+SXcf<5MImKZYcnum39zb z+nxrD-#r{8&c6}9_73tcodBx!A8Ld9ZMyZlUr`6yKHWbL?)2_}#qh@@2^C~968%_2 z-^lVR&)!Xw3`n{Q+LO=ye{q6{rFRlqEAOd3t3;mVCuueb+WX0WMEp|droCZ+9hF1bWB z>rIzL;H;1*=%6Rk#ne6)&}^4>A(5B#R)bc3TD2@l3@b5aF~T32yf%C#n0?j%%sKn1 z>v|72X|kCJzWImit~VFN8=Y6fK4Yyb9Qb3ef+IE}?=)0@{WKlTuY5v@FZVO;*Z|+q zUjHzAy@U-;UsI)TBey>PLiq}KHkZQYO>z)01niNNbwl;w4Y~w=D_8{fnHs&0)2sQ> zzXL2rcT)Fz6+-?Swrnt2tQsavUzVmm6wua*ljFcGP8__KDY3EIT|K#aCj&-C~Cc(nAfWng+o9VFpZ$Bp<1# zQc%H%8QdO-kAvIhj^11dyLp{f5J?g`p~mt6HVDHo&~(n(MjQbr;wu8XLF?KR^09 zd69g*C+uMS=)}^$T}tfz^m}P+22K;fD~40^bI+(0(AbaMR|_o#l==fW&!@Bh>~35F zb_fYE-vE?9&Cc9F-mOWRrd|Y9cj=H_;oz z4aR+`HfQNeE)*1#2Pids>NTr+^!6j64!waFva&H-T~u%?qO|> zZ66V-NQO0J?l|2r^+Siu$)NQ|FJbX=PbY^X58iK2K0+UtcY9oeT=Qini1hcY0JmGD zHWTG`)!`(pwZ+No%T)Srv-ieXCQiCRou6sKLVXdMZ9cqb;2#$Q)#?kNpPx@EWWN6+ z6;LYtuJ#so&v~)V4XysOE2k4W63Ds3hsMzLw!M9~M~mTV(IffrH60uJZEu11 z+VXm1p^$_A=&PWRBY^wp8|qH8`v>M%AI$j6q<>her*p329sl_th7A5#TQ83LTgllK z5bI8Ly0UwLeP~Djw43dD?Dy`;7kY}XS0d9R!K1-O^b027>r-p)+v)T1gYu`a4>j8Q z#WyMWf}0cCBSxAZ(TI3!ha!v_{LKd7|MAnF1|(W2M1JRv?g-#t_Ck8}_yG-iiUH9C zPm{>YJ>B(E#tE8h8X&R7h5xwQF3Ud^LDoRX_21VT)LY6TzY2r=)Ozcx6O%w`?zb5$ z+`%d0OXoXn`7^9f+(bkOGj8Lxe<>O?=dG6ORLiRD5aB$cHGH5`yYUhLK72j#$~eAe z?i%;M0EdU}y8y&{=M?}R3dzLi!-2dmw@>spFaiq9Vn^V{kB%W_pK8by*cfZG4}3ona>xII z{MfIw-t-@^K>-y=WY~0)K1SkjJY0g@Esm4{=!u);0OSsE07e3izXY?0(HIBKNKw$XXv3Ye<)No4<-+fr;B8`w>N~Wxupn{+r3MeptL*4L~!|%dw|*tp{#jt zWBI4=!=NKGI>6$4e8n__oh`6m4kwU12FdsK2Gs{w;Pqu3ZkzY@MXCYCS0w}!@%sw^ z*yXteW|37vm^SFyM+7tY!i7nIM8zZFQX9tpy6QLm2Z~d026`7gnYw$wbT-*qOHp|% zG@=VmcerploL%jE_PN3T^b&pi0C+rmpcMN&u^3h8C7k{d8Q=8Lc7rx4rwX{=5;FTC z>&NAaBWF++0zMJDw1K7ra>Xaf!rvm#lKOT2Y^R*j9fg4>kxAACIJ47VmChs5hK-{+ zG8j1iia8C4Ate@+H*Uc4*TJKh%oV2`9nF<6e9r|iM*|-E1AXO70e^veKw~eT!G1*( z!u0Gd!s+_!;ltC^l>4?FNsD`Af0-kf$zF0MqC zOwEeLFsqtG$TK{eHbt!?6zp(kz`G`TTIvCo$PCCuV|m2-%=OhSd2zo#I4n8>?YMmf znA~i6ZnWJ6?|t#)1~}*w(HoAtW?W(T)VmWbjhlB%X%D)h;L#x%TZbfQ#*S$vkV@FT zbPDX+hB(EC;KicxqytZ{jqE>3+`ILbwNw`hcq9To23)iP0F#rHoSd90W0(qL)R1t+XnkBYXYGD^7EOM#Y#1Voq=p0FvI*}%)uHqn z1cT?#c;{7s*Q+94$n_qnpH_Yu92oJ;Zc@{^ri0G!-_#wgehu+(L~ z%kdeT3uiA)ZKH(WIX9R0KGq8vpF9IBGbzC*2c@$aDzfty)D?f;+lULB=`Z7(HFz7tTAxo1CJ^Zt@9s zbJDl)dfZdG2QKd_5ba>UqIR+IZ{7F057jlm1^#&Axf61~0nfhDfa33`!jL#Dh01M0eVWIF(uiZtq)Y8~A8hz51UEgUI>2YJ09oZu4X4B58B zbQZTnAwwVzGXdwnWjrfYgYu8cS$iEtO1w&|%K@oX`;+#K<6NSCUH0!6fSs-GA5^&W zqm}*)L?1ok5eK@NU|0&m$=u!JT|<@Dj9TCFjkilo8V0QWu&@;3EX&||qzADfvH*KA zcKU-0CdeE5_I^J8-Hno2`!-V#ZA#o)P-*=2(bkEp{ii9zfu#qJVjmE9h-EfV>{Jt# zr>F>LEx|E;V_@jEG4$2@0-MG#VP*0Rk$L}>m0sWD|9fkfV9! zSpRY1jwNzcSJ0Mh?Su-rqLMgj5f=0tp^9Ex`kw7ByH2!W7I56@)5#?jU?MY_&ku*q5Q>_JpTJifm_rLS-RhC z53zPvlb+mX%G3qihtoH5DIZ}|<6gtir-##7iZv$*B;ASs_N9^f%;e0^hwJArjCq3@8TCDN)E=9EodHyg zwhHWP@!Nrov*t@h8DdG11VX1Bpp($p!c(d18u;UC%}>25qnJhI1QA@d!CuCPJ@dXFlQh3QlTDpTicY0ONffhgEh%9>delQ3ICSaEg>K_$jE zD&*p{=FV%{>56MN%3#T0(T(TOSm!On95CJvFpP9XCa0~=ux=Z`P~rZ!)Xcv93q5V= zBYS3whoklDXC~0V+<;S56OQ4OE%H^%J7tN4omfZ1#5M@B7ksXu)8=?pBkxtd?15~d zT&grou$Ze}57b`*XWskeq&^5V9AR%f;JZZk9*sxFfH+@ast7*~^S<`_x3!Xh*9HD+ z0^Gn>81qT!<~_g`21Lqu=|=F|4!YvA@?V-?du7Sw14WrQ959s`CvPTkFwm1?BUp{) z2-lbhD}qpfy_pEZANYM;y8_nrosl`)p=B)@ zm-Ae#c=mYwOOR~J?ut+44URP~rI=6!+-RJbaFG-fG>0Cer2A^~%SrE>m)oYJ;)bQ> zRbT4kNv*ufR`+1a1vF1wl@4qyjI3GuEzg%$orn=qUs}>6zKZ-mM z#Phw@C+PlR9(IoyA0dw-C}LH6m2`?bH&q^7mW_X56Pg0Owg~^~6&lpc{RNR0LZc!z zNt=iw#hKlkwX@Qyt*p#ngJC@tk_7^E_hxGCCF4vdc{4OdL=tJhx+BDawHCzYEDtF|v}g zq{}O}q!-wNE(^LAtl723_ozA=zA>PX(SX{mm@Fk0wQOEX)UK>&)sAAKYwJ0B)FGkO z%it^8Xn*LP1+;{Qyi}mxht>A<+F1^gAu39!YMphvh~bWW2>&XCuWV0cu^3GIP()%g z8?oJ;d%*#;a(_EjnVkPkE2IakJe(wd5>onp?S&o?e>)NG5c)>-0ZI=iWRjE}-rXE# z2{7FA=Do{mlK!ccW`I>?cx}xU`+mlom^kF=i4Oe|6Bu;2n@?nGVv={2FC6jTC&8O; zv4+0+JV5lu0escI&+N+rK6X-&#tTlA2Bjl{Y`?^u?6S>OEKio@`!n4|_i{G!k5E6> z5aPeU0yw%MkLvywf8|XD^sp&^hV8?Vua7{T`)u(4Q9i3&_hZcP=^n$bQ z@{`ufv5Ldn`BmlkQso?DqFZf?^DMQ}{+zQn%y)RgQA)|t{DlWi)IWT64`GqyI&ynz z>?&phOX70={W05WN*7KQTCnRTwsttP#>&X?wMy;rnF8}msqfU1hC0eo555U&z5Eaw znCxj1tf8ZfI=Pp^vlwSxolw}_0qmv}wCbcC>xw04)vZcvA*qj-xw0_cj}NJ0|MHwO zTW9`iT_p>&R-6(WAbUEMR%EVso3aha449KI=g);d)k`h&p60LcW{(*WBX+*Z1~|?9 z5i^nBjcIO$pYI#e0KohHtG&Q?bq3(&?Jf>r%eeD(CXk{LHs*VGccqYF+41S8-l$1G zDN~fS#zY>yDP$6V69B;bhpYspM_wvgC7rJzKO&H3Hv#5P-992S1g=|B>SrpYOfr5v zP0M&Q@@SDKj1ob#X6&NNL^bQ>4a2W~?up~w8%bLTMoQvbWY)Z!KVYvmbl%Ssb?ze8 z&!Ioi-`s&yww+9+!qPY+(=>28jpr6!Q%%>L&YDr|GH+yLseXW04u)-prFKrY0bEfu zO3zdH;J0poGBuoeUTe?H*pf(r9VqqI6uF?ISW+fsM!abiFwQGOL>q0WRT)--(uc8J zU+t|0x(bCR-VR%GDQv+up5l`CJwiPvCN%o&5oOcbewQJuuc4rcsTP(mm`V8~F_H!M zyLh_u1?1j**Up;)zHT5>p++tOZvbvnNQcLB>ZRSvpls7s|175!f)^yBz2^g~EdYSRyx{hPko)QOF7u&I}!!9FI}Ii8+`Diay9n`TF9;W*2?D zPOXE>Nxq2|t%&W@k5U6HGO?J2Yyp;;x|{6^ojbR%xnL3#Opb@F$G{petbKuZ%Z94z z;aa8WICSR)rq$F3B1U3z3RU7>gdwxojL&#b+Ri%J2!UkRmhi5ERf2wvvH(A=ggu5LErvqa24;hygqc%b?k78J&3LByx7UhYtng@30&@(1h zcoaxCUGmE_aVR-0iAa(*xuW|c--oSRa0utv6V*5@bz-@ZPhrd546 zl-25+Tu}C?Z9K%B*s3F#-^H0VvJ?udO~6QXNByr?gK@z#CW$a2W#)-kW#rJe*Tdz= z{OMJmj;WmwJZ%bl-|P1`ryJ6zK6k$AFC#mW;b4RiuY(ZNyhkAikk#7?ZinF8gMY+`(e3btZ223Po5+l>tCEznwhXRUXbF%r6&Fj{xC3r^-+9t<^g-bF33HXKtK|7H!GX7Ml>#1k zhEo)GGHq^VmU;)Gn#zQDl8i;#@*S5l+ic70{BR$r6vQRqd$!Wi;N3p&m58nxl_E)J z`I`c3Z`BFpX40Z}BUjZLzqsMNA>4S2^mJSL--K1EP=dYFBMyu+I!3=1ietK=f09=eSN1ALq6zId3PMD+5cN?1Xs6?J~=Z82+)j$=*cK=B; zQmcTRb6dLLBNZv;F2=Bx~ zdOr|4O>w#nU6HJI;d~_d&0eTmbocvu43U5@V3}ZODwScgc*(cYon{g(92_mBMk_~K z;%-7c1_+0ODbRZ|dWAcelX-9zE{R=Pt1VY-7myymga01NQH?o9F9lc*u7i}>W2v;C#cLh8fQQB0utun{T<3sfdG zP^&#D#mwO8`bq4-soL4Uy$^a8g^jVvUI$7hwX|*PKq87>+-~)w1+fsL1ae_ z0f^q&%s!O-#V>2KHj*mQDey+$BB$JF`mH(K%2X6FxeF255O@`%y9kp`5}(ADb>f@u z;8ngusUQ>=IZpze0x++mQ*bqE{}k$;iJf639Vp#gpyw!S+m>6)IUoC}^aAII!2XAqFadDA=M^M`VxAIu(M#eLO^O9z|}AIf;gV|Mn%*++|7j%!e7 zQ(E*ZgP?N;tO^?oz9yzR?Ff(O&EB)f*&;okPVXIvt+l&dLguaYPFJ5)2`EF7j9@6b z|MI)6$njK8&*0NpqBz+=`~#=SPGuAz17rtXLmUq5;x_U7o?Rs^w+V$WQ4AXf+Wkd3 zhseLaKsm?zZPYdYF^F~i45w+*hXcC&T2Uv?sSv~>fwJBlkn^sl89YY2=>)yKuyI@2 zGTuFtoX=fSv_+Y(6ws!cjW3&GaCl!DvmtCPKU)cwa*%~Sqz9^V?6us0_TK93s-&hvqTZX=qV z!yi0ed&rrM{s#;=xHFTNuC2tzi^BsoKNckxB82E{<$5Zs3!-qD>02&t1ty|ry^*nG#nmrnadb+A|92!u7<8AK5~yX1pYN>g z&)M?MC&B4&5-21)Pb&Z!y5IYza&xgo6&3TsRENAaX3gCz-NdAO%f-0k3>t=KE;>?O zWoKsI*Q(2yoVc=+ras>s%PS$cOdigP*K3kQ>eCFXpHEd%OPp=R@&0?=YURBaa9cH> zOXUq;nTSR-YHN^^DBHgB1z!}8KM|*albcF8ES`gmSQNu_y1*-qB?@`zsnc1)T4F4Q1ScK?mwd_{b z&8J5|;O_(50P6>0afT7!&Fjbmz0KCTd@0@KTPfeCp@O5>T{oxV6a z&|?fOSGXBUu=z4hdB-{NQU*&g7T4D(!yEBRTk#H*9KG14{Pyi9KP56vov`EmtJzs% zfq4UKn?E<*n!+&q21nN@N`<4!12UBBj}Fm%T2AHP7;kAJA=l5J4-BDp$H#tP<(d|p& zgfu#{)VwUeer%|sZx9aqR-@$AP&3a4d*&;kUTjC-MjUe8%J_YRj`?#U4Ctp2cu zwzze@kfHNGBD}?Dr$mT$RE(!$xN{`q(2W*c0_Z;^&cc zUwtnP4cf)u+4dLHl*C=`j8}+a;EBZFSbXiC_|0f%0sh0K+;z`e9YL6HWk4?$QXr;Q zY#a(gAjXR)kZ?5c_5f(Nrl5Eg9EH`;+XklXD$0QQA!zab^w%hOAxhUb_HUk~d#nKY z9;M>suaLY%|6hDw=Z|#(^1^{tv`y4isTRv_qasy;CkA7$Y40dCj`xBalh2TDutQaL za{8Ky_|=5b8M$atVj!j8#&y;N6p$ATghYc58ARVlX4?^wS^C$OR-*S1l-yLDcNp?c zN~H?4s&Q=V^xJ3J2{(3G3Uj`ZaXe(p_(hvPMQ4|kASemfxa2`Dxq&l4Cyci){`H)# zJ>>J*JYW)}+Wu!rrhXNHK0ulo)h~0>z};Mf%B9v$`}{lJW;|ivWyJlwHbMZvEsM;! zA>hZGn9YUg0e<*?6(hUAm=e@bc+58(xbAHz+0k!fCj76Q3&A1wnQUXKbjH>YYGM54*jTUDuZ?&63gx2&w48a(qt+-g=3Bnm;z1T0he~BT zsDW4?l#7pu=`g>Hml$^}H0Ki*Eb&5@H%?!;wK0Q)O4A(mU3v(VC#O{S3aeM!=Te#4zVF?8!QSCgRBah_9C&U56T}?W$F0w0+RrWYcO43zD6i|3-?;Vy!Ouv<*n>D$hEtIdBB9zK> zisIUuEtFa(vjg-H)d@q7+0l8I+A{WztwFU9GZP+E|DNm@sc{qK?7=nk7e39jkIVp- z0y;SZuV$c8!oU1)DD2ZhrZ_sfpTJwr9f0r)*YMeSZVd#ncWSy52!5tKX(;8?In>WW~rTdU?rDj zA3x|bcHPWMfOkL!z!h^E==-839 zj*v@oJ4dt+j^+VZq_)PTBQcr8GPbabkk@>ceddsw!+ofb_A<;7uoI8M+|%z&JfY}ChH!p9LVCCwDX z!y|HiW`xR+#K#NSOE3+J&Au5J!{CGk(NH9y2IQ>F?esQwzz8)jke0TM9!y;?4&gMy z-J0iHTx39qvkSE2fM6CG;D?Y!|4L%CzsuiuHgvW8{FcVO?diwJ>x>r^5*x^{@7L=iK|g7GZU8WW##mG?fiU< zxqVxz!REZ)$roRh_}HU7r$xpuaLIXA;L46C@cV2|OD<4-FEw<=exzXJtDA_dpL_jx4auqxI}6^>@hm4+iqwEr(t{rlrz zxxi)Ru&ySoW~dp^|61?@aG!|mt5oXSH!C>F$4CykKJurL{`W3CpiTEzp;UmTR4n@~ zHE3=_=M|jSgkANcchZ~-lb(X~vRsD*<~Tk_3)7go6A?;eit6>%YLka-wjaMll2!)A zG)&c$W`=YD9GF`KerQv^$oed??WsoOKSK5OS;c$~%y!-l&yf+Z>R;hTICS4V(MIRw zujY8n&^M%Sp*$1Dal%u0U)vk0iILqNZqCXcGC)tnD;!sgA_d6{H}WZ4ov_9}h;;u{ z4VC=I=#99&^>CQRv;wo(6^|h)Ewq&9pi#Ryvzv))`l!$Cqr-{=MbwqN9e-(TzAQYVYoaUph`D3(SennC-UBCED~ zunQ-#JN3IkS*+{$OFw^BMOg;j_Qd*8DgN$a;|2 z3dXT?Cqq+(AyOqzFIiXdicM7(z>Evt5B11oWE6`M0{kD+)jf!_iPCES+oDp@53`)J zTr_8o5vhRTa2=k=gUNw!HFdK^?NV3PDDryHHeHzpFyyueJR;Tj3=_!)uo4 zkGKHbT0v4rUR37>5lkAz4k;_o6{Zg!+F(%|*L6>K?bIi$@T zxJleUNl$jYKQ}5$0TYlon4fqn1qF90X~a~j1k|9W-W0WAic&yo1r}6(>rH&dqUt}l zOGHZ!31{Y$ScnY_Mv&Z7$w6Xew_EF#j*)#FnILYcM1bV2j;mw|kI?+n9Vr#H2bT|o z-ri#({MVg>w_{f&bk_6k*bva9bIV8SEb{xGk40*pL`yz9)m*I24*jQl8~-w7;;4|5 zILRU@Vx}gLbZ+#=qtzfhvhimAyR{e&DT|=L|JMet6H)btEtZ?~&z61&JCB8_B6@BT zPxM&$NVkw?K?oop`-AFEnASu^D&Wu+Jj}txo4GW+;F7&+a1KSo%WIpmU9KW8VMO&^|D?(a_-lP<<3J0kH~X~(XYjA z5A#E4r+=_5_=0*GL??lI>MQzxI*ppwnBU=2tpY}n^>V%+KVM(-dMnLxq(7FUl1-j? z!Jr7IMA!t!3|tSKTqKv!fP;oXm>ueVaV!3wJ%8)xBAh`FTw?4K zyxn;^ZVYR#sbHt{z`HLb6At7uc~1}=oRxaWB!M+omxNg#?RT@V4-7Y>uCbiWxWqpe z<07sa%hHle>Y=E>C@QcvLBkl0u-C3X3O8#^kr@{yt{# zqcCZ%w2hP(Xqpc=EStl$WBnJjf1pke*HMqxn;A!IVsN3I&);dXKs%P5pIp#j?riU$ zM-j#y@Ghrk3A~AJQRe8P?hP*Zz3&JJnpJVR^Pr`^|)v^RZtUnKMQI|-Jw$O?2o5*UeDL7#0FVuHK`M@*fLZDgxr2|(qWBiY(kq* zX++-0`gnO%fog7v#Y&ddt-r1=t7Kco1y1Xpg=Zj{3NuQZ*xr6P zM~bp=X6w{twX$E=KfQ6MvVWVMtfFCPv9K89{SuaN zB;bTTINJOsE95lq*bMLA)ehv3c-DBs^)K7pF96>HJ6}8Xmv1kF`MT~`Itj3z1|I?aEJ{jj zW=Yu3qD3u+kjg{Aj{$QJ<3qj@6sIk|4?Mv0J`!;1NJpHaYX;Idn9Fy$c_Tp;}+i!^X zu>Owfz>m?gq*1XNDe==o^hC70`q7eGq5>{NxE>3}ZbJ5N*=JcX(?Fc#_;?s=fb{@= z>fXZUm0Qg4>oX<)$Rkz3q?ve~CSf_+U-t~?iC3j4bt>+Ccm4o6Z9{*OcxGoX6<*~? z@^kZ;)Vl?|TDJCCyW#oPG&bX7mdzc*>{8GSqo`mm$l7R%UN0AytHbLg2}8W(2v|)Z zeGvG>bQVUFgOZwJslpyL{*jK~m@_3ZbZxi-ED#nnR!Jz73z|!8sUT1P7k{F!u><*b zeP$F>LV8I3-{>h>nfT<>b^6H`g|5v|F^3h*LL0+bDJ3r$rx@xMD^MmW51mZ-;}Uv( zwiWk5t6W11|8dH0fi0^7&iw7T-&qfhIszlf@era7ie?Xsc)Fi9(5)I0ayhIO8A_eA zLF@R*B>f>-H(k|b1G|%jiY9YBI|r3ZEF9nA!u{L2oE&X#f5{@8?zM%s*{FKy8pnz} zjrLbDhOX%aYu1^vN$~|Z&6n8Qo4k+5S)um*G^U0UuAP1y{47gv(#2n!3P;PvR%sBj zGwlehDO}bfJ^!JCGD4fOIna61iYlp1MGa`!s}7q@t%oa-WQF(}WeM~A6)RlsQyH&i zdToJ&ccMc*-Q7jYP?y#0Blw@@+~^z9`*wDs$So}Ty|2FaNW%8`gQpZSAQ9Aa`j9Ld#WIGpPV{$-APsXA<1Ofer0X=>VWt2J+s!on{iHPa zL~Mb9n9bNX+PN=nd{}IHXQF_^F7y(0qJGZEl}y^i%3Y}F-g&^chNo^!|IqTflP~KC zx*HZZ$gyC~84LvtOy)#tD2kl$X_Yu)F&AZcyJ+M3L;yEeo7hF*;CkdQE=tIZw#98l z`EWEu*E}DdHbUs99`tQ^f!K)8{z7tDiDL%nj-hzO+C@jw78WU*`KG1zNmv2*9q!y# zM|(bOhxsngyN?;V!P@e6XgsqYRY#_VhTZb(k1IZLL&a}gSAVtsZKn~;@f%jR;ARQF z(pkBlG(iSK6LLFU8c5yMA%n&yt811u3yRvmrvpTVDpf*`!f5cNiZi&NB}Z)C7{|W8 zzDD#)vF ziCO!hZmI^>uM|F`64n^?w#Xw~V)Hy{qh-hzmmIG+Bx>^o-L|C`dMoA`-azpxI)C#A zj9%n}g;Wi`x&AyvTvE3*+^C5c^ZO-jL6zsYwKQrF?DLdh{PP10N4B;3buf4>>mO3Px zKsA|+4XI(AZoa*%xQ1nc%)aHo(}g7O*rH~!{>8@d&K}3-R~D_jD#@?*#H8A6*N5hl z3UG@!%NtHPC1#;|UVdX2K=k*gNW{iHXHnY~W4K_jLle83UPw#RlQy~REt)*8NcyFQ z`IE~B6C~4ughk-l0)UpwB~?G%C3{}l@Kh6+oZFGb^;t@{!=@oy4bw~#lP&zAsjU6A zxYGSF^sqN^cFRxK*(-4QgfS51c9Dase!U3m_pB)*$u1Fyj~+>0AU&MQ65$9_tHpn0 z2di#GBbdG8jXsGxVYUYSak2I1;a^NhdDOE$gi4<`-@D4?`-YmP`#q4& zgx8&YZr3D0ECCpA3QMd4KIGMrp5j(XmOknwlRQkLhkbIUfvX{!zvjiF8|3e___p#8 za>(}zarb)VsDH1 z=z2EP#ut~@3SBd<_vP?6@0&Y%Ud~T;l!!^GDxLJZg5-XJ z(+#fbA%8stE%gYi$=95wCW|0KGY(si?M_}NwlgQF@s3%PF?8@2p^56BeLr@HDrUpz zS_GRiFO3yt#ZJNr_mV&2%$pRy^I6B2xT>^hRUpzCMx?P#FHjU#Du}sryLjfc!eh6c zhV*HNp<)EM1HBSNd?&bYj-&#TD~jYOjeCJR;a6?@?0CS}KpldX#lxB8K1VCN(+z+O z*=A*Q)_{^+`m{lLqk**nJ1ZU0u1uC^;`YgvCr=&KIdesoxfldnG^J^oHdSUU0sz^I z%yIZM-d{iY#$-qvDZv{)d^+2Y@OD=YJa+?FEz*Dq;a&~#W|q zGvy}?9H4(etra)m#u6;s3^qo-1iR`M1=#I~amYCo4HO12Y**fU{S=rkPWefyQJm0*m5PS&8G5UJQDMhT=aP;&I?ecEJ!J zU7@T#s{jOGSuXJV5PBq$;(3F}-xX-3*4PqGzXH;e`rEiND4hQ`1*PE~`6?T4$2Z#W z=PZ=kD}29if%WS(CewkjIbMMr%V1bK9^CSkr~kazI{b~4`9BZFkqP4>NSkr zqZqvRJe6mh{G~D(durgKgpHCK8Sq?W`3O;EJhanTJ$z`hvG8k)QHg#?BiF9g#X!$P zcEMSuoJcWvN@j~+QiB25;=_Ax3J7=cpw~_*w$mn8H|s^xH4slp#6{s)Gg(5^0(?@& zB97ii)5L+?!(#<;K(^NXZIk&Ks^rybn0heA{1sTOUSkq@;z{_lp|>n3>0rLW{OBA@ z1f;#+-q>ve`;&q|9x!At_j`sBu&eKTb&n68Pl$Q^)me7 zr6NvleHO$iyYod7lOz1xeR~eKZCAW*b_ix4PfIRHxAdZf1+U$0E!(??5g8{URGqT27VVY<9KH^U}~caqzk+ui4) zeYI+RKvKKKIl8rE`^UN?DcgmCZ*oSu3c_4$ZmzD$o<-eBWoPi>$N~D+-saXOH{-_p z-7uj8@Xo@(RwAWZHRNTX@WtBDY#bihjGK@ z7=#pC`q=)$o^$ZF+{A@<{Ilo{d%&Bhfo~0vh|5cNd^2_LL85>I z8#e4{fu=;mObYD2vh`~J{V$CUX>gzL=eV8EV4tV{)m7N88!TBkxMcnS2gITnoM4oP zsD`95xuh`?y`r;CSe<`+Zt7XmJXrr#)tmR)y0J!G{P5DsAX9s{$Ah_lOxJ%PR4iPdL&=yqvfti>utgI&X zQL-Wr#dSEn1-SZf$UeaZrQ9`SaTTg`uOq)oKPP?<$eMIhSL3%u3$8V(2ranOTHXuQ zRmL8E=hDdFcBRl$1JT3~9&;PZDG*p}?2iQwej6gE=Fx8s|is5^|yra(n_<#YJy zEi>0^OLh4>LluIeE4y2qqAQ{G9s`v^6vSwA))Eqx3f+JyWrJZtS~tC{N$Pm;rg6s9 z4@NDWJn!1rSjrwTDzT_SDiivU50uaCGCPeD9fwn$V$T^mCx@7}3U3~PXNy+LFWEc) z3SL)mG5i205O-(;!hlSjrk*(~Jf|I_;4VVx;pb7Y+yKyW&oIy%e+2lu9q<3u*2`+h?oI)zp+Q2VTe>@>VE_jt1d;AWdT2xhcHDi>p8a{B^ZXO{ z-p{>X@JJr!q$A<$g@(>$L?rjwtK=)_I;Eb2&R2B8RRRTa&0l5_KtWEwIsa{fVCJYo74A6dV<&jJ4 zai_s<*`rg(ZQ0Ypvfm?fgYXRY&|avE_M-m1;@ZxV73w+3BA;v}8DlIqQ0vuVzqDNJ z0>(aitS?LdnCZVJx$|#=@n!f5-* z9LvqrbX{x_r_yYLsNCLL48vPOo}cgl?eCD60Zjq?ZU33|e({6PdYu>HvtGby#ZR;G z`F7gAo`W)g4xe5yJQ1&v23un%n||Y%g`zAsTE>C#&ZnH6>&~@A!dXxzn(FDdb(D$% z({!LPNZdkIrs$%0l6jgeyP+=^ZT%kIoGd!*g86es4Y=fA`y%br?IINJdL%>nq{UL< z*1*Xok|?_jD}Y}*Z^`a=`pcTX6vf#(N2A%psjl}O8HR)a{~~j@z|$fVKfOW7c7t)htj5THXFRmLQ{Sq|zgC;mpIptUQ2g^( zw-{33W)v_tz0_&a&L!@nrA*qrWz>d{5((&wjFb;V!@E)s*y^Di==Hg4wc zW`sS+Mxb}IjCc=vR!p3*qG-G9Ocm@;toW)Cb=0sL{$Tu4aVb~#ORd(B>Z49&FsT(n z1q}#KZ;R=TsE^X>4zOO2l!a2u?T0k>N-TT=`9oSV|7JVsTct(Af%-3ktj9-|eJjwM zgW>?8*_y_YZhGrOLQx2tvTk^q{^<%=PpvsJGg3RLR^wvycE&i2+gAT!b{oCLkseg# zu9)M|^%Qa$GsTfox&BidcDhl=969q&Q#&UN^gWVHW7dvEZ~30M@Yvl7d&&WUOQ%^{ zccuR>O9fzcUoJC!Yk8|!NdU?%4sTZ~ebL(z||y*LF~ z*7UdE*4R7F(R1qDw`Nac145-F!ki`(KKO98*xb>CvX0`J-vza@s%WGt_U$-B+AdfB zM)+BUw4;xIC=G|rXb;u#v?I&=d}L%H8O2_PZjeiXySO;aN;V$5qnv0X^dAPf6}h$j z!t5L32jw2zW)e11aNkYmR0s-&D7-wXJxfMlbVoje?hPvBXfaC z-N3{xre7l2?miveae*ZCsjCe6>^4MB3<~cS@JyRCQ%;7|dQzHUCxKVLv%m5D8vaSS zE81BPL(+KTn(2?zj>)9fvc^^m{0q%4R$+9h&wqt*yq&S~$zHKpCW#)tjRMHt`Eeth zmf*wEt~o+T%IDHpbb$Pk7iy!PIRXj`Ef8+EH&lIcP;}R9C83%qsB_B9a%}4|UKG?p zlN^p7HB-Q-jNeUgd}hSg;y!1fg@jD;WljD_P$L4t>>Iftj^WnD`JwHGSGuu z=cR~<{KuqHbqiuk3t9n#1gy@MyMCKF?lSrK-@`jQLI7TP>!Y%3wH9%1kP>cK1&Qrk zR@BG&LDS27)y8C?9yYBLr>-mY(= z9ZAg=CHBa&hXv0SXwhm&e&4aiamNr<4t9F4DiOu`)_0&{|G4}WNFz-UCD5cvVFrnC zGAkB(ko_m)tG-zY?o5=C__jTXcQ4jCdvs{I95}(nDtDaPw|H)vyU0)!ahSTL$HOn?sk?bQAF~qqV=Lq^n;nNPJv$o9NWrkvsv8*plx? z(omc*PUu)CD7#`V#%J(zKM3_=Q(Uuhey_2EG(S(L2F;hmZI9qYbNdor#Y5wOpLA5Y zP|2Js!) z*WeHTExJdzbW@fcM{9vhTW!Iex>M`ceO+v>_bKoFIqqk6s&~lZqFoX4@~qCJd#K$E zoh!)$gWfoIC*W#Xe^(T7YtA3xk*GmDC%_FWv@PGA8kfi5$^2U>2x-M(yM=77j=vwG za}+lR<^0S;LmO2A_Ta$!!#VuMw&6JO_TA>@`B!(hfdZ}pV=qqs@~;)&jdx8wy9W*e zrWeeV&Bt#|QU`QC7CfGGV|x8mhqjyjq^fI;EOdcaWjCfDr^G4SFb9Ty-mU6dt|;d$ zK>VDk41b3&w)Y1sySDC#2PbI%7VD5<%1S%QNEO6Foc5{RWN6+;2wc}R=fKx&$! zMzqoaQ)BZrIXJi5Qsl85M@PqXg1!@kcQq1okULTTUMGT+f2xP}n4KOuyt>BEnx^_c zZsPL)x`{qS@nZuKtM0<|x;)>H+^G_-u2%AQBxnrHj;hL=$(nhi8bC(Y2?D#!iuhI} z4uQ-He$>oZH}pp(zpr?!sq+G_Hp8U@AP0|-12e%o#P^%jSnXZk;He}6s4R~2s@SuZ zm-lYiIy{3SyK$-kw97TxbkW&jfBHIjSV)7e8IH=XG7*mD8{Af^V-+|mHw!p>{0&C` z(irAeLrS#!zC^v;_&Gu79B{lBWOHcq^X*g>Su}1od>3Lmkt(YOgcve0g_nDz++(*@ zU!{h$Vls4!TRecWx;-3d$#$+p$`N&EapUb{6a>~gco}mqoi{uNLpQB2_j6erHxMhi z|LPhfb&v(45K*b0o>2_1f&2J=XE3^ynz5G!eFcwUxm?+rT3DE>2Gct#r15oT;_SpBcjjV&^NrlIvCxweShg1cQt}={IDI?+boxlYs=);xaq#$%Qg04vQ6xftrZJddog zq;1pO9KQ{!H-=d>OII0(7?%H`CK0~eaqVND4bo{LxARYd{$ebNvf%~>g+sipAn(KI zfT^C5xG3#92r-tD5dvcrYkZt3Nm*NVA}Ppk0__qJ70VZgCIpQDF~}w+c}ed980FQ) zo`kVhgnY>*?uf{?N!b1{}?%feP*@ z3qK?g7ABLa5xrKCidL0_N#{se^S`tWUChdz4W1+h|Evkz&1=a#Cq>gd_U3$$-HmwM zHEt5@ClNr79(T6`*G=z{5Y(`yvG-!20Q0^mCw{{eP|J+V&TTTv)n?v1nIJ=gwJ2T+aWH`iQLu0a*F<|xF7Q}k&^UgQPtKs(X;!d0ocV`rH7+sA*bwTPhj zb4|F)>a2D?1!xK&R*WLD>$rv#+r%UUK)wTRCoq_s8DY@T3Y66~q;;w9Q!s-`@aYXy z!=;U^-skCF%A-@j)`Lvx^>CBAdl?>FN-)zxMD63Q^IhJN+XO++V&?5f4+TssFesFL z1OLok@9suEH)IB~Z*++mOv{5W1|<~$QGe}3qzz(qH3?)5Iq?B#wP^q-YJDMX;`OCz zvC-4d7FL-CM`V2#>7*6erWel4KUc0!d(exu;-iS4CNRznTEE94$qf8Sf;ZU8?C83? zbONwUrw}^`ThcJ5&tDB28P07cRhZU^m8{^Q6wT)in~51I{O_^$;53b+GOv6AC=*xs z@=Ecm-uYto?|mftAz!(9t7Ll(2Fy_S5Bb$LizY69*g01BkYqtbM~qLq&>=F2mh9`r zVaJNM?aFQ)I75e-34y|0~{}x)aRl25;M4?_*WN{m0v}MD)R;7bW+uSVaF#D zXmb1s5#K;_?Hr?nn3Ln`0(sNqB$>so$CwCrSsk?NQ$EX0VWAn%8ef506Uo?;IaaEV z>@oRv>0VBIF@Z)q$ z37d`9Df>5<=o&reH|kRkFWSRBbm9Z_FFYbm{~QJ4BfBYvI0DDFde*N*ZGi~JI98on_aV1hO#t}`C;AI>LD6XrrKy}b%eEFk{u zoJZsum!hcy$wH!|$nmqJJfQ)1Ar|-dp(Kb*B|coZ|6?g~{NR>?y?bBw{T^h0GJSg! z^cS@rwyoy$A?mQ0SY(E*XHh$XKlsVgGw@t!x-4>xF}599F>Sir!$pvpk`i)M>@AE01W&P<(^0-*M7XgPlAUy|xg(lBxMJ z`B#jUJ?q^_ldOyDavJy5VhKIN(4~XgGXp>vZ!rQl_5F4mczf@Q!eIj~Q`43V=Q4(+= zu<-6<)iC&TRhCf+-vXlYU+e!)P4;c%m{nb$9dDw-0B3n7bGI;d6=5W&<9em84Yt#y zm6~?dP$bXx?pLoi2#frAt8dSvYgkX{H~I?M=9VW-2aoR=w);YDkhzOp^zK;0ctv%X z8`J7XKzreIXz`9*Y{z-vOh&c3dL_qKvgC|e_mL*U&me{$4xs2TwcT3p1nY9n*F?{S zuis%7{>z9}MyOq!H)kGnEX_ z?n@dieCc%z{BnPpaUqisNU}aLK%LW59F_R=`iabkwLCtKFwYQ@Wa!76Y Date: Tue, 21 Mar 2023 10:06:33 -0600 Subject: [PATCH 0807/1263] Updated namespaces template to include labels and annotations functionality --- clustergroup/templates/core/namespaces.yaml | 24 +++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/core/namespaces.yaml b/clustergroup/templates/core/namespaces.yaml index bf0bfc7d..8d02fe65 100644 --- a/clustergroup/templates/core/namespaces.yaml +++ b/clustergroup/templates/core/namespaces.yaml @@ -1,11 +1,31 @@ {{- if not (eq .Values.enabled "plumbing") }} -{{- range .Values.clusterGroup.namespaces }} +{{- range $ns := .Values.clusterGroup.namespaces }} apiVersion: v1 kind: Namespace metadata: + {{- if kindIs "map" $ns }} + {{- range $k, $v := $ns }}{{- /* We loop here even though the map has always just one key */}} + name: {{ $k }} labels: argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} - name: {{ . }} + {{- if $v.labels }} + {{- range $v.labels }} + {{ .name }}: {{ .value | default "" | quote }} + {{- end }} + {{- end }} + {{- if $v.annotations }} + annotations: + {{- range $v.annotations }} + {{ .name }}: {{ .value | default "" | quote }} + {{- end }} + {{- end }}{{- /* if $v.annotations */}} + {{- end }}{{- /* range $k, $v := $ns */}} + + {{- else if kindIs "string" $ns }} + labels: + argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} + name: {{ $ns }} + {{- end }} {{- /* if kindIs "string" $ns */}} spec: --- {{- end }} From e68b7d97bfa57849e5d1b34725894ffe5ba4297f Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 21 Mar 2023 10:07:42 -0600 Subject: [PATCH 0808/1263] Added schema validation to support additional formal for labels and annotations --- clustergroup/values.schema.json | 47 ++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 2377e31b..58d84cff 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -220,7 +220,7 @@ "type": "array", "description": "This is the array of namespaces that the VP framework will create. In addition, operator groups will also be created for each namespace.", "items": { - "type": "string" + "$ref": "#/definitions/Namespaces" } }, "indexImages": { @@ -318,6 +318,51 @@ ], "title": "ClusterGroup" }, + "Namespaces": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "string" + } + ], + "description": "Description of the applications that will be created in the ArgoCD instances. The Application CRD is the Kubernetes resource object representing a deployed application instance in an environment. Two ways of defining applications: Using a list or using a dictionary.", + "additionalProperties": true, + "properties": { + "name": { + "type": "string", + "description": "Name of the namespace." + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/definitions/NameValue" + } + }, + "annotations": { + "type": "array", + "items": { + "$ref": "#/definitions/NameValue" + } + } + } + }, + "NameValue": { + "type": "object", + "description": "Description of the applications that will be created in the ArgoCD instances. The Application CRD is the Kubernetes resource object representing a deployed application instance in an environment. Two ways of defining applications: Using a list or using a dictionary.", + "additionalProperties": true, + "properties": { + "name": { + "type": "string", + "description": "Name of the namespace." + }, + "value": { + "type": "string", + "description": "Name of the namespace." + } + } + }, "Applications": { "type": "object", "description": "Description of the applications that will be created in the ArgoCD instances. The Application CRD is the Kubernetes resource object representing a deployed application instance in an environment. Two ways of defining applications: Using a list or using a dictionary.", From 8aa8396e530693cb530e1d8ee76d238f4e94b91e Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 21 Mar 2023 10:08:11 -0600 Subject: [PATCH 0809/1263] Updated the values-example.yaml to include new format for namespaces --- examples/values-example.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 20b5d227..5b69c403 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -13,7 +13,13 @@ clusterGroup: isHubCluster: true namespaces: - - open-cluster-management + - open-cluster-management: + labels: + - name: "openshift.io/node-selector" + value: "" + annotations: + - name: "openshift.io/cluster-monitoring" + value: "true" - application-ci subscriptions: From 6b550451391aee8431dd9ad0fa8a4df9ac4b05e7 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 21 Mar 2023 10:12:48 -0600 Subject: [PATCH 0810/1263] Updated Changes.md to include new namespaces functionality. --- Changes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changes.md b/Changes.md index 15dcdf94..df2760b0 100644 --- a/Changes.md +++ b/Changes.md @@ -2,6 +2,10 @@ ## March 20, 2023 +* Added labels and annotation support to namespaces.yaml + +## March 20, 2023 + * Upgraded ESO to 0.8.1 ## February 9, 2023 From 75656486120257c33782f3d1694f990718522c66 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 21 Mar 2023 10:15:42 -0600 Subject: [PATCH 0811/1263] Updating CI tests --- tests/clustergroup-normal.expected.yaml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index f7e6d84d..a95b9d70 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -3,9 +3,12 @@ apiVersion: v1 kind: Namespace metadata: + name: open-cluster-management labels: argocd.argoproj.io/managed-by: mypattern-example - name: open-cluster-management + openshift.io/node-selector: "" + annotations: + openshift.io/cluster-monitoring: "true" spec: --- # Source: pattern-clustergroup/templates/core/namespaces.yaml @@ -143,7 +146,13 @@ data: name: argo-edge name: example namespaces: - - open-cluster-management + - open-cluster-management: + annotations: + - name: openshift.io/cluster-monitoring + value: "true" + labels: + - name: openshift.io/node-selector + value: "" - application-ci projects: - datacenter @@ -959,11 +968,11 @@ spec: apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: - name: open-cluster-management-operator-group - namespace: open-cluster-management + name: map[open-cluster-management:map[annotations:[map[name:openshift.io/cluster-monitoring value:true]] labels:[map[name:openshift.io/node-selector value:]]]]-operator-group + namespace: map[open-cluster-management:map[annotations:[map[name:openshift.io/cluster-monitoring value:true]] labels:[map[name:openshift.io/node-selector value:]]]] spec: targetNamespaces: - - open-cluster-management + - map[open-cluster-management:map[annotations:[map[name:openshift.io/cluster-monitoring value:true]] labels:[map[name:openshift.io/node-selector value:]]]] --- # Source: pattern-clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 From 185d11a3bf6e2691e1b656dd614c220dc41c6cde Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 21 Mar 2023 10:32:33 -0600 Subject: [PATCH 0812/1263] Fixed Markdown errors --- Changes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changes.md b/Changes.md index df2760b0..2a9d1e9c 100644 --- a/Changes.md +++ b/Changes.md @@ -1,8 +1,8 @@ # Changes -## March 20, 2023 +## March 21, 2023 -* Added labels and annotation support to namespaces.yaml +* Added labels and annotation support to namespaces.yaml template ## March 20, 2023 From 8c95d98ded9b07e3284547c94e47730d5851fdeb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 28 Mar 2023 17:06:31 +0200 Subject: [PATCH 0813/1263] Drop dollar sign from default password policy Sometimes they cause issues with some usage in patterns. Let's just drop them from the default policy. --- .../plugins/module_utils/load_secrets_v2.py | 2 +- .../tests/unit/test_vault_load_secrets_v2.py | 24 +++++++++---------- .../tests/unit/v2/values-secret-v2-base.yaml | 2 +- .../v2/values-secret-v2-files-emptypath.yaml | 2 +- ...-secret-v2-files-wrong-onmissingvalue.yaml | 2 +- .../v2/values-secret-v2-files-wrongpath.yaml | 2 +- .../v2/values-secret-v2-onlygenerate.yaml | 2 +- .../v2/values-secret-v2-test-override.yaml | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 761f542b..05a5917e 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -34,7 +34,7 @@ 'rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\n' 'rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\n' 'rule "charset" { charset = "0123456789" min-chars = 1 }\n' - 'rule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n' + 'rule "charset" { charset = "!@#%^&*" min-chars = 1 }\n' ) } diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 054d2306..d0e5881c 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -156,7 +156,7 @@ def test_ensure_no_vault_policies_is_ok(self, getpass): calls = [ call( - 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 attempts=3, ), call( @@ -205,7 +205,7 @@ def test_ensure_policies_are_injected(self, getpass): calls = [ call( - 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 attempts=3, ), call( @@ -213,7 +213,7 @@ def test_ensure_policies_are_injected(self, getpass): attempts=3, ), call( - 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/advancedPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl\'', # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/advancedPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl\'', # noqa: E501 attempts=3, ), call( @@ -378,7 +378,7 @@ def test_ensure_default_no_vaultprefix(self, getpass): calls = [ call( - 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 attempts=3, ), call( @@ -413,7 +413,7 @@ def test_ensure_only_generate_passwords_works(self, getpass): calls = [ call( - 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 attempts=3, ), call( @@ -421,7 +421,7 @@ def test_ensure_only_generate_passwords_works(self, getpass): attempts=3, ), call( - 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/advancedPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl\'', # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/advancedPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl\'', # noqa: E501 attempts=3, ), call( @@ -468,7 +468,7 @@ def test_generate_password_base64_works(self, getpass): calls = [ call( - 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 attempts=3, ), call( @@ -543,7 +543,7 @@ def test_password_base64_secret(self, getpass): calls = [ call( - 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 attempts=3, ), call( @@ -597,7 +597,7 @@ def test_password_default_vp_policy(self, getpass): calls = [ call( - 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 attempts=3, ), call( @@ -669,7 +669,7 @@ def test_ensure_override_works(self, getpass): calls = [ call( - 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 attempts=3, ), call( @@ -677,7 +677,7 @@ def test_ensure_override_works(self, getpass): attempts=3, ), call( - 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/advancedPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl\'', # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/advancedPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/advancedPolicy policy=@/tmp/advancedPolicy.hcl\'', # noqa: E501 attempts=3, ), call( @@ -733,7 +733,7 @@ def test_ensure_ini_file_works(self, getpass): calls = [ call( - 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 attempts=3, ), call( diff --git a/ansible/tests/unit/v2/values-secret-v2-base.yaml b/ansible/tests/unit/v2/values-secret-v2-base.yaml index dd3d67b6..bf9670d8 100644 --- a/ansible/tests/unit/v2/values-secret-v2-base.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-base.yaml @@ -14,7 +14,7 @@ vaultPolicies: rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } rule "charset" { charset = "0123456789" min-chars = 1 } - rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + rule "charset" { charset = "!@#%^&*" min-chars = 1 } secrets: - name: config-demo diff --git a/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml b/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml index 6b269ae3..9c1142aa 100644 --- a/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-files-emptypath.yaml @@ -13,7 +13,7 @@ vaultPolicies: rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } rule "charset" { charset = "0123456789" min-chars = 1 } - rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + rule "charset" { charset = "!@#%^&*" min-chars = 1 } secrets: - name: config-demo diff --git a/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml b/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml index 75eb1287..36b0e715 100644 --- a/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-files-wrong-onmissingvalue.yaml @@ -13,7 +13,7 @@ vaultPolicies: rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } rule "charset" { charset = "0123456789" min-chars = 1 } - rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + rule "charset" { charset = "!@#%^&*" min-chars = 1 } secrets: - name: config-demo diff --git a/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml b/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml index bfd849a9..35e5cfcf 100644 --- a/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-files-wrongpath.yaml @@ -13,7 +13,7 @@ vaultPolicies: rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } rule "charset" { charset = "0123456789" min-chars = 1 } - rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + rule "charset" { charset = "!@#%^&*" min-chars = 1 } secrets: - name: config-demo diff --git a/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml b/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml index e72c1e74..2a5ef0b6 100644 --- a/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-onlygenerate.yaml @@ -14,7 +14,7 @@ vaultPolicies: rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } rule "charset" { charset = "0123456789" min-chars = 1 } - rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + rule "charset" { charset = "!@#%^&*" min-chars = 1 } secrets: - name: config-demo diff --git a/ansible/tests/unit/v2/values-secret-v2-test-override.yaml b/ansible/tests/unit/v2/values-secret-v2-test-override.yaml index 04c3c05b..8efdd95c 100644 --- a/ansible/tests/unit/v2/values-secret-v2-test-override.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-test-override.yaml @@ -14,7 +14,7 @@ vaultPolicies: rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } rule "charset" { charset = "0123456789" min-chars = 1 } - rule "charset" { charset = "!@#$%^&*" min-chars = 1 } + rule "charset" { charset = "!@#%^&*" min-chars = 1 } secrets: - name: config-demo From 1e02e4541ee77f677bbe26d917328ac10ada5fc1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 3 Apr 2023 18:34:32 +0200 Subject: [PATCH 0814/1263] Make sure clusters always has an empty default Otherwise we could get temporary errors which is unwanted. --- ansible/roles/vault_utils/tasks/vault_spokes_init.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml index 5287c9e7..11603708 100644 --- a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml @@ -45,7 +45,7 @@ - name: Merge the two dicts together ansible.builtin.set_fact: - clusters_info: "{{ clusters | combine(cleaned_acm_secrets, recursive=True) }}" + clusters_info: "{{ clusters | default({}) | combine(cleaned_acm_secrets, recursive=True) }}" - name: Write out CAs ansible.builtin.copy: From 1e69787f29fa6e7f49d8d1f142b44d64ceabd962 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 3 Apr 2023 18:35:05 +0200 Subject: [PATCH 0815/1263] Do not validate certs when we use letsencrypt on the endpoints Due to https://issues.redhat.com/browse/ACM-4398 we cannot leave validate_certs always on. Since we do not want to always disable it as that would be a security regression, we simply disable it only when the helm variable "letsencrypt.api_endpoint" is set to true. In that case we know that we are going to use letsencrypt certificates so it is okay to disable the verification until then. Once/if the ACM bug above gets fixed we can drop this. Tested on MCG --- ansible/roles/vault_utils/tasks/vault_spokes_init.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml index 11603708..af1a02fd 100644 --- a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml @@ -57,6 +57,13 @@ loop_control: label: "{{ item.key }}" +# FIXME(bandini): validate_certs is false due to an ACM bug when using +# letsencrypt certificates with API endpoints: https://issues.redhat.com/browse/ACM-4398 +# We always verify the CA chain except when letsencrypt.api_endpoint is set to true +- name: If we are using letsencrypt on the API endpoints we cannot use the validate_certs later + ansible.builtin.set_fact: + validate_certs_api_endpoint: "{{ not letsencrypt.api_endpoint | default(True) | bool }}" + - name: Fetch remote ansible to remote cluster kubernetes.core.k8s_info: api_key: "{{ item.value['bearerToken'] }}" @@ -66,6 +73,7 @@ namespace: "{{ external_secrets_ns }}" name: "{{ external_secrets_secret }}" api_version: v1 + validate_certs: "{{ validate_certs_api_endpoint }}" register: remote_external_secrets_sa when: - clusters_info[item.key]['bearerToken'] is defined From c2ecb2ab51beec7202838b6e47d0c7a7e269a6b0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 14 Mar 2023 14:46:03 +0100 Subject: [PATCH 0816/1263] Add an experimental letsencypt chart This change adds an experimental letsencrypt chart that allows a pattern user/developer to have all routes and the API endpoint use signed certificates by letsencrypt. At this stage only AWS is supported. The full documentation is contained in the chart's README.md file --- letsencrypt/.helmignore | 23 ++ letsencrypt/Chart.yaml | 16 ++ letsencrypt/README.md | 68 ++++++ letsencrypt/templates/api-cert.yaml | 28 +++ .../templates/cert-manager-installation.yaml | 38 ++++ .../templates/credentials-request.yaml | 24 +++ letsencrypt/templates/default-routes.yaml | 46 ++++ letsencrypt/templates/issuer.yaml | 25 +++ letsencrypt/templates/namespaces.yaml | 20 ++ letsencrypt/templates/wildcard-cert.yaml | 28 +++ letsencrypt/values.yaml | 60 ++++++ ...rypt-industrial-edge-factory.expected.yaml | 202 ++++++++++++++++++ ...sencrypt-industrial-edge-hub.expected.yaml | 202 ++++++++++++++++++ ...ncrypt-medical-diagnosis-hub.expected.yaml | 202 ++++++++++++++++++ tests/letsencrypt-naked.expected.yaml | 202 ++++++++++++++++++ tests/letsencrypt-normal.expected.yaml | 202 ++++++++++++++++++ 16 files changed, 1386 insertions(+) create mode 100644 letsencrypt/.helmignore create mode 100644 letsencrypt/Chart.yaml create mode 100644 letsencrypt/README.md create mode 100644 letsencrypt/templates/api-cert.yaml create mode 100644 letsencrypt/templates/cert-manager-installation.yaml create mode 100644 letsencrypt/templates/credentials-request.yaml create mode 100644 letsencrypt/templates/default-routes.yaml create mode 100644 letsencrypt/templates/issuer.yaml create mode 100644 letsencrypt/templates/namespaces.yaml create mode 100644 letsencrypt/templates/wildcard-cert.yaml create mode 100644 letsencrypt/values.yaml create mode 100644 tests/letsencrypt-industrial-edge-factory.expected.yaml create mode 100644 tests/letsencrypt-industrial-edge-hub.expected.yaml create mode 100644 tests/letsencrypt-medical-diagnosis-hub.expected.yaml create mode 100644 tests/letsencrypt-naked.expected.yaml create mode 100644 tests/letsencrypt-normal.expected.yaml diff --git a/letsencrypt/.helmignore b/letsencrypt/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/letsencrypt/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/letsencrypt/Chart.yaml b/letsencrypt/Chart.yaml new file mode 100644 index 00000000..b5b1c31b --- /dev/null +++ b/letsencrypt/Chart.yaml @@ -0,0 +1,16 @@ +apiVersion: v2 +name: letsencrypt +description: A Helm chart to add letsencrypt support to Validated Patterns + +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/letsencrypt/README.md b/letsencrypt/README.md new file mode 100644 index 00000000..d277abaa --- /dev/null +++ b/letsencrypt/README.md @@ -0,0 +1,68 @@ +# Letsencrypt support for Validated patterns + +This is an *EXPERIMENTAL* and *UNSUPPORTED* chart to enable letsencrypt support in the pattern. +Currently the only supported cloud for this is AWS. + +In order to enable this chart in your patterns, please add and edit the following lines to `values-AWS.yaml`: + + letsencrypt: + region: eu-central-1 # region of the cluster + server: https://acme-v02.api.letsencrypt.org/directory + # staging URL + # server: https://acme-staging-v02.api.letsencrypt.org/directory + email: foo@bar.it + + clusterGroup: + applications: + letsencrypt: + name: letsencrypt + namespace: letsencrypt + project: default + path: common/letsencrypt + +Once the above is enabled in a pattern, a certain amount of time (~15/20 minutes or so) is needed for all the cluster operators to settle, all the HTTPS routes will have a wildcard certificate signed by letsencrypt. By default also the API endpoint will use a certificate signed by letsencrypt. + +## Limitations + +Please be aware of the following gotchas when using this chart: + +1. Once the API certificate has been replaced with the letsencrypt one, the `oc` commands might fail with x509 unknown certificate authority errors. + You need to remove the previous CA from the kubeconfig file. Run: `oc config set-cluster --certificate-authority="/dev/null" --embed-certs` +2. When you switch to non-staging letsencrypt certificates, things might fail if you asked for too many certificates over the last few days. +3. The cluster takes ~20-30 mins to fully settle when both the API endpoint and the default ingress certificates are implemented + +## Implementation + +This chart creates a Cloud Credential that is allowed to write and read DNS entries via Route53 in AWS. That credential is then used by cert-manager to prove ownership of the DNS zone and answer the ACME DNS01 challenges. +We ask for a single wildcard certificate for the default Ingress *.apps.domain and one non-wildcard certificate for the API endpoint api.domain. +We use Argo's Server-Side Apply feature to patch in the Ingress Controller and the API endpoint certificates. +Currently we also patch the main cluster-wide Argo instance to set the tls route to `reencrypt` in order have a proper cert there. Once issue 297 in the gitops-operator repository is fixed, we can drop that. + +## Parameters + +### global parameters + +This section contains the global parameters consumed by this chart + +| Name | Description | Value | +| --------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------ | +| `global.localClusterDomain` | String containing the domain including the apps. prefix. Gets set by the Validated Pattern framework | `apps.example.com` | + +### letsencrypt parameters + +This section contains all the parameters for the letsencrypt +chart in order to request CA signed certificates in a Validated Pattern + +| Name | Description | Value | +| -------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | +| `letsencrypt.enabled` | Boolean to enable this feature and request a wildcard cert for the default Infress (*.apps.domain) (defaults to True) | `true` | +| `letsencrypt.api_endpoint` | Boolean to enable letsencrypt certs on the API endpoint too (defaults to True) | `true` | +| `letsencrypt.region` | String that defines the region used by the route53/dns01 resolver in cert-manager (required) | `eu-central-1` | +| `letsencrypt.email` | String containing the email used when requesting certificates to letsencrypt (required) | `test@example.com` | +| `letsencrypt.server` | String containing the letsencrypt ACME URL (Defaults to the staging server) | `https://acme-staging-v02.api.letsencrypt.org/directory` | +| `letsencrypt.organizations` | List of organization names to be put in a certificate (Defaults to [hybrid-cloud-patterns.io]) | `["hybrid-cloud-patterns.io"]` | +| `letsencrypt.usages` | List of certificate uses. See API cert-manager.io/v1.KeyUsage (Defaults to [server auth]) | `["server auth"]` | +| `letsencrypt.duration` | Duration of the requested letsencrypt certificates (Defaults to 168h0m0s) | `168h0m0s` | +| `letsencrypt.renewBefore` | How long before expiration date should the certs be renewed (Defaults to 28h0m0s) | `28h0m0s` | +| `letsencrypt.nameservers` | List of DNS server (ip:port strings) to be used when doing DNS01 challenges (Defaults to [8.8.8.8:53, 1.1.1.1:53]) | `["8.8.8.8:53","1.1.1.1:53"]` | +| `letsencrypt.certmanagerChannel` | String the channel to install cert-manager from (Defaults to "stable-v1") | `stable-v1` | diff --git a/letsencrypt/templates/api-cert.yaml b/letsencrypt/templates/api-cert.yaml new file mode 100644 index 00000000..ed9e7c0e --- /dev/null +++ b/letsencrypt/templates/api-cert.yaml @@ -0,0 +1,28 @@ +{{ if and (.Values.letsencrypt.enabled) (.Values.letsencrypt.api_endpoint) }} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: api-validated-patterns-cert + namespace: openshift-config + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + secretName: api-validated-patterns-letsencrypt-cert + duration: {{ .Values.letsencrypt.duration }} + renewBefore: {{ .Values.letsencrypt.renewBefore }} + commonName: 'api.{{ $.Values.global.localClusterDomain | replace "apps." "" }}' + usages: + {{- range .Values.letsencrypt.usages }} + - {{ . }} + {{- end }} + dnsNames: + - api.{{ $.Values.global.localClusterDomain | replace "apps." "" }} + issuerRef: + name: validated-patterns-issuer + kind: ClusterIssuer + subject: + organizations: + {{- range .Values.letsencrypt.organizations }} + - {{ . }} + {{- end }} +{{- end }} diff --git a/letsencrypt/templates/cert-manager-installation.yaml b/letsencrypt/templates/cert-manager-installation.yaml new file mode 100644 index 00000000..59375b00 --- /dev/null +++ b/letsencrypt/templates/cert-manager-installation.yaml @@ -0,0 +1,38 @@ +{{ if .Values.letsencrypt.enabled }} +--- +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-cert-manager-operator + namespace: cert-manager-operator +spec: + channel: "{{ .Values.letsencrypt.certmanagerChannel }}" + installPlanApproval: Automatic + name: openshift-cert-manager-operator + source: redhat-operators + sourceNamespace: openshift-marketplace +--- +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: cert-manager-operator + namespace: cert-manager-operator +spec: + targetNamespaces: + - cert-manager-operator +--- +apiVersion: operator.openshift.io/v1alpha1 +kind: CertManager +metadata: + name: cluster + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + managementState: "Managed" + unsupportedConfigOverrides: + # Here's an example to supply custom DNS settings. + controller: + args: + - "--dns01-recursive-nameservers={{ with index .Values.letsencrypt.nameservers 0 }}{{ . }}{{- end }},{{ with index .Values.letsencrypt.nameservers 1 }}{{ . }}{{- end }}" + - "--dns01-recursive-nameservers-only" +{{- end }} diff --git a/letsencrypt/templates/credentials-request.yaml b/letsencrypt/templates/credentials-request.yaml new file mode 100644 index 00000000..27aad295 --- /dev/null +++ b/letsencrypt/templates/credentials-request.yaml @@ -0,0 +1,24 @@ +{{ if .Values.letsencrypt.enabled }} +apiVersion: cloudcredential.openshift.io/v1 +kind: CredentialsRequest +metadata: + name: letsencrypt-cert-manager-dns + namespace: openshift-cloud-credential-operator + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + providerSpec: + apiVersion: cloudcredential.openshift.io/v1 + kind: AWSProviderSpec + statementEntries: + - action: + - 'route53:ChangeResourceRecordSets' + - 'route53:GetChange' + - 'route53:ListHostedZonesByName' + - 'route53:ListHostedZones' + effect: Allow + resource: '*' + secretRef: + name: cert-manager-dns-credentials + namespace: cert-manager +{{- end }} diff --git a/letsencrypt/templates/default-routes.yaml b/letsencrypt/templates/default-routes.yaml new file mode 100644 index 00000000..8a01db6a --- /dev/null +++ b/letsencrypt/templates/default-routes.yaml @@ -0,0 +1,46 @@ +{{ if .Values.letsencrypt.enabled }} +--- +apiVersion: operator.openshift.io/v1 +kind: IngressController +metadata: + name: default + namespace: openshift-ingress-operator + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + routeAdmission: + wildcardPolicy: WildcardsAllowed + defaultCertificate: + name: lets-encrypt-wildcart-cert-tls +# Patch the cluster-wide argocd instance so it uses the ingress tls cert +--- +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + name: openshift-gitops + namespace: openshift-gitops + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + server: + route: + enabled: true + tls: + termination: reencrypt +{{ if .Values.letsencrypt.api_endpoint }} +--- +apiVersion: config.openshift.io/v1 +kind: APIServer +metadata: + name: cluster + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + servingCerts: + namedCertificates: + - names: + - api.{{ $.Values.global.localClusterDomain | replace "apps." "" }} + servingCertificate: + name: api-validated-patterns-letsencrypt-cert +{{- end }} +{{- end }} diff --git a/letsencrypt/templates/issuer.yaml b/letsencrypt/templates/issuer.yaml new file mode 100644 index 00000000..1370500f --- /dev/null +++ b/letsencrypt/templates/issuer.yaml @@ -0,0 +1,25 @@ +{{ if .Values.letsencrypt.enabled }} +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: validated-patterns-issuer + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + acme: + server: {{ .Values.letsencrypt.server }} + email: {{ .Values.letsencrypt.email }} + privateKeySecretRef: + name: validated-patterns-issuer-account-key + solvers: + - selector: {} + dns01: + route53: + region: {{ .Values.letsencrypt.region }} + accessKeyIDSecretRef: + name: cert-manager-dns-credentials + key: aws_access_key_id + secretAccessKeySecretRef: + name: cert-manager-dns-credentials + key: aws_secret_access_key +{{- end }} diff --git a/letsencrypt/templates/namespaces.yaml b/letsencrypt/templates/namespaces.yaml new file mode 100644 index 00000000..a4f65fe5 --- /dev/null +++ b/letsencrypt/templates/namespaces.yaml @@ -0,0 +1,20 @@ +{{ if .Values.letsencrypt.enabled }} +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager-operator +spec: +--- +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager +spec: +--- +apiVersion: v1 +kind: Namespace +metadata: + name: letsencrypt +spec: +--- +{{- end }} diff --git a/letsencrypt/templates/wildcard-cert.yaml b/letsencrypt/templates/wildcard-cert.yaml new file mode 100644 index 00000000..e7b82480 --- /dev/null +++ b/letsencrypt/templates/wildcard-cert.yaml @@ -0,0 +1,28 @@ +{{ if .Values.letsencrypt.enabled }} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: lets-encrypt-certs + namespace: openshift-ingress + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + secretName: lets-encrypt-wildcart-cert-tls + duration: {{ .Values.letsencrypt.duration }} + renewBefore: {{ .Values.letsencrypt.renewBefore }} + commonName: '*.{{ $.Values.global.localClusterDomain }}' + usages: + {{- range .Values.letsencrypt.usages }} + - {{ . }} + {{- end }} + dnsNames: + - '*.{{ $.Values.global.localClusterDomain }}' + issuerRef: + name: validated-patterns-issuer + kind: ClusterIssuer + subject: + organizations: + {{- range .Values.letsencrypt.organizations }} + - {{ . }} + {{- end }} +{{- end }} diff --git a/letsencrypt/values.yaml b/letsencrypt/values.yaml new file mode 100644 index 00000000..a95957b8 --- /dev/null +++ b/letsencrypt/values.yaml @@ -0,0 +1,60 @@ +# NOTE: This is currently an experimental/unsupported chart! +# Default values for the experimental letsencrypt chart +# Generate the README.md sections using https://github.com/bitnami-labs/readme-generator-for-helm +# +## @section global parameters +## @descriptionStart This section contains the global parameters consumed by this chart +## @descriptionEnd +global: + ## @param global.localClusterDomain String containing the domain including the apps. prefix. Gets set by the Validated Pattern framework + localClusterDomain: "apps.example.com" + +## @section letsencrypt parameters +## @descriptionStart This section contains all the parameters for the letsencrypt +## chart in order to request CA signed certificates in a Validated Pattern +## @descriptionEnd +letsencrypt: + # By default if you include this chart you enable the letsencrypt charts + # on both the *.apps. ingress and on the API endpoint + ## @param letsencrypt.enabled Boolean to enable this feature and request a wildcard cert for the default Infress (*.apps.domain) (defaults to True) + enabled: true + ## @param letsencrypt.api_endpoint Boolean to enable letsencrypt certs on the API endpoint too (defaults to True) + api_endpoint: true + + # These two lines need tweaking for every deployment. @example.com emails + # will be rejected by letsencrypt + ## @param letsencrypt.region String that defines the region used by the route53/dns01 resolver in cert-manager (required) + region: eu-central-1 + ## @param letsencrypt.email String containing the email used when requesting certificates to letsencrypt (required) + email: test@example.com + + # By default we use the staging URL to avoid any ratelimiting while testing + # To switch to the production certificates signed by a recognized CA, please + # switch the comments around in the two following lines + ## @param letsencrypt.server String containing the letsencrypt ACME URL (Defaults to the staging server) + server: https://acme-staging-v02.api.letsencrypt.org/directory + # server: https://acme-v02.api.letsencrypt.org/directory + + # These are only for metadata in the certificates + ## @param letsencrypt.organizations List of organization names to be put in a certificate (Defaults to [hybrid-cloud-patterns.io]) + organizations: + - hybrid-cloud-patterns.io + ## @param letsencrypt.usages List of certificate uses. See API cert-manager.io/v1.KeyUsage (Defaults to [server auth]) + usages: + - server auth + + ## @param letsencrypt.duration Duration of the requested letsencrypt certificates (Defaults to 168h0m0s) + duration: "168h0m0s" + ## @param letsencrypt.renewBefore How long before expiration date should the certs be renewed (Defaults to 28h0m0s) + renewBefore: "28h0m0s" + + # These two are needed because the DNS01 ACME solver needs outside DNS + # servers and won't really work with openshift's internal split-view DNS servers + # https://cert-manager.io/docs/configuration/acme/dns01/#setting-nameservers-for-dns01-self-check + ## @param letsencrypt.nameservers List of DNS server (ip:port strings) to be used when doing DNS01 challenges (Defaults to [8.8.8.8:53, 1.1.1.1:53]) + nameservers: + - 8.8.8.8:53 + - 1.1.1.1:53 + + ## @param letsencrypt.certmanagerChannel String the channel to install cert-manager from (Defaults to "stable-v1") + certmanagerChannel: "stable-v1" diff --git a/tests/letsencrypt-industrial-edge-factory.expected.yaml b/tests/letsencrypt-industrial-edge-factory.expected.yaml new file mode 100644 index 00000000..b5aded2f --- /dev/null +++ b/tests/letsencrypt-industrial-edge-factory.expected.yaml @@ -0,0 +1,202 @@ +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager-operator +spec: +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager +spec: +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: letsencrypt +spec: +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: config.openshift.io/v1 +kind: APIServer +metadata: + name: cluster + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + servingCerts: + namedCertificates: + - names: + - api.region.example.com + servingCertificate: + name: api-validated-patterns-letsencrypt-cert +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + name: openshift-gitops + namespace: openshift-gitops + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + server: + route: + enabled: true + tls: + termination: reencrypt +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operator.openshift.io/v1alpha1 +kind: CertManager +metadata: + name: cluster + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + managementState: "Managed" + unsupportedConfigOverrides: + # Here's an example to supply custom DNS settings. + controller: + args: + - "--dns01-recursive-nameservers=8.8.8.8:53,1.1.1.1:53" + - "--dns01-recursive-nameservers-only" +--- +# Source: letsencrypt/templates/api-cert.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: api-validated-patterns-cert + namespace: openshift-config + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + secretName: api-validated-patterns-letsencrypt-cert + duration: 168h0m0s + renewBefore: 28h0m0s + commonName: 'api.region.example.com' + usages: + - server auth + dnsNames: + - api.region.example.com + issuerRef: + name: validated-patterns-issuer + kind: ClusterIssuer + subject: + organizations: + - hybrid-cloud-patterns.io +--- +# Source: letsencrypt/templates/wildcard-cert.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: lets-encrypt-certs + namespace: openshift-ingress + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + secretName: lets-encrypt-wildcart-cert-tls + duration: 168h0m0s + renewBefore: 28h0m0s + commonName: '*.apps.region.example.com' + usages: + - server auth + dnsNames: + - '*.apps.region.example.com' + issuerRef: + name: validated-patterns-issuer + kind: ClusterIssuer + subject: + organizations: + - hybrid-cloud-patterns.io +--- +# Source: letsencrypt/templates/issuer.yaml +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: validated-patterns-issuer + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + acme: + server: https://acme-staging-v02.api.letsencrypt.org/directory + email: test@example.com + privateKeySecretRef: + name: validated-patterns-issuer-account-key + solvers: + - selector: {} + dns01: + route53: + region: eu-central-1 + accessKeyIDSecretRef: + name: cert-manager-dns-credentials + key: aws_access_key_id + secretAccessKeySecretRef: + name: cert-manager-dns-credentials + key: aws_secret_access_key +--- +# Source: letsencrypt/templates/credentials-request.yaml +apiVersion: cloudcredential.openshift.io/v1 +kind: CredentialsRequest +metadata: + name: letsencrypt-cert-manager-dns + namespace: openshift-cloud-credential-operator + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + providerSpec: + apiVersion: cloudcredential.openshift.io/v1 + kind: AWSProviderSpec + statementEntries: + - action: + - 'route53:ChangeResourceRecordSets' + - 'route53:GetChange' + - 'route53:ListHostedZonesByName' + - 'route53:ListHostedZones' + effect: Allow + resource: '*' + secretRef: + name: cert-manager-dns-credentials + namespace: cert-manager +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: operator.openshift.io/v1 +kind: IngressController +metadata: + name: default + namespace: openshift-ingress-operator + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + routeAdmission: + wildcardPolicy: WildcardsAllowed + defaultCertificate: + name: lets-encrypt-wildcart-cert-tls +# Patch the cluster-wide argocd instance so it uses the ingress tls cert +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: cert-manager-operator + namespace: cert-manager-operator +spec: + targetNamespaces: + - cert-manager-operator +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-cert-manager-operator + namespace: cert-manager-operator +spec: + channel: "stable-v1" + installPlanApproval: Automatic + name: openshift-cert-manager-operator + source: redhat-operators + sourceNamespace: openshift-marketplace diff --git a/tests/letsencrypt-industrial-edge-hub.expected.yaml b/tests/letsencrypt-industrial-edge-hub.expected.yaml new file mode 100644 index 00000000..b5aded2f --- /dev/null +++ b/tests/letsencrypt-industrial-edge-hub.expected.yaml @@ -0,0 +1,202 @@ +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager-operator +spec: +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager +spec: +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: letsencrypt +spec: +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: config.openshift.io/v1 +kind: APIServer +metadata: + name: cluster + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + servingCerts: + namedCertificates: + - names: + - api.region.example.com + servingCertificate: + name: api-validated-patterns-letsencrypt-cert +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + name: openshift-gitops + namespace: openshift-gitops + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + server: + route: + enabled: true + tls: + termination: reencrypt +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operator.openshift.io/v1alpha1 +kind: CertManager +metadata: + name: cluster + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + managementState: "Managed" + unsupportedConfigOverrides: + # Here's an example to supply custom DNS settings. + controller: + args: + - "--dns01-recursive-nameservers=8.8.8.8:53,1.1.1.1:53" + - "--dns01-recursive-nameservers-only" +--- +# Source: letsencrypt/templates/api-cert.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: api-validated-patterns-cert + namespace: openshift-config + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + secretName: api-validated-patterns-letsencrypt-cert + duration: 168h0m0s + renewBefore: 28h0m0s + commonName: 'api.region.example.com' + usages: + - server auth + dnsNames: + - api.region.example.com + issuerRef: + name: validated-patterns-issuer + kind: ClusterIssuer + subject: + organizations: + - hybrid-cloud-patterns.io +--- +# Source: letsencrypt/templates/wildcard-cert.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: lets-encrypt-certs + namespace: openshift-ingress + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + secretName: lets-encrypt-wildcart-cert-tls + duration: 168h0m0s + renewBefore: 28h0m0s + commonName: '*.apps.region.example.com' + usages: + - server auth + dnsNames: + - '*.apps.region.example.com' + issuerRef: + name: validated-patterns-issuer + kind: ClusterIssuer + subject: + organizations: + - hybrid-cloud-patterns.io +--- +# Source: letsencrypt/templates/issuer.yaml +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: validated-patterns-issuer + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + acme: + server: https://acme-staging-v02.api.letsencrypt.org/directory + email: test@example.com + privateKeySecretRef: + name: validated-patterns-issuer-account-key + solvers: + - selector: {} + dns01: + route53: + region: eu-central-1 + accessKeyIDSecretRef: + name: cert-manager-dns-credentials + key: aws_access_key_id + secretAccessKeySecretRef: + name: cert-manager-dns-credentials + key: aws_secret_access_key +--- +# Source: letsencrypt/templates/credentials-request.yaml +apiVersion: cloudcredential.openshift.io/v1 +kind: CredentialsRequest +metadata: + name: letsencrypt-cert-manager-dns + namespace: openshift-cloud-credential-operator + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + providerSpec: + apiVersion: cloudcredential.openshift.io/v1 + kind: AWSProviderSpec + statementEntries: + - action: + - 'route53:ChangeResourceRecordSets' + - 'route53:GetChange' + - 'route53:ListHostedZonesByName' + - 'route53:ListHostedZones' + effect: Allow + resource: '*' + secretRef: + name: cert-manager-dns-credentials + namespace: cert-manager +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: operator.openshift.io/v1 +kind: IngressController +metadata: + name: default + namespace: openshift-ingress-operator + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + routeAdmission: + wildcardPolicy: WildcardsAllowed + defaultCertificate: + name: lets-encrypt-wildcart-cert-tls +# Patch the cluster-wide argocd instance so it uses the ingress tls cert +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: cert-manager-operator + namespace: cert-manager-operator +spec: + targetNamespaces: + - cert-manager-operator +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-cert-manager-operator + namespace: cert-manager-operator +spec: + channel: "stable-v1" + installPlanApproval: Automatic + name: openshift-cert-manager-operator + source: redhat-operators + sourceNamespace: openshift-marketplace diff --git a/tests/letsencrypt-medical-diagnosis-hub.expected.yaml b/tests/letsencrypt-medical-diagnosis-hub.expected.yaml new file mode 100644 index 00000000..b5aded2f --- /dev/null +++ b/tests/letsencrypt-medical-diagnosis-hub.expected.yaml @@ -0,0 +1,202 @@ +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager-operator +spec: +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager +spec: +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: letsencrypt +spec: +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: config.openshift.io/v1 +kind: APIServer +metadata: + name: cluster + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + servingCerts: + namedCertificates: + - names: + - api.region.example.com + servingCertificate: + name: api-validated-patterns-letsencrypt-cert +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + name: openshift-gitops + namespace: openshift-gitops + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + server: + route: + enabled: true + tls: + termination: reencrypt +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operator.openshift.io/v1alpha1 +kind: CertManager +metadata: + name: cluster + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + managementState: "Managed" + unsupportedConfigOverrides: + # Here's an example to supply custom DNS settings. + controller: + args: + - "--dns01-recursive-nameservers=8.8.8.8:53,1.1.1.1:53" + - "--dns01-recursive-nameservers-only" +--- +# Source: letsencrypt/templates/api-cert.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: api-validated-patterns-cert + namespace: openshift-config + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + secretName: api-validated-patterns-letsencrypt-cert + duration: 168h0m0s + renewBefore: 28h0m0s + commonName: 'api.region.example.com' + usages: + - server auth + dnsNames: + - api.region.example.com + issuerRef: + name: validated-patterns-issuer + kind: ClusterIssuer + subject: + organizations: + - hybrid-cloud-patterns.io +--- +# Source: letsencrypt/templates/wildcard-cert.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: lets-encrypt-certs + namespace: openshift-ingress + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + secretName: lets-encrypt-wildcart-cert-tls + duration: 168h0m0s + renewBefore: 28h0m0s + commonName: '*.apps.region.example.com' + usages: + - server auth + dnsNames: + - '*.apps.region.example.com' + issuerRef: + name: validated-patterns-issuer + kind: ClusterIssuer + subject: + organizations: + - hybrid-cloud-patterns.io +--- +# Source: letsencrypt/templates/issuer.yaml +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: validated-patterns-issuer + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + acme: + server: https://acme-staging-v02.api.letsencrypt.org/directory + email: test@example.com + privateKeySecretRef: + name: validated-patterns-issuer-account-key + solvers: + - selector: {} + dns01: + route53: + region: eu-central-1 + accessKeyIDSecretRef: + name: cert-manager-dns-credentials + key: aws_access_key_id + secretAccessKeySecretRef: + name: cert-manager-dns-credentials + key: aws_secret_access_key +--- +# Source: letsencrypt/templates/credentials-request.yaml +apiVersion: cloudcredential.openshift.io/v1 +kind: CredentialsRequest +metadata: + name: letsencrypt-cert-manager-dns + namespace: openshift-cloud-credential-operator + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + providerSpec: + apiVersion: cloudcredential.openshift.io/v1 + kind: AWSProviderSpec + statementEntries: + - action: + - 'route53:ChangeResourceRecordSets' + - 'route53:GetChange' + - 'route53:ListHostedZonesByName' + - 'route53:ListHostedZones' + effect: Allow + resource: '*' + secretRef: + name: cert-manager-dns-credentials + namespace: cert-manager +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: operator.openshift.io/v1 +kind: IngressController +metadata: + name: default + namespace: openshift-ingress-operator + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + routeAdmission: + wildcardPolicy: WildcardsAllowed + defaultCertificate: + name: lets-encrypt-wildcart-cert-tls +# Patch the cluster-wide argocd instance so it uses the ingress tls cert +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: cert-manager-operator + namespace: cert-manager-operator +spec: + targetNamespaces: + - cert-manager-operator +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-cert-manager-operator + namespace: cert-manager-operator +spec: + channel: "stable-v1" + installPlanApproval: Automatic + name: openshift-cert-manager-operator + source: redhat-operators + sourceNamespace: openshift-marketplace diff --git a/tests/letsencrypt-naked.expected.yaml b/tests/letsencrypt-naked.expected.yaml new file mode 100644 index 00000000..73aa94a4 --- /dev/null +++ b/tests/letsencrypt-naked.expected.yaml @@ -0,0 +1,202 @@ +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager-operator +spec: +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager +spec: +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: letsencrypt +spec: +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: config.openshift.io/v1 +kind: APIServer +metadata: + name: cluster + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + servingCerts: + namedCertificates: + - names: + - api.example.com + servingCertificate: + name: api-validated-patterns-letsencrypt-cert +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + name: openshift-gitops + namespace: openshift-gitops + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + server: + route: + enabled: true + tls: + termination: reencrypt +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operator.openshift.io/v1alpha1 +kind: CertManager +metadata: + name: cluster + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + managementState: "Managed" + unsupportedConfigOverrides: + # Here's an example to supply custom DNS settings. + controller: + args: + - "--dns01-recursive-nameservers=8.8.8.8:53,1.1.1.1:53" + - "--dns01-recursive-nameservers-only" +--- +# Source: letsencrypt/templates/api-cert.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: api-validated-patterns-cert + namespace: openshift-config + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + secretName: api-validated-patterns-letsencrypt-cert + duration: 168h0m0s + renewBefore: 28h0m0s + commonName: 'api.example.com' + usages: + - server auth + dnsNames: + - api.example.com + issuerRef: + name: validated-patterns-issuer + kind: ClusterIssuer + subject: + organizations: + - hybrid-cloud-patterns.io +--- +# Source: letsencrypt/templates/wildcard-cert.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: lets-encrypt-certs + namespace: openshift-ingress + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + secretName: lets-encrypt-wildcart-cert-tls + duration: 168h0m0s + renewBefore: 28h0m0s + commonName: '*.apps.example.com' + usages: + - server auth + dnsNames: + - '*.apps.example.com' + issuerRef: + name: validated-patterns-issuer + kind: ClusterIssuer + subject: + organizations: + - hybrid-cloud-patterns.io +--- +# Source: letsencrypt/templates/issuer.yaml +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: validated-patterns-issuer + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + acme: + server: https://acme-staging-v02.api.letsencrypt.org/directory + email: test@example.com + privateKeySecretRef: + name: validated-patterns-issuer-account-key + solvers: + - selector: {} + dns01: + route53: + region: eu-central-1 + accessKeyIDSecretRef: + name: cert-manager-dns-credentials + key: aws_access_key_id + secretAccessKeySecretRef: + name: cert-manager-dns-credentials + key: aws_secret_access_key +--- +# Source: letsencrypt/templates/credentials-request.yaml +apiVersion: cloudcredential.openshift.io/v1 +kind: CredentialsRequest +metadata: + name: letsencrypt-cert-manager-dns + namespace: openshift-cloud-credential-operator + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + providerSpec: + apiVersion: cloudcredential.openshift.io/v1 + kind: AWSProviderSpec + statementEntries: + - action: + - 'route53:ChangeResourceRecordSets' + - 'route53:GetChange' + - 'route53:ListHostedZonesByName' + - 'route53:ListHostedZones' + effect: Allow + resource: '*' + secretRef: + name: cert-manager-dns-credentials + namespace: cert-manager +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: operator.openshift.io/v1 +kind: IngressController +metadata: + name: default + namespace: openshift-ingress-operator + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + routeAdmission: + wildcardPolicy: WildcardsAllowed + defaultCertificate: + name: lets-encrypt-wildcart-cert-tls +# Patch the cluster-wide argocd instance so it uses the ingress tls cert +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: cert-manager-operator + namespace: cert-manager-operator +spec: + targetNamespaces: + - cert-manager-operator +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-cert-manager-operator + namespace: cert-manager-operator +spec: + channel: "stable-v1" + installPlanApproval: Automatic + name: openshift-cert-manager-operator + source: redhat-operators + sourceNamespace: openshift-marketplace diff --git a/tests/letsencrypt-normal.expected.yaml b/tests/letsencrypt-normal.expected.yaml new file mode 100644 index 00000000..b5aded2f --- /dev/null +++ b/tests/letsencrypt-normal.expected.yaml @@ -0,0 +1,202 @@ +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager-operator +spec: +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager +spec: +--- +# Source: letsencrypt/templates/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: letsencrypt +spec: +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: config.openshift.io/v1 +kind: APIServer +metadata: + name: cluster + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + servingCerts: + namedCertificates: + - names: + - api.region.example.com + servingCertificate: + name: api-validated-patterns-letsencrypt-cert +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + name: openshift-gitops + namespace: openshift-gitops + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + server: + route: + enabled: true + tls: + termination: reencrypt +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operator.openshift.io/v1alpha1 +kind: CertManager +metadata: + name: cluster + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + managementState: "Managed" + unsupportedConfigOverrides: + # Here's an example to supply custom DNS settings. + controller: + args: + - "--dns01-recursive-nameservers=8.8.8.8:53,1.1.1.1:53" + - "--dns01-recursive-nameservers-only" +--- +# Source: letsencrypt/templates/api-cert.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: api-validated-patterns-cert + namespace: openshift-config + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + secretName: api-validated-patterns-letsencrypt-cert + duration: 168h0m0s + renewBefore: 28h0m0s + commonName: 'api.region.example.com' + usages: + - server auth + dnsNames: + - api.region.example.com + issuerRef: + name: validated-patterns-issuer + kind: ClusterIssuer + subject: + organizations: + - hybrid-cloud-patterns.io +--- +# Source: letsencrypt/templates/wildcard-cert.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: lets-encrypt-certs + namespace: openshift-ingress + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + secretName: lets-encrypt-wildcart-cert-tls + duration: 168h0m0s + renewBefore: 28h0m0s + commonName: '*.apps.region.example.com' + usages: + - server auth + dnsNames: + - '*.apps.region.example.com' + issuerRef: + name: validated-patterns-issuer + kind: ClusterIssuer + subject: + organizations: + - hybrid-cloud-patterns.io +--- +# Source: letsencrypt/templates/issuer.yaml +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: validated-patterns-issuer + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + acme: + server: https://acme-staging-v02.api.letsencrypt.org/directory + email: test@example.com + privateKeySecretRef: + name: validated-patterns-issuer-account-key + solvers: + - selector: {} + dns01: + route53: + region: eu-central-1 + accessKeyIDSecretRef: + name: cert-manager-dns-credentials + key: aws_access_key_id + secretAccessKeySecretRef: + name: cert-manager-dns-credentials + key: aws_secret_access_key +--- +# Source: letsencrypt/templates/credentials-request.yaml +apiVersion: cloudcredential.openshift.io/v1 +kind: CredentialsRequest +metadata: + name: letsencrypt-cert-manager-dns + namespace: openshift-cloud-credential-operator + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + providerSpec: + apiVersion: cloudcredential.openshift.io/v1 + kind: AWSProviderSpec + statementEntries: + - action: + - 'route53:ChangeResourceRecordSets' + - 'route53:GetChange' + - 'route53:ListHostedZonesByName' + - 'route53:ListHostedZones' + effect: Allow + resource: '*' + secretRef: + name: cert-manager-dns-credentials + namespace: cert-manager +--- +# Source: letsencrypt/templates/default-routes.yaml +apiVersion: operator.openshift.io/v1 +kind: IngressController +metadata: + name: default + namespace: openshift-ingress-operator + annotations: + argocd.argoproj.io/sync-options: ServerSideApply=true, Validate=false, SkipDryRunOnMissingResource=true +spec: + routeAdmission: + wildcardPolicy: WildcardsAllowed + defaultCertificate: + name: lets-encrypt-wildcart-cert-tls +# Patch the cluster-wide argocd instance so it uses the ingress tls cert +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: cert-manager-operator + namespace: cert-manager-operator +spec: + targetNamespaces: + - cert-manager-operator +--- +# Source: letsencrypt/templates/cert-manager-installation.yaml +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: openshift-cert-manager-operator + namespace: cert-manager-operator +spec: + channel: "stable-v1" + installPlanApproval: Automatic + name: openshift-cert-manager-operator + source: redhat-operators + sourceNamespace: openshift-marketplace From 7572b826427fb1cd4fd419a1ff41e0c679707713 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 3 Apr 2023 22:40:49 +0200 Subject: [PATCH 0817/1263] Do not run kubeconform on the certificate stuff just yet --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c4d98414..6e3a638d 100644 --- a/Makefile +++ b/Makefile @@ -110,7 +110,7 @@ helmlint: ## run helm lint @for t in $(CHARTS); do common/scripts/lint.sh $$t $(TEST_OPTS); if [ $$? != 0 ]; then exit 1; fi; done API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.10/ -KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition' +KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition,ClusterIssuer,CertManager,Certificate' # We need to skip 'CustomResourceDefinition' as openapi2jsonschema seems to be unable to generate them ATM .PHONY: kubeconform kubeconform: ## run helm kubeconform From d2b58edb7aeb9b838bf3161688c3fc3fd96098f8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 6 Apr 2023 22:24:27 +0200 Subject: [PATCH 0818/1263] Update the gitops patterns CRD --- ...ops.hybrid-cloud-patterns.io_patterns.yaml | 53 +++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml index 1acd4ad2..c4563288 100644 --- a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml +++ b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml @@ -1,5 +1,3 @@ - ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -84,7 +82,7 @@ spec: type: string operatorChannel: description: 'Channel to deploy openshift-gitops from. Default: - stable' + gitops-1.8' type: string operatorSource: description: 'Source to deploy openshift-gitops from. Default: @@ -102,19 +100,24 @@ spec: from TargetRepo is broken type: string originRepo: - description: Unused + description: Upstream git repo containing the pattern to deploy. + Used when in-cluster fork to point to the upstream pattern repository type: string + originRevision: + description: Branch, tag or commit in the upstream git repository. + Does not support short-sha's. Default to HEAD + type: string + pollInterval: + description: 'Interval in seconds to poll for drifts between origin + and target repositories. Default: 180 seconds' + type: integer targetRepo: description: Git repo containing the pattern to deploy. Must use https/http type: string targetRevision: description: 'Branch, tag, or commit to deploy. Does not support - short-sha''s. Default: main' - type: string - valuesDirectoryURL: - description: Optional. Alternate URL to obtain Helm values files - from instead of this pattern. + short-sha''s. Default: HEAD' type: string required: - targetRepo @@ -126,6 +129,8 @@ spec: status: description: PatternStatus defines the observed state of Pattern properties: + appClusterDomain: + type: string clusterDomain: type: string clusterID: @@ -134,6 +139,36 @@ spec: type: string clusterPlatform: type: string + clusterVersion: + type: string + conditions: + items: + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + lastUpdateTime: + description: The last time this condition was updated. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of deployment condition. + type: string + required: + - lastUpdateTime + - status + - type + type: object + type: array lastError: description: Last error encountered by the pattern type: string From 20bb833a103bb4318a49b3f87cb6e0014e6b280f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 6 Apr 2023 22:24:47 +0200 Subject: [PATCH 0819/1263] Switch to gitops-1.8 channel in common --- acm/templates/policies/ocp-gitops-policy.yaml | 2 +- operator-install/templates/pattern.yaml | 4 ++-- operator-install/values.yaml | 2 +- reference-output.yaml | 2 +- tests/acm-industrial-edge-factory.expected.yaml | 2 +- tests/acm-industrial-edge-hub.expected.yaml | 2 +- tests/acm-medical-diagnosis-hub.expected.yaml | 2 +- tests/acm-naked.expected.yaml | 2 +- tests/acm-normal.expected.yaml | 2 +- tests/operator-install-industrial-edge-factory.expected.yaml | 2 +- tests/operator-install-industrial-edge-hub.expected.yaml | 2 +- tests/operator-install-medical-diagnosis-hub.expected.yaml | 2 +- tests/operator-install-naked.expected.yaml | 2 +- tests/operator-install-normal.expected.yaml | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index d2ead9d3..f3fc4139 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -35,7 +35,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: stable + channel: gitops-1.8 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index d5a6c543..efe9f3ba 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -9,11 +9,11 @@ spec: targetRepo: {{ .Values.main.git.repoURL }} targetRevision: {{ .Values.main.git.revision }} gitOpsSpec: - operatorChannel: {{ default "stable" .Values.main.gitops.channel }} + operatorChannel: {{ default "gitops-1.8" .Values.main.gitops.channel }} {{- if .Values.main.extraParameters }} extraParameters: {{- range .Values.main.extraParameters }} - name: {{ .name }} value: {{ .value }} {{- end }} -{{- end }} \ No newline at end of file +{{- end }} diff --git a/operator-install/values.yaml b/operator-install/values.yaml index b6e67044..d3ff7fe7 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -4,6 +4,6 @@ main: revision: main gitops: - channel: "stable" + channel: "gitops-1.8" clusterGroupName: default diff --git a/reference-output.yaml b/reference-output.yaml index cd59ac24..dbb4c6dc 100644 --- a/reference-output.yaml +++ b/reference-output.yaml @@ -112,7 +112,7 @@ metadata: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: "" spec: - channel: stable + channel: gitops-1.8 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index d3da852a..27ea02fe 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -87,7 +87,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: stable + channel: gitops-1.8 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index e468cb4f..9494fc34 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -289,7 +289,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: stable + channel: gitops-1.8 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 5b3300b4..9cbcb1d8 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -280,7 +280,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: stable + channel: gitops-1.8 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index d3da852a..27ea02fe 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -87,7 +87,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: stable + channel: gitops-1.8 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index c6e589ae..ec0d5d05 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -790,7 +790,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: stable + channel: gitops-1.8 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/operator-install-industrial-edge-factory.expected.yaml b/tests/operator-install-industrial-edge-factory.expected.yaml index dc07be36..80fd98de 100644 --- a/tests/operator-install-industrial-edge-factory.expected.yaml +++ b/tests/operator-install-industrial-edge-factory.expected.yaml @@ -11,7 +11,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: stable + operatorChannel: gitops-1.8 --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-industrial-edge-hub.expected.yaml b/tests/operator-install-industrial-edge-hub.expected.yaml index dc07be36..80fd98de 100644 --- a/tests/operator-install-industrial-edge-hub.expected.yaml +++ b/tests/operator-install-industrial-edge-hub.expected.yaml @@ -11,7 +11,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: stable + operatorChannel: gitops-1.8 --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-medical-diagnosis-hub.expected.yaml b/tests/operator-install-medical-diagnosis-hub.expected.yaml index dc07be36..80fd98de 100644 --- a/tests/operator-install-medical-diagnosis-hub.expected.yaml +++ b/tests/operator-install-medical-diagnosis-hub.expected.yaml @@ -11,7 +11,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: stable + operatorChannel: gitops-1.8 --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index 6b4cddc7..d9a00c03 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -11,7 +11,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: stable + operatorChannel: gitops-1.8 --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index dc07be36..80fd98de 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -11,7 +11,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: stable + operatorChannel: gitops-1.8 --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 From cc90589a3f20881124c99828c7764869eb63e770 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Apr 2023 11:47:53 +0200 Subject: [PATCH 0820/1263] Add a change entry for gitops-1.8 --- Changes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changes.md b/Changes.md index 15dcdf94..c254c9eb 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## Apr 7, 2023 + +* Moved to gitops-1.8 channel by default (stable is unmaintained and will be dropped starting with ocp-4.13) + ## March 20, 2023 * Upgraded ESO to 0.8.1 From 46f97963c58861d82710208b3b454c8745fcf267 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 11 Apr 2023 11:31:33 +0200 Subject: [PATCH 0821/1263] Do not apply the ocp-gitops-policy on the hub via ACM The subscription for the gitops policy is controlled by the operator. If a user changes the gitops subscription channel via the operator, ACM will actually override this and push its own channel. Tested via MCG: * Before the fix 1. Installed the hub via the UI and chose "stable" as the gitops channel 2. Observed that once ACM started applying policies the gitops subscription was "upgraded" to gitops-1.8 * After the fix 1. Installed the hub via the UI and chose "stable" as the gitops channel 2. Observed that once ACM started applying policies the gitops subscription stayed the same on the hub --- Changes.md | 4 ++++ acm/templates/policies/ocp-gitops-policy.yaml | 4 ++++ tests/acm-industrial-edge-factory.expected.yaml | 4 ++++ tests/acm-industrial-edge-hub.expected.yaml | 4 ++++ tests/acm-medical-diagnosis-hub.expected.yaml | 4 ++++ tests/acm-naked.expected.yaml | 4 ++++ tests/acm-normal.expected.yaml | 4 ++++ 7 files changed, 28 insertions(+) diff --git a/Changes.md b/Changes.md index c254c9eb..d4fe67a7 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## Apr 11, 2023 + +* Apply the ACM ocp-gitops-policy everywhere but the hub + ## Apr 7, 2023 * Moved to gitops-1.8 channel by default (stable is unmaintained and will be dropped starting with ocp-4.13) diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index f3fc4139..10f5ba33 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -76,3 +76,7 @@ spec: operator: In values: - OpenShift + - key: local-cluster + operator: NotIn + values: + - 'true' diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index 27ea02fe..86d7277d 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -48,6 +48,10 @@ spec: operator: In values: - OpenShift + - key: local-cluster + operator: NotIn + values: + - 'true' --- # Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: policy.open-cluster-management.io/v1 diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 9494fc34..f9772238 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -121,6 +121,10 @@ spec: operator: In values: - OpenShift + - key: local-cluster + operator: NotIn + values: + - 'true' --- # Source: acm/templates/policies/acm-hub-ca-policy.yaml apiVersion: policy.open-cluster-management.io/v1 diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 9cbcb1d8..5e064480 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -112,6 +112,10 @@ spec: operator: In values: - OpenShift + - key: local-cluster + operator: NotIn + values: + - 'true' --- # Source: acm/templates/policies/acm-hub-ca-policy.yaml apiVersion: policy.open-cluster-management.io/v1 diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index 27ea02fe..86d7277d 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -48,6 +48,10 @@ spec: operator: In values: - OpenShift + - key: local-cluster + operator: NotIn + values: + - 'true' --- # Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: policy.open-cluster-management.io/v1 diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index ec0d5d05..79a9dde4 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -530,6 +530,10 @@ spec: operator: In values: - OpenShift + - key: local-cluster + operator: NotIn + values: + - 'true' --- # Source: acm/templates/policies/acm-hub-ca-policy.yaml apiVersion: policy.open-cluster-management.io/v1 From 5df6a8324f49f2c68e49e86974fa8fc43c29ed3e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 11 Apr 2023 11:42:50 +0200 Subject: [PATCH 0822/1263] Do not hardcode the gitops version that gets installed on spokes Let's derive it from main.gitops.channel just like we do in other places. Tested on an MCG hub/spoke setup and set the following values: main: gitops: channel: "gitops-1.7" Correctly observed gitops-1.7 installed on both hub and cluster --- acm/templates/policies/ocp-gitops-policy.yaml | 2 +- acm/values.yaml | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index 10f5ba33..4691c18d 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -35,7 +35,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.8 + channel: {{ default "gitops-1.8" .Values.main.gitops.channel }} installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/acm/values.yaml b/acm/values.yaml index e423d531..b7c27d88 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -1,3 +1,7 @@ +main: + gitops: + channel: "gitops-1.8" + global: pattern: none repoURL: none @@ -23,4 +27,4 @@ clusterGroup: secretStore: name: vault-backend - kind: ClusterSecretStore \ No newline at end of file + kind: ClusterSecretStore From 527ca414e70c7749ef8b6bb00ab99a25a5e11986 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 11 Apr 2023 19:06:53 +0200 Subject: [PATCH 0823/1263] Fix up kustomize example In the same vein as Industrial Edge 57f41dc135f72011d3796fe42d9cbf05d2b82052 we call kustomize build. Newer gitops versions dropped the openshift-clients rpm by default which contained kubectl. Let's just invoke "kustomize" directly as the binary is present in both old and new gitops versions Since "kubectl kustomize" builds the set of resources by default, we need to switch to "kubectl build" by default We also use the same naming conventions used in Industrial Edge while we're at it. --- examples/kustomize-renderer/kustomize | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/kustomize-renderer/kustomize b/examples/kustomize-renderer/kustomize index 5f62b40c..3266d453 100755 --- a/examples/kustomize-renderer/kustomize +++ b/examples/kustomize-renderer/kustomize @@ -5,11 +5,10 @@ if [ $BASE = $PWD ]; then BASE=./ fi -cat <&0 > "$BASE/helm.patch.yaml" +cat <&0 > "$BASE/helm.yaml" # Including at least one log to stderr allows us to see the full -x output echo $HOME $PWD 1>&2 ls -al 1>&2 -kubectl kustomize "$BASE" && rm "$BASE/helm.patch.yaml" -#kubectl kustomize "$BASE" > "$BASE/result.yaml" +kustomize build "$BASE" && rm "$BASE/helm.yaml" From 3a77a96d25fe4dfca564033a1a72de3ef84958db Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 11 Apr 2023 16:26:34 +0200 Subject: [PATCH 0824/1263] Upgrade vault-helm to v0.24.0 Tested on MCG with hub and spoke --- hashicorp-vault/Chart.yaml | 2 +- hashicorp-vault/charts/vault-0.23.0.tgz | Bin 56309 -> 0 bytes hashicorp-vault/charts/vault-0.24.0.tgz | Bin 0 -> 45289 bytes hashicorp-vault/update-helm-dependency.sh | 1 + hashicorp-vault/values.yaml | 2 +- ...ault-industrial-edge-factory.expected.yaml | 20 +++++++++--------- ...rp-vault-industrial-edge-hub.expected.yaml | 20 +++++++++--------- ...-vault-medical-diagnosis-hub.expected.yaml | 20 +++++++++--------- tests/hashicorp-vault-naked.expected.yaml | 20 +++++++++--------- tests/hashicorp-vault-normal.expected.yaml | 20 +++++++++--------- 10 files changed, 53 insertions(+), 52 deletions(-) delete mode 100644 hashicorp-vault/charts/vault-0.23.0.tgz create mode 100644 hashicorp-vault/charts/vault-0.24.0.tgz diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index 5e929ec1..6829cc46 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -6,5 +6,5 @@ name: hashicorp-vault version: 0.0.1 dependencies: - name: vault - version: "0.23.0" + version: "0.24.0" repository: "https://helm.releases.hashicorp.com" diff --git a/hashicorp-vault/charts/vault-0.23.0.tgz b/hashicorp-vault/charts/vault-0.23.0.tgz deleted file mode 100644 index 7e8660a0c767be9b5bb3f0aaa8c7bea5a6bcc877..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56309 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

BX z1QFvgzT!EqLbS);^9mS`li$aA32?0D{t(e(3VSh@a1JsSxKaDlb^d)Hh6r=nypI;E z@~XL;=f@^_w@O`S(e6KPxaANIt&m!_$O-}(35G95tA>h$rFs`(_!n-$vyf$=8{q+> zv7bazzZ=Ki4^#gD31D$g>W2T_AIf-$x7b7RR%-LxLgB{o@E|@GDpN;{vowD#W!du~ zqN8|}x3**cU5V-!a{jFXu0mpGEPLK~GPmq<2`^EUW(PSl;%u_-B{CU(CMZ+7p#*?- z;$ryv5OUHa_(WV4oW#svnG}psIdl;uo>vi7rx~ehR9QAHj$|D&X;)l5kSa;R6LYd# zFc;P2Y#)oKv%hj@rLN9Pe1V#8f8tp{?9t_tFr|ZFe{o4495Y2yg4Wy?9F)1z4A3eq zwWi%*?AL(hOiY_OMkOz&hHT#{IzeeHtnR7Lrd{WOx)bgwcQv-20F*|zGUD$+jr3;u zmbm>NhZL$!`1Tl}W7V7ZhGM+hT@~k*QdL;;>60>zESN(P(AJv%b1-31fTvM$;j3n{ zR+8r$rU8U-eHu-G%me_zx4pt|1kt5y`-YYJf3=VCcT&(+Y%1-md4k=cmE+>J(x_x) zC9ib~de$yTU=3HHeZl;eLJuUpYRBZ| zL1GDlQ(Be5O=2Q8{50z__q@m0n`w-~vI&U``t&}y@N$P7OK42rL-LY5ST}{mYo23> z2%8MfxUM~Z=aQS3mzM<38@Iin*%W>*$$l%{9xi>96^8Y&pe8XdJapl%Fyydr4!Ag_ zxOqp{uC!`ggnRR9I}tRFJFQ*bU@V+nv4FHw2{7+Ms+-mhCW3A z+m1ObbIT&9;g>~+(U@IExT!Ux57jv*74`ZX>b!b}8*VsQ`}~0q*sp9x?68ml=9swE3Ie^BQ6=cdOEJks#I^ zFx}(>{%zqO4l-ez11YwVW{Tjm3){uD!?$z11nd+z#YEActD)`>%Zf>8{QHXW`i}bH z4`x;aAc)IE(5!%h=<5r-M^rKPOWjDOgRox@Vx=;tJeKV+hZ|pVhy-;NJ999>)a^a# z<=mVYD3@={s2Z)57ffQ!bBu9vV*_GKx(Qh-A9G^)rHtjf7d;!m>DXkz(*YlDmU63*9qUg9MWUjg$?f_3 ztc4t-Rqr{jNwiB|(^|huD8~SGE7&`+sGkRA)^tgn)Lzmsh23h(?YhQ3p2T*4=MZ5< z{iY{Q?w~4Sg&hf;98yk_vs-)m+QqF^4!ucl0eggTsgiVs_-^s;0ehU_;~hAHeoUVw zG?N}Fgp5$p%=}YjvCl{7AMV142KHE`a4b26rf6w=I0NwqTduzjhI-u?h>BZ zB`L8jy(8y^p)CuvUUj^}gd~px7ZKDEvYcG-5Q|-hu3W?^ETOe+aT?FvEZpNV9qa50WalW$2otmh!aiHhYaqmwU|XZXa5Hj5^8|4-jKsTr zBkJv%uuk8E7jyWHuaUjeUz&{$44>;W;H}BR2N4T~*z?3)KQ8K|hgz8o3f1z)5-Fd` zhg|K#VKbDL+*5y|ZFGKkkbj|()r}-Kv0SzjvMeXt!S281ax~1t>7B$0Gho9_S^cdH z+Jp82Jr~ca38f3-5i4xsio*o_1|6&^j|~K?w!CFTYrbR!jUVI{1!d-g8nX(jfHS%{ zE~Jq4Ai8Fo+a$LXM-fas`yiW}lf+W{Dwgx%nnNhp!q1&8Dj3+;23HINdyHB<0` zZsdi5mMcw!Pt^yY8f7Il@Q#=&2LP1?Hse5h)+Z|C56(zA^BQ7C0UfKJ8hMFqaH-9$ zhu`{zIN%M@rF%=Wwk%rjJN{VF@|a#~XeOikzD18-OjGWI3$7UUHyUzx_XeJu6C%Wn zqz`ha5xmL`f8L1MScOe}u#7Xyr%TD#!LC)rg1VYo2UA;0$*V zl}iCNVC~7j%349~VO?aOjVyrjYnrZb3koDE>zmSz-c|R*9i^okq^H;rOXbk8PhGS} zIN)1yiRwxtC=v+4w!dQs>-ZI{&_t!{m22cA$b0w2f{s#7Y*I%-j?@wok~Qq~1WAS@ zR&;uvl_ceOzv;j0x+w$5Y!lF$FAcym;8EjD6*r%_McNdd%P+@xl)Jogho}u%mwhY{ zLzxO!Cz4;ow9Y8%5QA*&1&QL zorjD_69iiuqExYJ2jQW}`tpsR4C8W?*4>1Mm8jtFwfEdp{7axe^uV;|h|k!Ji(`ek zP)GV5=TUs=y)`1`?ahcVwUunnx|9`fWzJ|WBL9d${&Y-nn#}muAkkKml+0MPtETjU zLMvNh`p8~NQbY^YgsW(b;VmvwinJP1_p9|FxX>Yu!B-j`_QdP@T-5@v@)b+$wAfi) zj2aI00zh+B&_jLD`mK9rlqw?6#~CA^nn_*S_uh*zZkG%ycW@_)zBKCVw6dHDv_i&Z zI@BboD~>6;BCwKS2(@!!7h6;YGhWk@&U+|E^SBKfzp<8J7Z``NIYPYhTq@q885h0D zs7j3)6?W||0mt`5k0diDrrfLf`;nH_)t6JO=nLC#kdhl0G=Gs(eo|Q&!QS8%uuXBb zugHkJ(BX1c|2XQDd!f#fx;sc^B*Ky+k9;ABCp*xF&)nQ% zI#&_heb~^-#UQmyLfHBn>qw&NeQZp6y9>w(&8^-i;dQx<%Xvm^g(9N1Nn>k*5hIXD zxC0>#T9EAlJM7^+!K-lJz9weTDCTBF3q>CUFBsO+*n%-|4If6KXxL`Vi^(nvDcj?{ z5WGMa+UsXn85vbYqR*OH8dHtq>B5gSNZ|6#`+^Is3H+D+nzh?h9NAz_z7}^A3Z&_> z#^k%;KLSwWU$mM5s7Q{!m7W6$&jA~WRNy}tgMp;#b%wTQ&%a>{lQZ80M z-qtd|5H|P3@H359>`Z(YKkmBLyo&(+{Z3Y+I=*J$)sFDa_uQLic}FHr$j+bq&%A!` z4IZ*SSoxeLCns`X@ww^QU0ws_ouuSeL{?>MJ4cXeSf52_VNC;l=Oqrw|HV}Q<9(W0 z;VAbw5Adn?5P9UF(0^sC3ymj35_-mR$^%31xVZ+wRap23wHNbUb_|)MQjX=~d_C`9 z)TbHCImyLjD0NzoX!1+023ic}BV^SH#K1(CI))nR233oqJ5_|?#2Ll>+k~z%fXb6u zoOzTU+0;?OXcOWRlHESjm=-g7^S&#t ze#$e^Y448Q_OUAkLvZoo-I9{47Q<%QI1u0cl>=SLeHPFZufjX#I17%uybFb{UMPPz zbL)Z4xNdn7Dvw6A6%~8Pij5y==b^b4{I>Q0(#^As&5nc$^Aqid{^riHY@lWC0idPs z9SlAx6G%5{RXvp57E2*cvsYjBs-HJ#V}KcD(5Lpg5DHfWr^t^Pr!g?`dbk)~4H$v# zTBeo86VX1kG4y83j3^&Q%i@1WgjTtR%s1>cnvpsMh*oigd#oUbE4{!ugV;*8z-a;M zKxm$gOA&(;Yy42Bsq!mOZ*A%oX~M}|vA&F3Q+>z|h=fBG=^PC)N?wlJsp1Ai?Ovj* zt~f$gacuk{um7#ecUl8%K^Zcmz!+IX_Z{qr-ElIn>q5~nVJrTZ-rJGqot9LGNi;y2 z8VkS_T~6Vbd)Tx(k=h7>cBw?mY}GB&uP~$3=Vd0j&DkP!7z{iTTVqZ=uyp5kGz{9@ z5pAR*ABM6gRWc`fJIEipt zy_sdSpG&u2&<*xh6X|sweh^)%tgmP@Fs;zJNHzhZ9_9_gJ{4CA@tIV4OCH9L+v8CK zW!v5-x$~k^=SMD;_4&Db|F5**yN4O0zZS1g3rcJSL*%zAf>ucBysxle67wJqSZjbi zxb7J&70R@Tv_fV8+}8B`UWx>>e_X5t+Id>Do~af0`a6&?1t5d@!oc3s^XKtkuf^F~ zE5p{A7xNLC6M8BvKSsrbINY?989@`Q{Z_<+8nA5fv|RS=-y4FWhSWO8$T`eiw7aqf zI*G$|q5Ih{aLHL;jZS^QIvY&X&>|flsl6@5%pa(_Ck6&#a?On{d;Qr4@WUjZ$a~Q4 zP_>`>-b%nJOQ~PElv1TV=D4zXPh9O-0jitxKm$KzFo|cCX3-~z&HnAaLc4m zjwyymsn_j_0%%J?I!9Ay9@WQ+&DVnFBMlpwhM4hx(MRqZ6q{Y`t4zGb^pR55LsnQa z>jL#TwG*TbDI#s|s6cer*5$c>_0KGha$(W?{9w-86>jM%OFa)UUf~K)wv4`i`Y|k# zgSw*gXGIlG1)HMtevA*x;76j%gdD<3iwnIhAN+?ARL_3^$M`V>iR*!gB(B0^aGTFV zhVU7nhFo}Eg{s|Fb?p{@Yiz7BD~RQ+xh;`fa%gUb7i!&F{j6aJO)gqn*{FktcV=W^ z5bGijt~l#$8) zOnJ0VT@{CgKsDhkgy8|LWl-r#O?J4pfnqTJ5QNFH-A%IHlgd%pv~84$p{ZPe4zb6j)URc1I$XG`SAuoIJz(s+@c)%-!!SGa)#t zQK*968D;qsEkcZ*IBsWtdrq36HX)^@Ehwz2J^Y=>83hkSW*j`(N z!*eSd%SBO?24iXn`SHErk34#gbCxgb5A^xl6SP*=8Wdb80k&g`q!&u%h8MzcyBK%Z zp7`fS=1n2Ri$pC}4w->lB{i7^yuQB5hi%(JJK=smWvxFy&L}3lIzQ`_#S_VK_rstR z8XtOx1e;L$YdwbdKdX|yY~$piQxKbVrURNVN&(0TBn(pPlc-^up^u-e zD5x|2>vJhg;5!t`(CT(g-THA(qxPQGxee8%mmOS*^&jG*(^n^16Fo6z)xq>I((&-0 zZD%J-Q;ntVi!cq=lmU6Slo*cIJub~0d%ur-$jdqN2Tr6XZ2p%9BEpYp&jx6peMb)5 ziKs;S{4Wz^Jj#jETyVaLiZ{sc)i%#7HSkA3VXb}4E%-g{tHgu3N7vy&h8DxFhOUXy=@K z0Z(-DA1Yf=B&zURie<8-3@S0hjkycMun|y7y-TaE%^3e+R3gfk+XGaREL(~a6Ov7x z2BD=tvI~|Q)#8y!BsX_zpGZ`;hxIQU=D(=AKif>5fQ0psiOcP)2k^-P1&Ns;eG{l$LYwwriMF~Mb;br zvnzhw7Dv;_RFZPeO<}-b(*vt1>=HHhRmSDw;sk9!dT_d~>7b)T>@*jV`o<1kC(QM| zsO^qOEj0Rz3CHJXJKn7;oDISywC~seOdwIC||Hae!KAg;K zyl-UX!RzJWsf_QeUMS1cinG8i2%q35p_1P-0FeaOvM%R&PMM&LiuVf!Ei)~sX}8*5sx0G9~`z? z%kX-QM*8XTLIJRdu*!_HL_2%UU%g}oaZ%v3e|i@HIY{l#d?$_qNfNC9T;m|=+svMzGFjI7gZ(N-)jF=hYGm4pJBz<6@=iHx66ut2=*513 zpsTpc``i~94{^*8q})CHN~%QLtF^?P0`aST2!IwYK{_oN^2zeID(?^@V!{_Cu5)*; zPENLz2ks5oTiE&^H~tQ%p~1OZoJX9Q^t2@ZK8|F#K#cea&3;BCSMi!FZu)8Vld)2e zKN9Nsa4}o=*pr9l7)Kt2kn!`Q*IgnbvPI_XekM2}1VmU4!f7_<2iW2!S5n7_AYAdz zTaQx)Z+ydP`l7~zzj=C8&WW+jH9p7|lFMgP0Seq(^rt-Bi`=m|$0-LgG~nSVgWy#^ zJ@M4EOE)`i&7tnA?W}m|eAIp!$vvp0sB*l9jAG8t^H-fSi%2fAb+nN$l@ zSk(5F?_;bg>?;Ig52aiHR0UL%es0u+!yVIUj)`hX5q&Tizs|bCs112GZZ&P7Ang$+a}a*~3Q4FR8su-W$Fhf7Y zQ1Io6heWI2h4zgazhq164qblSk;rXMOp__;0Wz;_oaJhfX|S!D%`<*?K&*D3QI-78 z;dl~6G7~b9DQeLqvWr9P@MkJJsd(1ncy=s-huV0~OENF@%R(|C6(N#+9CQQvfH+0wb3({Mv2knu#6Nezu2QdP`Rii_3ha_0`$2`*j=gAUjwzm(W$81-q*SN z(|b#%u5S1V-LPT|^WV=zLrc@n?uX$rCoDpjH-4TT?dMmAik*qKpG; zRTIuT>j7`{PY3K$ZpxPWNGJ)Z@E&(9^s!!`dW(8U()5=T&1%K{lkPDW@1>n`0y2L{ zAreP|*5AhYsl#)98>5yDH1;`xf9q+V35v;sWl%hP3C$pS#|lXf-35w4!az3+v}CD< z?syq;CKd0CS!5O80-uwzzzAi=RZ#R&$%y@Op^;%kfkn6gT7Kx!O@E3Y$!!6|iU}}R z8UJi|n)U~tCnlqZ@viVf$||V$ZiE@+w{FG!UUL-8dA`4phtv`lsaTPpu}3j4!dDz| zb_v}hRJ@hX2@mX5Hc70#Oho0Dqe@^o@2AFQW9;P5ns~ z4(x8U0~~6j?uCgWo`OwjqkOS|_;^mxZj zZXUTbR1>keTAe$ls}zfn+$QeBE1pswqMVb0VXjj2D2)Z#rJmeoSkYV5S&kp=AgOy$N`r<$0^2d@Jb> ztC6(Se4xDiF#J}r9wZt~twf_)?F#9of3Or?L-%c}zMVo*&cvhkES|X5hyH{DFw2$PW28z5Btgbh5<)j$)S1Glr zS?GaMH;|dcdD&amgl3+7tif2l@|_j{n1Ahl(&aHJqBhwa6BLDfv;pCzJng3*tg)|Xd^a>tV1}db$i7C{R)W$9c6@!GVH0#cYYkG zHj{9wvw=INEaa4`@F2V}KI!FIZ9()OY7^CMm6zv7V#^Y@AUK{l zjkA|=4;v|8YQl_`3h6pzl4Z6XuB%R%&$qz~J);d^)hB=HzRU-l(m?luc<_{n7J!%M zRY3}%I+80?-|DqwK?c41echD(3ePUeciYH+_1L+WM7QrkcgVU_Yeng3CoSdCmy!#j z!e?h?Z;72c0M0dQi7%ALu6dNxFrK4vm2nxW(G3(h)PvH7tQjYHi3^#He>oG6o+O5l zcMk7hycU+zC{^(*>OeKd3^7T!h5GUZ!dch`p$aV>KYn=qqBb+4y^qM%yNqOg9pv*T zag<29z)0X(V*B%TNAi!w61z>cc2<|ohYV%#nAg6OX{1qW#SD0fT}0l}&0(N1tmMk6 zP!6v0zq(HqXn@3L(xND6F&yZ zH?#Bxb|@Ae8^pCwss7bFwQ?pl(__@glzP0;7jA^>Vjn4#V%%S zjLw%+)#|RLS|c*5A6XOeln|+Pl&LI_)1NPqqlS%^Bc^Y$g*nMhwMZVfuZwt+sCNr$ znLcO~yAq3ukL2-*KZF`HsX6l!LC#cZOXYH-^OY})Ub(KP&%JRGRFX``YC2jF*fVh6 zGHNiq@KlQ}M50X@Wdm3~k)C%|n?qhdo}2_=RK0`Y)xhOC@;C28h*&=5AS{9Sn{`tn z$C9vPvozSN4od73RsXhXQCYdJs2~Qd0vj3s?UGT+G%WKEPTEM_#BwA6@m?~_^MQ7i z^ZmQ@i<m`3OfESExo_s^)SHLirFALIEV<*$Z09c_%dTxa%CsbqrQ=pa>UzCz;d; zu>@x-`I2`Pg^!cLg01mE2C}lu)=Z$AgPLpB4PbHIqUy~kmw(oZ4isEzNB$&`ugz_b-IrRRTj4WO5 zC5e1-(uJrHgsvv+Tmq$BjxwR(z=Yf()vCgX3NvDCKtZ=4y-PnaiQvWkK5!m=%7j5b zR&+^`T8laS6&ualK@6%&H}DlsI{pj#e8%pI8P%M3i%>ekw(`gcStZ|Td3pO^*1eOK?Jzu&)6g^&IGb?#}1@#5L;?d3#F&y)nPXI#zjt@(v7cg)z|af1?EMnq{l@~MaZ6&&>Aqu zcXpBhgRR_FR!2If(@j@~AU7U)si^jMcWaW|b9+zzgn9K|I#$jz_ELV!he}b~M>Hoq z?-9d|?*akFr^s{Nk-`RS<=cAtRjBo}cdGJ`7nX4%Bfb^rEAP~zUgTctRlQv5_k{K{ zQ>>sUHp;64Tj^OGvUp9esb{YPh#QPb)N848&g2`v$1e&Y!_Ez5u)U&VBqWnIOd7Lb zLR1F*V^xCFO9Y<1JWktLp&a`yQ4Su8$~dva4fqpM+IgU<>sJ+YCiAVf&U|w%v} zAHdWYq_i~b)Q`6kRw`-%o4mT=)jdnR@yXj8fggn7>Cp~Zk;jVRRHz$pb?VhYf~js} z7^lpKzgF=hXzA!(MX1XWUK-5!hqn~W8DO~4X)})qZFYY`ZWQ<9%t5end+e#3#>#VmB%Lou}80Wy7R3O2S6=OcUR0 zu}4tFbWl>9g!~JDXPW3apm-TCh+1j;Cl#*iZkwJ9lrIEfb~vgN!@+wx3Jof3S(+4{ zdlc8ug7mbzb~Gf6Dfak~c&Xdk}JT+joNM z*oF@#@pbZWhP2wzoM^0-hWpNRH zGBAJ2An$%q+MWP*3E>_~*2X`0sEX4HAV=ni0lk%02gnDH%OE?lwCekbZ;dg6y#3s` zhAG&tK)}Qi=tvZf6klEbV{OhIYHHwN*7Jz-%9#xB`%Qa0P`$z-0a>mt7PZtz;_ppz zrL6Dl9Ebzo1c6uqv{znvQTd;rG~B0wl1QkkA59@XzJ(y_=R21nnU8n6Xn4i;hpP52 zg1Hs+=~q)$47rAyiCL=ev!iQi^jN)kJLWGRMO~F%OXHD`&aZL|X?p;r;g;W(PQL2h zb7{@4;udMm&v9m-mgawCh9| zVJzkwhh|*LF^-`{JmxLEAaHE@K>f3mdg%yT2tUv>3KEwaD|q7+Bw-A~r6_?lre!leRucZ0s;t1jxNbp3;yPo*+nEsyej&$vmA`hFpZsI* zd>QM07#Lk!^s^69vi}O;|1DpV`62MDYxo;lnrO z^OHj7Nebk%o1!3{;cow3mgm0AEWvv~u}&PtlXGb=naZ%ApS9$J;zpHX=Ty<_V}uvN zr>)N>%-zL|Yl1*yn`$LX2mrUQk5e*mZ3uq7q*iZd7~5-PIm99J8NdF{MxqDLR^hdo zeaV)XaR=;SxCADtm7j`uCoM4ybj`>yTsrRHA`6wY&m0+AqJRiZs(mKU+<#-d_@&Eu zEt>5%u5e0!0(5?CSoAU|f{sSe-i(mq&kM>ir^*^NJ3iMbM&nP?{Z1b#L(id=Rr@jG zzCqh>#QGfmw$_qfWaUR@U!mrfW~ixZl0Zjs8OP%xNO1H(?uHcAg_8D3&*tuo9&CQ) znw1lX=@@z=Ll7(jxb;>*Z7A`2U)X~2%{2$Ad~=D%rH9j)PjBcyPtWMpy($h&h-17Q z8c8>yOguczHHedBP$s+!`^-k)K(?;$-)sp^2RGP^unW{6lJDh``iZ~XdraErh*;ox z(nPIFmjN}UmJJP_i#r?`OfI2ketJVp#4OueRZ;&^AIb91*NrJ$!j!_JdsjXlpNf4@ z?rylE_^ohG_5(N=4=l!qJHz@-2GHZqoqTrWSKDuN+$!s>Td0Tj{;NU-7#jo9mDc9E z5n~@~m93oc^Y0jT-KbJjPuJ&}%F=#_tcG4*I^ekZ*MY4JGBi!1;3w8n$(N<#)nFah zGQAzgJZ;L-hkl;1vFa%=8msycfmY3l5Z}O7cOI+qhucqxKg7iuN~VHHmi6a0ucgnM zE)Tod*^1ql1#0on<~gWO-*X%b-n3J!%iEQ;&B1SLo=x4>FZ8cv8@t=4pM{-`ZHwFH zbq=n3ZBI|j&QDYu-<+t5k63;IS?+6^FGN;c;N&UU_fLlJ8`N^&L6La%t;vSHn+;*UN5D~lZMCepVrh!ZF#8WR_UnLRi^ z1rA??-JorJF?5!LMNP&@Oi9a$a>Hu0po!Y=&wBb-eduR?&Mx#aJ;!R)RXRgUP z{SsPJ7mn(u-c6a0w^-%h=9M8+2~+h|^zl+6=Gh zdM@?kPCbGVfADH%pJY6-3tz(}dnb)?FLHD|ifg(`K2VTdBDOwZQZ|Bx*})*MF)5^c zd4cE~NUtX6hdHu+IM>IBJ7`B5_8w%%Gh?ytQa8cU#&bA5}W$$$rW83%ru0JriE z((wDZQMKmp8D24XaOWoGLidmAq{d{%xZXtCz8a#3%6n@z_8U=0lr-Q|+?O$@^-CG< zPe)D;&>tFQZR3z-YGY7Kd?b!;QY&Qqr)Gg1dBzAu^C3O*%Ze)0zf*7@0tLk)vA_Rq z_+6pUy#UqB@P$zKMRq|yX!{I!-y%+O$hrPG7st2L^I0|-^~`-Pa=Nz(?W3QJnpBIa zR@Y0zM>T?k6Nqeo>C;>jUz__Pt)Z1^|IZmn@juN#8ee>sE0{ghsY0WZ`jh~#m}m&irJ z;%{QSP`|pQpF`@NiQentoMCmJ)7eNk+Xkk@B1Vn#qI3Ev%cBhPn|nIw zH>Jj#3=mKcV^O@o?LJwV@218Y&2xO2eiF-zedQ3X&TMdy>z_x(^$LZ~iGq)h7!QOG z^MGTR`=?p=Y|;99jaJqO7#{y1pJb?}DAIobZeTEk^Cz_g@s#0Q*!K1`*`0I0_`<@e z^;E=i6#6Vg1E0XbM^kQ^o)Q(gdFqPgu}o4Rdw`KqCUA)cFIP;E!8^M&PtW*!kMz-Z zIU|k61ulut-C%v_E0Aiptx8nJ!Q$4b*$;e$(4iVN=QZq@VRz zjx0#GxbvRR(~ntR=%?)!@@vbZ!$gBTWKyrOh-WY{&TtSTquNKq@w@DoclM)}l+xp4 z`^>yX55uc4Owj?s%+~<$LnnV9L%n!Aq>+^zYU&3=itden_i0WYMf<-*;adA_Kt6%I{(B?sY&V7lvJmVRn_|6FRtPFf=W-;dWX5)L^-mhHYj zCc$LN0UMEGL(l#>Hj;Qc}g-XNxFp%Z;M@~ys$*MD^h+<&`EGYLLf(*x+g{;COQRnTw{-JvIJQ~ z(+_2Up3PK5@0utBiNt;yQZqgsLN~s^Ux|2`v;GBWj5kQ}Ed;WH12L_huD@alBk7b(WK-hBB!K{!3rK?tl5sB+itDuIC{Gt&Bsu zmxO2j@n3@PF%wTS-0l69jIL{9>SQOoho>Y2Jjj+gD(jB_ZcfgTeOe!rAOl{>d3CU; z6tV+0TG*fj4-q)dO45t3iQDI}vX0|m^yfSQ9B4~+J%k+783ylE`y66r;6K}P4LnGK zLvLlo%93x(Ae?*Y%3Mh8mc|=iO)+5Pybe%pA%jN+BY$^Mw+q-`CUo|3lXRs*G0_mr z*@xP*29<}L+oi1o?v4H}%i%Vmft{xe3ebAoz`R2K&b@xrU)5uV1obQP=5{%r`C|`( z4b%i-AeJy1KAqJdaPZG;l2$rmZcLS285-$0>7}s+{GWPxgCqZ{AC(1#p?C$u1KY> zQ$M=)L_Lyi>8%*8@(8px6Buon+uo_oJ|INKM50<>7a$3##~Tec^BSNKt(LA&t?hQf z0(kXMdGJe{g2G>W&aLIuU24g=8AD8|AOmMdk`xpNRtp) zdlaJ9x!XrH=E4tyun4}z$>uY4`(xgtzF;GT4C@Xsz#=^Qm)9?0FP4JPm9-c0W5Mx1 z@H*E3PYm)7B9+(>BJCT%xPwUFt^SqHmio!a8ZJ}=A7FsAH@SOeq=Df;bR>3B@K`)E zfIz141KJ^R4R8*Xhouf+Fng^_0As!lY|FgOD|@IP0lR)&(T-}~jJppxbjAqM`gB}hjvi<>R703gVW`+|`G z?$%Y&%L8-^M(WvFSJizA>hW&vy8lou?`4L$wcc52)cOlM8Nj!Q0Z~oK5TyC5fU~b( z`rqNd&IEfZQDxzb^eB)d2lp{N?2K24HgR-vD`DN%O!4M%) z=bR;}^f}KeJk7U!cHP2_qqwWB8{QF4@50E%&wPzbx(hW0LmvUUs)zFc7NU@dV{pMW zVIK;Rslr?3wxeH~`AqKOjrZ+R>q{~obvc*q9UnfR@hwYth@j|s^&`;VD{WUuT1NuO zIJ1x-ZI*;Z#Ap)C&iBmwUpjDh+9I$4W4C#Gl4bR){x?-v(lyzY>wF2z zd4@JOin;Rw973rm++b%7CFD3JACBulEU8$-RDCc#p8g_6!*1jzY1nT&BpIb4(S+r~ zSrd^{Dq~so;2s4b>%%e+p}S^$%5?V5PPna-+z6WG->ZF}m;7Jg@Vb%Z1%9H|zEfXl2osSCaO^1+0uWqPRRFuye3PL)d z8t>nUTX+WV9{2Fq5EUTZvnWz9b#PwXHc+n6o2P|mP?yjY5_E?Bue$t z3%gds{D%!ho9%w2J0ww4OzHSGBQRNhwA>8Takf!l$OLG0qk1h+)aM`}DY&zy-e(jG zTg&H3X08M^roz{;ybFYFHk(Hdz}ew1_4(89ZTKfTno@smYN(@vJZu$g5%cLGcwrW~ zgbI;fJI`BS0!{%xr&T~6v1^F%|5Sww0)BJ@IgBz5ZdLaO1qUmkj;i5Xftu$j??^tkER@4pkVfP^W)gZdnmgTdLKAC-9#w^G;3OJ8_y3f%-T49$0 z87cr`v;Z_84FWOvi*_AGkvYiuWUXE+7Za?esoqo|bfgi!CP2gr!*$h40tj$}R`1Eu z@L?g-raX$+3?youoG=8nKbqQj=k|1|>k$Mnk&qXTE@NYe{vA~#H%O4WbC-4}E-Qi8 zMiSSda!V|yJZpk88wau|r1j{44%JH3y-%vLG*ZDGdVRXz85BNx28g|?klHPT7Q zM9fQutVQMm%ogJV-lPP(BP)?Dp9w7u){TlDjibFv8(_f}qu!pj@#Exn^YL)=@bdHW zc(^tDx&Iu_oNv^c-Fu-{#_u}2pL;F3X?QGckmjQk{8%~g9l^r-(*XR@2CK!_?4G-W z;>zG9MW{DozR3j=bO!=ZYziPlaM3E1d~#rZVp9NY%xMZhTF2#DyIFTH(pB|GX2&En ziHV4~ey0EUMVMuv@fO8nC2X!pi}m*!x~m8O6U@Wr^~fd4q`A-dmB27K_+0Ap8d?*u+-#1C`j z)C~;=zRX2qxzkrZ*C!PV2&ao16*=N4u6VlRv-MGSgS?YU z1iQ0g9ej+D+E`pYhE}b|Za%;~7Rm7V=p1Z7e=pBSY9q~UgdO}F5mJhmww}Ly-OQj} z1V2e@3~;1JYl^}DzhEJK3s9vRAv}*hBa_f`<_pgf5$v$v zRRY-{IyFfV-!|v}tqP6Ce_<%U#8uc-Oy`3jt?>}I@j>n>K)>3h4tv8mnF++m+*Z4t zVUKPEYt^z+siu69JNI3gmIkCNNXQpr$Wvq#)ufY|)mk(oA1pYspehTVFQ(Cdm4xhY z3ZCv$RRpC&pZT+66E6WoSN)p$TwL{ihX_J-Zin%z{}^e`23-h{s+;#{oFcHXFG}ON zn}vB;reQ&0D55yWSVWqjH4^mOaNU9~APmX<7c7KBYk>VfVc`zk+R$>&9>z8fr?e}b zm>%~>e`}+DF}7tD3&xnU%RBmlu#z_jvHmj)V%rXB5maw%I0VHU3-Eki3dkc5ILblq zn+HKZFjGywWDWO4y&PRpBI7C9@FzO4fmvv{iSdXj@ZpB6z7__Z0Y|>>^GBt`(pgdP z!NTAE|E>#P!z0X)sIqQptN+Uu%F+-|vvLP{+p4EpegP%+f5nA5>xMQL>s^f$%wVtB>$*s;q!NNNv;QkERQV}$ zD>baS8&g&`6hKbdcmmnVuYp4A-_g)An9@`px53!QL{(M@By4pI7Akjn6vo9LBmwIHAHVVA28kd-?ZmT0mG$_8P-Sw;>Da-vy`%&9X z7C>&3fZlut^LN3g!kZ~s(HkugNH+js#V$aDoWR;56O0Chk&kR3=96AyAML0$IsOhAqcIMrBUb$+HM%!QD_YtIVGS<~pejyGXMFuaAE*~W zP8gB-QnPR=DOm)6A3}_J%n-ST^L|HkGJ4a_MX9+y_SC4baVBe0HtknIXRh`f?oak9 z6fixRhNI55^Ybr-q5S)4Un1;cO%6t#x%$W^5A0<4PwZ2JC_#PJv(SSqAMP^mLIX@# zfMRlo{o342eAk8Oi5b9)UR}Lso4|;Bn%@rPF4z1mj;m`r8ccq=DLGxH z_E~Ff|9<+wsu`W6pH2d$?XWCOP^)p%y~%A75L;G*|b)KuE{`g(u&?+ zgC~Xg0`!@#k~x6ahnYjzu}YwZ<#&bhJOU$1WMxsUvw6yp?#VEfPWpanPbL+12cOU| zr2BLYb>>Gpom}?i&Fr~dw_9d{* z-`8?smU27B!`FH%=FxM|JH}lMJ}C(sW&psyGI}>Dur5yFMhu`&?~KvQ86K^4qu^A8 zP1$#{hefA3^?>EB6guY_JIaQ;>sPKI(dFX@+##RIeZ^Fk#1iU42zTIAUqcny5X z6l&f|)iAfQsJC9FC2vy25Lryl)N90WrHTwuhAxS#i<&BZYz+i3t;EqQaJkBfkCo@B zh7`i37cdwGs3;`2hI6hAeKTt=Q6$EEp8&HNo7Zbp0BJNDy2R_0RQ4Yc-&z!Dd&Bnp zdxXYZ5ijWG*YQTh{aMZ@kA0~?OZG?MniNcbNgBW@YaU^^L)&Jy1Zg{#DDUXJ6URd! zkTz-Vb`xhKYhPqTUH|YG-!y`G#vQ5$azfDXxT)L2`Wsd)^!oVvO>t!cXNCt$i9Lf* zD7`%!8^k|)njZ$S);FPy-96B6h&aF6Y@AX>J55%+^^X+t@Q=eJ`9ou`D}rfSlH#7F zA@0hfvnwfj6n!Mgg$Dm)QJNf@40}sDe(;_x;|Rt*1&p*7Si0NvOc(p`xkv^*xyE&! zoaNGQ-Xxqt0V-t?;XQxG{&e8Q)!-noe?3U{(fOFkjLV>JA)nl3ZGcCr3j9u(Z3e| ziVU#>O3)Ig0sC{ra4J93LXUerlYz2*c(8P1#?@A>wL-wR6apBN*~b8Q;PA#@%HPH5 zS3BfwkkAu;gzC{>RTgK2q?HTKG?+6^%Sgrx!vDhza3rI zcvNA*;y6&qt=_9J zLm_%MWbz6(`J2sN-BY}E`>ypKy<=W*b_f9vUJ+rfYzlIm1EfQ87>3Sau{5>HO%8sM zBrLkNv?d`YF8*|TdSWQj0#_C6$d9`X&kf&}LC9A%5X$mRQ($qTE_)X>r3Y#6yCe0! zH9{uW90T_a0n|SPeHh3Y>!6ReCmWP`qGOYLay$yb8)z}pr_S&?0|dLv*tURuN+W|Q zM5e}8!%^K6#9tQt){f);CGh1u@=^y+UswCiyNW;kF8`xg_}t+yH#D%T1{v;s4oI?! zdy3`Yc`xn^L%0ejl=M1Nb00OB!}qZ9=H!#ac@R8_r4o~#(tIcLWgi#jjs5JV$veCc zt(PuoNu0?XnU{ovGB5KU)BcERW+KLQV&aWjHsz5GR0+J|xr=w}GZdy;FbheBt?gs1 z{<(8Md~!b^vG5h1aYhlW1TiPmown)54%v2*@&l3wxC>inRs}_$Wb}Qb%0Q!DZi$l&bQWt58hc zq;thrQjw7ZsHjR_(q*!0K#TZ)Acy5`!ROO0{gi~dmcm$Ym(ZBdIhpTXzc%rW2=?z} zjm`r`D>Xz&>hZsy=*MYfGIduagezv|#sW7uM9B}{b=_|tYY6B+g|sT>Gf2%xG!nAN zL;*||`niTir&!D{!c}EHE$B*ps!4hecBfEsOV$41&y$PpP6F1;NWA*QPzJk#ZE1c8^}LQ zF?syYyDk0t2A*-#*DmF2ba-tdsClR~P0<(|rc@`tE-R1XOOoJHt=R*ah3t@E%0%yd z1s`K2j=r%xNmEZWXnTAeJ6-Evl9+5@ED*E-W#U(oWmu|zj{*#QsriG+ z&KtkNW6y^7hBzYa#VnsT2gqu@^n7J8X3<5vDEBBPuCy2N!Ibcgh&6FFg{0>Sq%>IF z&xBjaG26Su&Ui1&h~#teMB|Iz7i?f3bnueSP^DPIM*-rw-O$M}L{DOyW~(qCckAOm zm*D(d_IzjIoJy*TEpO68t`CkrGa19~gr-S^zlBM=(PYI`+zd}5Cu`|5Nn}O+E6Qvx zUkk+-`0nFQO1Q&CpB9T*dO*tkz4S9We2l&N*l+mc%C^%)V)OS1u|D86N%n)WNjJtd z7MYNC^2pr+QYzY5Or}nRK*wuB3e2q(r_*3*YS(}9;cQEYnd`s$klp85A2zl<>qFd$ z^S2KX5fKH_AISmmv{oEuF0wVP3biX+o!eoM?)>0>Fb+5vulDE%7=@_it>Onff?9fE zP3g)_=1P_rk^`zT+h&J3CXR5+(KEgKluWY_+G`=1o&VN{(Ta2b=tFdp|JH}9JJ0&? zr(&@U62fAFjOl_%e$m2IJ+ zP)#+jGSLEZepBp*BI8!;U~L9b@wXW5``2#2Ta2JUqApt{l?U3$HP+%_*NZtb22b;z zJ_e$6T(PLGYAtD4$i`Qy8$97vILJ93N_2za$`w_d^jkM?spZL z?4#KKf)A|~{s%q;(cS@C>ro0T8o2%^Q#iWdx(m} zWOI(au@@DqcG@Qmqonkh20q~r=>U3h?5-0|&!txZ$SvKS%FJ_QcwQ+yGp)YPI*(jAAjBAL}43uR~WPNZ6#OBNc>35V}2u%zyhf36BA_w^~_R_@#ne9B#X9Ng6h7jK+NwkEvGK5+0T2jT1zyP-4}`f@yE zV5ky4>Hco9-jyVIiL?1tIK014L~vevrH=I3k$KgGu+6A7&*$wna9`ni%NeBOblK#R z?2J%D>^f7rVPRf7W-$UDD3dM_%LIiXP?04wzECP`;3W?Gd%)|^dVD5)?IKEHgDNuf zKTDrOI+ZjAxiqBTc^Ov=9N-9neNTdvZ}vEHSB$}TdR!uIzVVU;PgDi z%lMYd4VfQ`#Kc*~!uAPTZB^9K=FAk+YS9XPj>-J);tfb1Nd($>Rip7Ms#uJwGH2Hg zg^r6HjbU(L3W3aVx-}V4*RKvBbY#K58}RehL>-yHxS&RT#NKo7JhB>|62%r5+{&(I zS569|--!|nAVeBlQ5W@3Imn0#N@BHXBZOdB`iUZVgBDg^x-fuSnXS^b4@2WE0Hint}5vgBNg~g763}=GGo}mvY^umAQ@M+r0OkQ zfY-fhf8uA8HW9nsl4lf18oLbHqF&Ri$_tW6u^Y8PUTWT}4a)Vh|7s-3IhnpJMP)Xn zI>r=~&$!fdD2caOv#})=K|t3YGih8pF`Udt$85``ItNtFVL9I+k8U*F$z}Fb=kR`; zuw}sDx2kg2w56dE(-fxpMT!eC6jgZq68M-WN ze;a1|e`0|YklxHH4{eU0wKiH7LtQzNKT?p_ z!Y-&PyOVVb^4F;gird7Cr=%uwi4?Y}hvZ+3c(AK{n{O8&Va9K&UG`XVJz%OnEs~`` z{yov}!^SAIh+M?aMfm|#7iVR-Bk^NYg^tfz#+ZJG;&2ZcvrK~>6 zmKQ5Ju;pN9RiR*5R2_i@MaAcuVG~$Ymw0!oggy!6HPXr0{JX;`=hdomxet5xR!LbLvi0 zYb+>{y{8R)|_Bm9z?tavWz4GsuRN?A?vBacQVluw75A)N(hs%dLqBr6es><9hq7{Hn_6j&M?Unn4#6jd4Yo+rf9(g zoUeXeezNwbwIXkHMDSBcxZB(o*)>|#1cO|Iz2v#%CKm324z8gS^Qe8xtaw=b8{p6u zultI|Xai*3j(Cm_e4m^cJyaJ|zY|tf1^kXbHtC0-Zs0QG@0ju90C?S}M}o%7s;bjZ zg6#sCBP^U}B__;nnP1y=4(mGM{XWZcn7c=;YbdhV8vY%v-R+wfU|ARb#rjXC-lUg- zz(?a*%TVB~RjO-lO2?=Qs$vJjp?ce6Q(qpWvFLo&4( zV|DHF#&J07p63A$s9?VYdk`ezRN5Ytig2aLkm;J<93+(k`tppQpsRiSwN<7wGrsZ7 z7kro3HQ_O1*iH?MvFr4l&VgyBH$s%XSO<{i!NV#yP7sEXzgkJ8>=yfQNz5Gzs!_6hnh<)g(Fl8EfHv zW+l7KglzKOQrXsV)GGj@5g+wPyDrv_-CkNJO}S)Ru!J$su(a*nb|n)qQkbX+iDw^~ ztZ8R}3@{!#&vKl`0gI90Bl%~_DcP5p9F9dZNauc1pWI0i1M>6!auS#;J)fupo>>y3cNm|1soiEWnK;wamT>FMp64p)e}vYE z5&euhU5X81_LBqLL|<54oN;hK!S^X_i<2K2N}LqyBKFkDN;J7M3V&HLdt^Gm&4IKD zg9BjWlJs5+XeP^*$3^6H#GF0II89+)&Sxbwl(GnxU}H_|;fQv1ZSz%K`@k&%D|6&< z8N$>J0yD`Ie0=Vc2Qb3kKFF!9ILrD)^JoW9)mih5IlN>*NZ`IQ*!YNvxcGvJE8e%q z1{!@jn@lG7!h_viTie=lh&SDTaPRx1{-pIZeNXk>Inh5{z@zp4N-tUa!dLDqWrk8| zTfJ)&`}+%5$)A6-PW#((Ba!P+pF)Lgn|pu~gjy}>IqeAs63m0pJ`gL`9EGr zrrlmkOYI@*pc8yB7^KaA59XfD|Ng z5Rxe(SZ!ip-CC$KyRDLrE-mpP`Xs@NV`3WUGewz(WT>C9X+gn5wCcm7;Xn4;z5RBt zpH7KGNZux-OS>~!w$@7oM94U_yA%d`~@Rit$CpSN7l6==T^m*eBukVSez__#!0}X#IEIX!Zui+=wWpmAX$KRXS zw&2%N(xo|GVx4A}9Gfg9H;<~%Ov$oMN46akH}0HU><`)Ne|>;q>-XE;R`+#-k2tT> zB919z)`3?+yn&f&UK&gJI48t4NLMLelTh znmA}iOyE&49Ga%;bCFU1Por@`?vcHaY?gZ=tuRusp^-IKc$T;9gS8-zrXY=wJ4ApL zKoQ7DWD5};ITnC!aL#`Z>;(M+T+q}ppdOM|lB!9!8b%9j;=@KGLmcQ(fSpF8@f)y5 zWWvNq7DlRtDC<3qe1aC|oEs2SdbW0qYwaqR47TM++YDMUK zL^&jak+2;r309D2j%~{E)fu+z#GFr^`HAbFGIEe#bYSU3pQ-buWbC{z$dmuy^@h>) z-|r54@9OnmMf&=+g}m8EO@<9^z=4rY7oyTcZPWrRrn%G^l5_>V34Pu}Z}6Y+=Chtdy$D znxh0`L_hGVSpnGpwC{Xc7Hpg1B~#j*0)-J|J*nmCHS;Zq(z;zoOHHu3Qh9H^(d9$b zUdY-Z`ip^MjYMDBZ<^ex_vU!6ky}Y?<1aQ;C~1t6A1R9IwT7mq1TU~Swy}I>asQKMo}%sA3q?v zmfSi+jo4!SC((ZxTxhdt5)GH46Uw9i-C>OW!+$mXuOfZ@+WE0@OpLXu&KEX%Bku)* z-&=I5%k9QweDZ7S3>JWH;nt}FsHx6WY#yTX@mZ@6`}-d|RM6RolZ3v+7=n2#$!*E8 z7sOQmk10>uckdZt3s?om&?~^u-I+xQ-32c2%!s<_Xk1YQqxrK*9D{YEg_@L#0IBgb zN%Wmd(Hkk0LrY}?F4jMDZU7y*!ZX@-j~`@=E#!)`))E-1<=j%04P#5Z0=J1r zobzm1N5M^9&Aaka68%>syd<#L%Q_LxrvJS`uPf?*-wpO_`M;9%1nECeJ1ZddCkz=U z*xs4KZ>0!&W<)=vu|Z+AoZ^il)xMb08S5b%`9PClliy&u7bx}@(qU=<5758|;Za)t z9GRFXppe0Vt3I;~EYrbu447R!fqHB_U-RL&*M>VpiLHtjY17=oC=@EUl)hZ+!SRC& z0r(d^2)Sl?58Z)^W-Z>dhq!EPP`5!W6)3`*Dz+1|*wQ?r zk#-@DVVK86Q{{DRQh7PU&k${oWX?k)$w#x*f|)jms@oqr-jH=%%{!6rV!0K3>&!oh zgpt0C)|H5`Y^@zZE0Xditj{2Surho7q{0z&r3vdRv8x<$J|A75O>d4)POg$#yXlFS z*cN)k0)-Fxfxuy>Q$YFe_kkXaY z$SL%4NlT3-djaF;uYmXvm4F*wTlo1U#g7(61Z=0bD2hG#1ChAu!lM(P<&>otDHL9P zMdm|OS6IiEH8#mDj!pvd#5KW1Z#7JA(Vs>#nR9&@INjt%sw4Puf?TVEHF`E=wMrR z#g4!dB#&9R^S7a(CYIV*T%(IU);V9Y9&=LA?}kZ1j)Zr1`SZ=$==10-Useiwppp3* zUSTaJCB~*{GPwsI8d<%cf7s3R? zt#AsAED59SEyI+sIDSAK_8Ph#ZNm~Pssk(P97f4pr%zA@*2H*hDzZe> zRaxl3>hk!j&)_E9sfmtC0Oz1SSNoPy()nN1HfZ-lz&!hp{(j8=3dgW7uxBxL@;*EFjna zzbn%JUawdC|5uV8mi~L#l)?mr&oNh{@D{<#+Xk8kWOc;P$}>HU-puWU@+LMkRhB66 zQgwR|_4ZIY1EScUz<9XRdvvW-$6olZ!>heo1{0#r!OzD=;yP`e(&>MBPC&WxfA>J7 z|I{U@rvDYBCrAG)_5_O3(z1Pl9+Ofhcmtu|o076rEjqd;o~xE^hDWveMun`j%SENv-|!+%+Y9$?WXseIzIXj_tcure=_(JG`tq{A2}7F|GPQ? z<@^7<6X<_`Q2YN^lfEJPzl$f3zy?Zm0D4pwP^Ak{1}81i2`Dz4#oT~uMw60K=|2zc zL*v*zaQv4({2Pe+|AT{?{#TNo9R2?eo%oo?LQ>HXP5ye%@DuV&mB0XAhAxL6l`jpV zLmLi}E9CqBIG&;`a>O53qwVawlHn7QFgn}%gd}uSIGymjX1Pu~z7uxcjYGj;DsbCQTwlWohH zG(Ns1=U+&{GZyv?hG9lhy)q&dW;+Qzl7mhXRoSlY%;Mjkdr^0mewI= z!Rrbp9ac*xsoA`K`hU0fWRvSy^}mdME3NiByiTk<4ee7K1!#1cia{GHS7!6#b>4is zoJ=Pr)eP(H?KXu{S8%z|C_UJEwddLFr!W!MVn4)1sDQ3Wrc#WPT#Y_o{xZ6`yttWs zx}Kg~{wIZ7CQ<-{`Y6dg!&s*y0;#NkBCnNgr`1(xcFF8=Z~LN7Jj{GRStb<3iq~byb_CG6K2h z=wx(t1ARW8US6g2mQ|hz@8aluG#MWqCsNCgCRvJ(c?iu^tSPQ$Iq@6!iSTyHZC{zP z_1ZihU&BKA-|Nw&fJ04`?Va%T0DQ!7uJE4@4b@$Ulh8WPPR~zws_{%+ss-yiAD!>q z$0c5F)JIz9ZheFrH|XQZ=p&PMrla%m+0k@kfyb}b=NZL>0=GI!tv;Uqd^7s>bV@1i z6(bTKk5137uXb+>k6;ZVvua=JS3q6XAGvF3VSy_XE)7Vp%G@aJ3KkAFPhpQ{LOTl* zMRyD))1&F>F{R+peHelZN6%9RN;Qd2RlT1j(~55! zGrgm{p&4JE+)&E*utYDEHPtuMm28Ah{eS(V7kc$@{_=@>q=bDFLVVYDh(1lH2V#f$Ws06Q^4-zd7T1td++NMfIE0#r$Brp-}~y2r(~b~G`QuUPifx!X%Ndmpu&Fq zh+c(a+K%y@@h9Ac@~|fYwH(}ufGh!XBJj$qpBS(}$z~`};BfUuqLt)Bh^cle7Qyz8y=3 zbgOExqk^S-G(L5ih1G`Up-eX>;3G|u!{p>|gb#B!D0d4?sTUT-IsZC$*Uo?V(m4*h zO_k!MHhcE2390r+F1b01SpvEr+{>v#e%NjQGIDw~Ict);zYj&|dlNr#a{=z{wIkU> zC#bO^(Tr~Y8BZ))RVZ`##CY^Yl5PLEn3|!DW;5n8fi8fzHB$EGnNx?lv?l<##x}vGc1x@dAKe!d)Oa%7$h?eW z`DdK6^pm42LWq9Ru*A&+WAmp&>>%<#cb)XM^u+M2KIyO=UyY8YBRVwK;}iPp55FZ5 zPs`|gVPJ!A5r0dNOsHF>O9P3Sa8B%x%)}MU_5cgA&>*3J+s)XQL#h;TrKSymX04ip z7SKa(u8N3gB2s7qVeUhdkS%SiR$!EqQsqCL8`8G;-fSlypxpg`Pu&0ahPC`xO?s;G zU)aVbW}}oG95O7ox!6m+mPLV%~<`pyvh8a?cL4MK475 zq6y3*r|_)=c115)c4dHYaNt%_U~t1EHj#;Th^EKm2+MiVTqKqL83=?3Ur|Nnl> z|Fb_F)bziK^o-~~V5DnTS%3i)2pv#X%ou3bR;uM~_UzSe(i20-yyg<&YcW{NYr1)H zorhQ!6lz zCFzCJ{}N-ye8X%Y2qP2XoI4Iwk$^No+!)FoQXF{SS-#4q|0P}joJarrgI?_Z z=lweVZ#C&#rT@N32|uFq?Mf!Dg!%~TpC>}iljeyIZTiK`hIe_865JW|9inbVstL1Z z6Uc#ARGW|e+l4Ip+9bqoH;Pk<4_Ahs@bDsC~oNj&2o)3l3Ee1)DS9FU^r+kPk|3*(*F{4e>CON z|3N(dfA65C|5c=#{%=kHOVk|-Y#<0f{Sr9Ac0sCl(?KTj-G|jSdPVbNRaeRsQt7`J zLal@?K)(H#82`W98`ku{lJw;0f7VTqWsq8KW&8r@YI;yszOXa&eV#FuXJ${G{JE-z z8&(Yl``jX3GC|TE7x3hddXYWmSSDDWy=2pYJo;b8CBS*~zdMMX{}221`A;S3S<(Mp zZUTOp0sb>tK-L|+FP0#_uq4ud!z8e}7TB>`!B3{F+nxpFo&SThFZ%!P@7MP~R+7r3 z|GwTP8jX93>Di-guG;}dT``Pec(unBqi@3AZtjVHdihJ{lE<9iidN=oiuTZ61De}v zwIZx$Npu=On`%2{Vq#)ZU(!%SiU_c67*OL8;!0G_ZMai!=SRf#=?j0}_A+?9i=yW)iux>ma|S<& zK&l!1lV|Y4)ITl${xCtaQp*qU$WLsT{mj%S{IpLXAonH); zl--L<2|o;?>Yg^)kNKK$9S{Sfx}m}&YULaZP=#3NmTXD}p7J0ln*fRw450DwvssP* z3jtPR|9&Mm2JWiYvDTJ%*@XMWl$Jg&%-H=QA?M{NdfA#4jkf}`O^ulG)`_F1pb^CvQ(=Yb6AKty9 z%{V(qFb56FDR|ZN!oLX`1@H1tcKe=2blm3n8&e>(7n1)e_00L$_d(l+dUNnw9W#G} zwA~v$c7KygTWENvma8gQ$gtJ>tJUzgsdQQ+Th+brb1=irJ@1^mg}%4f2Bsk;BB^IJL*ug0v!Uf4N*ZK z$I!^_382Re}PolGS_T7uVD;8fz}0T zh^PufurM#F$xz=Cn{$lbD5`7&oeCpOli|!>HcE#3MJpMb*tXaIwul~t%N)czw0p%H z)EODkgojOjJ~r#C)2meZ50FSE@MB$uA82m;w}ZYY|MhDB&uUV|@?W}y7gJ0p1cHd( z?(S{I+{Y6SWiu#sS)QKx0$ou#F}l#ceMvAeq#SpzOm?nxqsRTud@A319-MDo*Uh)S z&*odt&+?DN;GVTsJ2@4i|2wLHbM623Mf%?#cKbE`uOhuX`v06&%e(576PIJLFT_AA7H zI~dmcze-Z+_y6i7xB)D7aSs)%;-FNJ$uH;%)@=Pj z$qdIFuSl}xlS`FSyq}ZuWV1Y!%=5giaPwV{6sDe22_5`EBbMs+3^jkST0dC--`=~f zHF9MMqV>1*6wyjgC%BM6+%ip%yBh<}q@77}5;8sYd1@e#0u)6^AxDHDRb`)VUgLb< zIWIR)a@K9%;vy+PkZBvuv@4PJb?vp+eXrGYe{J%hO^2}LhQS07B@kALCMXMphAl?L zo{FQkVjsGR?u67*_$T#qboU?3k8RcH*SX6<9rC149QXe30x%73)tNE~+;DXDdaW|} z-gYn+Qws>vSEMnSPAf9CmWd^nH?~88q)Pk!|7}TR{L~SgrRxWYGvVieh9Sr^S%{x;=Y`b?A&&C4EcHT+Mdhj}( zceXMty#D6;T2=kcLc>mF!*U@fhf}n0-bJ7{QJEp3ymvtg{UdlqD(L?LtAEa>|Lghq z|IQ2i`HB8N!smHngtf6k8o-P$h5|7)G6_aBe) z`CZfhhbsd5o1^A`tg4^(NUhixYs@j7V{r7O^SKV51*txqFcx4vId?r$VER zr$VFi6dL`qB#{dK4-m)}RsM4(|HsFk`TF0RPyGK;J`4B%OppDlAKk^6ScvVaAEH$! zqKYSd%y=rLV!^zw3QH@|P!!$uDOl;wf{3yxPFR$Qx(kMC|HK;;PHk!B@M^cu^P*WkA8Oc#0AE4Sp*4{{pK& z&0+s7)_+}Jf2#lSD4$2j|LB_XtYGM>SZKH9Vyi;k_s!%VhCjloR0MP@*ipD8$E)c4 z-P1V7l~9!Lcil3`-vY#K}04uuHRldtol)di?aiP-VXo0 znuy|~qV#hN%57_+_rJhu4#mfyo}NmUX`wNGB4@>AqDy|MdD-Q@JH6{(%Xe#}jS@+v zTr`<3o|MZYFP;9bOv2w3HeS3G*ic=Js!o@e;V{f54#Y~@W$`GE;;YHY2u;s>FbAv} zD{Rc}7-2b`+^R~Ok|RKHKG(L)LN`jn7`Qq8+c7;Y#AS~8y<~25HS_Vi{Rf;FNJYiGA9p{zIX(N~<;fYY;Ar>l z!NS*|gchQ#GZujhI62b6nng+8E_AiCy4qQP(O!f9*V#e) zdvn?>$A@Jzl~cUF-v(7@x7SNtT(;I$LdK_bDwY7Nz16Rl7;TqJwpv0hT^OAFaCm&S z_xjzBM-|iC1;|Reo*bO+?Uvq{seSQ^e^xZ#d-LJs^x%E1lNW8b{^{Et3-7V*X{TcX zzT8gfz!mtG+w<#xh#j3du_Ncyy-VKqY5me5I|4!YaB7nJ@^gE?`~B(J(YyVFv%~#I zM4lP9==HmDRE9%aMd50dPNrD~;~a=U0eKVL1krRTym|NP?9IW)gE!SQqk7!pGO974 zILh}_Qg8P1zJK@r?e1xv_34r8EN}^NG%rXZ#)ImE!C7QlvvWSo+4hHEG6NIe9PS+) zogADU@1DLc-zJ~>YQzJt7B@X*^0MugLZNW|L98l@Z2~WskC4E9FuVzqc!(CEkA8wZ z-9iRN0`IKPJLU=P;_f4}w(=Gg!PLI8rFPJ9UPE^eE2rDwhx+i1w{EA^$a!+BhSP%V zaq$H%f2xd(g&LgG2JMs%N~GU59*OKgRbR9#SA0)v%(X26%_J-n4~4MN33N1x_Il1L2}7O z_%mPm)bVH(6r`3ak>i=S*AKeaqPP5;HwMz=BbaUDe+qEU#>voI%k@3&2VU?g%m6Q8 zElF^R=B`xoJNl7~x|8eWd24{mYNo=E9SmAdk~k^+vQF{Jg-*dIV-!>$r6qemY1Mdz z_2CtPnJmt0Wl`EnuftL6hRVQFzWf~Eiq!bh52JAsoc5C-?Z;7Xjx)`7$q$KEl+cg% zgUFvuvBl*^E$NjXcr?qrmq)&G>mGm7aTiT&bvB;wk{>eTBEj=7(Ctv9C36y(D7*<~ z&=;r4&V8taJK5ccL~e{l(};O++FineP}&`I5g46=)O``!9q#`4?2cC(oulwF=uWy( zkf*F4iLjXm54|`@htD&h%-sN{^iB?6fhPQxl9G5oXdhB;6qK_;caw7SS7`7Th~rYy z73v9u7#9m8{&4u_P1~af5xIf7{x}U%FHBR|JI9LBXrQ)wX@8vIbCVPgpzryq*9(&1 z3g3vJj_8G18bp`50cqXnu7LrT{t*u@!azA4(&e^`B1v$P#iRW7EaMe6wpY zqom0*Z0@&o3PU(;RmLei9Uq{3{TBSG+Wa z!}}S)mmvV7h`bE|{ej=h%ep)YbkF!{O3%CPhfx5Ke4ZMY4^=8CY>`)8=2apnp5V_0 zKfQnbSMM4Z(^3fa)zT>J#g{BKESJEj$W%*QU~!tAWPXvV=^yHyX5j}rTV2J}1x*k? zRS5{)kt;OUl8kv3!giT!g}Y1>xM#RfN*3Yha@rrknq1m+!n3zk1Ve?Jsf*9JijiV} zgq%P2Cm;WXexG!8v2$RQ*Nd&Ks`y|0Q2f`{#>V>A2DIyJQT?~A#V(`#^9PUrDnEaT z0>nQS@Ca;H{pa=VeEj#?#>P|p-=loKsQABVMP z@GvWGs%WWx9A7tgJRDAky47GxVnWQv0sCXt>-qd>K zk$dAoeMP0>Hlw^N@6-YKQqIGkHheDt?~ytvQ)24oygefnWPmw41?F69pe>_8#Kns5h{ ziGd8ypip2Im*qlxM&pYpO#4T1_CD}?6O5z^(ll`G=rD0rI~m3J z^;>nk^##`p%%m=vTk*ku`0utizf=bOLo4;R2f*Yu9m+TPVJka0;43`KF+I&&97jG^ zoJwu;G{I*SU6vr4);Lr8MzP=f&W{kUl6)8YxySER`pEPDGazG_@v5DT7Q_7iG=u+l z;J403&i=Q(vGMf&|4}}RLFDpqSkKw7~m6^VVBxx z-u4E%k&@KA2*~;7r7_TThz_=dB1A;}_zoCR z6820zv&sg)Fh~#>_|I@~G6sV7$!oT(gh3b!qw^pQB>|FxcY6=_)OMl>OVU>*!fw^2r?r1#kTw~5CbLn88b*#wqZ=@z194Q^G;0d=P39j;ZMP#emj16o4 zx2iw)3q4asIkEYdrq?`c*4icS=HSx^mrtqraEkcSth;i>E;7!I74=uQ2cp&(fE~)J z($MQAA)3PAE=oj7HC$68+-p=mLp)9cSgqHz8!S;gl@qEBKTnJ6#^!~v@X9`|id>^p zCRkpc4=UmC?<5M(0~6JDI$#Q|N}!|K)CSS?jt>YYF`~`^CC5Wh0i#@-5(w$g=6dj% z*6rb;8<&3kNnPDhFsr z!IhuIcZdBh470CBnF@2a?$VTm-|w z{}c|!11X$|QrFnjhu7Ih$SH))kK^8cm?q;9n!>&t_pSmE&M3vQ2w#*Fduh13zTE0~ zw-jDCL_m{Vr{@?1_^(t6dHK{eyS*Mp=z%^H`jOW~RR$NJR`YFVMTQ1FChf=LsHbH= zn0*ap-`gOn7Ia6wi#BZhgW0L0(IA*X^euTcs8+Cy!S5V3Wh$+nDLuQ@aiN<#$acH<`@E#x^G5ubBRqdv|7 zzcrwsNTY~=QbGy7(-5dfcsXfkG7`mCVYl+k*t)LLqs>Wuv^@_wLQ(D$LmC-H(Jr}M zyk5m7cE&b`7Vru!Q@>e(!VOA6Q4IUI>u_cH-h-QI406_N(0$H{0DtZ=4<;inPE*CV zGa!?G6c4A;{X2C`ieWt@1-<#AEuoskc>p>~4b9%EmXu%NmW(w>VV3 z?tR68#rSl&7jNObxu^Aob+b0t*F1;b*jO_?qNPqe%gf3+9_#71lS-&1uemLVaj%`J zz^}2&X{WKaxw+;-V;X_S1U@9oYHC9_k|1k)>36nbk@D;=r~$Qg8Hm`>R9)26;AhU`QT*)2=ROu$phbyQgMsK z^Qrfz{z6eYnz@|Wf|#%6Bn*gF(ZgCrOkHUfw^K=7I9*1$uv&ybKIVG=;N#i*ckfQm zPT&1-a8!|#ri=xk>IDCX#f?tm>tHy0nm30hr!WPg(!t3|`8>^^c6R=z(?HFJ<841g zG0{L%@n11?;G04ebGay)Q#Y$o0I{VdOi`>kT}KYyMH?)vX#PrKd7x4E9E7Z-AH~yf z=F=_n*fO@LDg6jWzw=tkNAvD{e zs7#$y+cZS!TBK#@7=_mXnFyXEuLHI_?5%kH_!a~ESA_nlzC0|D@?MaJiEA94VGK|& zdFQhT%t4&yp!3W2I|&z>Cdu>E^mOx4#bxcftI{Lpr`euy3To_Z74n38WpuaXRTTbi z6~xTP0~a1*+|4^vxIpO$L(|v%$aMT8K^T`KqoVfY$h`S`?;gFJ}3_U}_h&aC9 zW&3@#X+@{TpH1QO`&X;dJ5%2{slLC;M(C+PQ{}tjVHC!&uM&0zqKwz1Sp;e}Do81M zI2+ktO4MC`j$+=kYVk)8T>2EXAOKB0)YOe$t3KS~nQ32Q#jzE9j`upFDWlx&l!^l` z?3{{2FYKf$t3urS@=d8XQTsMluTpQmvdR4Sb&gr?b%CqLU-XTxp6MQlQ+3CyC#!Sm z=S7#oop@l}s58$!3CAPg#~gDlkhG|}F%Q7nouV{%peMSwjV1a^TN9f>GD-%TjRff2 zEu#-+u<0jWcHYydff={-bPE=a8XSJwDNnubRp(cNs`ovt=|j~%uF~Naa$uz+-~Y%& z(#A)Y`i<=a+)20kQ|(KM-A~l&^hWJ(=f0NZL*4aWI-@P}Vmbpa{ASKL?kSgZ%0U(| zbrGjFW3anl(8Zrz`OphLJ1fMBYYTd*6c?OwY^uquBU9~oW$G`LkZQ&ext*sUP1UjJ zG3+DRbdDr{9Mg_M4`&ZnZn~d-1RQ{dx5P3oRT>v;`%@{vm3nO&TAFG;QP^B`t#qjS z85OJ%+f!w~ItB%6(D}s4`A$Y(kC{hgH0GJ7RMzy)=QMq=`x+J&dRC>wKFDdQS2FXR zn0l5{HxGfU8f^CIRi0egnC&9DDm%TYgs)`SszK*Eys#WtG&BoOObQE|wMF{jt_PoI3D5?_=563%r+Q!_R#`?MD*sGmMftos|FvWnecw ziia4-lk?`&Pp^`AJaXIzdi7Uf&+?XZ*4EY>7YX+MkYX-~vd-gtXS?s^U8cPs+MDe1 zwUOT{5LxdE=#CD;R36nG?}OQyGtGQAqt@*(Gud~fyy{YUp}0Di#mqKyuDjoxnFr!W zQG9!xgg0RnTm=Vd*N=!y?|783(rMfENB%_^g;^NnoP?g&ONv?j(Q&!^=FMGkTNO3M zM>a@V)MJAZ+%~aG)5`=;6M|=Sz8A5tJH@>O=2^2WOo*N}(HGAOWFL271k$xO8<;WW zy)Vj*TWjP_S7jDMs53+jQgpBDvv4j^Yt9Aoi(2!3*_@*andi{MBo{?3eN5$Ns8-9S z+IZ&}?%pa}<(T)^+{%VtoB~%i`n~kHveUl}E-(lEoTo0WcPmdDgA136@4%(s^0K{| z<&p>E#R*hE_#7Cp^Z>Ij*Tt2&7hv(Cxxi2#u^wzG_r=2JIlHz(!uSHeQ1easrL}DDyXQV67;ae)kxQ%Ml_OkBv$#`y*yHo7kK=-;9l4~%ZoiERw`AO-t zbV9kTNu`-2r>1MNP*qWdrCivp_2#_bur15nq;~9PbTCn~@{fEKjHzQyOZK2JxRz{F z3c@=IHs$NT59-f3V*T4Nd*HiYklAA|PN2EM_uBKEur!_DRaesCuE$q7)Sb_*Y{YvX zSlOV9Iw?@kv$+@wHJtyH3eE3&Mim3y`Gkt&-TQosLoVucoR`Zd^K<|C{c|o&sDkr6 zL~!xc??K4kBc2l#6sUDv)Z-4PR_Is%@TLxNpEE2Dyttz*j{Tq~O2Kv)lR8H#UFx4{ zNyplUI%AA`oNC#~3p>`b!5{Qo!y)IKt8=ibPyLfE>sk9~XAN?Xvn?HZVTW5f`W;Vq zuEQP1!@lpF>`|NrJKm3h*Y%?ab80@PHqSYarZ)^Y*M!bM7b;b78h4dE7Kw7X_@hx2 z@<2F?^qGlPw37NbRp?e5p{9;-4{R!qyD(HL4t;+raUAzf#O5z`FXx~MqjWUKk%^}v z*SNM^JZ+fzr&Tf9-Os3Ez(vt-+tW7>wYHo{^Ixj+PIopEOdIa*_p4&m``@mLf$x8U z*2{1kjO4|oR1=(Vq3SHB<|LRSJw$GwB`F%FH*(w+cf^H?@$QKWRioY;7pew+SX|&7 zCDV~%da8l?*ifDZWNNEL;6v4b_e6-wVegF*l_M|63uQd}IzxlbU=K_1`XHk*?VnV> zane0!n5gswExmF4Sg9e5E2pxrcJSy-M030$x!*3eS8Ucsiw)~+wWRlF*en+InYB-+ zO`R>e9T&DcBw~9fhk(+%S2$i8_JXdT)ZDd?edpSHu4P>az-0sK%E_FI_v1>k1?Iu&p@O!=xW|0 zoig(Mj&|-q(mNdSoUx+|Kk6w_lG7JBD?oD2-kw9K7JBZ5ITm~RvmzfCfCSTn9OuG? z#Uup4WkQoV@&S~zGytfT4q)m?vyPHjR{kvWqb=mAaoNC&cxY@m>L`hCLQIrC^F5_n z%Y5uK*V3%Cz)a^krqYq-Ii}*B%MVu(tY7nh%9-dQ4yaaC3od8wm==2v3yfWvXSlnO z%Lj>CnKAIaB%(tih;o>@G~Owu*FdTX6msBa-gPk9xxpo^AY4ZAXp90MwEA;_nNF*l>Z*vQ_6d<$`(!| zltv(m(LRgP{V=s_v^D!#HfrjXn!0QJOWK@`65#;!%j0nrF~n03v`wihG#Zm*6u zqp)&pod0si#w;7QNOccWLpj@w3O0Hf#AmQ8CF zp`=1XKmnUB{z~^#{MBvyZTXM7YGGE|#rf8TrPz_n*QwJu^Ek~nvETd7kNja56r^SQ zm!r5B90TN<>M29%ctU(ir?U4^jS76^k+lxDypDc3hyV9M3o+AAcG~&bQ#nV(8Hw$q za8eIDpz^GfwFf*rW?;GBF_en!avvT1Q(`&ajJ%YAfiZuKq9ef_7; z@!>S{%9xX`^4)Cp{T7E#wbb!VK;pXN9Bc5|-rU6h?Y!7pv;P-A8{2DZf9h;*cD6P) z*0(mc{l|3^y-9JCHmkjC8C;aNPR8D%D({W&oXBptCpqxRjOx3 zy>f3u=m#x)P*_s?dVB$g6J+S)iC6V1b3M%hD>{Zl4Ad)IY;~W3KLqiq*SeUvy%fNu zBbU)&xa`QuMg|U`7IRna6g)Wr6gQs~|g`(Z{qZn6fC8U@B=*0$xRVUK<%4;$s9uGwD#io3>bY z)(Uf$0V*kNEfs_50P*GeTBig~@^$Jws=PB5nN6V;RZ*`v=crh57t)l2Pd6Tn;vq1< z9SI+;CPO9iUFAPNH~+7e|0KqG0RrGG`EP4$D=+`OSX+OR{~qJ>B>z=BEvO>^s#hNm zC;`e8Pa>e&;6q4yrB89{iF!|x-jk%K5$Ul-z1*}xR(*! z$wip|X32l+8=V(<`EPS$<4OK|jL+{>{&VFsmdk`2E?zdX24Ho1Q6-lzh3nr14~rc6H^X7S>`w*%Uj*CNpIP>w%@><4|L?rm z-g>hCJj&;Z|KFScKe7FXw8@mDJ9x6lOtr{7!^9Q0J|-WOgoJ?Ym}TN305%$U{*50- z6c88O1VedpHhG=|l=T=3Nls{#6c&TwKVjR2_s=vqK(2GYZs=fVW z45~^(@wir?;gk7i(dM7uudvDTUmMv`cr}cZhY)aP$bW0w>m8E+HnulktYN#(W@q!o zll=D>pJ(2&pCJJt4#6nEB}6aAVbsGIYYO5|NF)p}46Zo5bZL|Xm*FS4%M^b3FUxK3-7uPXSf_NvOY-mq zLKF^zcB8$2a)vnt0vKX19t>cBk9#LxFHF)#`zp*<>A$@EMjJW|d?of7GAp&2ZHWdI5VKkG#t;!bD8%^mYXQz3{K$zu5r({eMH#kA4!yr24%*WQ7jL?K|S_k(7%-VLi%gzXp`o`S4$>vh`e>(Jqx z(Cl2Qpi;`SPP?z&gZ927g!bC5E1NV?x+qC^A;#z7hP zqn*{w&j0k&tJkfUo1MQck*Kq?y2@9t4IF;8k7{lg)ZA6JRU!*q#rGzw&|}$J4M?OW zG#o2oDVfng?<>g4b-tI7D zI-7MN)7f0s!?vzprum^^Rf`xIK;Ketf^$>cbMd8@6%Uy&>l?CfoLeRrbtMD)aVD=| zZM*IY*0%Gv^1L+EQ7H-Mgf{s$x%eJMTL}4BuYn;z4(Z7=H>5Y%z&9Bh1%$P4ZKpt1 za*I>1SLM(BFzo`8CWXaTuGIoNnge=j056H<%F{nC!XXjShuTM_-ezxrY9py_5Jd+H z5RWsY6GSd?Mvu&3b|GzE^G5A8BMbX(eC43-|3$A?&z08M_mzDHjS%PZj*ur@t3BbG zCO3HNmfiszh}vw|qc%bl)@tu~ZDT66SyQCoZX@fh0VO>NMj4Y*o08hJ0&xUL{UCsI z9jEWeW`uEbtVl?rkw{^JLpz{P5_Z!zll>QU*%9<8gEIp}0Kp)B@v<&WgdPauQa-8V2H)PnY+QV+?lk1R-H@?DGvYj;>CrKAL$-~N81E9_e0Gpgh zUGf1v=4DoEiB?La}pgT9Rf_E05zAuScYuq86Jf!a8>xrG}j3U@=I_H2|s z|G4|%&FR_h{{H*(d{%{!SQ)HSUV3v{8cDzH@;8yIl?V%fPyj%-O%*Zjvx{nRN3lCA z5o#_s4-psSy$P~2&dk!YQo)_)+sjI~U({Xc_6tYeKA)O0=&_e&R1{iURiWRzE^DhU zTZSIXH1@rARV{leoBdST(=qgH zZ5?m}b-+V3(A`ibE4tZl$GB~PfGTTu8%7b3D=&&cXh_5XLI9yWh$cxEA}X(>t}+8X zEO}HhDn-{+q98G%aGkH7?XFYOQOGGGDHlEI0KFB=QICexi{KJ%xdduqq#L#IedC37 zU~7C}o$U%~K`|jE>*LyQp(|{N;%oUVlztPSE|al2 znXJaB#{i2N1Hso$8<vGtRcfPLBqI2%wKs$@>x^M#%`l)tM)~1o zT5Li$aIK~lu(J&vUZX;{(cbY`kT4`9D=}>7bcrh@)~x7rwl6NN<(t~X`I|MxU35SR z;|}k!6#*!Mq$Nr<>GCV|U!~CVS}^MjFX3ffB!V6`e;84?!Rk#1s~+r!X)-4D{kw4w zDI%PNEA@8%A{?b~JdD$|+0_eXS7hEK0hQHrPl5!zrM)*FPEHTrlO(z9+A;|M>$O?L z+BddwgJwRj3Q;(BXHWcr+!?CRph2TkqqH$W49+Fhw`|Ix8H&WA7=w3wpl{HAjeWPaydfKdBRezUH)PUjos zI{)Pl$9@8$LWQ3C(V|egUVQtT_PRsK+}~mBO=yreQv^AS_^1T$9Wu{kr}jPD+AxS6 zB!h5BV0@Lpl|K%WkZO#?Lr|MaF)UE?)h#o(eX&2!_)XlOa2-G=UO#% zST-oj(Q9TX6gkEzory6g41#XoABO3GBy5y*Udb z)=J2k+%N@BKnA@1uyjL!bi{orRL{AaX zdyaU*O%G5D;LLcWtmBF(s48cj%Z~#XBUZ2;G-e3BtPP=;`dXl=#B$H9e^XAgtFUro zI#yErhMLIn18EaMZlaUV%oOO#gAknvlLF~hX{^HFsEj)t1CxlwFp;)*8hcDIXfKvG zQ>w;S?jHd)CNl`Y!K`DAJ%c1(kQSbhEyX*BwtK2G)24QTAdA@G7y{1wahf4L&g(Qc zp`S-u)hQ*Er8_xf$~4ezCB zvHu!J6B9|(Df)kl!b|We%-#*(2U#-t9=I?P*4aYK)^=@Hwq2?V2v#X%mk0#A1i=bB zB7nyYK~rHv4;tuo_&eD(Nd@PX)pasKk7Y|O>1n&7V{fB38>ClLP=p%Ii<2v$I{)x3 z%&&{STgIJXi|fgFI8@x;?{OPG53LpX2+fbM^Y={tL+qTI>XoXGZ4? z5OMb@TX?7hpng1#dPFaI>Eu39QU_nD+Sv1AEx$4;=01B})bJr!Rffdp6{)Gu8Haq8 zIonSXe z$tVfaK;A7n)NZpjdTzcjQy9%!IV@8dwyu`AMInhww){T)RiEBX-sav$x_f1))+P@f z9kJZ$n27w&=2Ri;T$4cKN)0|?V*W4#5iPwAM;?V|MiJ{?AM+%5v$mCR^CcsDu%L7@I(Dshrycyux)6sR&hQKx4qsQr50#jf>XR={{7u~p z1U+!#11gGwRs;~zLB+(;F9M+g&RYj9@>pWTW^K`UbDOBaoA?S;shc1onj^(*toadz zdtm}F0R$3qC|DN7SKm{6H{b=neuD#^%R$st$)HE22ysp$os$*c_Ks>;5K{0t)a516 zBY*RecZ%EbJ;qMcw7A!OrSF>P814V%hYX`20f5m9I9unZ5$LRRiOBJ9`D3MGf*))es2(_#2;0(4y|UZHYB$+ zB)>r;+4sRk!al~K(cBdx9O@{xOTY$B;~+LHR@wYv7Pge82>O#E1ht04L( zhaO6utcLaB||2JNL$;6dXKDZNP0>fU|tFRVY}_6dam4WEoV`>*Fp-ozONwftV=U&SGe4) zEf#IESfpo3;utB>H3p59ut>+P0Z4)WhbFsI>>+FD)Fz-Ej{Y?S^E(Mmv10hnZzMEe zqc(xw_=d*Mg(8#=zPVPL>#V(W&__{q_qePW)0plpIheja-aN-&)+<~#CII9{|kR!P$vwT0RZ z7HS>KbA?cA3G|{BZqyE)+ThTsQ`=%}&1XFlAj;>H>lgl(gJqIYTY_G*^l8hiQ1+&Q zk2E1SJ%t;G7|N=WQ*n$JZ@uo-h91oMT`APO!$kKT@V`DK@c}+Kz_0;|gjKP{?46wQ z)I^0E^9t+ACL0veWtqCj`9@kOzWE!CGhx6KBl>Y2t=u5guS{%~?eerzNHV%Vak)_zzEJ(k<$Az7LB8jkeaIWGg* zWPnb_399v2h%K=WnswDf+oHBn+l{pGqAVs>U?d{%TaYl$3(Mtj*^dH7n!UCdv%zA_ zJN-~p;RzlJTG5XwIF6o!@ZEi$7H!Fe;M2~Fx>OT-&?_=|t8{!Q@0F@3B9K>*&aO_#SfluN@OIxmW|vPU2%=8+Qv@|(IW7dyzNkQzwVJ9W+qqdcG!}un; z{&(YHFX9Ar7XeaODl822&}X&);!oJ)Qv>xMB_U$s{GxytP&@R0gELj&0yL#i=1C5G zm$fjdd(C5N`)1bJH>1ek;ql(tn|FIZU<5YneyKz` zrMLbFaauhj!*j9KJ*NG25e9lJ{|%irsTgJ{_?llqVXbDs0qP<-9C@1c_1e2!-=Jyu z!34c<#?-|ej8_1&!$WDp+Aqd(m-Ys2BeL>6m^`}Pxd}RLU%#ZRi{=doU$45d5%ib>;4UR}a3V@I zoqN4o7G$x}sm-7|FA6$~ehnuupomYw*}9Azde9DeWE&@OC$I~7^t$rpr~Gz0|7gC5>3BRn-y%>tiS|BI%%=^(|{qK1$M7caJ?=AfF6i~bfGE8)i?>&3zMSpSPLQWWf!TMd2N>VeG)%_G2RBj z(6Xs$?lR9_0&(Ms*4sKM+8cvAQ-7fsG|f%k4KhP06ND=UB=on zj)k4QQ1iS9%yqJ>Uvb;z}CH!r@?cVMHvVf?)v+m@>AaTZ@I zackYM!nys^GW&cdc2DNIq^g@ zPIYDsO5C5{up3bk_e9-xb8%vYldtvW&?B@D?E1>5$Q!Y1D|?nfssgf%M^_25UPKjW zMz5*Jd_jbn;$tN3m^xz4FD7rT^f;il#V9MCyGG!1ZwXObeJk#)^JU#n=e1qMoVOAE zlOQI}Qgs=S;L!U8rAnz=NEsQ;Q|5IGPxV%n#pE1k!_?#$9PvF~Wx5aN`jRMvBx*0p zP6=NdF~`hm`=2W>2cIX>Nbf?PQmVt(zv}qE-1k-aDCEuM(w?e6vf`--^A*p2w7KF1 zS=TlgQ51>fYN5@naQ2q*ke%^ysgmbP0)!;x3o5_n zF%iC{FagJu${6;BPaG^^kVNHN&tBZU4wR;6zjwLtQJ5gR_R!m-eC>OOHYft^F+dX? zAZ5>X8l+>)vzOZQ<7KNtpAt6`MuE_(Fv3`cN0hb8Ur6;L32$V|8Tgrol3c_Wl7&9N z0R@m1CCZ0^$nQu;&FcVj-wX@lpaB&tfVb3`>=nxc;ED2AGJq!N*qKRWHwcM(oE*M7 zJ$U~XgyAa?2U7jO1UaU1;0gz;x-@JW=h*B6!?Zv8i7kF;n>0T~P^JMtZFuFZ-i)6` z=|0;>i_T9Gt`!~1;%*#CMifKMVG8!6ko;q%Y;grA>jg;eZIQ1pu3RUgJ35h;bfyRB zSobKl>d2JbP>Zc8=JIlVZH=yl#fkKO3IvCqyPp?*zm`9O51uy4j&gYsb%A9V1!>xz zN~^^WF<53>#gpQ#pOgGgY)3J7NfNqVxD5u|6r;MO3F)oBda54|Fs2lbPU1O@IETfY z1TIS)k054FYFTzIEUAD*K^7bJtz?;;E+7TM>q}{2R^H2uX+v}j7QamNE=w;HMv+`2 z?qZg#afAfJdT2J}d#Je?Dq^i(d^Jh1Fi}~bDE?Z-6KdGxW3K^qVR;uoZP6Yz z8ewBzBnH4>7)9Kx2Mjk;dfxx5b|-EcC9&OLtaA+l>0UtL9r&u%$_kP~>RYRU*N zmKpT5$NUIGe5GB5-uj8X700q|!4Lg_p^xm8Qs&pKAHw%+@7>Yq`@`=(oE{!MKnalP z^*`1(I%}Kx`akPW^?x4cgOzWFAk|!qGv`(z-GgiK^)VM>04^WweEl_6mdh}VgBsDp z2Uh$^e2B*hUy0|a(*T}Q>iIOFayikY-8j7TCh-`EY!n7Sn(*I!{|1FH6s3GK8i~}N z3XWUxM!X6j6C}&8QL4-P$&kETU3p_B+1Yvf81%iT0_$6yo{Nim4Yh*hqk_SYVVwh6Mm%aV+mK-)f{3E{5JXJTd4FQdWqnDy$9&S3Qtdz= z{s)8eE*toqfw;2d{6pFWTjbmoeg;rI#bu=cxfOw`lpn!*X259|9l1#huQ}mApOwLk=1x)h$fkxI-e4u`1F-3NTe(VZSRl}W)!D*`;pP! zqUE3MD=ZdacNC4}QU{+#KzM>-mu|rUrp^_C7RC`QCC-hP&(n)IAHe^fQxaX$r7#lX zQj88)V-RlubO0W27~HDGaibViJQzo>oO0EAm(XkgFzq8Szg8L1$r{e_F9C=ly>VBp z$p|IBiQ%Q*tgfPnO>kRidZVdQeus!>i%t+N_l-#&pkLdbP;gaP2h(S-kk*!h+JmY? zA0(xJVT%WnS4u630jHykD2rJKDo{W_==!p&g^WNGBlMsE6!??~htP>O)W#6PC@_Ir zOuUGC_b^p)8230Dxy_hE$l|?G_U%Ta!(>soLMm4Tw1!4wJ@=Eml9mMG=--Hw@E?5N zeT%nIy$Sy!ra}~0y42fKja8T#QbSIFllfNwNVwntet$5+tg9M127YqQ={TGCT@%){ z(e^$JN!|+yYrDXTaDyx6kf9q!mw8ce$t=MaKoC_8K}lL78V>(L$sX;>6FBAII$d5- z>_gQ`(RUUzlC)Yl^aWk7fB6sm(-#T=hk_%i>#mmq3*O8DQQELBpCjA!2xI^}(C#B= zisL82CA!$V+?mt!V{;%lX^KXJG}bwg;isO^ZoFd}UJ)iW!rO$^|E}qrw{lBl>;cqL zP+DQzTLqQ@LS6^K2!}Lh0O|o*f^fcJZhVfu!q*<31er{N1lBOT%P}f0fFJ>=jxfKx zHz50XD+Z1o^tj|Tx&i%!v74%eppMzV=@=6^)l@$mNmL_+FO7%tIwPNfrOjRGclLiV z*B0fCuBhmKRw_Fc_Q!ek5r?y&K_p5T0_{Jwlyl?Mxb!hwM-cR|I-=L(JR?1P-y4sR z!L?~Yg-kJeL1pWZHyGBd3`(lj&7yOm0#sJz1F^h)w%F-#M}`3B&*oev${PnN-?(f1 z&IQY!dgte>F|`kZXMF}FI-rH8AX%kI6{Vp>p#$aBx#>}lKZoNBx<~;~JfW00fVn&r z?f^y3BN3i6XVE$g(m))eA$6u7XA+i8VnQO4U{|<4C$-$KGHBwDv}@3C&(F`PTxUbf zpWMB5>3#Lj;~)2bU8U-i{M#xnyv_fnJi;YSyq3zyg$ryE7lkPrN5o#>(sajJU>0B! zxfKDhQ&=0qp#|vOY9#}&b>lU80XwvUW`15iU0xmi2d@S#<`AVjn|&dZrqQ|TLf9>0 zhOl{Lv?Ot%9ON||+V5_IC=JK4qAY86+f>fsrlN#Y7x^J}i|1&jqrICu^j3V5_qa5` z4~d^sHh;Elx67`}*WORxL9bGOEDqNKsWCABS_U!-YrOz@5=}M<2utd*hi=17aOBC) zX57(a6t?5!YE{awu1o%W{;VQNa#S;#>4ML*?m=FB0^ zaPzRFG{LfMp{LzCPdQ8am?jO>qDwP#p?Pyf+)nJRpoA5v!oDIEZlDyx27HsRfVd$! z`AB(8uxR`sp!g;f#}r*Dq3IxCPytDmX^KYjQk3c2HXS@A<0Z-&06D4Dn|txWOK$vh zE_kl$`JT`3>d!#BT}5er)g5Em5YvWMu@$=m8le=Vi-eyo!zy3>aFAo5!nGQjY4MXW zCf33Q+FUKUV)%3fd!*5ccBU>~Ra3fY^d zlcL56KDQUm#e`O%!NX5fOKo=b2Phr1tO{K6OjcGd4iOeD`uvnXW81_eW!)PNguPW< zp<;z>5TxC6F?+n!_`lCu3;6Izjzdt*0lT0mZ4H_W(i$3?(xu_3Ogg{=Mn_ z)2tr<@gq8(q#Umf#G(+Wkk^f;E!>jUr6yM^)>t6tA9KAg8*6Zgwnb&V{Q$%@bPm>I zG)nzV$*wg~PfxTAMtTmHGv}9sC?K$oBF|H?#ZWz-MHi2c2F;YLW^d^@h2-{K2k1BI&H0;-AJKxEJPqdTGn+|A>r)DXDfs_+So_|=XT)Z zB5_dm1*ZB?#g=KwzA7BRI7rT0E%^Pmg@V<&cNzIt1kWL=i38e~-y_?sb^FQ3B1nn- zgN%D>GYKmza;PF~12E2=lQ`GM#m=;$Hws5gzh{df?Z)}!=X3Toqf|-RfX4jLnQh4| zcJVFdR5o}Bl-5@2<_uR<`8$fQVU?O~#w5{(7MJAHCDN+1G+9PwYaagsmjKXPBC3aD zMZxeY>n}4@&RYpX%VAoaCdQO8P5)fMo=Ti^&VsiW zHbg^!91&^hQurgXh9|Vo%|Ma`g5Q2PC&MaZ3Y=*#4k)04(CCta7wO=T2%bAM^neu^ zAfm4+rUM_F;ad<PgeX2ND`JJWIU#CvUIiz_);y-^bJi%Q83sxnMxp0m zni;4ktvLiS_>5>rN9R2^d0#Pb=UY1<@A>1QsXc84x)MsTnSEjhe6a5Zz^w5&_(Xx1 zfMqv<#$?}0a;-&m#p)Ffi;9)7w--4%YNaxQ?E~RK+Ou#)Onm69hGkP|J_-tYV}BL| z;n0s&3KjJuU|=mP^}&vcF4Sr@V$GimDD&r;`10pJS78b-prxwH0zfpzNGHSC_^5UfNhMx-Dk>v^@w0paFlozP0@}oV!zZG=?5)1J$7%9q|_z5oL?~mkIm+Y3*;- zODo0PKbN2_Z2|H1iwAxL7p5lYrOSf?-dR?CusZ}oj65D1sYHDp#O$C4`hOc6i7h2Z z^{tZsuVRjIO9S99zSxcn!j_+CF-b}2#$b-TE>(&9i0P4M75@iL3mx%sa$O)7Ab1Wl zG^A)WVJiiPwkj@D#!=GAu)F7HfCpEr`#Rctr%V{C$6~zNOlAu97MP7&Z}eNr_g3IC zzj#5nAA8MKtJPed&2*l#9^w7Fw^oPa(A-5SwmqqFa; z03KUbu;iLFn?w{5;RSvgM&yl6fgizK5=mom6onC5<`9*KU3z=Q0qd!J^Sf6`K+%Pb z1{qP=R>#9pG>GN1#h6NxqoxkzEj9Pv>^7GhQrMT{In)COQHFHz2F7l@34Bo1==!nd zciW$MuhCYzhXo`|w5JCH6W6^}sy}`qVF#9x<@WYAAN&#d%XLEzpFUsEhvPrr9PS+) zog6&S9Mj`JH(soF^6{S=Ya8oN@t=?J*^5V$gluU`-DQj^erYJg3TUTYAS7?&e}qxw zd&l^Mt*a>0iY}eCj@9WpZH(Zj=Ok$7ZeS&8@BjSo|JR~v|L1@Ie=H<;gTMzC9iVm4 zD&dXq>4PiEo|w_I9`epmfjR@Rkdy%BtASo*f)V79+IKff5cJ2e{2Lo6FGt$b;E4`N}GVB23sN7qbJJ z|0i|Erv-1g3ocn{exGcEE1!dg+>{~RZuwzPIMw|ghUcJP2LUb?=RPNx!jN-&e|>!T zlx04%PjxmRL173ysQw3^`r!r0d%J8BKcD~sGCR}wuxxrhm7kb`;drdKaSs)IUqip5 zLpWr_LaT%DFfQ(t7!M4tAqnD30?8JE1W?_iTKW<7Qm(*@?`8$#Qv{QNl0eLF8;kio zkWoNkXUhc%6Iz&3VERre)%lb9HxJ*jQrin_;sPGv(<}K*Ok-dz%4iqEj4A`c z-_x%lE(-&Kr!P(0p|E)=nsk_D9{Y4?dyyXw5JF7UEt(=TTQD1Omo9c$LL;yte2CK& zn}%9GZE!_iL9M79B`Y!>o$QDZ9o>F3KjXid!eAwGI-Fj8hXES96k4?5fJ!vSbivwHiN@Tt_g)&1>UQp58kU_NM^^p`1H|;Pb<3d8;rY6 zWVqQqJErF@!X(c1k%0<5H*ii8OC}U^pO|2FIuLwV6A9Pj&9f|$$Lzx1`R3-R_lP{0 ziO!L6&1NBs4)7c-ocuK&BhoKHQ~nDD$K^cE=LARJ6&-_5LDCI5ppWU3Vpn16!i3|x ztk;WNWQz^qs z-%r@pdjVJs$bp>58Q5AOI*H&$Dm%$5q$d0(Xiu9Mu?5gn{!K85ViFty4G1x8@`?w* zCr2bKM7`E+h{Pm`Cw`PoT9-)>tSGs5*a|*jkQIhm%SEyH6rVYIVk3M4j5x~ibyyvS z@o~u#X@s;gwdw`l1yvR5~oiu>365<2Y1P2P#m>I@meiYrFz}8lt})QQcwsOnJ8I=%Mf)qqsE6EIB@$b3>0J-!Yx$PK7BOH2yzU=-Mr=xgkyTFJL5e>8l$q zt3$S+c1Z7%tYfgK$fYo7^Ge1qXqKvyIc;ycl*$hcSrp@vm&6)y2|M0WxQwKa4HYDX z3wa8Y9$nB0nkZaSVk_hMshKZ?;c}TVrIMJ~3RtS3za*|?e2ONNB6rvHU~=b{t&Mlf zX@{0c@c;@3u3{BH^}#6IX-jTvWH;DBu{fj+vW$!H6cknAbBfNVD>_kt{HeAR#aA(F z)%Jx)s2X_4D>D7WNa{t8;36s4x?_m3^Z3|wW}}ToDBxOy1a_hoK5h4qEyA6%*MW4_ zsii6$6zv_NhvD)GtGfL-)KAbni^!B)FZjfKOZn8sMiG(J+IYMr^!upIfS$R&jObTp zp(t7=$b2`#P^>HyKVMl-1F8)M)m9syxWYbRb%fbf@j~Ne9f^>X|5);*`WL#5L0fPqShobglDe!sSeH|?~-}_;L34F`&U2GpH z@C+q;8>yQ%=Koc__LsvQQQ4tu z(?MdysMHI~L?|X4h5Rn-!`{?`V@vU2NH_{z1kW#6IPGQmeMf$X-whS|1?6+|k@e$S zZN;6C6Ewa+%L+wwjwTfgmZvTl%QzUZBo(3JtLUxD?!`tey(M6KIoe%ffZRX{FMrxO zJExrZAyGt|0+yY3-ad}8ca~%zGFh%4oe9}yFM_=Bw+x3gLj&qzTca&n*I|fHIdMms z^9l=xvzl^Dr6zoBL8L?R)0k@@vCd1~(O^lHRfj`Vd`U3En2IYjuG{e@BzmNo9uB5- zZkt$3^>%Vfhr%1{uxQ2kO_s0=2WxFLAtk#EQ_W0z!nLIc?~MDesMiWTbDvZnTZ<`k zq-e>*!gD6YR8CInYPk4qpgk4O%fw(r{f#2(JDQxpQvp-R5v3b3aCLg9nG`TB5kg?; z0*G{W#QU7=?yjoY$pz9SDOZ;;6PWm7f`izjn)5|sFt zR&>C*j4J%bB9nNu^SG*3+`Y1VlbVyrZ2c)JqIjhaWN_S44-4~?4cdO`uMtigd@Z;R z&={fY#u-%};OybR^~VXZ`S~Qq;_|dD)&d6I!irTBMsJdOM@b`H+~tTc)VSR1gmNlB z*b{kOU00U7(M7D-bza($7As0c)PGZgBhXdQ!wjRsE>R3&v!b!(r<$=wp`^oHwz95D zjlt6E>@s5))pY!>zF)X~-f-FegpjhkN%2#BnfO#)sWVK;`;9)>T zn+d8qr!dgyE0aRhzO^pvD+ONLdmZ0mtnrHBATizsvT(r8h@K%RE`q)vU8>~yRPCC- z(x;kJ{w(2(tb8b9U!-v~rZiBLI75XsvaL|aQDOUL=POlPM{+}OWzq!`i~NCcuSPBq z^Qn(&BVa2W+3?k^VkT!nDWvVmZmm;n2!hyT`n0pEKfOsQH+s^!Jp3Kx>qT zmy#9*ZIqJA0O2T9+-7-}_q0?HX(-NQnG@;dkCd9N63=_Q#+28H3DJ6Fn{SXv(?dij zJy)%p#8r){JLIUyv*yYLPWNDwghmbrC9#TlWKKr$P&Au0Jkb(-GK`{i1JKKU=c-hn zQRH`3!W+zyq@1~gC8}m@L|e8+$g~n@Wk$mTA*wojN$oC)DegnAfWZX=(-poMB};D* z7gowY@^X43E8aHWWxa4*KzH%+#x)v9IrW-6|HOf4)nwEJtdqPu{7j7za;q)Lm?{mm zn$400R^GT}L9Xs>cxea<&RV`=G^#p&genWr32Wpgda2 z*j|!=azNl9h{z;*rXn}3z_^s@iHegBvoI@$f3tN(MyFjLo=hWr#?ESn)r#sp;U(Lz*@&T+rTq>Aq>ZVUSMUu!yU;^!$cJ zSPs?mWXisXK{0yAoTWPGDMP9fFLSd9b<5*ee!+%$+P)i&$p-!#dH5&1l2K;1iR+<@ zy53gC1rFlLkSu13nXC7DmKz(nHowNBj7ThiDsF201RMxLW@oWD^#b^cGN(zQgr73AD@%#~WA)4p$0Jvx|;Hb3(WP$Ciy$k0@G)2<6K;qrQ=uaiV5;&45KdnNf!r|4oEA-NpA_9BK{;o<5<05T z)ff%c))*S;IJrTWr1cBHWt+?fB5NT`eiTuQq@FSpp;r1!*1&}F|NQU&*T;K|F%p*h zIUW;QYgta@>gy1*$%}?4`JOOHNnC~`)G#jwM7J!iD@u;ahgHc0Vc=N$iyOg606Gr+8gS$fQDR3n21LdZ`hIxK>00C>ESy*W5TgO?!2a?8Z1iv4BD2; z(JL;0rK?u0P{*g>a3`Fkp)72T&LohH;2BG>mxHC22VXL5)g!M7}S7TvST zi>4?SX1EMhauGxYO950NN+wuIvP95LgL@8lA>yoxaER;$#FOj!nCDJ;MhO`}-G^OK z#ErV5rT0^H<;s02$=z_%fJ(q$%4UQQ5m9tqVe4I*#iJ4QS|R>CrW{QmW=X>60qE-E zVV8#&cfG{Nl5#}HLJ72zuCsVr0NLq~CupgLJneEG4v}WVd(cytmh~ZB0f}`39$aoj zsxy*xbz;{L$p11cSstNy!(~P_CA5 zoJI;vL=qb>@d7K!itg+#*~GN4MHN^ipSr`Q#9@DtPzq}{2?K=B$PehHxUA{h(l>8&wP!`Y-Nv$wMbhm-0g zugaB-8PVW)vsQfBlH+A1DJ4^CU#GFMB7bOp!k&cXBHzLz%TU6J*lgjEiGnRfeuZUP^GPY8F5FRG1lNeHB<+xK_*!p5YMJpk>cS#L=8}QMI zK-MAQ^kWCmD7e*4R4}l5nf5&v5YR?T+h3Mu0dS|DHof?k7->A6G8{~_LE&0t@HO$7 zB?D5(h4TP|qbp5_tQ356Gldsi(-bYDVp~+q+Es`=LA|EwaXPgz42FoQ8V|=%hNnmB>T z*1q&{6I4L&daHs_^8k@j6yl83v1Syr7R|mWrN~8xSuM@^+D2U8tSTRl=#8Cs^o(jJT|O0fxPklM z&{<@cRc$=W(yre=nGz?M?aq(|-&pRnceU5Eqbb=rni(G}Wo1)tWBn?^!kzB`cjkY| zIxjZmMwI5&NW7JQAPb!}%YuDhs0c!^|-~|HIn$#@1Fo|HJ0? zQ~j?;`J5h{oSq%N-FlCiXM6A7ADkV(fA?Ppd#6s@UU1V|-`w8nY&M=@u}YPI*T>?6SCORpYLqQ=Y1!D_+dDWu-96enIKy4Q;_NKx&yZ%LhC*r7t!f!ObGz~3 z=SWd>7|yM&$ljVzL_ncEm@~kfAel{_sv;xeU$D9 zC$L+SSl0OA@M!;R@6Cr3SjgGY?px`6fG)G0p0)8EkHp<4>IqoC4-5}RF-0C@5B53!}rV6D^sXe zG6|luH3yYepc$}6!i}dYE0}5u?!1I6GW2ft#;Z8STcXkog9_0TW7dt2P98M3HgdD>G{Vc^XK(4NrE+{+UP1g` zZv2@{2{6le)FLdi79JqNy5OvW$w|ovEqLUh-|nFLI1&{>tWP* zJaI|{NCsmf zh@{Kp%k$)~J@jCuhcd*=Yt3A#BEIgNLz|UV=iXbhg~OZpI_NcAqA;9`DqMA=c--Tv zxl~UI*on$y+fv1LTdnJ0(n3;Pel~JZ{>+56mH8w5Z#Ymj16IHKXMO>{RzOWdSAovL zyE{Y8U~v&>h2z{=6j1)GF9?Ndcy56b`)77Mv~K)F(f(?TrdBxKSvLwPAF0>EpsNVnKYPvp_0>NMLfNlhebvO=nEzAy%3{hN$Da}0Sblp)MzVx8ravvR$d%3G z@$W){aX81#>TQfFQ&i0hYgaz$8u8R;R5U z0JPI+w2xoEJNoO6=ZKF55y*&3y4+Neo;hgbUR$eK;l38w*5_8XzQ7Ob|62TikU3`P z{~MhbYkB>D{l)f^{{I-CXLe20JyZ@GjeqxcZhi8C?s!fG@UOq#xnBivG$}XkQUaXT zx8j`@3xRv85crwbC@Tg2Q+X}$zby?wyUpUA@}HMg5ofI8lZF)OsPlJGvWiPdPh;}w zS|zkN)dCfc!#EtfW*DxFw*=J7RP0j4!ngt#ziddo0R~%)BJ!*@Frgt?flIn$*|&fb zQAmmpsfMGRome*#BZRRIjmkCWo0K3hwY7YF;c=xy-f1|jX|8G}H_&axi=Z}U8M;zC zzQ;%3gOtfe0N9JQ7vQEBCVC{IPCJaL64ELrxu)`O*Dq77uhS^j={)7IE%NK*9#V4E zuXsPbF01pmx~zRLRs~W#H7s-3DAu+^Q%ROdK{)GY+!z6lD`ikUUWy#RG$`mUCD00a zRi&88GpC$#WrI2Jk(x5vg{g$*f>?;@V*B4|0odtLOoZu2dD0Q9)&n)T_PVfdr=Vv=RPz{oK^wQh1zx1OcM-R>Y?BML95(d(6WOpT>1gVV zKKXjSln={)f=F1j8j$jmiw7)hjv4ab+KbJ6{_pMewaq8_?=e1Dy}-@@o=^V$%sV;W z{~s;!)wT}F?0g9%eg~rgT9o?xnNS^!n@PCHBGMnLTBuaPNF}%wOJ%U8u6YO$h}zW(n`K#us4|St|@b%nt8Ai#tPSLr=jDt_AI-YL;o|@7#aOX|qTin|l8{ zoz3<24Z8nZ8yo9e8(VPyw>IJDr~Cg1A1+JN{ECmh378{6Ifw*ew4uNp{^zqMzvpUF z-85(&Q>c;pR+3zT&;!4ziZ}3mHw~RHs)x|ORt%+or>j}|)9k_LokX{jS~*i2In!Rm zF&4jWR<{1Nd__Flr!4VzldI{Aa^}PLE|a^>MucbnnWh3%x1JNQraj21$8i*PCv&xT zR>W0Et2eJcQ-GkyG^F?Qt^8@sr1rLnL1OL;9p-$`gnS~S{^GlOb?!#Gw zi*dE!HCwtb8*`TKF#DS6`KA%vDNrrmo6>d8)H-Hq8)c(T*De+;r_|6Q79Z!GP&GGaHMHYq4`uoUg#IJ#zy&H*4mpK zI@=ko1bnjz00HB*mFbA&HQS5bK#)4roN=HvL z&-&bv<_+gE57zKk--F&|QMw`ScGW8I?eXiV8GAIAg+v%SZZQ0XJhHwXrgxI38bQFn(Wf-v6tacA43ko(W=NeMoM z*}LKUAWJ6Sv+7vimobxUs?R*`6ihW7c9~bd$2xk+Att@JDUoJoF6>KovY4RC$0AXG zSUddzUNE80JQB{A!n?*<|Jcv^ixq*pfF6&>3H|TJ!(JV+>t37?mNeCNx6s+HgURDf zmJ$uDHQxdaxS!81`x|l41d~CO1mHWv@zrgvv7~T)(4KvQ7$uFu$(r5`Px|Am7vBySEPKowD86V_ZSH{Z z-fj6E^7Dk;Ei72zfp;>BLB5}y zj4+CAj|$mfp{3vYzHWnyejLwXIe5CW-gFK-pNU5|Ux-`K&$|7`oN#`1tT63;B6sIh zZSr&eMiA$V3}O_zu=|BNiW=pA*+CziLZz_kKT62Q7kY4|9==|foxsI@L~FB8;mU90 z-XnRge#d;TuEB7QZOwL8>8202uL0^ZQ$5eR=|X&);XfYBi^^AxtXO|6KPtu&><3sQ z`48**==qudL~9EZKGE7ITKga~{b6ZsFSvOu0s~WF+MLt)I;eI-6z0Cqv3^-J{WlXH zq-(WF+R(>B5RM!BA$TM!$sNMf9yH>kMh1`C;aJDu{NS%CGkuSnI<_ zxE&6|?6(Awhlt3dfjOwYHo$8D(d740I5n0tKkb(zzD$df=K2l4 z!;RH@L^H>pWUX`TYL<;3KX=DKDZ6Q$1L=%Xvscel<1FWNuQSd(1;J)MqiNF29Vcn}UK6_kzO* zUB?65nwde0h2=h|oAT+2!UE^}VxB0#z)3L0V771rbr#+PCo{qY3zOfAA3PbU7X(PQ zLikz#ajGKVYpdEb&EVr<77XTO;k5(=>QPfjpgy?Cxm!Eys*|>P4mL?+kLEhu(&fxJ zXlFH_g`EneoBg6d_gDm>j~+4WBA(tUkMkHTtj@M(k%4<~u+NeR_i+jDA1|-sqxmPF zGB_?^()--r-_rZgA;0~e_i6cpNX>Ksy)f%NB(5*Z*ZO;UF$&scc9Qw?ypycqm>4C& zNfwVzq97Q}y4GLlJIv3Krud0@Kj_R)EOh~s-h*yF)GK7z=pzO)KG}&%KfeV#(Ofo- z-;}5KF}3(wdi6u*$bRfc-E`$Yx8u$x0^S3>8(wlBkdH?_rG?R=aW?MuR>-!mdT zz!47P;gpyw-R$Z7W_~`;`8`Qei}SbLoSJ>uao&HUj#o}d5r!oVKjCQMC@ zRaol%)b8;`6sG;7IC~%Xy$Oa}f<|1YVD`OG6|tpv@!5;BeiSyXcPh{?|hG55o? z8{Y(VVgi4&Ozp0e{65#e?kO$F-ZwnW0`NAd8{%vitAMg6~(sgX1Hi9c12JZGj^-QmF>Y0Q~{Hou_Xl8io2;OZP1Yjxw_AP7DK zbIXEc5Dw`Yze<3w9na+yzgv*TQ|iwzeP82I5BSK5q>`N)qkE?~nx}HM3tZ!Zs=PGH z|FZKYj>EzP(^=l^oQY&zprSy>aqrzQnj=iHmO%X*N_P00$<-o8{;8IkyK#nk{)0P_ zp4w*TJ1TOllFzhYEr5VbwRz2Vq_Wz@6Z$!FM)$pBtpst2cf$ z4$}6-A4Cr@#~OULH#hNrJ1@4@?Ei&-ZESR2to^C8x!Kv;*jV4%*!t62XLEgR^H1K| z1FWU+8H1Lcc;24|VYeSdL1nvHzyC%*&%C{OG)cm%e&#K8m%Z11+7I{QWVGTP4!iBf zGwEnu;*p{AWXa- zm!$R>&AiJv!5=)56ufTVPqKESaTUcEezXH*;F%}f(+g8-IPfuti~csf4YO{)?VSW! z2E+@RV7u7Ml5ya{*q45k1~krX7)8d+zQiNHTMAcqO=rE zR%j^%8rbN>y9m5=6m${DJk}wMf@>L&bf_J;5vV!!3#gox1QJX z(1yLkzqfGNEnYV7=Jd@8Ej$?ZT3OtJ|ABvYlgTK9Wq|H5OwpYTO-(g-8yE+|>^&kV z9w+7Yd}he{#8rWI!i~L?Lk~pCo3IBh8FUOD=UnLs)xq_GQ4~)wffGW*cP67#$`_^t zPSljsx|H5Da_qeu1;dkmc!^sAv38i^zjp+8sK*{TG`wTdoXK>=A`I7&^x)cU#OS@CtC;Wfk8^E%|Rum2cPZi7V$)N*oz@0?6brB| ztOFeR>+ah(JenmU?|8omcZG9CRBtO2Slf?AeZPG@z5r;!Wlh^*yb6~hz72ZENqiYb z0olZNyk6YB4w6;52rCKbc;O&Wf6MUx2yXo#PR#fhK{nU$<>Sq@Tpuk&J06qkw$MeJLN;4;5hzGnQ8(cj~2;Xu?RQZD6WFZROA8)2;0VJ#m_vrL|NYl zVgkA<{KqCx8D7AQ_zHVAi9%x0Wtawtc|cUyDY9u(0+&-<(jO}hKk_|v{sYOvJ;D;D z{v#6LQ4DwlOYm0ZOPosnnVZC-SNp*z32wBzA#x zhEEN1l`v=buaE{a?}A>f>zAoCw%DM3zM5`e(to_ z+a3PGBxl(HxPs<)3{ zR#$UGH}0mZFl0A?tG24frjDxve~5yLo}^_~y-I_2N8>l)AO!l*+*(^3G}V{y0lYzq zU$1YeuLr?Ewe76Gd>iU-P2=rN{92YP;Lu#yL%o)O$}$tkB_P$o4feXAK*R&n5b;CN zBE{g3<1`JKveMy%!)`S00fTA&J&lJ=Le3`e_a=8xW9{&l`8CNPBC*nmK2LS0Glrsv z&gXyl?tn~zcZ4klpTg|j@IA7b@A;Xa&@AD?cz|6NBHsg13X($%Zim?oarls;a2O-< zq0Az%aFQb69YJXhQQ!GNVaAULNFNy&G{*OefKESXEMby~oG|~C(L>IhDwcQ^-XKbj zM>J}_?T)uDvmK2GFf_^1><;n#OQgQ64hVvIaPe^e30@qTfY-+l8)02ET*))X9dAo! zdJX(KzC=#81ji(^EhDsmG2@+LE{MZ2VY8hyX2EY^lEHX@i$n1Rbp}r!99q|$SC^4Y zh%Hg3M$wTQUXCN#X5S|;A$*7hdS3qVBC_l0pt=Q`)VF7)bBM>8Ae-|nk>OE=Deed zpa%HKaK#IfgcL!QE`+S+mvQwvSP&~LBDVl;mF}qcv!vHt5kaHPvvJN7yFp<}w2;{n zBc6TNPe>i&iqK-(%di*&3qxst%yt_r6HO%CtsHGBD^hh%T~}*jr@Errsx)ttpxYb| zwSd*sV(&$OSO}6LT6qG*s~D|$+7xqq=zRpqk)&82^vB@hH4dtbFo5P!+`UH24r_d* z!}kHF?KPl$H#WI=S6Brgb(U5P@Ht0m>YO!pH99K#Q$*k-xP+?(V(=;#(P<%W4(Y%D zSViS*Nb+OL?+wBfH$lR15=RkA!_Ne0EnWjpY11sNFI^3R>L_{Asw$~PRz@~|1eegq z`v{6t7lm^?)^6V)ULke#kv=ncy<{BWJ-w7M@vFCElpKdwKQhGjY^nZn;6Xcs6|@qT z?$Z{1V}pl?94R=xAVUVxw!mgVX2(1DTkB9qw%vV)?fom zz)LjQ$XJ5BT{CFMuro@Z=wUdf2w zKYvw+;MQAR#0hY&g3kqg-p}1Y@4{jZxU->ZI`ngcYJ)tDdGKk3&Ji-z%l9qsIslRb z^fzan#$%K=N5&NSTrqNoXgyb!$h;8p0J6X=NnpAhaCaHt(Io(+ zzm?p7_5l^hhI>ehqTZFX`^%u*<;24w9ThaYjB?CDuPdqb>=ScIjeA@Rq4T9w{tKY- za~)EVx=%Tw*_1s`*XKPXi&(lRREy{qYj&#@pnFPlDN~+Ioj9VGB6=!h0o`9{nZWOI z9xk*?%UGi%yuojPJ>h!DOX)iAG+jn#mpHUB*p0#)Y*ozkAkV%sw8tRtrl_9B=xpeE zZ2l)#{{a1Dv{35e1D;$BWi;QDa5q#`ND3El>1p6^;B&|^1DXOf(SjS#vw*cj%U0a5 z>wh;M_M(9O^Qii4Z?*uRVM#sj_~5Pnw+*)o?xO4{d%IdY@==w(8V0>mc+>Cp?ISp+ zZFV~i#%U%_Ck4vO+#wI){^06?Jw+II;SaTIPs1q+L3zhnTMXia-BLK-br6gc0ni)g z@Nw~FsBe`EfItLe(E`wo0SLnX7VU>9n!-8stXrWr8VN78ux3Mr#@`nfYqam&rf5zoidJ~6)0lki~=SulpQhr2gVg$dcy$8k)KEuhT6mq#<#S)34>U6 z!(YN54Iz0PMnJYbfUhx_fykicEeU@zh^Q@rjl9ne`{kY|!J#OMl#GYF=}|ny+Lzix zqknjn#N*KpN_y>&9L1*%LswzX`$bRESzB8Jsn72FA;mbZvaaKNSJU(IZc`#SE^R0% zZz#%;7#ng7Ck^;<6&$2pKVs*h70N}(H}WsSD8vYKi(L1Tcx3&Fa_{b&Hw`ZjQl^Ul zYoeAkpjwNfpW4|dO`cibD8LCLGBXZ>0l90}x3=Gg`rGbkj9;w{pX>mzTbKd5MZab&eZqk5L60f}FZDSC9g}%!y*Z zm#$)8{7*|Qrj^pmF_d7{)3H^9<0fiEFnw+`uY8!sfuVA3)b%l%@XY_+@f_z5C(;pBHv_b5HIJ7xIQw8o1MD6QM=x;yRj)eX$ub@ zm$>>2|Lf=^u8L@FLvetz5Xq$5-|_rGZ+jDULA#UIFpecBw%5g^*rn%HNSkfqHRez= z=AzI9M2wQ7f3*zUX9TdaF2=%OeifrpG^tudsga2{BUn+`M}amK(Ad|wpq6BfRkj+% zDTRdUg`Dt^g@vP#rOfn5l{`t|?=68+i0)Hzn~jloh=>GChaJ96;D$uj(C zJqvVV{tk8Qq)7K#M`wg>cKf6eS9EQ(2`;OPbcH#DnOC-zuDh9J21at5Z#XV-X5&cU|POfgoXtUMi5NbEl zSQ>n^pBZSE8VVRu6!eT~ie(nEAi!-p>j%*Y!ydCy#IzHMv<_CZ4M_QL=4akd2fD7k zhmTwT#^C0c8)WBV^tGc8Y%2>!TBEi~gKbjmGF%& zgV(7xH%T95JW+-RoIqP!c|-}^O${0$+~T5rsM)lZia@k18h2T(H37-9P5D9a?!Bm> zTJ>BJdbZ*PS+}jN4{{l#;x(RqH!x&@Z#Hu(WO+R}rfz1K9HX(oTx-3oPtg>PwD!#V z&P3S{V;N2N)6Q-*TlqcMVq@;u@3JUo2KPJIUJ~{sNI}ic(cbvr$+Mi`)5FBIlZi%R zO5~{w3EruS^~E1gOcEYVRO;yown1B^^ntsx`iw1WEg`od#eWr(H2@gi4fjD1%z}KJ zj*1MrLTX^5l-x*m^Ezswp-NNNB9Th-E<{3>%t};X(u#+1OAR+_sre;ZY7V);A6pG@ zC;X?j1xOT~qWUp6G}c-6j?hpfRakUHf^bqrR8ekeD|TtwCPMOa>UVAYuJAI(dGJiw zt7#%iWC)N@6M2A#Pf^l9(~=O8VJu$|UvOU|;g#lXmcB{BuOcHUv)=v;kqlo-?C7nR zN^+|8FZy43QvyxgCM{^k8v@pSs%V-;Y0K{t-A2)&shfG8wplE4ctxy8wu4ii5DfDL zAeHwADQ-`DjVw4=F%E z!gs33%G{MK@xG@DLF%U>fNI4TD)OMsdP*~<2Q>WV%p0J()b&#*l4k*Mz{Ryt3}w}s z8(vwI-qj|J>m>&8>i~5^W#_a`Dtw z)%#&sm{QB=Yg$i2VH9dbq{=$>khXrpYAdz^d=}yj)=B)JIwszWsqSmm0c#&or0(k1 z4RvEL{O&a@#SX;D>9_o0>t&ioEkHk#O`M~Y zi^bJMIZPEZnNsKlhP19a;OLFEF5a;m)DY|srtK&3TrMs~jg%`o6 zpvzj>YK-9aMLg!||SA5hW! z2y+<(?W;DEQ5eqmP7Yta{%|bj7}T5-Bc_xt+i@1qQg-3mFzCc4ED$4uVy_pn*5cY% z;i}P#?o>k#M=?eKmfk?7Dh*CRVe6h`6&7YMjz-iB%9vD;^pp`xTAD*6TLw{%ImN`r z+L}g7Mph<wB6QI~BTUXq}6A$Lew^p7hY&Fsh6`t$<`;TU)ekUwB1i01=gMI;-g< zUF`>cl=c5ZN-xCM-x`4lpQ9(4_Ja(CfakWLGH170uCF9Cfb8IcX)`(O``t^(${6sopht1h0#kC3p6z|H$Y>}53)3e>Zy@QjJvmXxrs--~N$akaD$(disS6e;=ZKUckh$zPT0Z7{b_oL{{ zCE!%*GyrhGMi>T^Vti~Hwj)LyLub#7g-nPq&zu3+_DR5p< zh6TyH*2lOQ{m#wag#&XA{ScS<5{7s18s7S&=5kK(9!ykbO7%TU3-q!<$Mz$TihDX| z*x!@^QhkXYX-@~Jh%?U792vB@zl1U$30yH)B8&g(4 z^ZS^$f^gKpXq=EKmlKx`5XD0f=lztQ*Ptq=m#g$jH*4380SaKZUlhk5_{2XszCbPGHv}fJNVu??$MB#aSPoa@k{AhO+ZzjU?+td7FFqE1 z-mr7S#qS=%J1x@w_Y~4TbBsI3u=DM)e!pi3y9t4Q3SiF#u;)SZ6u$n&gs&fn6;(w- zk4#2o_td0;gm6_^5dscBF@i>5#iNke5gRjf%L_-}VGxY#`R#_7?zFSkrvJCPzGgv{&l_IRj zau8b49u)_zhpczt(+*n?>AIxb@_osv&pH*cGcN8}j7yyZ?y8Q3NSB<8EaDG#FvED* z_ji83`@;8Wg0Tq%kAy07!3!@Wbjn> zRmF!9tRAW01|pVRUW5nSj{|N8It%uua5lvi_^PV&(|h83yvNu3@EDUW4TEzzYu-d& zHz{KhT{VCw@!*s~S|m{!-Gg6M$qZD=-B27E$arSe>OdAXPK>r^#e=Qn!mqi9_JUrJ zFN0hn#TBfm?B=*%vK{k5zy!{@$XIzdijH1uDgZY%a(UkC94l59W*N-HO$`R)At%S6 zbD)DZ!B`yYc$5NqiBiJRzv@co%+z$Cb46XTpc7usfG}Q%ei>vjDP) zIDmy+?Xi_yap^o>sNFfB(viEx*k2e@CRA;XKKSO2{N}6VwM6!pMgc#=$KKgW&kyH)wdOlnb7H~)RuWiD#eHnc~C@ zoPZ*QmmsxWX8e*$Hm`=l^oa185qbF;7s^u1k+a;+m%Ch%uiW*nJ3$cIne2qSz#_Q@ z2|vs&@gtv7B|Ir%5?fA6%?Pzl2K;wK#EiO{5_-;f3lu41X0D6qY z;-Ww9g>3zg;wzM?a5?6Ms1h?*jJq5EKwilHUHr?$^fg4-vVbfxUR=OQO{gY2)rHRt z8ZZTUrd+pXfpZ}{HJ^gq^m{pW+ZD|o6TN9kPxw6zzu%9C#?jk^g*1u%3WrG4TkK)% zU5*oC6%>xYg5Qwi-O94J(oh25FwO;W6rT4`oxo2RL!_KqB%Je7@SpLRaT7yqv)>5}U(HCIr&8$0WTE(%mC_Pj5=Wiw2k?6UYdA{|UKke-NZ3mSB<2@F` z#92wJj%lTB+~c8DWfsMKDJ3Y+#+P)iORhs&EJwL)#>{l-$$wfC<)=84Db8eyZ{5e$ z&_%h@)&D9&B9y}nPQgjvmPobO*D|8=Yo^>IDNKfwN=%`Oty__=Ud1sIlf5V&_q-q8 zo|vp4IN&SzhS(6ly6_{iuyAzQihjMJtorr?=Ul>BV?jmw8}bXcIH5eF=IQ>cy<=K~ zYK;r^npfRX_j-^TaFE-P5t#n|Rzeh7h`24}@K!^7a|u9D4LL&4ZUEamBT`rxi6Pax zPVDAX?KeKdj3yP$gNbfG*V*Vor54U92A1}222tEp4nAR&wwf*7RJLf3WtSt zZmgx&F66SEsTc}%QSD{tne6Lb!(35RA6f zp)rgo5sBl?8vjmkHJufF-Iv;hN3Lh3$fX4P{G`!qH>QXAtyVo)f)9RoTx@J ztI9Pjl<-$Sho_LM;h1t5nALt%`I?C#(ulqAlCpefxKFi6_(eH-N^}WR9d^SOoCvI= zkO*H1KJnbIcXf}tdj2ytxUox#3aD~bTbh?u-Sar^p=n%ym440Q-#HW>jfMCz8LtTD=nYNXu#(wS41lt98LzPw_n4$VmS&T>h}uzI*m zM-ZBm$zoe#LqeY@Jw_gHSco~s&_|Ws#cM?7=@zW9_Eg2MVkPEaa zQm8uw3x8)$zKf<+MbPq+Kc-&F#7|2MKuPmLgQiALO5z_!Ni1kzUBZ(x{mV+emzx_! zg|oxsotIl?dVMA4zYtGqoKfRi>zmtKoz35kHm3n%XDa3ME1~+-f*hFYMX@CIdCRZu zO&&?Bh$gu;p&thS+=gl2dXZekhUEE^zIU7g&%Xe%>QVJgGV(7lEwM(^J0&LetiMg} zVf+-*=io$cI^ROoo~7aH`f{ryPM)DIHKg_5<`7mYPp8Y|?k^Yfrf;b>t+cR>4JyvH{rd4&#wX;AjSj(zs}vwMyzZQ3Eg{ zg~+gq@yyNO2ppL1NO>CU6J34PgoNeG8X5j+JKgx-jtlysAE^NR4W!am!mG}1Woo_$3>$tA-n zhA7&Bu1zUhgT-mdCR};Fw+Am(V*EiZ5s~hVKb9)?6 z=;wavHx8XVpr7HPpRXKNlW};3EOQN@Jlrbl1oY=(_F)O zJwF&=uB6P6C0znTMpMFa)sXPR$VbUKnye_B)w8mEl?L@%X?TU`RzBb}nYabBwyGNG z<^HJTKUobI-Q-Q0N*G2lBeR{>d}wga9x7fGCe5JOMtC zGVDToO!#1roY$u0w-S=sn7haXK|*cQtvIpK79-d65)yyJ3?nMP2q(EVnQ%<4ghI|7 zN{DR%?2nsrqzJr{pjY^@*--Ytd~%UH)x4lO6{fg_DQD4Hf2qq^n(XIHBy7f+UR_l^NQRQ}@Im`@t#?PvcnN>`&C>$%oOJX%y=9kE3O$jMD zkmKY~!xO#Y(1x98>dTZ8{WMpDtZT815^kY8C=eozjWAcTnw-AB=5J9ulaX)y1k>cK z$}jE-RGCAHWY>V?p2iUv)TlnAK)1sb`+z zT4m+6rpr4{1twLUNqsLRq0sl*5XsABR0UR5WL9}bqlzs#97&2a<$2cpeH%<<3+Y+t zTxR5EnYV)FZ{NCo*ZZ+hi??%C<}iQL(S9h@+sLK0wsRG?8|CS_e}8F$pVC@SOKWYA zr8=)Qj#HA@`UzEj|ESV1ZnczaBdRioq|V?MPL+jYWGa4g5dwXJgSW~Sm{(ZjE6&r; z8c)@na3vVS^_Zp4B)>B;jFz63##b;m)(L6s5qG(a{HuJK2b40Nna8inq|wEimR

aFOR>ouU5Ky>;4i6C!YV-PK;Ac#q#agEGVfM%^d9LF!{`u=UNgV$`4ZrcsGAKEl zwqy5w82;Aesg*P|x-!kVq>A-Y9dFAaAQJ&d!pL05rQX6kT~&~8YIS4t$Eo^z z$3jKrUNDbMi(xixZ*QDnSUy@73F?##$LpyqI5GweYempVq_gt)s%V;E*rQ@yD;V`h z?tfid(mgFxYnS2P5V4$K?*_q{!(~&08;wKa+x)?w&p6)gnZ8Ffp?N{rR_D(3Q|Od2 zLnoG2)#Z{%DhX3#&pjOKJB;xVm~fp6r^ zt3#3;2<;UDyiBnZYq_RuYw84P+4Y(&!Gq(Ei%2-y#ssoYJ> zLy_j&Ci>o(-=|>20t~cAK}4!jvIzlFs-}JwBYFPRD#fKOtqw8C=vZh1z@!gM(3%rF z(&HIkm$<1eG1m~qCTU%ytk{%>P)m{;WfKZCZP8$G>ysc@MWg4ce1X=Lmemc`(7je{ zHBX@Kx)ue8vhQk+kXvsq4WE^_Smb_5#K0EbT>PJVoNZST&b_vF)2n7&EJ&0HcUTp_ zKr$O8U0zYHKkTSMCiT8{tQ#i=9&x5RsnC#a-ynC-M7FRlL)uMztTie$OT;ejfca=N zlulXf$cM>Gf1aKGEe$cz0(CY^d_Rr?odkK&*Ybfb=Jczno9y#@c9+fJeog%)i5Erq z?M6Y8y`{tki4+_4@hdAxRr}~Diqh#2Qi(5ELp0H8K2*mt6r7EO?vkxlOvS^gm{^L7 zv=V5hSy^IFM}IiAmAw|jMR{sohgW?#s1b_lh-RjtYa+0}N25!cQt3WKxJ0^hsgn58 z2#pw()liMIy!SCNxS{F#C^ERzzPV>NV@ z6zcPUu=6?Lhw)DL17H{a2cP;i{`B;D`aFG}K2M*g&(r7W^YnT8Jbj)%PoJmH)92~) R^m&xe{|_cAFBt$}1ON|csLucZ literal 0 HcmV?d00001 diff --git a/hashicorp-vault/update-helm-dependency.sh b/hashicorp-vault/update-helm-dependency.sh index fafe3dde..76e4ac14 100755 --- a/hashicorp-vault/update-helm-dependency.sh +++ b/hashicorp-vault/update-helm-dependency.sh @@ -22,6 +22,7 @@ pushd "${NAME}" for i in ../../local-patches/*.patch; do filterdiff "${i}" -p1 -x 'test/*' | patch -p1 done +find . -type f -iname '*.orig' -exec rm -f "{}" \; popd tar cvfz "${TAR}" "${NAME}" rm -rf "${NAME}" diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index a0e91908..585e9274 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -49,4 +49,4 @@ vault: termination: "reencrypt" image: repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.12.1-ubi" + tag: "1.13.1-ubi" diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index 2f8643d4..24ad0554 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -205,7 +205,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.13.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -346,7 +346,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.13.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index 2f8643d4..24ad0554 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -205,7 +205,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.13.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -346,7 +346,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.13.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index 2f8643d4..24ad0554 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -205,7 +205,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.13.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -346,7 +346,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.13.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index d4b110b5..6206b720 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -205,7 +205,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.13.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -346,7 +346,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.13.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index 2f8643d4..24ad0554 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -205,7 +205,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.13.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -346,7 +346,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.23.0 + helm.sh/chart: vault-0.24.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.12.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.13.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR From 817bf1fe9d694ac94c05e997d67c2589dfce9c27 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 15 Apr 2023 20:27:09 +0200 Subject: [PATCH 0825/1263] Add a hello-world ansible playbook example Just a simple example that reads a helm value and puts it in a configmap --- .../playbooks/hello-world/hello-world.yaml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 ansible/playbooks/hello-world/hello-world.yaml diff --git a/ansible/playbooks/hello-world/hello-world.yaml b/ansible/playbooks/hello-world/hello-world.yaml new file mode 100644 index 00000000..6dff5530 --- /dev/null +++ b/ansible/playbooks/hello-world/hello-world.yaml @@ -0,0 +1,22 @@ +# This playbook is a simple hello-world playbook to show capabilities +# It creates a config-map inside the imperative namespace containing +# the helm variable "global.clusterDomain" +--- +- hosts: localhost + connection: local + gather_facts: false + become: false + vars: + ns: imperative + tasks: + - name: Create secret with managed cluster's CA + kubernetes.core.k8s: + state: present + definition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: "hello-world" + namespace: "{{ ns }}" + data: + hello-cluster-domain: "{{ global['clusterDomain'] }}" From 248ac4691901b6019690000be25d0f5007858107 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 15 Apr 2023 20:34:21 +0200 Subject: [PATCH 0826/1263] Inject ANSIBLE_CONFIG in make ansible-lint --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c4d98414..76bc0bc9 100644 --- a/Makefile +++ b/Makefile @@ -134,7 +134,8 @@ super-linter: ## Runs super linter locally .PHONY: ansible-lint ansible-lint: ## run ansible lint on ansible/ folder - podman run -it -v $(PWD):/workspace:rw,z --workdir /workspace --entrypoint "/usr/local/bin/ansible-lint" quay.io/ansible/creator-ee:latest "-vvv" "ansible/" + podman run -it -v $(PWD):/workspace:rw,z --workdir /workspace --env ANSIBLE_CONFIG=./ansible/ansible.cfg \ + --entrypoint "/usr/local/bin/ansible-lint" quay.io/ansible/creator-ee:latest "-vvv" "ansible/" .PHONY: ansible-unittest ansible-unittest: ## run ansible unit tests From bcc7b24c0c4daf50cd2d686847ebf58e84f2b4e0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 15 Apr 2023 20:35:16 +0200 Subject: [PATCH 0827/1263] Use new ansible-lint action --- .github/workflows/ansible-lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ansible-lint.yml b/.github/workflows/ansible-lint.yml index 3b2de754..8df838ce 100644 --- a/.github/workflows/ansible-lint.yml +++ b/.github/workflows/ansible-lint.yml @@ -11,8 +11,8 @@ jobs: - uses: actions/checkout@v2 - name: Lint Ansible Playbook - # Using the latest as of today (2023-01-24) v6.11.0 - uses: ansible/ansible-lint-action@v6.11.0 + # Using the latest as of today (2023-04-15) v6.14.4 + uses: ansible/ansible-lint-action@v6.14.4 # Let's point it to the path with: path: "ansible/" From 879737f06024264bed6840a1770431c9ca27b8bc Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 15 Apr 2023 20:37:52 +0200 Subject: [PATCH 0828/1263] Fix some ansible-lint warnings --- ansible/playbooks/acm/acmhub-get-ca.yaml | 3 ++- ansible/playbooks/hello-world/hello-world.yaml | 3 ++- ansible/roles/vault_utils/tasks/push_secrets.yaml | 1 + ansible/roles/vault_utils/tests/test.yml | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ansible/playbooks/acm/acmhub-get-ca.yaml b/ansible/playbooks/acm/acmhub-get-ca.yaml index 8c6d2684..770333ff 100644 --- a/ansible/playbooks/acm/acmhub-get-ca.yaml +++ b/ansible/playbooks/acm/acmhub-get-ca.yaml @@ -1,7 +1,8 @@ # This playbook fetches the hub cluster's CAbundle from ACM's objects # and puts it in a secret inside the imperative namespace --- -- hosts: localhost +- name: ACM Get Hub CA + hosts: localhost connection: local gather_facts: false become: false diff --git a/ansible/playbooks/hello-world/hello-world.yaml b/ansible/playbooks/hello-world/hello-world.yaml index 6dff5530..c0a992a7 100644 --- a/ansible/playbooks/hello-world/hello-world.yaml +++ b/ansible/playbooks/hello-world/hello-world.yaml @@ -2,7 +2,8 @@ # It creates a config-map inside the imperative namespace containing # the helm variable "global.clusterDomain" --- -- hosts: localhost +- name: Hello World Example + hosts: localhost connection: local gather_facts: false become: false diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 0fdaa631..101b83af 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -102,6 +102,7 @@ ansible-vault view --vault-password-file <(cat <<<"{{ vault_pass.user_input }}") "{{ found_file }}" register: values_secret_plaintext when: is_encrypted + changed_when: false - name: Loads secrets file into the vault of a cluster no_log: false diff --git a/ansible/roles/vault_utils/tests/test.yml b/ansible/roles/vault_utils/tests/test.yml index 0998beb6..b4da5c68 100644 --- a/ansible/roles/vault_utils/tests/test.yml +++ b/ansible/roles/vault_utils/tests/test.yml @@ -1,5 +1,6 @@ --- -- hosts: localhost +- name: Test Play + hosts: localhost remote_user: root roles: - vault_utils From 12fd2f8fb521ff0b7ad6967fedf84a561438b8a3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 15 Apr 2023 20:43:57 +0200 Subject: [PATCH 0829/1263] Fix up python versions --- .github/workflows/ansible-unittest.yml | 2 +- .github/workflows/jsonschema.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ansible-unittest.yml b/.github/workflows/ansible-unittest.yml index 3c8b5c46..cd6d8e92 100644 --- a/.github/workflows/ansible-unittest.yml +++ b/.github/workflows/ansible-unittest.yml @@ -20,7 +20,7 @@ jobs: name: Ansible unit tests strategy: matrix: - python-version: [3.10.10] + python-version: [3.11.3] # Set the agent to run on runs-on: ubuntu-latest diff --git a/.github/workflows/jsonschema.yaml b/.github/workflows/jsonschema.yaml index 00232f0b..e865ff9e 100644 --- a/.github/workflows/jsonschema.yaml +++ b/.github/workflows/jsonschema.yaml @@ -20,7 +20,7 @@ jobs: name: Json Schema tests strategy: matrix: - python-version: [3.11.2] + python-version: [3.11.3] # Set the agent to run on runs-on: ubuntu-latest From a3b22b5aa6b8dec6d76aba5e22f57607f8e4c2ba Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 15 Apr 2023 20:51:07 +0200 Subject: [PATCH 0830/1263] Skip cannot find role error Avoid checking those two playbooks the action seems to be too limited to understand where the ansible.cfg is --- .ansible-lint | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.ansible-lint b/.ansible-lint index 67a7552c..f3e5e8a0 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -6,3 +6,9 @@ skip_list: - template-instead-of-copy # Templated files should use template instead of copy - yaml[line-length] # too long lines - yaml[indentation] # Forcing lists to be always indented by 2 chars is silly IMO + +# ansible-lint gh workflow cannot find ansible.cfg hence fails to import vault_utils role +exclude_paths: + - ./ansible/playbooks/vault/vault.yaml + - ./ansible/roles/vault_utils/tests/test.yml + From dde9699afed48c7461a739e5da0348bd932a3f38 Mon Sep 17 00:00:00 2001 From: day0hero Date: Thu, 20 Apr 2023 10:16:55 -0500 Subject: [PATCH 0831/1263] Added health check for pvc resource in argocd.yaml This allows argo to continue rolling out the rest of the applications. Without the health check the application is stuck in a progressing state and will not continue thus preventing any downstream application from deploying. --- clustergroup/templates/plumbing/argocd.yaml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 68400b95..36bef2d1 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -12,6 +12,27 @@ metadata: annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: + resourceCustomizations: | + PersistentVolumeClaim: + health.lua: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + if obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs applicationInstanceLabelKey: argocd.argoproj.io/instance # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize From f2010a33a8deda3fbbdad0dc715a88a80c211055 Mon Sep 17 00:00:00 2001 From: day0hero Date: Thu, 20 Apr 2023 10:20:37 -0500 Subject: [PATCH 0832/1263] adding tests --- ...roup-industrial-edge-factory.expected.yaml | 21 +++++++++++++++++++ ...tergroup-industrial-edge-hub.expected.yaml | 21 +++++++++++++++++++ ...rgroup-medical-diagnosis-hub.expected.yaml | 21 +++++++++++++++++++ tests/clustergroup-naked.expected.yaml | 21 +++++++++++++++++++ tests/clustergroup-normal.expected.yaml | 21 +++++++++++++++++++ 5 files changed, 105 insertions(+) diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index dfb795d4..8973c068 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -443,6 +443,27 @@ metadata: annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: + resourceCustomizations: | + PersistentVolumeClaim: + health.lua: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + if obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs applicationInstanceLabelKey: argocd.argoproj.io/instance # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 9c43c8cc..8f7a3d01 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -1076,6 +1076,27 @@ metadata: annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: + resourceCustomizations: | + PersistentVolumeClaim: + health.lua: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + if obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs applicationInstanceLabelKey: argocd.argoproj.io/instance # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 89ba22d6..acae3ec6 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -1252,6 +1252,27 @@ metadata: annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: + resourceCustomizations: | + PersistentVolumeClaim: + health.lua: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + if obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs applicationInstanceLabelKey: argocd.argoproj.io/instance # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index f0c12937..64067c5e 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -266,6 +266,27 @@ metadata: annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: + resourceCustomizations: | + PersistentVolumeClaim: + health.lua: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + if obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs applicationInstanceLabelKey: argocd.argoproj.io/instance # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index f7e6d84d..d2aabb0b 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -841,6 +841,27 @@ metadata: annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: + resourceCustomizations: | + PersistentVolumeClaim: + health.lua: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + if obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs applicationInstanceLabelKey: argocd.argoproj.io/instance # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize From e06ad889ca23d227a5941c7665b24dbc7ac0816a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 20 Apr 2023 18:54:31 +0200 Subject: [PATCH 0833/1263] Update super-linter image to latest --- .github/workflows/superlinter.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index a3e22028..36f082cf 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -21,7 +21,7 @@ jobs: # Run Linter against code base # ################################ - name: Lint Code Base - uses: github/super-linter/slim@v4 + uses: github/super-linter/slim@v5 env: VALIDATE_ALL_CODEBASE: true DEFAULT_BRANCH: main diff --git a/Makefile b/Makefile index 76bc0bc9..c8be49b6 100644 --- a/Makefile +++ b/Makefile @@ -130,7 +130,7 @@ super-linter: ## Runs super linter locally $(DISABLE_LINTERS) \ -v $(PWD):/tmp/lint:rw,z \ -w /tmp/lint \ - docker.io/github/super-linter:slim-v4 + docker.io/github/super-linter:slim-v5 .PHONY: ansible-lint ansible-lint: ## run ansible lint on ansible/ folder From 35459457de3ebff21d53c200cd4917b5dd889194 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 20 Apr 2023 18:54:31 +0200 Subject: [PATCH 0834/1263] Update super-linter image to latest --- .github/workflows/superlinter.yml | 4 ++-- Makefile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index a3e22028..1f8e987b 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -21,7 +21,7 @@ jobs: # Run Linter against code base # ################################ - name: Lint Code Base - uses: github/super-linter/slim@v4 + uses: github/super-linter/slim@v5 env: VALIDATE_ALL_CODEBASE: true DEFAULT_BRANCH: main @@ -30,7 +30,7 @@ jobs: VALIDATE_ANSIBLE: false VALIDATE_BASH: false VALIDATE_JSCPD: false - VALIDATE_KUBERNETES_KUBEVAL: false + VALIDATE_KUBERNETES_KUBECONFORM: false VALIDATE_YAML: false # VALIDATE_DOCKERFILE_HADOLINT: false # VALIDATE_MARKDOWN: false diff --git a/Makefile b/Makefile index 76bc0bc9..f68d4692 100644 --- a/Makefile +++ b/Makefile @@ -122,7 +122,7 @@ super-linter: ## Runs super linter locally podman run -e RUN_LOCAL=true -e USE_FIND_ALGORITHM=true \ -e VALIDATE_BASH=false \ -e VALIDATE_JSCPD=false \ - -e VALIDATE_KUBERNETES_KUBEVAL=false \ + -e VALIDATE_KUBERNETES_KUBECONFORM=false \ -e VALIDATE_YAML=false \ -e VALIDATE_ANSIBLE=false \ -e VALIDATE_DOCKERFILE_HADOLINT=false \ @@ -130,7 +130,7 @@ super-linter: ## Runs super linter locally $(DISABLE_LINTERS) \ -v $(PWD):/tmp/lint:rw,z \ -w /tmp/lint \ - docker.io/github/super-linter:slim-v4 + docker.io/github/super-linter:slim-v5 .PHONY: ansible-lint ansible-lint: ## run ansible lint on ansible/ folder From 7fa15b19af94977121e6a98e8298d48377a69c6b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 20 Apr 2023 19:20:44 +0200 Subject: [PATCH 0835/1263] Update CI workflows --- .github/workflows/ansible-lint.yml | 5 ++--- .github/workflows/ansible-unittest.yml | 4 ++-- .github/workflows/jsonschema.yaml | 4 ++-- .github/workflows/linter.yml | 2 +- .github/workflows/superlinter.yml | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ansible-lint.yml b/.github/workflows/ansible-lint.yml index 8df838ce..1bf2f7dd 100644 --- a/.github/workflows/ansible-lint.yml +++ b/.github/workflows/ansible-lint.yml @@ -8,11 +8,10 @@ jobs: steps: # Important: This sets up your GITHUB_WORKSPACE environment variable - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Lint Ansible Playbook - # Using the latest as of today (2023-04-15) v6.14.4 - uses: ansible/ansible-lint-action@v6.14.4 + uses: ansible/ansible-lint-action@v6 # Let's point it to the path with: path: "ansible/" diff --git a/.github/workflows/ansible-unittest.yml b/.github/workflows/ansible-unittest.yml index cd6d8e92..af326b66 100644 --- a/.github/workflows/ansible-unittest.yml +++ b/.github/workflows/ansible-unittest.yml @@ -32,13 +32,13 @@ jobs: # Checkout the code base # ########################## - name: Checkout Code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/jsonschema.yaml b/.github/workflows/jsonschema.yaml index e865ff9e..a0c60c11 100644 --- a/.github/workflows/jsonschema.yaml +++ b/.github/workflows/jsonschema.yaml @@ -32,13 +32,13 @@ jobs: # Checkout the code base # ########################## - name: Checkout Code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 8a276b0c..28c3944d 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -29,7 +29,7 @@ jobs: # Checkout the code base # ########################## - name: Checkout Code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index 1f8e987b..30cb00a8 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 From 6b88bcdfa0e025b8e3e71a9f6821b0d302873ebe Mon Sep 17 00:00:00 2001 From: day0hero Date: Thu, 20 Apr 2023 17:34:12 -0500 Subject: [PATCH 0836/1263] updated template with why implemented comment --- clustergroup/templates/plumbing/argocd.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 36bef2d1..76ffad73 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -12,6 +12,8 @@ metadata: annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: +# Adding health checks to argocd to prevent pvc resources +# that aren't bound state from blocking deployments resourceCustomizations: | PersistentVolumeClaim: health.lua: | From d6ab88172ad3015dd6883894792fd2d26b9f538e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 21 Apr 2023 12:51:43 +0200 Subject: [PATCH 0837/1263] Add dependabot settings for github actions --- .github/dependabot.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..a175e666 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +--- +version: 2 +updates: + # Check for updates to GitHub Actions every week + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + From 8faea73b61a41c1a5deb165a3c5cef94e20f9a4d Mon Sep 17 00:00:00 2001 From: day0hero Date: Fri, 21 Apr 2023 09:10:48 -0500 Subject: [PATCH 0838/1263] adding tests --- tests/clustergroup-industrial-edge-factory.expected.yaml | 2 ++ tests/clustergroup-industrial-edge-hub.expected.yaml | 2 ++ tests/clustergroup-medical-diagnosis-hub.expected.yaml | 2 ++ tests/clustergroup-naked.expected.yaml | 2 ++ tests/clustergroup-normal.expected.yaml | 2 ++ 5 files changed, 10 insertions(+) diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 8973c068..37d78f2c 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -443,6 +443,8 @@ metadata: annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: +# Adding health checks to argocd to prevent pvc resources +# that aren't bound state from blocking deployments resourceCustomizations: | PersistentVolumeClaim: health.lua: | diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 8f7a3d01..d8f82078 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -1076,6 +1076,8 @@ metadata: annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: +# Adding health checks to argocd to prevent pvc resources +# that aren't bound state from blocking deployments resourceCustomizations: | PersistentVolumeClaim: health.lua: | diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index acae3ec6..67662435 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -1252,6 +1252,8 @@ metadata: annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: +# Adding health checks to argocd to prevent pvc resources +# that aren't bound state from blocking deployments resourceCustomizations: | PersistentVolumeClaim: health.lua: | diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 64067c5e..b173a8fc 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -266,6 +266,8 @@ metadata: annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: +# Adding health checks to argocd to prevent pvc resources +# that aren't bound state from blocking deployments resourceCustomizations: | PersistentVolumeClaim: health.lua: | diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index d2aabb0b..ceb8a226 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -841,6 +841,8 @@ metadata: annotations: argocd.argoproj.io/compare-options: IgnoreExtraneous spec: +# Adding health checks to argocd to prevent pvc resources +# that aren't bound state from blocking deployments resourceCustomizations: | PersistentVolumeClaim: health.lua: | From d3b6faf2cd16d7e6b3242b4469067f826000aa5f Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 21 Apr 2023 11:48:30 -0600 Subject: [PATCH 0839/1263] - Added functionality to support the following format for labels and annotations: labels: openshift.io/node-selector: "" annotations: openshift.io/cluster-monitoring: "true" --- clustergroup/templates/core/namespaces.yaml | 8 ++++---- examples/values-example.yaml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clustergroup/templates/core/namespaces.yaml b/clustergroup/templates/core/namespaces.yaml index 8d02fe65..dfa6ae1a 100644 --- a/clustergroup/templates/core/namespaces.yaml +++ b/clustergroup/templates/core/namespaces.yaml @@ -9,14 +9,14 @@ metadata: labels: argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} {{- if $v.labels }} - {{- range $v.labels }} - {{ .name }}: {{ .value | default "" | quote }} + {{- range $key, $value := $v.labels }} {{- /* We loop here even though the map has always just one key */}} + {{ $key }}: {{ $value | default "" | quote }} {{- end }} {{- end }} {{- if $v.annotations }} annotations: - {{- range $v.annotations }} - {{ .name }}: {{ .value | default "" | quote }} + {{- range $key, $value := $v.annotations }} {{- /* We loop through the map to get key/value pairs */}} + {{ $key }}: {{ $value | default "" | quote }} {{- end }} {{- end }}{{- /* if $v.annotations */}} {{- end }}{{- /* range $k, $v := $ns */}} diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 5b69c403..77584dc8 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -15,11 +15,11 @@ clusterGroup: namespaces: - open-cluster-management: labels: - - name: "openshift.io/node-selector" - value: "" + openshift.io/node-selector: "" + kubernetes.io/os: linux annotations: - - name: "openshift.io/cluster-monitoring" - value: "true" + openshift.io/cluster-monitoring: "true" + owner: "namespace owner" - application-ci subscriptions: From 9d6fc02131d673fd5062434f7f20215dc7310fa7 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 21 Apr 2023 12:05:35 -0600 Subject: [PATCH 0840/1263] Fixed CI Issues --- tests/clustergroup-normal.expected.yaml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index a95b9d70..1cdd4683 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -6,9 +6,11 @@ metadata: name: open-cluster-management labels: argocd.argoproj.io/managed-by: mypattern-example + kubernetes.io/os: "linux" openshift.io/node-selector: "" annotations: openshift.io/cluster-monitoring: "true" + owner: "namespace owner" spec: --- # Source: pattern-clustergroup/templates/core/namespaces.yaml @@ -148,11 +150,11 @@ data: namespaces: - open-cluster-management: annotations: - - name: openshift.io/cluster-monitoring - value: "true" + openshift.io/cluster-monitoring: "true" + owner: namespace owner labels: - - name: openshift.io/node-selector - value: "" + kubernetes.io/os: linux + openshift.io/node-selector: "" - application-ci projects: - datacenter @@ -968,11 +970,11 @@ spec: apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: - name: map[open-cluster-management:map[annotations:[map[name:openshift.io/cluster-monitoring value:true]] labels:[map[name:openshift.io/node-selector value:]]]]-operator-group - namespace: map[open-cluster-management:map[annotations:[map[name:openshift.io/cluster-monitoring value:true]] labels:[map[name:openshift.io/node-selector value:]]]] + name: map[open-cluster-management:map[annotations:map[openshift.io/cluster-monitoring:true owner:namespace owner] labels:map[kubernetes.io/os:linux openshift.io/node-selector:]]]-operator-group + namespace: map[open-cluster-management:map[annotations:map[openshift.io/cluster-monitoring:true owner:namespace owner] labels:map[kubernetes.io/os:linux openshift.io/node-selector:]]] spec: targetNamespaces: - - map[open-cluster-management:map[annotations:[map[name:openshift.io/cluster-monitoring value:true]] labels:[map[name:openshift.io/node-selector value:]]]] + - map[open-cluster-management:map[annotations:map[openshift.io/cluster-monitoring:true owner:namespace owner] labels:map[kubernetes.io/os:linux openshift.io/node-selector:]]] --- # Source: pattern-clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 From 28639991ea168585c04d00e90455e17471f6f393 Mon Sep 17 00:00:00 2001 From: day0hero Date: Fri, 21 Apr 2023 14:21:39 -0500 Subject: [PATCH 0841/1263] Applying @claudiol recommendation --- clustergroup/templates/plumbing/argocd.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 76ffad73..949c77cf 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -24,8 +24,7 @@ spec: hs.status = "Healthy" hs.message = obj.status.phase return hs - end - if obj.status.phase == "Bound" then + elseif obj.status.phase == "Bound" then hs.status = "Healthy" hs.message = obj.status.phase return hs From fd004fb867896818be502cb4ba6c9bc04166e7ac Mon Sep 17 00:00:00 2001 From: day0hero Date: Fri, 21 Apr 2023 14:22:12 -0500 Subject: [PATCH 0842/1263] make test --- tests/clustergroup-industrial-edge-factory.expected.yaml | 3 +-- tests/clustergroup-industrial-edge-hub.expected.yaml | 3 +-- tests/clustergroup-medical-diagnosis-hub.expected.yaml | 3 +-- tests/clustergroup-naked.expected.yaml | 3 +-- tests/clustergroup-normal.expected.yaml | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 37d78f2c..2d3d7a96 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -455,8 +455,7 @@ spec: hs.status = "Healthy" hs.message = obj.status.phase return hs - end - if obj.status.phase == "Bound" then + elseif obj.status.phase == "Bound" then hs.status = "Healthy" hs.message = obj.status.phase return hs diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index d8f82078..1982541e 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -1088,8 +1088,7 @@ spec: hs.status = "Healthy" hs.message = obj.status.phase return hs - end - if obj.status.phase == "Bound" then + elseif obj.status.phase == "Bound" then hs.status = "Healthy" hs.message = obj.status.phase return hs diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 67662435..cd66eaca 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -1264,8 +1264,7 @@ spec: hs.status = "Healthy" hs.message = obj.status.phase return hs - end - if obj.status.phase == "Bound" then + elseif obj.status.phase == "Bound" then hs.status = "Healthy" hs.message = obj.status.phase return hs diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index b173a8fc..b01ed5e0 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -278,8 +278,7 @@ spec: hs.status = "Healthy" hs.message = obj.status.phase return hs - end - if obj.status.phase == "Bound" then + elseif obj.status.phase == "Bound" then hs.status = "Healthy" hs.message = obj.status.phase return hs diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index ceb8a226..cd79a041 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -853,8 +853,7 @@ spec: hs.status = "Healthy" hs.message = obj.status.phase return hs - end - if obj.status.phase == "Bound" then + elseif obj.status.phase == "Bound" then hs.status = "Healthy" hs.message = obj.status.phase return hs From d1cf5439f152d595d1710a14da6fbb631a448030 Mon Sep 17 00:00:00 2001 From: Lorenzo Dalrio Date: Thu, 27 Apr 2023 09:38:24 +0200 Subject: [PATCH 0843/1263] Avoid exited containers proliferation When running the `pattern.sh` script multiple times, a lot of podman exited containers will be left on the machine, adding `--rm` parameter to `podman run` makes podman automatically delete the exited containers leaving the machine cleaner. --- scripts/pattern-util.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 82416a2d..149e8af7 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -27,7 +27,7 @@ fi # Do not quote the ${KUBECONF_ENV} below, otherwise we will pass '' to podman # which will be confused -podman run -it \ +podman run -it --rm \ --security-opt label=disable \ ${KUBECONF_ENV} \ -v "${HOME}":"${HOME}" \ From 37c8f3a6cd71eff3083393fa7449c88147c3ab49 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 28 Apr 2023 04:04:20 +0000 Subject: [PATCH 0844/1263] Handling of pre-release builds is too complex for a helm chart Generating the ICSP and allowing insecure registries is best done prior to helm upgrade, and requires VPN access to registry-proxy.engineering.redhat.com --- Makefile | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 94d10489..9ff0886b 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,6 @@ endif # INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:394248 INDEX_IMAGES ?= -INDEX_OPTIONS=$(shell echo $(INDEX_IMAGES) | tr ',' '\n' | awk -F: 'match($$1,"/"){print "--set main.extraParameters."NR".name=clusterGroup.indexImages."NR".image --set main.extraParameters."NR".value="$$1":"$$2}') TARGET_ORIGIN ?= origin # This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL @@ -15,7 +14,7 @@ TARGET_REPO=$(shell git ls-remote --get-url --symref $(TARGET_ORIGIN) | sed -e ' TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) # --set values always take precedence over the contents of -f -HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) $(INDEX_OPTIONS) +HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) ##@ Pattern Common Tasks @@ -35,7 +34,7 @@ show: ## show the starting template without installing it # warnings when the chart gets applied the first time, but the resources were # created first via the VP operator's UI .PHONY: operator-deploy -operator-deploy operator-upgrade: validate-prereq validate-origin ## runs helm install +operator-deploy operator-upgrade: validate-prereq validate-origin load-iibs ## runs helm install @set -e; if ! oc get crds patterns.gitops.hybrid-cloud-patterns.io >/dev/null 2>&1; then \ echo "Running helm:"; \ helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS); \ @@ -55,6 +54,24 @@ uninstall: ## runs helm uninstall load-secrets: ## loads the secrets into the vault common/scripts/vault-utils.sh push_secrets $(NAME) +b.PHONY: load-iibs +load-iibs: + @set -e; if [ x$(INDEX_IMAGES) != x ]; then \ + echo "Allowing insecure registries"; \ + oc patch image.config.openshift.io/cluster --patch '{"spec":{"registrySources":{"insecureRegistries":["registry-proxy-stage.engineering.redhat.com", "registry-proxy.engineering.redhat.com"]}}}' --type=merge; \ + for iib in $(shell echo $(INDEX_IMAGES) | tr ',' '\n'); do \ + echo "Processing $$iib" \ + export iibnum=$$(echo $$iib | sed 's/.*://'); \ + rm -rf $$PWD/manifests-iib-* ;\ + oc adm catalog mirror --manifests-only $$iib $$(dirname $$iib) --insecure; \ + sed -i "s/name: iib$$/name: iib-$$iibnum/" $$PWD/manifests-iib-*/catalogSource.yaml ; \ + echo "Applying $$iib manifests" \ + oc apply -f $$PWD/manifests-iib-*/imageContentSourcePolicy.yaml ; \ + oc apply -f $$PWD/manifests-iib-*/catalogSource.yaml ; \ + done; \ + fi + + ##@ Validation Tasks # We only check the remote ssh git branch's existance if we're not running inside a container From 3c81c4867601f09e488ffc036bd3e8df7b8ad1d9 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 3 May 2023 12:43:42 -0600 Subject: [PATCH 0845/1263] Fixing issues with operator groups --- .../templates/core/operatorgroup.yaml | 25 ++++++++++++++++++- examples/values-example.yaml | 5 ++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index 74febe94..8497086e 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -1,7 +1,23 @@ {{- if not (eq .Values.enabled "plumbing") }} -{{- range .Values.clusterGroup.namespaces }} +{{- range $ns := .Values.clusterGroup.namespaces }} {{- if empty $.Values.clusterGroup.operatorgroupExcludes }} + + + {{- if kindIs "map" $ns }} + {{- range $k, $v := $ns }}{{- /* We loop here even though the map has always just one key */}} + +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: {{ $k }}-operator-group + namespace: {{ $k }} +spec: + targetNamespaces: + - {{ $k }} + {{- end }}{{- /* range $k, $v := $ns */}} + + {{- else if kindIs "string" $ns }} apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -10,8 +26,13 @@ metadata: spec: targetNamespaces: - {{ . }} + {{- end }} {{- /* if kindIs "string" $ns */}} + --- {{- else if not (has . $.Values.clusterGroup.operatorgroupExcludes) }} + + {{- if kindIs "string" $ns }} + apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -21,6 +42,8 @@ spec: targetNamespaces: - {{ . }} --- + + {{- end }} {{- /* if kindIs "string" $ns */}} {{- end }} {{- end }} diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 77584dc8..6f84820c 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -21,6 +21,11 @@ clusterGroup: openshift.io/cluster-monitoring: "true" owner: "namespace owner" - application-ci + - excludes-ci + + operatorgroupExcludes: + - excludes-ci + subscriptions: acm: From fa9f2dc9ba110957789de4ec56cba5da9878d679 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 3 May 2023 12:43:57 -0600 Subject: [PATCH 0846/1263] Adding CI test --- tests/clustergroup-normal.expected.yaml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 541ff594..6ab2d12f 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -22,6 +22,15 @@ metadata: name: application-ci spec: --- +# Source: pattern-clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-example + name: excludes-ci +spec: +--- # Source: pattern-clustergroup/templates/imperative/namespace.yaml apiVersion: v1 kind: Namespace @@ -156,6 +165,9 @@ data: kubernetes.io/os: linux openshift.io/node-selector: "" - application-ci + - excludes-ci + operatorgroupExcludes: + - excludes-ci projects: - datacenter subscriptions: @@ -991,16 +1003,6 @@ spec: # Source: pattern-clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup -metadata: - name: map[open-cluster-management:map[annotations:map[openshift.io/cluster-monitoring:true owner:namespace owner] labels:map[kubernetes.io/os:linux openshift.io/node-selector:]]]-operator-group - namespace: map[open-cluster-management:map[annotations:map[openshift.io/cluster-monitoring:true owner:namespace owner] labels:map[kubernetes.io/os:linux openshift.io/node-selector:]]] -spec: - targetNamespaces: - - map[open-cluster-management:map[annotations:map[openshift.io/cluster-monitoring:true owner:namespace owner] labels:map[kubernetes.io/os:linux openshift.io/node-selector:]]] ---- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup metadata: name: application-ci-operator-group namespace: application-ci From a0e2d9157ececfaa350231a88d0233a1ea078c10 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 3 May 2023 12:55:36 -0600 Subject: [PATCH 0847/1263] Updated operator group template --- clustergroup/templates/core/operatorgroup.yaml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index 8497086e..2a9b75df 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -31,19 +31,30 @@ spec: --- {{- else if not (has . $.Values.clusterGroup.operatorgroupExcludes) }} - {{- if kindIs "string" $ns }} + {{- if kindIs "map" $ns }} + {{- range $k, $v := $ns }}{{- /* We loop here even though the map has always just one key */}} apiVersion: operators.coreos.com/v1 kind: OperatorGroup +metadata: + name: {{ $k }}-operator-group + namespace: {{ $k }} +spec: + targetNamespaces: + - {{ $k }} + {{- end }}{{- /* range $k, $v := $ns */}} + + {{- else if kindIs "string" $ns }} +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup metadata: name: {{ . }}-operator-group namespace: {{ . }} spec: targetNamespaces: - {{ . }} + {{- end }} {{- /* if kindIs "string" $ns */}} --- - - {{- end }} {{- /* if kindIs "string" $ns */}} {{- end }} {{- end }} From 3f6d9b5367b6f4ca15a8b78ce0ca2003f878602d Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 3 May 2023 12:56:12 -0600 Subject: [PATCH 0848/1263] Updating CI issues --- tests/clustergroup-normal.expected.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 6ab2d12f..641a1850 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -1003,6 +1003,16 @@ spec: # Source: pattern-clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup +metadata: + name: open-cluster-management-operator-group + namespace: open-cluster-management +spec: + targetNamespaces: + - open-cluster-management +--- +# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup metadata: name: application-ci-operator-group namespace: application-ci From 656668881fbce2dbbf1739996ebe654c35251663 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 5 May 2023 08:25:35 -0600 Subject: [PATCH 0849/1263] Removed duplicate code for operatorgroup by using multiple conditions --- .../templates/core/operatorgroup.yaml | 37 ++----------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index 2a9b75df..66774fa6 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -1,8 +1,7 @@ {{- if not (eq .Values.enabled "plumbing") }} {{- range $ns := .Values.clusterGroup.namespaces }} -{{- if empty $.Values.clusterGroup.operatorgroupExcludes }} - +{{- if or (empty $.Values.clusterGroup.operatorgroupExcludes) (not (has . $.Values.clusterGroup.operatorgroupExcludes)) }} {{- if kindIs "map" $ns }} {{- range $k, $v := $ns }}{{- /* We loop here even though the map has always just one key */}} @@ -27,35 +26,7 @@ spec: targetNamespaces: - {{ . }} {{- end }} {{- /* if kindIs "string" $ns */}} - --- -{{- else if not (has . $.Values.clusterGroup.operatorgroupExcludes) }} - - {{- if kindIs "map" $ns }} - {{- range $k, $v := $ns }}{{- /* We loop here even though the map has always just one key */}} - -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup -metadata: - name: {{ $k }}-operator-group - namespace: {{ $k }} -spec: - targetNamespaces: - - {{ $k }} - {{- end }}{{- /* range $k, $v := $ns */}} - - {{- else if kindIs "string" $ns }} -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup -metadata: - name: {{ . }}-operator-group - namespace: {{ . }} -spec: - targetNamespaces: - - {{ . }} - {{- end }} {{- /* if kindIs "string" $ns */}} ---- -{{- end }} - -{{- end }} -{{- end }} +{{- end }} {{- /* if or (empty $.Values.clusterGroup.operatorgroupExcludes) (not (has . $.Values.clusterGroup.operatorgroupExcludes)) */}} +{{- end }} {{- /* range $ns := .Values.clusterGroup.namespaces */}} +{{- end }} {{- /* if not (eq .Values.enabled "plumbing") */}} From c7dadbf22a60f77333ac6b4d033cf69e7550fdf0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 15 May 2023 21:18:16 +0200 Subject: [PATCH 0850/1263] Allow overriding the pattern's name This is especially useful when multiple people are working on a pattern an have been using different names: $ make help |grep Pattern: Pattern: multicloud-gitops $ make NAME=foobar help |grep Pattern: Pattern: foobar --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9ff0886b..08bcdde5 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -NAME=$(shell basename "`pwd`") +NAME ?= $(shell basename "`pwd`") ifneq ($(origin TARGET_SITE), undefined) TARGET_SITE_OPT=--set main.clusterGroupName=$(TARGET_SITE) endif @@ -20,6 +20,7 @@ HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set ma .PHONY: help help: ## This help message + @echo "Pattern: $(NAME)" @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^(\s|[a-zA-Z_0-9-])+:.*?##/ { printf " \033[36m%-35s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) # Makefiles in the individual patterns should call these targets explicitly From a6bb0736047d6f06a353938ed02acb167286ea72 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 May 2023 11:10:01 +0200 Subject: [PATCH 0851/1263] Add precise instruction to upgrade the vault subchart --- hashicorp-vault/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hashicorp-vault/README.md b/hashicorp-vault/README.md index dbc0f016..f7687db4 100644 --- a/hashicorp-vault/README.md +++ b/hashicorp-vault/README.md @@ -1,5 +1,12 @@ # VP hashicorp-vault +## Updating the chart + +1. Edit Chart.yaml with the new version +2. In the hashicorp-vault folder, run: `helm dependency update .` +3. Run `./update-helm-dependency.sh` +4. Git add the new chart in `./charts/vault-.tgz` + ## Patches ### Issue 9136 From ea7186e7056826bfe6340a53d294f91840efc68d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 May 2023 11:14:02 +0200 Subject: [PATCH 0852/1263] Upgrade vault-helm to v0.24.1 --- hashicorp-vault/Chart.yaml | 2 +- hashicorp-vault/charts/vault-0.24.0.tgz | Bin 45289 -> 0 bytes hashicorp-vault/charts/vault-0.24.1.tgz | Bin 0 -> 45763 bytes 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 hashicorp-vault/charts/vault-0.24.0.tgz create mode 100644 hashicorp-vault/charts/vault-0.24.1.tgz diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index 6829cc46..6df9f5ec 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -6,5 +6,5 @@ name: hashicorp-vault version: 0.0.1 dependencies: - name: vault - version: "0.24.0" + version: "0.24.1" repository: "https://helm.releases.hashicorp.com" diff --git a/hashicorp-vault/charts/vault-0.24.0.tgz b/hashicorp-vault/charts/vault-0.24.0.tgz deleted file mode 100644 index f226a7f5224cd867aacc3e89268097d7ecb93095..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45289 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 R^m&xe{|_cAFBt$}1ON|csLucZ diff --git a/hashicorp-vault/charts/vault-0.24.1.tgz b/hashicorp-vault/charts/vault-0.24.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..94b385007a0493996ce6d1de00b03472a10e8937 GIT binary patch literal 45763 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 literal 0 HcmV?d00001 From c09126f2104bf86609c818a1a4eae0e32a2ccfa4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 May 2023 11:46:08 +0200 Subject: [PATCH 0853/1263] Add an item to README.md --- hashicorp-vault/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hashicorp-vault/README.md b/hashicorp-vault/README.md index f7687db4..435ba291 100644 --- a/hashicorp-vault/README.md +++ b/hashicorp-vault/README.md @@ -5,7 +5,8 @@ 1. Edit Chart.yaml with the new version 2. In the hashicorp-vault folder, run: `helm dependency update .` 3. Run `./update-helm-dependency.sh` -4. Git add the new chart in `./charts/vault-.tgz` +4. Check that the images in ./values.yaml are the same version as https://github.com/hashicorp/vault-helm/blob/main/values.openshift.yaml +5. Git add the new chart in `./charts/vault-.tgz` ## Patches From 70b778cb11ca1095ed4cf50a7dfdc2af81f75008 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 May 2023 11:49:39 +0200 Subject: [PATCH 0854/1263] Fix up common/ tests --- ...p-vault-industrial-edge-factory.expected.yaml | 16 ++++++++-------- ...icorp-vault-industrial-edge-hub.expected.yaml | 16 ++++++++-------- ...orp-vault-medical-diagnosis-hub.expected.yaml | 16 ++++++++-------- tests/hashicorp-vault-naked.expected.yaml | 16 ++++++++-------- tests/hashicorp-vault-normal.expected.yaml | 16 ++++++++-------- 5 files changed, 40 insertions(+), 40 deletions(-) diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index 24ad0554..8377766b 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -346,7 +346,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index 24ad0554..8377766b 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -346,7 +346,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index 24ad0554..8377766b 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -346,7 +346,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index 6206b720..c8506883 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -346,7 +346,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index 24ad0554..8377766b 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -346,7 +346,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.24.0 + helm.sh/chart: vault-0.24.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm From ee59eee2b9fed7b2c6f47ec5b590413a7f402c9a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 May 2023 12:01:16 +0200 Subject: [PATCH 0855/1263] Fix super linter --- hashicorp-vault/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hashicorp-vault/README.md b/hashicorp-vault/README.md index 435ba291..84065ffd 100644 --- a/hashicorp-vault/README.md +++ b/hashicorp-vault/README.md @@ -5,7 +5,7 @@ 1. Edit Chart.yaml with the new version 2. In the hashicorp-vault folder, run: `helm dependency update .` 3. Run `./update-helm-dependency.sh` -4. Check that the images in ./values.yaml are the same version as https://github.com/hashicorp/vault-helm/blob/main/values.openshift.yaml +4. Check that the images in ./values.yaml are the same version as [upstream](https://github.com/hashicorp/vault-helm/blob/main/values.openshift.yaml) 5. Git add the new chart in `./charts/vault-.tgz` ## Patches From 0e661bf2ffc6c171faf6fce7ce3508430f979248 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 07:51:16 +0200 Subject: [PATCH 0856/1263] Set gitOpsSpec.operatorSource After merging https://github.com/hybrid-cloud-patterns/patterns-operator/commit/235b303fbbe1efdbbacf7d32862f477cf3796c4c it is now effectively possible to pick a different catalogSource for the gitops operator. This is needed in order to allow CI to install the gitops operator from an IIB --- operator-install/templates/pattern.yaml | 1 + operator-install/values.yaml | 1 + tests/operator-install-industrial-edge-factory.expected.yaml | 1 + tests/operator-install-industrial-edge-hub.expected.yaml | 1 + tests/operator-install-medical-diagnosis-hub.expected.yaml | 1 + tests/operator-install-naked.expected.yaml | 1 + tests/operator-install-normal.expected.yaml | 1 + 7 files changed, 7 insertions(+) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index efe9f3ba..d8b3df81 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -10,6 +10,7 @@ spec: targetRevision: {{ .Values.main.git.revision }} gitOpsSpec: operatorChannel: {{ default "gitops-1.8" .Values.main.gitops.channel }} + operatorSource: {{ default "redhat-operators" .Values.main.gitops.operatorSource }} {{- if .Values.main.extraParameters }} extraParameters: {{- range .Values.main.extraParameters }} diff --git a/operator-install/values.yaml b/operator-install/values.yaml index d3ff7fe7..5f7cecc2 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -5,5 +5,6 @@ main: gitops: channel: "gitops-1.8" + operatorSource: redhat-operators clusterGroupName: default diff --git a/tests/operator-install-industrial-edge-factory.expected.yaml b/tests/operator-install-industrial-edge-factory.expected.yaml index 80fd98de..5fc96bf3 100644 --- a/tests/operator-install-industrial-edge-factory.expected.yaml +++ b/tests/operator-install-industrial-edge-factory.expected.yaml @@ -12,6 +12,7 @@ spec: targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 + operatorSource: redhat-operators --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-industrial-edge-hub.expected.yaml b/tests/operator-install-industrial-edge-hub.expected.yaml index 80fd98de..5fc96bf3 100644 --- a/tests/operator-install-industrial-edge-hub.expected.yaml +++ b/tests/operator-install-industrial-edge-hub.expected.yaml @@ -12,6 +12,7 @@ spec: targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 + operatorSource: redhat-operators --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-medical-diagnosis-hub.expected.yaml b/tests/operator-install-medical-diagnosis-hub.expected.yaml index 80fd98de..5fc96bf3 100644 --- a/tests/operator-install-medical-diagnosis-hub.expected.yaml +++ b/tests/operator-install-medical-diagnosis-hub.expected.yaml @@ -12,6 +12,7 @@ spec: targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 + operatorSource: redhat-operators --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index d9a00c03..4c7837fe 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -12,6 +12,7 @@ spec: targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 + operatorSource: redhat-operators --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index 80fd98de..5fc96bf3 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -12,6 +12,7 @@ spec: targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 + operatorSource: redhat-operators --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 From d9db6331d44e7ec52ef5d82d8adb8dde9261a008 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 07:54:52 +0200 Subject: [PATCH 0857/1263] Introduce EXTRA_HELM_OPTS This variable can be set in order to pass additional helm arguments from the the command line. I.e. we can set things without having to tweak values files So it is now possible to run something like the following: ./pattern.sh make install \ EXTRA_HELM_OPTS="--set main.gitops.operatorSource=iib-49232" --- Changes.md | 4 ++++ Makefile | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Changes.md b/Changes.md index b9213c5c..80da55fd 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## May 18, 2023 + +* Introduce a EXTRA_HELM_OPTS env variable that will be passed to the helm invocations + ## April 21, 2023 * Added labels and annotation support to namespaces.yaml template diff --git a/Makefile b/Makefile index 08bcdde5..965841e4 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,10 @@ ifneq ($(origin TARGET_SITE), undefined) TARGET_SITE_OPT=--set main.clusterGroupName=$(TARGET_SITE) endif +# This variable can be set in order to pass additional helm arguments from the +# the command line. I.e. we can set things without having to tweak values files +EXTRA_HELM_OPTS ?= + # INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:394248 INDEX_IMAGES ?= @@ -14,7 +18,7 @@ TARGET_REPO=$(shell git ls-remote --get-url --symref $(TARGET_ORIGIN) | sed -e ' TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) # --set values always take precedence over the contents of -f -HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) +HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) $(EXTRA_HELM_OPTS) ##@ Pattern Common Tasks From 8bfb05da88809c05ddbe34435c70662947cd1df8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 09:01:15 +0200 Subject: [PATCH 0858/1263] Disable var-naming[no-role-prefix] in ansible lint --- .ansible-lint | 1 + 1 file changed, 1 insertion(+) diff --git a/.ansible-lint b/.ansible-lint index f3e5e8a0..a49a5c05 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -6,6 +6,7 @@ skip_list: - template-instead-of-copy # Templated files should use template instead of copy - yaml[line-length] # too long lines - yaml[indentation] # Forcing lists to be always indented by 2 chars is silly IMO + - var-naming[no-role-prefix] # This would be too much churn for very little gain # ansible-lint gh workflow cannot find ansible.cfg hence fails to import vault_utils role exclude_paths: From b4e5967babe25befacd9a08131d783667b5c0396 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 May 2023 19:30:10 +0200 Subject: [PATCH 0859/1263] Add new ansible role to deal with IIBs --- ansible/playbooks/iib-ci/iib-ci.yaml | 8 + ansible/roles/iib_ci/README.md | 42 +++ ansible/roles/iib_ci/defaults/main.yml | 22 ++ ansible/roles/iib_ci/handlers/main.yml | 2 + ansible/roles/iib_ci/meta/main.yml | 29 ++ .../iib_ci/tasks/install-iib-in-cluster.yml | 52 ++++ ansible/roles/iib_ci/tasks/main.yml | 35 +++ .../iib_ci/tasks/mirror-related-images.yml | 251 ++++++++++++++++++ .../iib_ci/tasks/setup-external-registry.yml | 45 ++++ .../iib_ci/tasks/setup-internal-registry.yml | 101 +++++++ ansible/roles/iib_ci/vars/main.yml | 2 + 11 files changed, 589 insertions(+) create mode 100644 ansible/playbooks/iib-ci/iib-ci.yaml create mode 100644 ansible/roles/iib_ci/README.md create mode 100644 ansible/roles/iib_ci/defaults/main.yml create mode 100644 ansible/roles/iib_ci/handlers/main.yml create mode 100644 ansible/roles/iib_ci/meta/main.yml create mode 100644 ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml create mode 100644 ansible/roles/iib_ci/tasks/main.yml create mode 100644 ansible/roles/iib_ci/tasks/mirror-related-images.yml create mode 100644 ansible/roles/iib_ci/tasks/setup-external-registry.yml create mode 100644 ansible/roles/iib_ci/tasks/setup-internal-registry.yml create mode 100644 ansible/roles/iib_ci/vars/main.yml diff --git a/ansible/playbooks/iib-ci/iib-ci.yaml b/ansible/playbooks/iib-ci/iib-ci.yaml new file mode 100644 index 00000000..d7bda53a --- /dev/null +++ b/ansible/playbooks/iib-ci/iib-ci.yaml @@ -0,0 +1,8 @@ +# This playbook invokes the iib_ci role +--- +- name: + hosts: localhost + connection: local + gather_facts: false + roles: + - iib_ci diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md new file mode 100644 index 00000000..7a675169 --- /dev/null +++ b/ansible/roles/iib_ci/README.md @@ -0,0 +1,42 @@ +# IIB Utilities + +A set of ansible plays to fetch an IIB (Image Index Bundle, aka a container created by the operator sdk +that contains a bunch of references to operators that can be installed in an OpenShift cluster) + +Run `make lookup` to see which IIBs are available. + +Typically IIB are pre-release stuff that lives on some internal boxes. What these scripts do is fetch +the IIB internally, mirror it to the registry inside the cluster, parse all the needed images and mirror +those to the internal cluster registry and then set up the registries.conf files on all nodes so +that the images used are the ones pointing to the internal cluster. + +## Usage + +By default the operator to be installed from the IIB is `openshift-gitops-operator`. You can override this through the `OPERATOR` env variable. + +### OCP 4.13 and onwards + +Since 4.13 supports an internal registry that can cope with v2 docker manifests, we +use that. Run `make iib` with the following environment variables set: + +* INDEX_IMAGE=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329 +* KUBEADMINAPI=https://api.mcg-hub.blueprints.rhecoeng.com:6443 +* KUBEADMINPASS="11111-22222-33333-44444" + +### OCP 4.12 and previous versions + +Due to the lack of v2 manifest support on the internal registry, we use an external +registry. Run `make iib` with the following environment variables set: + +* INDEX_IMAGE=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329 +* REGISTRY=quay.io/rhn_support_mbaldess/iib +* REGISTRY_TOKEN=: + + +## Useful commands + +* List all images uploaded to the internal registry: + + oc exec -it -n openshift-image-registry $(oc get pods -n openshift-image-registry -o json | jq -r '.items[].metadata.name | select(. | test("^image-registry-"))' | head -n1) -- bash -c "curl -k -u kubeadmin:$(oc whoami -t) https://localhost:5000/v2/_catalog" + + diff --git a/ansible/roles/iib_ci/defaults/main.yml b/ansible/roles/iib_ci/defaults/main.yml new file mode 100644 index 00000000..c73f4d66 --- /dev/null +++ b/ansible/roles/iib_ci/defaults/main.yml @@ -0,0 +1,22 @@ +rh_internal_registry: registry-proxy.engineering.redhat.com +iib_image: "{{ lookup('env', 'INDEX_IMAGE') }}" +iib: "{{ iib_image.split(':')[1] }}" +iib_local_folder: "/tmp/manifest-{{ iib }}" + + +external_registry: "{{ lookup('env', 'REGISTRY') }}" +external_registry_token: "{{ lookup('env', 'REGISTRY_TOKEN') }}" +external_registry_email: noemail@localhost + +kubeadminpass: "{{ lookup('env', 'KUBEADMINPASS') }}" +# E.g. https://api.mcg-hub.blueprints.rhecoeng.com:6443 +kubeadminapi: "{{ lookup('env', 'KUBEADMINAPI') }}" + +internal_registry_ns: openshift-marketplace +internal_registry_email: noemail@localhost +internal_registry_user: registry-custom-user +internal_registry_pass: "{{ lookup('env', 'INTERNAL_REGISTRY_USER') }}" + +# We can use default(, true) below because OPERATOR is a string and not +# a boolean +operator: "{{ lookup('env', 'OPERATOR') | default('openshift-gitops-operator', true) }}" diff --git a/ansible/roles/iib_ci/handlers/main.yml b/ansible/roles/iib_ci/handlers/main.yml new file mode 100644 index 00000000..a983544d --- /dev/null +++ b/ansible/roles/iib_ci/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for vault_utils diff --git a/ansible/roles/iib_ci/meta/main.yml b/ansible/roles/iib_ci/meta/main.yml new file mode 100644 index 00000000..c9d7005d --- /dev/null +++ b/ansible/roles/iib_ci/meta/main.yml @@ -0,0 +1,29 @@ +galaxy_info: + author: Validated Patterns Team https://github.com/hybrid-cloud-patterns/ + description: Internal module to work with IIBs (Image Index Bundles) + + issue_tracker_url: https://github.com/hybrid-cloud-patterns/common/issues + license: Apache-2.0 + min_ansible_version: "2.1" + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + platforms: + - name: Fedora + versions: + - all + - name: Ubuntu + versions: + - all + - name: Debian + versions: + - all + - name: EL + versions: + - "8" + - "9" + + galaxy_tags: [] + +dependencies: [] diff --git a/ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml b/ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml new file mode 100644 index 00000000..54d875e3 --- /dev/null +++ b/ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml @@ -0,0 +1,52 @@ +- name: Remove manifest folder "{{ iib_local_folder }}" + ansible.builtin.file: + path: "{{ iib_local_folder }}" + state: absent + +- name: Create manifest folder "{{ iib_local_folder }}" + ansible.builtin.file: + path: "{{ iib_local_folder }}" + state: directory + mode: "0755" + +# This generates files in /tmp/manifest-IIB: +# - mapping.txt +# - catalogSource.yaml +# - imageContentSourcePolicy.yaml +- name: Mirror catalog manifests only to "{{ iib_local_folder }}" + ansible.builtin.shell: | + oc adm catalog mirror --insecure --manifests-only --to-manifests=. \ + "{{ iib_image }}" "{{ rh_internal_registry }}/rh-osbs" > catalog.log 2>&1 + args: + chdir: "{{ iib_local_folder }}" + +- name: Mirror IIB to "{{ mirror_iib }}" + ansible.builtin.shell: | + oc image mirror -a "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" \ + "{{ iib_image }}={{ mirror_iib }}" --insecure --keep-manifest-list > iib.log 2>&1 + args: + chdir: "{{ iib_local_folder }}" + register: oc_mirror_result + retries: 3 + delay: 2 + until: oc_mirror_result is not failed + +- name: Template mirrored catalogsource + ansible.builtin.template: + src: ./templates/catalogSource.yaml.j2 + dest: "{{ iib_local_folder }}/mirrored-catalogsource.yaml" + mode: "0644" + +- name: Apply mirrored catalogsource + ansible.builtin.shell: | + oc apply -f "{{ iib_local_folder }}/mirrored-catalogsource.yaml" + +- name: Wait for catalogsource to show up + ansible.builtin.shell: | + oc get -n "{{ internal_registry_ns }}" packagemanifests -l "catalog=iib-{{ iib }}" --field-selector "metadata.name={{ operator }}" \ + -o jsonpath='{.items[0].status.defaultChannel}' + register: oc_catalogsource_result + retries: 30 + delay: 10 + until: oc_catalogsource_result is not failed + changed_when: false diff --git a/ansible/roles/iib_ci/tasks/main.yml b/ansible/roles/iib_ci/tasks/main.yml new file mode 100644 index 00000000..95d762a3 --- /dev/null +++ b/ansible/roles/iib_ci/tasks/main.yml @@ -0,0 +1,35 @@ +- name: Check that IIB env variable is set + ansible.builtin.fail: + msg: "IIB: '{{ iib }}' is not set" + failed_when: + (iib is not defined or iib | length == 0) + +- name: Get cluster version + # E.g. 4.13.0-rc.6 or 4.12.16 + ansible.builtin.shell: | + oc get openshiftcontrollermanager/cluster -o yaml -o jsonpath='{.status.version}' + register: oc_version_raw + changed_when: false + +- name: Is OCP pre OCP 4.13? (aka registry supports v2 manifests) + ansible.builtin.set_fact: + use_internal_registry: "{{ oc_version_raw.stdout is version('4.13', '>=') }}" + +- name: Set up internal registry (OCP >= 4.13) + ansible.builtin.include_tasks: setup-internal-registry.yml + when: use_internal_registry + +- name: Set up external registry (OCP < 4.13) + ansible.builtin.include_tasks: setup-external-registry.yml + when: not use_internal_registry + +- name: Install new IIB in cluster + ansible.builtin.include_tasks: install-iib-in-cluster.yml + +- name: Mirror all related images + ansible.builtin.include_tasks: mirror-related-images.yml + +- name: Remove pullsecrets tempfolder + ansible.builtin.file: + path: "{{ pull_secrets_tempfolder.path }}" + state: absent diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml new file mode 100644 index 00000000..f5e678a3 --- /dev/null +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -0,0 +1,251 @@ +- name: Get default channel in the IIB for "{{ operator }}" + ansible.builtin.shell: | + oc get -n "{{ internal_registry_ns }}" packagemanifests -l "catalog=iib-{{ iib }}" --field-selector "metadata.name={{ operator }}" \ + -o jsonpath='{.items[0].status.defaultChannel}' + register: default_channel_raw + retries: 10 + delay: 10 + until: default_channel_raw is not failed + +- name: Set default channel fact + ansible.builtin.set_fact: + default_channel: "{{ default_channel_raw.stdout }}" + +- name: Get all related images in the IIB for "{{ operator }}" + ansible.builtin.shell: | + oc get packagemanifests -l "catalog=iib-{{ iib }}" --field-selector "metadata.name={{ operator }}" \ + -o jsonpath="{.items[0].status.channels[?(@.name==\"{{ default_channel }}\")].currentCSVDesc.relatedImages}" + register: related_images_raw + +- name: Set related_images fact + ansible.builtin.set_fact: + related_images: "{{ related_images_raw.stdout }}" + +- name: Fetch all operator bundles from mapping.txt + ansible.builtin.shell: + set -o pipefail + grep -e "^registry-proxy.*bundle" "{{ iib_local_folder }}/mapping.txt" | cut -f1 -d= + register: operator_bundles_raw + +# all_images will be a list as follows: +# [ "registry.redhat.io/rh-sso-7/sso75-openshift-rhel8@sha256:d5829e880db4b82a50a4962d61ea148522a93644174931b256d7ad866eadcf40", +# "registry.redhat.io/openshift-gitops-1/gitops-rhel8@sha256:5ff915a399c1cc12d4f932652b410bf7399850934833e755267bdd409f4ce11b", +# "registry.redhat.io/openshift-gitops-1/argocd-rhel8@sha256:81e0574159c6aaabe7125d27782a5e6e5e72383a4a0ba76b44d465f3a3098759", +# "registry.redhat.io/rhel8/redis-6@sha256:53598a6effeb90e4f1b005b2521beffd2fa2b0c52d0e7f2347ee2abd2577cab3", +# "registry.redhat.io/openshift-gitops-1/gitops-rhel8-operator@sha256:efbfb010f24894f715a50832a4b3d2cdc221f283cbbdca05e388850586e9d792", +# "registry.redhat.io/openshift4/ose-haproxy-router@sha256:edf7ce748b703e195220b7bd7b42fa2caa4cdfd96840445e096036a0d85f1ff2", +# "registry.redhat.io/openshift-gitops-1/kam-delivery-rhel8@sha256:10c5a1b6a0858a812117e6fb2b28d37617d9eb83da5e4fb647059ff740a14461", +# "registry.redhat.io/openshift-gitops-1/dex-rhel8@sha256:6a3eaee6a4f8cb9a35363bf4c7f83a7fa2042ae62bdaa700ecd0893dd52276f5", +# "registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-gitops-operator-bundle@sha256:e463314596098a4e774e0dda..." ] +- name: Set all images fact (related images + operator bundles) + ansible.builtin.set_fact: + all_images: "{{ related_images + operator_bundles_raw.stdout_lines }}" + +# A mapping.txt file will have lines like the following. Note how the image to the right of '=' +# does have a shortened hash! : +# registry.redhat.io/openshift-gitops-1/gitops-rhel8@sha256:5ff...=registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-gitops-rhel8:8256cca6 +# registry.redhat.io/openshift4/ose-haproxy-router@sha256:edf..=registry-proxy.engineering.redhat.com/rh-osbs/openshift4-ose-haproxy-router:a636cbea +# +# Now what we are doing here is the following: +# 1. For every image we get from the bundle (contained in all_images var) we check if it exists. If it does great, skip to the next image +# 2. If the image was not found above, we take the corresponding URL on the right hand side of the '=' sign in mapping.txt +# except that we drop the hash that exists on the right hand-side and just use the one we were given with the image. +# If the image is found, great. If not we need to error out because we have no idea where we can fetch it from +- name: Find out which images really exist by consulting mapping.txt + ansible.builtin.shell: | + set -o pipefail + left_sha=$(echo "{{ image }}" | sed -e 's/^.*@//') + right=$(grep "{{ image }}" "{{ iib_local_folder }}/mapping.txt" | cut -f2 -d=) + right_base=$(echo $right | sed -e 's/:.*$//' -e 's/@.*$//') + right_log=$(echo "${right_base}@${left_sha}" | sed -e 's/\//-/g') + if skopeo inspect --authfile "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" --no-tags docker://"{{ image }}" &> /tmp/skopeo-"{{ image | regex_replace('/', '-') }}".log; then + echo "{{ image }}" + elif skopeo inspect --authfile "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" --no-tags docker://"${right_base}@${left_sha}" &> "/tmp/skopeo-${right_log}.log"; then + echo "${right_base}@${left_sha}" + else + echo "ERROR: both {{ image }} and echo ${right_base}@${left_sha} could not be found" + exit 1 + fi + register: all_existing_images + with_items: "{{ all_images }}" + loop_control: + loop_var: image + +# The dictionary below will be in the following form: +# { +# "registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-gitops-operator-bundle@sha256:e463314596098a4e774e0ddaed0009bfdad4d79b664e28fef219c796679ee6a0": { +# "source": "registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-gitops-operator-bundle@sha256:e463314596098a4e774e0ddaed0009bfdad4d79b664e28fef219c796679ee6a0" +# }, +# "registry.redhat.io/openshift-gitops-1/argocd-rhel8@sha256:81e0574159c6aaabe7125d27782a5e6e5e72383a4a0ba76b44d465f3a3098759": { +# "source": "registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-argocd-rhel8@sha256:81e0574159c6aaabe7125d27782a5e6e5e72383a4a0ba76b44d465f3a3098759" +# }, +# "registry.redhat.io/openshift-gitops-1/dex-rhel8@sha256:6a3eaee6a4f8cb9a35363bf4c7f83a7fa2042ae62bdaa700ecd0893dd52276f5": { +# "source": "registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-dex-rhel8@sha256:6a3eaee6a4f8cb9a35363bf4c7f83a7fa2042ae62bdaa700ecd0893dd52276f5" +# }, +# "registry.redhat.io/openshift-gitops-1/gitops-rhel8-operator@sha256:efbfb010f24894f715a50832a4b3d2cdc221f283cbbdca05e388850586e9d792": { +# "source": "registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-gitops-rhel8-operator@sha256:efbfb010f24894f715a50832a4b3d2cdc221f283cbbdca05e388850586e9d792" +# }, +# "registry.redhat.io/openshift-gitops-1/gitops-rhel8@sha256:5ff915a399c1cc12d4f932652b410bf7399850934833e755267bdd409f4ce11b": { +# "source": "registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-gitops-rhel8@sha256:5ff915a399c1cc12d4f932652b410bf7399850934833e755267bdd409f4ce11b" +# }, +# "registry.redhat.io/openshift-gitops-1/kam-delivery-rhel8@sha256:10c5a1b6a0858a812117e6fb2b28d37617d9eb83da5e4fb647059ff740a14461": { +# "source": "registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-kam-delivery-rhel8@sha256:10c5a1b6a0858a812117e6fb2b28d37617d9eb83da5e4fb647059ff740a14461" +# }, +# "registry.redhat.io/openshift4/ose-haproxy-router@sha256:edf7ce748b703e195220b7bd7b42fa2caa4cdfd96840445e096036a0d85f1ff2": { +# "source": "registry.redhat.io/openshift4/ose-haproxy-router@sha256:edf7ce748b703e195220b7bd7b42fa2caa4cdfd96840445e096036a0d85f1ff2" +# }, +# "registry.redhat.io/rh-sso-7/sso75-openshift-rhel8@sha256:d5829e880db4b82a50a4962d61ea148522a93644174931b256d7ad866eadcf40": { +# "source": "registry.redhat.io/rh-sso-7/sso75-openshift-rhel8@sha256:d5829e880db4b82a50a4962d61ea148522a93644174931b256d7ad866eadcf40" +# }, +# "registry.redhat.io/rhel8/redis-6@sha256:53598a6effeb90e4f1b005b2521beffd2fa2b0c52d0e7f2347ee2abd2577cab3": { +# "source": "registry.redhat.io/rhel8/redis-6@sha256:53598a6effeb90e4f1b005b2521beffd2fa2b0c52d0e7f2347ee2abd2577cab3" +# } +# } +- name: Create dict with full image name+sha -> url where we will fetch it from + ansible.builtin.set_fact: + image_urls: "{{ image_urls | default({}) | combine({item: {'source': all_existing_images.results[counter].stdout, + 'source_nosha': all_existing_images.results[counter].stdout | regex_replace('@.*$', '')}}, recursive=true) }}" + loop: "{{ all_images }}" + loop_control: + index_var: counter + +- name: Create dict with full image name+sha -> mirror destination (OCP >= 4.13) + ansible.builtin.set_fact: + image_urls: "{{ image_urls | default({}) | combine({item: + {'mirrordest': mirror_dest + item | basename, + 'mirrordest_nosha': (mirror_dest + item | basename) | regex_replace('@.*$', ''), + 'mirrordest_tag': iib}}, recursive=true) }}" + loop: "{{ all_images }}" + when: use_internal_registry + +- name: Create dict with full image name+sha -> mirror destination (OCP < 4.13) + ansible.builtin.set_fact: + image_urls: "{{ image_urls | default({}) | combine({item: + {'mirrordest': mirror_dest + '@' + item | basename | regex_replace('^.*@', ''), + 'mirrordest_nosha': mirror_dest, + 'mirrordest_tag': 'tag-' + item | basename | regex_replace('^.*@sha256:', '')}}, recursive=true) }}" + loop: "{{ all_images }}" + when: not use_internal_registry + +- name: Create dict with full image name+sha -> image key without sha + ansible.builtin.set_fact: + image_urls: "{{ image_urls | default({}) | combine({item: {'image_nosha': item | regex_replace('@.*$', '')}}, recursive=true) }}" + loop: "{{ all_images }}" + +# At this point the dictionary looks as follows: +# "registry.redhat.io/rhel8/redis-6@sha256:53598a6effeb90e4f1b005b2521beffd2fa2b0c52d0e7f2347ee2abd2577cab3": { +# "mirrordest": "default-route-openshift-image-registry.apps.mcg-hub.blueprints.rhecoeng.com/openshift-marketplace/redis-6@sha256:535... +# "mirrordest_nosha": "default-route-openshift-image-registry.apps.mcg-hub.blueprints.rhecoeng.com/openshift-marketplace/redis-6", +# "source": "registry.redhat.io/rhel8/redis-6@sha256:53598a6effeb90e4f1b005b2521beffd2fa2b0c52d0e7f2347ee2abd2577cab3", +# "source_nosha": "registry.redhat.io/rhel8/redis-6" +# } +- name: Print dict with full images + ansible.builtin.debug: + msg: "{{ image_urls }}" + +# OCP 4.13 uses the new fangled "ImageDigestMirrorSet", older OCPs use "ImageContentSourcePolicy" +- name: Template out imageMirror.yaml (OCP >= 4.13) + ansible.builtin.template: + src: ./templates/imageDigestMirror.yaml.j2 + dest: "{{ iib_local_folder }}/imageMirror.yaml" + mode: "0644" + when: use_internal_registry + +- name: Template out imageMirror.yaml (OCP < 4.13) + ansible.builtin.template: + src: ./templates/imageContentSourcePolicy.yaml.j2 + dest: "{{ iib_local_folder }}/imageMirror.yaml" + mode: "0644" + when: not use_internal_registry + +- name: Template out mirror.map + ansible.builtin.template: + src: ./templates/mirror.map.j2 + dest: "{{ iib_local_folder }}/mirror.map" + mode: "0644" + +# NOTE(bandini): mirror.map *must* have a tag (we use the IIB number) on the image on the right side +# otherwise, the image will be uplaoded and will exist in S3 but it won't exist in the registry's catalog!! +- name: Mirror all the needed images + ansible.builtin.shell: + set -o pipefail + oc image mirror -a "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" -f mirror.map --insecure --keep-manifest-list 2>&1 | tee -a image-mirror.log + args: + chdir: "{{ iib_local_folder }}" + retries: 5 + delay: 2 + register: oc_mirror + until: oc_mirror is not failed + +- name: Fetch MCP observedGeneration worker + ansible.builtin.shell: + oc get mcp/worker -o jsonpath='{.status.observedGeneration}' + register: worker_observed_generation_raw + +- name: Fetch MCP readyMachineCount worker + ansible.builtin.shell: + oc get mcp/worker -o jsonpath='{.status.readyMachineCount}' + register: worker_ready_machinecount_raw + +- name: Fetch MCP observedGeneration master + ansible.builtin.shell: + oc get mcp/master -o jsonpath='{.status.observedGeneration}' + register: master_observed_generation_raw + +- name: Fetch MCP readyMachineCount master + ansible.builtin.shell: + oc get mcp/master -o jsonpath='{.status.readyMachineCount}' + register: master_ready_machinecount_raw + +- name: Will the imageMirror trigger any changes + ansible.builtin.command: + oc diff -f "{{ iib_local_folder }}/imageMirror.yaml" + failed_when: false + register: oc_mirror_diff + +# We only run this piece if there is an actual change in the mirror digest for images +# cannot use 'is failed' as that is always false when setting failed_when: false above +- name: Apply imageMirror and wait for MCP to complete + when: oc_mirror_diff.rc != 0 + block: + - name: Apply imageMirror + ansible.builtin.command: + oc apply -f "{{ iib_local_folder }}/imageMirror.yaml" + + # NOTE(bandini): The reason to not fail on these two observedGeneration waiting + # tasks, is to make this idempotent: If the 'oc apply' above does *not* trigger + # any changes, the observed generation tasks will just timeout. And then we still + # wait to make sure that the readyworker count is correct. + - name: Wait for MCP new observedGeneration worker + ansible.builtin.shell: + oc get mcp/worker -o jsonpath='{.status.observedGeneration}' + register: worker_current_observed_generation_raw + retries: 10 + delay: 20 + until: worker_current_observed_generation_raw.stdout != worker_observed_generation_raw.stdout + failed_when: false + + - name: Wait for MCP new observedGeneration master + ansible.builtin.shell: + oc get mcp/master -o jsonpath='{.status.observedGeneration}' + register: master_current_observed_generation_raw + retries: 10 + delay: 20 + until: master_current_observed_generation_raw.stdout != master_observed_generation_raw.stdout + failed_when: false + + - name: Wait for MCP readyMachineCount to be the same as before applying the digest (worker) + ansible.builtin.shell: + oc get mcp/worker -o jsonpath='{.status.readyMachineCount}' + register: worker_current_ready_machinecount_raw + retries: 30 + delay: 10 + until: worker_current_ready_machinecount_raw.stdout == worker_ready_machinecount_raw.stdout + + - name: Wait for MCP readyMachineCount to be the same as before applying the digest (master) + ansible.builtin.shell: + oc get mcp/master -o jsonpath='{.status.readyMachineCount}' + register: master_current_ready_machinecount_raw + retries: 30 + delay: 10 + until: master_current_ready_machinecount_raw.stdout == master_ready_machinecount_raw.stdout diff --git a/ansible/roles/iib_ci/tasks/setup-external-registry.yml b/ansible/roles/iib_ci/tasks/setup-external-registry.yml new file mode 100644 index 00000000..a9a9b10a --- /dev/null +++ b/ansible/roles/iib_ci/tasks/setup-external-registry.yml @@ -0,0 +1,45 @@ +- name: Check that we can push to the external registry + ansible.builtin.fail: + msg: "REGISTRY: '{{ external_registry }}' and REGISTRY_TOKEN: '{{ external_registry_token }}'. Both need to be set" + failed_when: > + (external_registry is not defined or external_registry | length == 0) or + (external_registry_token is not defined or external_registry_token | length == 0) + +- name: Get current cluster pull secrets + ansible.builtin.command: + oc extract secret/pull-secret -n openshift-config --to=- + register: pull_secrets_raw + +- name: Add external registry to pull secrets and set auth fact + ansible.builtin.set_fact: + pull_secrets_new: "{{ pull_secrets_raw.stdout | from_json }}" + external_registry_auth: "{{ external_registry_token | b64encode }}" + +- name: Add local registry to pull secrets + ansible.builtin.set_fact: + pull_secrets: "{{ pull_secrets_new | combine({'auths': {external_registry.split('/')[0]: {'email': external_registry_email, 'auth': external_registry_auth}}}, recursive=true) }}" + +- name: Get a tempfile for the pull secrets + ansible.builtin.tempfile: + state: directory + register: pull_secrets_tempfolder + +- name: Store pull secrets in tempfile + ansible.builtin.copy: + dest: "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" + content: "{{ pull_secrets | to_nice_json }}" + mode: "0644" + +# We cannot store the logins back in the cluster, because quay.io would be overwritten and not have +# access to the images openshift needs. See: +# https://github.com/moby/moby/issues/37569 +# - name: Update pull-secret in the cluster +# ansible.builtin.shell: | +# oc set data secret/pull-secret -n openshift-config --from-file="{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" +- name: Set Mirror URL fact for external mirror IIB + ansible.builtin.set_fact: + mirror_iib: "{{ external_registry }}" + +- name: Set Mirror URL fact for external mirror + ansible.builtin.set_fact: + mirror_dest: "{{ external_registry }}" diff --git a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml new file mode 100644 index 00000000..b9b7b3d5 --- /dev/null +++ b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml @@ -0,0 +1,101 @@ +- name: Check that KUBEADMINAPI, KUBEADMINPASS + ansible.builtin.fail: + msg: "KUBEADMINAPI: '{{ kubeadminapi }}', KUBEADMINPASS: '{{ kubeadminpass }}'. Both need to be set" + failed_when: > + (kubeadminapi is not defined or kubeadminapi | length == 0) or + (kubeadminpass is not defined or kubeadminpass | length == 0) + +- name: Login via kubeadmin + ansible.builtin.command: | + oc login -u kubeadmin -p "{{ kubeadminpass }}" "{{ kubeadminapi }}" + +- name: Get kubeadmin token + ansible.builtin.command: | + oc whoami -t + register: oc_whoami_raw + +- name: Set kubeadmin token + ansible.builtin.set_fact: + kubeadmin_token: "{{ oc_whoami_raw.stdout }}" + +- name: Expose internal registry route + ansible.builtin.shell: | + oc patch configs.imageregistry.operator.openshift.io/cluster --patch '{"spec":{"defaultRoute":true}}' --type=merge + +- name: Fetch internal registry route value + ansible.builtin.command: + oc registry info --public=true + register: registry_route_raw + retries: 20 + delay: 10 + until: + - registry_route_raw is not failed + - registry_route_raw.stdout | length > 0 + +- name: Set route fact + ansible.builtin.set_fact: + registry_route: "{{ registry_route_raw.stdout }}" + +- name: Set registry allowedRegistries + ansible.builtin.shell: > + oc patch image.config.openshift.io/cluster --patch "{\"spec\":{\"registrySources\":{\"allowedRegistries\":[ \"quay.io\", \"registry.redhat.io\", + \"registry-proxy.engineering.redhat.com\", \"image-registry.openshift-image-registry.svc:5000\", \"{{ registry_route }}\"]}}}" --type=merge + +- name: Set registry insecureRegistries + ansible.builtin.shell: > + oc patch image.config.openshift.io/cluster --patch "{\"spec\":{\"registrySources\":{\"insecureRegistries\":[ \"registry-proxy.engineering.redhat.com\", + \"image-registry.openshift-image-registry.svc:5000\", \"{{ registry_route }}\"]}}}" --type=merge + +- name: Get current cluster pull secrets + ansible.builtin.command: + oc extract secret/pull-secret -n openshift-config --to=- + register: pull_secrets_raw + +- name: Add local registry to pull secrets and set auth fact + ansible.builtin.set_fact: + pull_secrets_new: "{{ pull_secrets_raw.stdout | from_json }}" + internal_registry_auth: "{{ ('kubeadmin:' + kubeadmin_token) | b64encode }}" + +- name: Add local registry to pull secrets + ansible.builtin.set_fact: + pull_secrets: "{{ pull_secrets_new | combine({'auths': {registry_route: {'email': internal_registry_email, 'auth': internal_registry_auth}}}, recursive=true) }}" + +- name: Get a tempfile for the pull secrets + ansible.builtin.tempfile: + state: directory + register: pull_secrets_tempfolder + +- name: Store pull secrets in tempfile + ansible.builtin.copy: + dest: "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" + content: "{{ pull_secrets | to_nice_json }}" + mode: "0644" + +- name: Update pull-secret in the cluster + ansible.builtin.shell: | + oc set data secret/pull-secret -n openshift-config --from-file="{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" + +- name: Before proceeding here we need to make sure that the MCPs have all settled + ansible.builtin.shell: | + if [ $(oc get mcp/master -o jsonpath='{.status.readyMachineCount}') != $(oc get mcp/master -o jsonpath='{.status.machineCount}') ]; then + exit 1 + fi + if [ $(oc get mcp/worker -o jsonpath='{.status.readyMachineCount}') != $(oc get mcp/worker -o jsonpath='{.status.machineCount}') ]; then + exit 1 + fi + retries: 30 + delay: 20 + register: mcp_ready + until: mcp_ready is not failed + +- name: Login the internal registry with podman + ansible.builtin.command: + podman login --tls-verify=false --username unused --password "{{ kubeadmin_token }}" "https://{{ registry_route }}" + +- name: Set Mirror URL fact for internal mirror IIB + ansible.builtin.set_fact: + mirror_iib: "{{ registry_route }}/{{ internal_registry_ns }}/iib" + +- name: Set Mirror URL fact for internal mirror + ansible.builtin.set_fact: + mirror_dest: "{{ registry_route }}/{{ internal_registry_ns }}/" diff --git a/ansible/roles/iib_ci/vars/main.yml b/ansible/roles/iib_ci/vars/main.yml new file mode 100644 index 00000000..56894088 --- /dev/null +++ b/ansible/roles/iib_ci/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for iib_ci From 5cbc2c0746ede7d0e2782df25374e4437a0269f2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 May 2023 19:31:34 +0200 Subject: [PATCH 0860/1263] Simplify load-iib target --- Makefile | 21 +++++---------------- ansible/roles/iib_ci/tasks/main.yml | 2 +- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 965841e4..a55043d0 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ show: ## show the starting template without installing it # warnings when the chart gets applied the first time, but the resources were # created first via the VP operator's UI .PHONY: operator-deploy -operator-deploy operator-upgrade: validate-prereq validate-origin load-iibs ## runs helm install +operator-deploy operator-upgrade: validate-prereq validate-origin load-iib ## runs helm install @set -e; if ! oc get crds patterns.gitops.hybrid-cloud-patterns.io >/dev/null 2>&1; then \ echo "Running helm:"; \ helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS); \ @@ -59,21 +59,10 @@ uninstall: ## runs helm uninstall load-secrets: ## loads the secrets into the vault common/scripts/vault-utils.sh push_secrets $(NAME) -b.PHONY: load-iibs -load-iibs: - @set -e; if [ x$(INDEX_IMAGES) != x ]; then \ - echo "Allowing insecure registries"; \ - oc patch image.config.openshift.io/cluster --patch '{"spec":{"registrySources":{"insecureRegistries":["registry-proxy-stage.engineering.redhat.com", "registry-proxy.engineering.redhat.com"]}}}' --type=merge; \ - for iib in $(shell echo $(INDEX_IMAGES) | tr ',' '\n'); do \ - echo "Processing $$iib" \ - export iibnum=$$(echo $$iib | sed 's/.*://'); \ - rm -rf $$PWD/manifests-iib-* ;\ - oc adm catalog mirror --manifests-only $$iib $$(dirname $$iib) --insecure; \ - sed -i "s/name: iib$$/name: iib-$$iibnum/" $$PWD/manifests-iib-*/catalogSource.yaml ; \ - echo "Applying $$iib manifests" \ - oc apply -f $$PWD/manifests-iib-*/imageContentSourcePolicy.yaml ; \ - oc apply -f $$PWD/manifests-iib-*/catalogSource.yaml ; \ - done; \ +.PHONY: load-iib +load-iib: + @set -e; if [ x$(INDEX_IMAGE) != x ]; then \ + ansible-playbook common/ansible/playbooks/iib-ci/iib-ci.yaml; \ fi diff --git a/ansible/roles/iib_ci/tasks/main.yml b/ansible/roles/iib_ci/tasks/main.yml index 95d762a3..375c7f77 100644 --- a/ansible/roles/iib_ci/tasks/main.yml +++ b/ansible/roles/iib_ci/tasks/main.yml @@ -1,4 +1,4 @@ -- name: Check that IIB env variable is set +- name: Check that INDEX_IMAGE env variable is set ansible.builtin.fail: msg: "IIB: '{{ iib }}' is not set" failed_when: From bb97c58d5cfeda53bbdbfcd23683b4e031aef573 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 May 2023 19:48:31 +0200 Subject: [PATCH 0861/1263] Add templates folder --- .../iib_ci/templates/catalogSource.yaml.j2 | 9 +++++++++ .../iib_ci/templates/htpasswd-oauth.yaml | 14 ++++++++++++++ .../imageContentSourcePolicy.yaml.j2 | 19 +++++++++++++++++++ .../templates/imageDigestMirror.yaml.j2 | 18 ++++++++++++++++++ ansible/roles/iib_ci/templates/mirror.map.j2 | 3 +++ 5 files changed, 63 insertions(+) create mode 100644 ansible/roles/iib_ci/templates/catalogSource.yaml.j2 create mode 100644 ansible/roles/iib_ci/templates/htpasswd-oauth.yaml create mode 100644 ansible/roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 create mode 100644 ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 create mode 100644 ansible/roles/iib_ci/templates/mirror.map.j2 diff --git a/ansible/roles/iib_ci/templates/catalogSource.yaml.j2 b/ansible/roles/iib_ci/templates/catalogSource.yaml.j2 new file mode 100644 index 00000000..99087603 --- /dev/null +++ b/ansible/roles/iib_ci/templates/catalogSource.yaml.j2 @@ -0,0 +1,9 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: CatalogSource +metadata: + name: iib-{{ iib }} + namespace: {{ internal_registry_ns }} +spec: + image: {{ mirror_iib }}:{{ iib }} + sourceType: grpc + displayName: IIB {{ iib }} diff --git a/ansible/roles/iib_ci/templates/htpasswd-oauth.yaml b/ansible/roles/iib_ci/templates/htpasswd-oauth.yaml new file mode 100644 index 00000000..8fc41821 --- /dev/null +++ b/ansible/roles/iib_ci/templates/htpasswd-oauth.yaml @@ -0,0 +1,14 @@ +apiVersion: config.openshift.io/v1 +kind: OAuth +metadata: + name: cluster +spec: + identityProviders: + - name: my_htpasswd_provider + mappingMethod: claim + type: HTPasswd + challenge: true + login: true + htpasswd: + fileData: + name: htpass-secret diff --git a/ansible/roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 b/ansible/roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 new file mode 100644 index 00000000..d0f417ec --- /dev/null +++ b/ansible/roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 @@ -0,0 +1,19 @@ +--- +apiVersion: operator.openshift.io/v1alpha1 +kind: ImageContentSourcePolicy +metadata: + labels: + operators.openshift.org/catalog: "true" + name: iib-{{ iib }} +spec: + repositoryDigestMirrors: +{% for item in image_urls.values() %} + - mirrors: + - {{ item.mirrordest_nosha }} + source: {{ item.source_nosha }} + mirrorSourcePolicy: NeverContactSource + - mirrors: + - {{ item.mirrordest_nosha }} + source: {{ item.image_nosha }} + mirrorSourcePolicy: NeverContactSource +{% endfor %} diff --git a/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 b/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 new file mode 100644 index 00000000..d23ab9f2 --- /dev/null +++ b/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 @@ -0,0 +1,18 @@ +apiVersion: config.openshift.io/v1 +kind: ImageDigestMirrorSet +metadata: + labels: + operators.openshift.org/catalog: "true" + name: iib-{{ iib }} +spec: + imageDigestMirrors: +{% for item in image_urls.values() %} + - mirrors: + - {{ item.mirrordest_nosha }} + source: {{ item.source_nosha }} + mirrorSourcePolicy: NeverContactSource + - mirrors: + - {{ item.mirrordest_nosha }} + source: {{ item.image_nosha }} + mirrorSourcePolicy: NeverContactSource +{% endfor %} diff --git a/ansible/roles/iib_ci/templates/mirror.map.j2 b/ansible/roles/iib_ci/templates/mirror.map.j2 new file mode 100644 index 00000000..ecef721c --- /dev/null +++ b/ansible/roles/iib_ci/templates/mirror.map.j2 @@ -0,0 +1,3 @@ +{% for item in image_urls.values() %} +{{ item.source }}={{ item.mirrordest_nosha }}:{{ item.mirrordest_tag }} +{% endfor %} From 65dda37f76a4d0ae51798cab4dd99b7eb0367fe7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 May 2023 20:03:42 +0200 Subject: [PATCH 0862/1263] Fix a couple of linting warnings --- .ansible-lint | 1 + ansible/playbooks/iib-ci/iib-ci.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.ansible-lint b/.ansible-lint index a49a5c05..325abec8 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -7,6 +7,7 @@ skip_list: - yaml[line-length] # too long lines - yaml[indentation] # Forcing lists to be always indented by 2 chars is silly IMO - var-naming[no-role-prefix] # This would be too much churn for very little gain + - no-changed-when # ansible-lint gh workflow cannot find ansible.cfg hence fails to import vault_utils role exclude_paths: diff --git a/ansible/playbooks/iib-ci/iib-ci.yaml b/ansible/playbooks/iib-ci/iib-ci.yaml index d7bda53a..dc6e45cb 100644 --- a/ansible/playbooks/iib-ci/iib-ci.yaml +++ b/ansible/playbooks/iib-ci/iib-ci.yaml @@ -1,6 +1,6 @@ # This playbook invokes the iib_ci role --- -- name: +- name: IIB CI playbook hosts: localhost connection: local gather_facts: false From 4dfeecb725cc6db31b0b4cfac76c355c6b095f12 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 May 2023 20:08:04 +0200 Subject: [PATCH 0863/1263] Fix some super-linter complaints --- ansible/roles/iib_ci/README.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 7a675169..d0a6a8fc 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -5,7 +5,7 @@ that contains a bunch of references to operators that can be installed in an Ope Run `make lookup` to see which IIBs are available. -Typically IIB are pre-release stuff that lives on some internal boxes. What these scripts do is fetch +Typically IIB are prerelease stuff that lives on some internal boxes. What these scripts do is fetch the IIB internally, mirror it to the registry inside the cluster, parse all the needed images and mirror those to the internal cluster registry and then set up the registries.conf files on all nodes so that the images used are the ones pointing to the internal cluster. @@ -19,24 +19,23 @@ By default the operator to be installed from the IIB is `openshift-gitops-operat Since 4.13 supports an internal registry that can cope with v2 docker manifests, we use that. Run `make iib` with the following environment variables set: -* INDEX_IMAGE=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329 -* KUBEADMINAPI=https://api.mcg-hub.blueprints.rhecoeng.com:6443 -* KUBEADMINPASS="11111-22222-33333-44444" +* `INDEX_IMAGE=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329` +* `KUBEADMINAPI=https://api.mcg-hub.blueprints.rhecoeng.com:6443` +* `KUBEADMINPASS="11111-22222-33333-44444"` ### OCP 4.12 and previous versions Due to the lack of v2 manifest support on the internal registry, we use an external registry. Run `make iib` with the following environment variables set: -* INDEX_IMAGE=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329 -* REGISTRY=quay.io/rhn_support_mbaldess/iib -* REGISTRY_TOKEN=: - +* `INDEX_IMAGE=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329` +* `REGISTRY=quay.io/rhn_support_mbaldess/iib` +* `REGISTRY_TOKEN=:` ## Useful commands * List all images uploaded to the internal registry: - oc exec -it -n openshift-image-registry $(oc get pods -n openshift-image-registry -o json | jq -r '.items[].metadata.name | select(. | test("^image-registry-"))' | head -n1) -- bash -c "curl -k -u kubeadmin:$(oc whoami -t) https://localhost:5000/v2/_catalog" - - +```sh +oc exec -it -n openshift-image-registry $(oc get pods -n openshift-image-registry -o json | jq -r '.items[].metadata.name | select(. | test("^image-registry-"))' | head -n1) -- bash -c "curl -k -u kubeadmin:$(oc whoami -t) https://localhost:5000/v2/_catalog" +``` From 33dfdb35a116cb9a1ea3fadc9b91abbbd85670ad Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 May 2023 20:16:47 +0200 Subject: [PATCH 0864/1263] Skip the iib-ci playbook --- .ansible-lint | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ansible-lint b/.ansible-lint index 325abec8..e2120ac0 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -12,5 +12,5 @@ skip_list: # ansible-lint gh workflow cannot find ansible.cfg hence fails to import vault_utils role exclude_paths: - ./ansible/playbooks/vault/vault.yaml + - ./ansible/playbooks/iib-ci/iib-ci.yaml - ./ansible/roles/vault_utils/tests/test.yml - From 93fd8c8ea26b73cd048c8aaafe439127d6af0e7d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 May 2023 20:20:42 +0200 Subject: [PATCH 0865/1263] Drop var-naming[no-role-prefix] linter --- .ansible-lint | 1 + 1 file changed, 1 insertion(+) diff --git a/.ansible-lint b/.ansible-lint index e2120ac0..353222eb 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -8,6 +8,7 @@ skip_list: - yaml[indentation] # Forcing lists to be always indented by 2 chars is silly IMO - var-naming[no-role-prefix] # This would be too much churn for very little gain - no-changed-when + - var-naming[no-role-prefix] # There are too many changes now and it would be too risky # ansible-lint gh workflow cannot find ansible.cfg hence fails to import vault_utils role exclude_paths: From 6263afeca2ed59a8c9ed99c912b6eed49151f495 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 May 2023 14:55:18 +0200 Subject: [PATCH 0866/1263] Allow for multiple images when calling load-iib --- Makefile | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index a55043d0..eda0fff9 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,9 @@ endif EXTRA_HELM_OPTS ?= # INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:394248 -INDEX_IMAGES ?= +# or +# INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:394248,registry-proxy.engineering.redhat.com/rh-osbs/iib:394249 +INDEX_IMAGES ?= TARGET_ORIGIN ?= origin # This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL @@ -39,7 +41,7 @@ show: ## show the starting template without installing it # warnings when the chart gets applied the first time, but the resources were # created first via the VP operator's UI .PHONY: operator-deploy -operator-deploy operator-upgrade: validate-prereq validate-origin load-iib ## runs helm install +operator-deploy operator-upgrade: validate-prereq validate-origin ## runs helm install @set -e; if ! oc get crds patterns.gitops.hybrid-cloud-patterns.io >/dev/null 2>&1; then \ echo "Running helm:"; \ helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS); \ @@ -61,8 +63,11 @@ load-secrets: ## loads the secrets into the vault .PHONY: load-iib load-iib: - @set -e; if [ x$(INDEX_IMAGE) != x ]; then \ - ansible-playbook common/ansible/playbooks/iib-ci/iib-ci.yaml; \ + @set -e; if [ x$(INDEX_IMAGES) != x ]; then \ + for iib in $(shell echo $(INDEX_IMAGES) | tr ',' '\n'); do \ + export INDEX_IMAGE="${iib}"; \ + ansible-playbook common/ansible/playbooks/iib-ci/iib-ci.yaml; \ + done; \ fi From c776ed0c0a3b2c6ce2044e7afe5f377bd9c33c79 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 May 2023 15:29:27 +0200 Subject: [PATCH 0867/1263] Add help for load-iib --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index eda0fff9..16197baa 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ load-secrets: ## loads the secrets into the vault common/scripts/vault-utils.sh push_secrets $(NAME) .PHONY: load-iib -load-iib: +load-iib: ## CI target to install Index Image Bundles @set -e; if [ x$(INDEX_IMAGES) != x ]; then \ for iib in $(shell echo $(INDEX_IMAGES) | tr ',' '\n'); do \ export INDEX_IMAGE="${iib}"; \ From b6b4836a496648d4c11a4cf29520a8b9ad07b29f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 May 2023 15:51:01 +0200 Subject: [PATCH 0868/1263] Output index_image in make --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 16197baa..d91c5524 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,7 @@ load-iib: ## CI target to install Index Image Bundles @set -e; if [ x$(INDEX_IMAGES) != x ]; then \ for iib in $(shell echo $(INDEX_IMAGES) | tr ',' '\n'); do \ export INDEX_IMAGE="${iib}"; \ + @echo "Working on IIB: ${INDEX_IMAGE}"; \ ansible-playbook common/ansible/playbooks/iib-ci/iib-ci.yaml; \ done; \ fi From 51b9fb4cee5963d5dd47b004475f0277ee4be8fe Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 May 2023 15:51:35 +0200 Subject: [PATCH 0869/1263] Output index_image in make (2) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d91c5524..8e967bda 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,7 @@ load-iib: ## CI target to install Index Image Bundles @set -e; if [ x$(INDEX_IMAGES) != x ]; then \ for iib in $(shell echo $(INDEX_IMAGES) | tr ',' '\n'); do \ export INDEX_IMAGE="${iib}"; \ - @echo "Working on IIB: ${INDEX_IMAGE}"; \ + echo "Working on IIB: ${INDEX_IMAGE}"; \ ansible-playbook common/ansible/playbooks/iib-ci/iib-ci.yaml; \ done; \ fi From 7567a736c982b9c8a77d031091b29c734b510f21 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 May 2023 15:53:55 +0200 Subject: [PATCH 0870/1263] Set facts later in the playbook not in defaults/ --- ansible/roles/iib_ci/defaults/main.yml | 3 --- ansible/roles/iib_ci/tasks/main.yml | 12 ++++++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/ansible/roles/iib_ci/defaults/main.yml b/ansible/roles/iib_ci/defaults/main.yml index c73f4d66..af735209 100644 --- a/ansible/roles/iib_ci/defaults/main.yml +++ b/ansible/roles/iib_ci/defaults/main.yml @@ -1,8 +1,5 @@ rh_internal_registry: registry-proxy.engineering.redhat.com iib_image: "{{ lookup('env', 'INDEX_IMAGE') }}" -iib: "{{ iib_image.split(':')[1] }}" -iib_local_folder: "/tmp/manifest-{{ iib }}" - external_registry: "{{ lookup('env', 'REGISTRY') }}" external_registry_token: "{{ lookup('env', 'REGISTRY_TOKEN') }}" diff --git a/ansible/roles/iib_ci/tasks/main.yml b/ansible/roles/iib_ci/tasks/main.yml index 375c7f77..ba6eb7c8 100644 --- a/ansible/roles/iib_ci/tasks/main.yml +++ b/ansible/roles/iib_ci/tasks/main.yml @@ -1,8 +1,16 @@ - name: Check that INDEX_IMAGE env variable is set ansible.builtin.fail: - msg: "IIB: '{{ iib }}' is not set" + msg: "INDEX_IMAGE: '{{ iib_image }}' is not set" failed_when: - (iib is not defined or iib | length == 0) + (iib_image is not defined or iib_image | length == 0) + +- name: Set IIB fact + ansible.builtin.set_fact: + iib: "{{ iib_image.split(':')[1] }}" + +- name: Set IIB local folder fact + ansible.builtin.set_fact: + iib_local_folder: "/tmp/manifest-{{ iib }}" - name: Get cluster version # E.g. 4.13.0-rc.6 or 4.12.16 From ff6b73ffabb04456bb73370d571a876f31e2b53d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 May 2023 16:02:37 +0200 Subject: [PATCH 0871/1263] Fix how we export vars in make load-iib --- Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 8e967bda..d6835807 100644 --- a/Makefile +++ b/Makefile @@ -65,9 +65,7 @@ load-secrets: ## loads the secrets into the vault load-iib: ## CI target to install Index Image Bundles @set -e; if [ x$(INDEX_IMAGES) != x ]; then \ for iib in $(shell echo $(INDEX_IMAGES) | tr ',' '\n'); do \ - export INDEX_IMAGE="${iib}"; \ - echo "Working on IIB: ${INDEX_IMAGE}"; \ - ansible-playbook common/ansible/playbooks/iib-ci/iib-ci.yaml; \ + INDEX_IMAGE="${iib}" ansible-playbook common/ansible/playbooks/iib-ci/iib-ci.yaml; \ done; \ fi From 7dfa5cf0e2d794ca63e89c79beaecdebf5c4b05b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 May 2023 16:05:49 +0200 Subject: [PATCH 0872/1263] Fix how we export vars in make load-iib (2) --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d6835807..7159835f 100644 --- a/Makefile +++ b/Makefile @@ -64,8 +64,8 @@ load-secrets: ## loads the secrets into the vault .PHONY: load-iib load-iib: ## CI target to install Index Image Bundles @set -e; if [ x$(INDEX_IMAGES) != x ]; then \ - for iib in $(shell echo $(INDEX_IMAGES) | tr ',' '\n'); do \ - INDEX_IMAGE="${iib}" ansible-playbook common/ansible/playbooks/iib-ci/iib-ci.yaml; \ + for IIB in $(shell echo $(INDEX_IMAGES) | tr ',' '\n'); do \ + INDEX_IMAGE="$${IIB}" ansible-playbook common/ansible/playbooks/iib-ci/iib-ci.yaml; \ done; \ fi From 5e51a638382849ba11093e6e3a664aa528b54528 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 May 2023 16:20:54 +0200 Subject: [PATCH 0873/1263] Use machineCount to register the number of nodes that need to be ready --- .../roles/iib_ci/tasks/mirror-related-images.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml index f5e678a3..bf4a51be 100644 --- a/ansible/roles/iib_ci/tasks/mirror-related-images.yml +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -182,20 +182,20 @@ oc get mcp/worker -o jsonpath='{.status.observedGeneration}' register: worker_observed_generation_raw -- name: Fetch MCP readyMachineCount worker +- name: Fetch MCP machineCount worker ansible.builtin.shell: - oc get mcp/worker -o jsonpath='{.status.readyMachineCount}' - register: worker_ready_machinecount_raw + oc get mcp/worker -o jsonpath='{.status.machineCount}' + register: worker_machinecount_raw - name: Fetch MCP observedGeneration master ansible.builtin.shell: oc get mcp/master -o jsonpath='{.status.observedGeneration}' register: master_observed_generation_raw -- name: Fetch MCP readyMachineCount master +- name: Fetch MCP machineCount master ansible.builtin.shell: - oc get mcp/master -o jsonpath='{.status.readyMachineCount}' - register: master_ready_machinecount_raw + oc get mcp/master -o jsonpath='{.status.machineCount}' + register: master_machinecount_raw - name: Will the imageMirror trigger any changes ansible.builtin.command: @@ -240,7 +240,7 @@ register: worker_current_ready_machinecount_raw retries: 30 delay: 10 - until: worker_current_ready_machinecount_raw.stdout == worker_ready_machinecount_raw.stdout + until: worker_current_ready_machinecount_raw.stdout == worker_machinecount_raw.stdout - name: Wait for MCP readyMachineCount to be the same as before applying the digest (master) ansible.builtin.shell: @@ -248,4 +248,4 @@ register: master_current_ready_machinecount_raw retries: 30 delay: 10 - until: master_current_ready_machinecount_raw.stdout == master_ready_machinecount_raw.stdout + until: master_current_ready_machinecount_raw.stdout == master_machinecount_raw.stdout From 1b80705df69925f92725e33ae670ff303c0326cb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 May 2023 16:40:25 +0200 Subject: [PATCH 0874/1263] Add helpful debug messages --- ansible/roles/iib_ci/tasks/mirror-related-images.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml index bf4a51be..caff1e4a 100644 --- a/ansible/roles/iib_ci/tasks/mirror-related-images.yml +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -27,6 +27,10 @@ grep -e "^registry-proxy.*bundle" "{{ iib_local_folder }}/mapping.txt" | cut -f1 -d= register: operator_bundles_raw +- name: Print found operator bundle + ansible.builtin.debug: + msg: "{{ operator_bundles_raw.stdout_lines }}" + # all_images will be a list as follows: # [ "registry.redhat.io/rh-sso-7/sso75-openshift-rhel8@sha256:d5829e880db4b82a50a4962d61ea148522a93644174931b256d7ad866eadcf40", # "registry.redhat.io/openshift-gitops-1/gitops-rhel8@sha256:5ff915a399c1cc12d4f932652b410bf7399850934833e755267bdd409f4ce11b", @@ -41,6 +45,10 @@ ansible.builtin.set_fact: all_images: "{{ related_images + operator_bundles_raw.stdout_lines }}" +- name: Print all_images + ansible.builtin.debug: + msg: "{{ all_images }}" + # A mapping.txt file will have lines like the following. Note how the image to the right of '=' # does have a shortened hash! : # registry.redhat.io/openshift-gitops-1/gitops-rhel8@sha256:5ff...=registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-gitops-rhel8:8256cca6 From aef9717769e45e3dc063a71748cf41993b841784 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 May 2023 16:43:13 +0200 Subject: [PATCH 0875/1263] Add | on shell now that we call pipefail --- ansible/roles/iib_ci/tasks/mirror-related-images.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml index caff1e4a..d40df9d3 100644 --- a/ansible/roles/iib_ci/tasks/mirror-related-images.yml +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -22,7 +22,7 @@ related_images: "{{ related_images_raw.stdout }}" - name: Fetch all operator bundles from mapping.txt - ansible.builtin.shell: + ansible.builtin.shell: | set -o pipefail grep -e "^registry-proxy.*bundle" "{{ iib_local_folder }}/mapping.txt" | cut -f1 -d= register: operator_bundles_raw @@ -175,7 +175,7 @@ # NOTE(bandini): mirror.map *must* have a tag (we use the IIB number) on the image on the right side # otherwise, the image will be uplaoded and will exist in S3 but it won't exist in the registry's catalog!! - name: Mirror all the needed images - ansible.builtin.shell: + ansible.builtin.shell: | set -o pipefail oc image mirror -a "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" -f mirror.map --insecure --keep-manifest-list 2>&1 | tee -a image-mirror.log args: From 14209b3ece0b92de274492c32a080c0a958825f6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 May 2023 17:21:28 +0200 Subject: [PATCH 0876/1263] Test dropping nevercontact source --- .../roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 | 4 ++-- ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 b/ansible/roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 index d0f417ec..7fd8cdfc 100644 --- a/ansible/roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 +++ b/ansible/roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 @@ -11,9 +11,9 @@ spec: - mirrors: - {{ item.mirrordest_nosha }} source: {{ item.source_nosha }} - mirrorSourcePolicy: NeverContactSource + #mirrorSourcePolicy: NeverContactSource - mirrors: - {{ item.mirrordest_nosha }} source: {{ item.image_nosha }} - mirrorSourcePolicy: NeverContactSource + #mirrorSourcePolicy: NeverContactSource {% endfor %} diff --git a/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 b/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 index d23ab9f2..2d8ce892 100644 --- a/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 +++ b/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 @@ -10,9 +10,9 @@ spec: - mirrors: - {{ item.mirrordest_nosha }} source: {{ item.source_nosha }} - mirrorSourcePolicy: NeverContactSource + #mirrorSourcePolicy: NeverContactSource - mirrors: - {{ item.mirrordest_nosha }} source: {{ item.image_nosha }} - mirrorSourcePolicy: NeverContactSource + #mirrorSourcePolicy: NeverContactSource {% endfor %} From 7d173480102dcea4b24f3129cd34aa86678cbc0a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 08:19:05 +0200 Subject: [PATCH 0877/1263] Skip insecure tls when logging in --- ansible/roles/iib_ci/tasks/setup-internal-registry.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml index b9b7b3d5..dd69d5c7 100644 --- a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml +++ b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml @@ -7,7 +7,7 @@ - name: Login via kubeadmin ansible.builtin.command: | - oc login -u kubeadmin -p "{{ kubeadminpass }}" "{{ kubeadminapi }}" + oc login -u kubeadmin -p "{{ kubeadminpass }}" "{{ kubeadminapi }}" --insecure-skip-tls-verify=true - name: Get kubeadmin token ansible.builtin.command: | From dbdbc8c283215975f95d97d2c3796f4c59941e49 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 08:55:52 +0200 Subject: [PATCH 0878/1263] Also allow gchr.io --- ansible/roles/iib_ci/tasks/setup-internal-registry.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml index dd69d5c7..eea3f543 100644 --- a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml +++ b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml @@ -38,7 +38,7 @@ - name: Set registry allowedRegistries ansible.builtin.shell: > - oc patch image.config.openshift.io/cluster --patch "{\"spec\":{\"registrySources\":{\"allowedRegistries\":[ \"quay.io\", \"registry.redhat.io\", + oc patch image.config.openshift.io/cluster --patch "{\"spec\":{\"registrySources\":{\"allowedRegistries\":[ \"gchr.io\", \"quay.io\", \"registry.redhat.io\", \"registry-proxy.engineering.redhat.com\", \"image-registry.openshift-image-registry.svc:5000\", \"{{ registry_route }}\"]}}}" --type=merge - name: Set registry insecureRegistries From 0355fa423ac77c12e1bac95f81b2fa48b3e15048 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 10:22:52 +0200 Subject: [PATCH 0879/1263] Revert "Test dropping nevercontact source" This reverts commit d8746a37fce2663018f52203c892f00b825e32a7. --- .../roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 | 4 ++-- ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 b/ansible/roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 index 7fd8cdfc..d0f417ec 100644 --- a/ansible/roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 +++ b/ansible/roles/iib_ci/templates/imageContentSourcePolicy.yaml.j2 @@ -11,9 +11,9 @@ spec: - mirrors: - {{ item.mirrordest_nosha }} source: {{ item.source_nosha }} - #mirrorSourcePolicy: NeverContactSource + mirrorSourcePolicy: NeverContactSource - mirrors: - {{ item.mirrordest_nosha }} source: {{ item.image_nosha }} - #mirrorSourcePolicy: NeverContactSource + mirrorSourcePolicy: NeverContactSource {% endfor %} diff --git a/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 b/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 index 2d8ce892..d23ab9f2 100644 --- a/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 +++ b/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 @@ -10,9 +10,9 @@ spec: - mirrors: - {{ item.mirrordest_nosha }} source: {{ item.source_nosha }} - #mirrorSourcePolicy: NeverContactSource + mirrorSourcePolicy: NeverContactSource - mirrors: - {{ item.mirrordest_nosha }} source: {{ item.image_nosha }} - #mirrorSourcePolicy: NeverContactSource + mirrorSourcePolicy: NeverContactSource {% endfor %} From 47855e09db572357de7acc9fd4900872ba72efaa Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 11:38:05 +0200 Subject: [PATCH 0880/1263] Fix typo --- ansible/roles/iib_ci/tasks/setup-internal-registry.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml index eea3f543..b5b0304a 100644 --- a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml +++ b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml @@ -38,7 +38,7 @@ - name: Set registry allowedRegistries ansible.builtin.shell: > - oc patch image.config.openshift.io/cluster --patch "{\"spec\":{\"registrySources\":{\"allowedRegistries\":[ \"gchr.io\", \"quay.io\", \"registry.redhat.io\", + oc patch image.config.openshift.io/cluster --patch "{\"spec\":{\"registrySources\":{\"allowedRegistries\":[ \"gcr.io\", \"quay.io\", \"registry.redhat.io\", \"registry-proxy.engineering.redhat.com\", \"image-registry.openshift-image-registry.svc:5000\", \"{{ registry_route }}\"]}}}" --type=merge - name: Set registry insecureRegistries From 49f018a8ffd7d41bbf7be00da3042fd7d44d013d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 11:41:06 +0200 Subject: [PATCH 0881/1263] Clarify instructions in the README file --- ansible/roles/iib_ci/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index d0a6a8fc..541f11cc 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -13,6 +13,20 @@ that the images used are the ones pointing to the internal cluster. ## Usage By default the operator to be installed from the IIB is `openshift-gitops-operator`. You can override this through the `OPERATOR` env variable. +For example, to install openshift-gitops from an IIB on OCP 4.13 you would do the following: + +```sh +export KUBECONFIG=/tmp/foo/kubeconfig +export INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:iib-492329 +export KUBEADMINAPI=https://api.mcg-hub.blueprints.rhecoeng.com:6443 +export KUBEADMINPASS="11111-22222-33333-44444" +# This will push the IIB and all the needed images for the default openshift-gitops-operator into the cluster +make load-iib +# This will install the pattern using the gitops operator from the IIB +make EXTRA_HELM_OPTS="--set main.gitops.operatorSource=iib-492329 --set main.gitops.channel=latest" install 2>&1 | tee /tmp/install.log +``` + +*Note*: This needs VP operator version >= 0.0.14 ### OCP 4.13 and onwards From d1dc09fa5b7d5bd8922dbf11f2d1a247bd9d4c6d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 12:18:11 +0200 Subject: [PATCH 0882/1263] Automate the channel example --- ansible/roles/iib_ci/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 541f11cc..3d640798 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -23,7 +23,8 @@ export KUBEADMINPASS="11111-22222-33333-44444" # This will push the IIB and all the needed images for the default openshift-gitops-operator into the cluster make load-iib # This will install the pattern using the gitops operator from the IIB -make EXTRA_HELM_OPTS="--set main.gitops.operatorSource=iib-492329 --set main.gitops.channel=latest" install 2>&1 | tee /tmp/install.log +export CHANNEL=$(oc get -n openshift-marketplace packagemanifests -l "catalog=iib-492329" --field-selector 'metadata.name=openshift-gitops-operator' -o jsonpath='{.items[0].status.defaultChannel}') +make EXTRA_HELM_OPTS="--set main.gitops.operatorSource=iib-492329 --set main.gitops.channel=${CHANNEL}" install 2>&1 | tee /tmp/install.log ``` *Note*: This needs VP operator version >= 0.0.14 From 729232acddc98f106472f09bf575c0fa459738a1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 12:23:52 +0200 Subject: [PATCH 0883/1263] Find out KUBEADMINAPI programmatically --- ansible/roles/iib_ci/README.md | 2 -- ansible/roles/iib_ci/defaults/main.yml | 2 -- .../iib_ci/tasks/setup-internal-registry.yml | 17 ++++++++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 3d640798..a916964e 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -18,7 +18,6 @@ For example, to install openshift-gitops from an IIB on OCP 4.13 you would do th ```sh export KUBECONFIG=/tmp/foo/kubeconfig export INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:iib-492329 -export KUBEADMINAPI=https://api.mcg-hub.blueprints.rhecoeng.com:6443 export KUBEADMINPASS="11111-22222-33333-44444" # This will push the IIB and all the needed images for the default openshift-gitops-operator into the cluster make load-iib @@ -35,7 +34,6 @@ Since 4.13 supports an internal registry that can cope with v2 docker manifests, use that. Run `make iib` with the following environment variables set: * `INDEX_IMAGE=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329` -* `KUBEADMINAPI=https://api.mcg-hub.blueprints.rhecoeng.com:6443` * `KUBEADMINPASS="11111-22222-33333-44444"` ### OCP 4.12 and previous versions diff --git a/ansible/roles/iib_ci/defaults/main.yml b/ansible/roles/iib_ci/defaults/main.yml index af735209..7605dba5 100644 --- a/ansible/roles/iib_ci/defaults/main.yml +++ b/ansible/roles/iib_ci/defaults/main.yml @@ -6,8 +6,6 @@ external_registry_token: "{{ lookup('env', 'REGISTRY_TOKEN') }}" external_registry_email: noemail@localhost kubeadminpass: "{{ lookup('env', 'KUBEADMINPASS') }}" -# E.g. https://api.mcg-hub.blueprints.rhecoeng.com:6443 -kubeadminapi: "{{ lookup('env', 'KUBEADMINAPI') }}" internal_registry_ns: openshift-marketplace internal_registry_email: noemail@localhost diff --git a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml index b5b0304a..c46a9215 100644 --- a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml +++ b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml @@ -1,9 +1,16 @@ -- name: Check that KUBEADMINAPI, KUBEADMINPASS +- name: Check KUBEADMINPASS is set ansible.builtin.fail: - msg: "KUBEADMINAPI: '{{ kubeadminapi }}', KUBEADMINPASS: '{{ kubeadminpass }}'. Both need to be set" - failed_when: > - (kubeadminapi is not defined or kubeadminapi | length == 0) or - (kubeadminpass is not defined or kubeadminpass | length == 0) + msg: "KUBEADMINPASS: '{{ kubeadminpass }}' is not set" + failed_when: kubeadminpass is not defined or kubeadminpass | length == 0 + +- name: Get kubeadmin api endpoint + ansible.builtin.shell: + oc whoami --show-server=true + register: kubeadminapi_raw + +- name: Set kubeadminapi fact + ansible.builtin.set_fact: + kubeadminapi: "{{ kubeadminapi_raw.stdout }}" - name: Login via kubeadmin ansible.builtin.command: | From d4eb91432b486b53254984a89d82f5439810240b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 12:29:03 +0200 Subject: [PATCH 0884/1263] Use command instead of shell --- ansible/roles/iib_ci/tasks/setup-internal-registry.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml index c46a9215..218a6099 100644 --- a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml +++ b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml @@ -4,7 +4,7 @@ failed_when: kubeadminpass is not defined or kubeadminpass | length == 0 - name: Get kubeadmin api endpoint - ansible.builtin.shell: + ansible.builtin.command: oc whoami --show-server=true register: kubeadminapi_raw From f73f75daae7a0f5608100724ee9f07ac323de3bb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 16:28:56 +0200 Subject: [PATCH 0885/1263] Do not grep for operator bundle unless it is the gitops operator --- ansible/roles/iib_ci/README.md | 14 +++++++++++++- .../iib_ci/tasks/mirror-related-images.yml | 17 +++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index a916964e..fbeaefc6 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -23,7 +23,19 @@ export KUBEADMINPASS="11111-22222-33333-44444" make load-iib # This will install the pattern using the gitops operator from the IIB export CHANNEL=$(oc get -n openshift-marketplace packagemanifests -l "catalog=iib-492329" --field-selector 'metadata.name=openshift-gitops-operator' -o jsonpath='{.items[0].status.defaultChannel}') -make EXTRA_HELM_OPTS="--set main.gitops.operatorSource=iib-492329 --set main.gitops.channel=${CHANNEL}" install 2>&1 | tee /tmp/install.log +make EXTRA_HELM_OPTS="--set main.gitops.operatorSource=iib-492329 --set main.gitops.channel=${CHANNEL}" install +``` + +To install ACM from an IIB we can run the following: +```sh +export OPERATOR=advanced-cluster-management +export KUBECONFIG=/tmp/foo/kubeconfig +export INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:iib-499623 +export KUBEADMINPASS="11111-22222-33333-44444" + +make load-iib +export CHANNEL=$(oc get -n openshift-marketplace packagemanifests -l "catalog=iib-499623" --field-selector 'metadata.name=advanced-cluster-management' -o jsonpath='{.items[0].status.defaultChannel}') +make EXTRA_HELM_OPTS="--set clusterGroup.subscriptions.acm.source=iib-499623 --set clusterGroup.subscriptions.acm.channel=${CHANNEL}" install ``` *Note*: This needs VP operator version >= 0.0.14 diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml index d40df9d3..574b73fa 100644 --- a/ansible/roles/iib_ci/tasks/mirror-related-images.yml +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -21,15 +21,20 @@ ansible.builtin.set_fact: related_images: "{{ related_images_raw.stdout }}" -- name: Fetch all operator bundles from mapping.txt +- name: Add the gitops operator bundle from mapping.txt when the operator is openshift-gitops-operator ansible.builtin.shell: | set -o pipefail - grep -e "^registry-proxy.*bundle" "{{ iib_local_folder }}/mapping.txt" | cut -f1 -d= - register: operator_bundles_raw + grep -e "^registry-proxy.*gitops-operator-bundle" "{{ iib_local_folder }}/mapping.txt" | cut -f1 -d= + register: gitops_operator_bundles_raw + when: operator == "openshift-gitops-operator" -- name: Print found operator bundle +- name: Set gitops operator array + ansible.builtin.set_fact: + gitops_operator_bundle: "{{ gitops_operator_bundles_raw.stdout_lines | default([]) }}" + +- name: Print found gitops operator bundle ansible.builtin.debug: - msg: "{{ operator_bundles_raw.stdout_lines }}" + msg: "{{ gitops_operator_bundle }}" # all_images will be a list as follows: # [ "registry.redhat.io/rh-sso-7/sso75-openshift-rhel8@sha256:d5829e880db4b82a50a4962d61ea148522a93644174931b256d7ad866eadcf40", @@ -43,7 +48,7 @@ # "registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-gitops-operator-bundle@sha256:e463314596098a4e774e0dda..." ] - name: Set all images fact (related images + operator bundles) ansible.builtin.set_fact: - all_images: "{{ related_images + operator_bundles_raw.stdout_lines }}" + all_images: "{{ related_images + gitops_operator_bundle }}" - name: Print all_images ansible.builtin.debug: From b660f9316ea17524b2f72c280e0e7f6237215ca3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 18:18:56 +0200 Subject: [PATCH 0886/1263] Also whitelist ghcr.io --- ansible/roles/iib_ci/README.md | 2 +- ansible/roles/iib_ci/tasks/setup-internal-registry.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index fbeaefc6..35087753 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -35,7 +35,7 @@ export KUBEADMINPASS="11111-22222-33333-44444" make load-iib export CHANNEL=$(oc get -n openshift-marketplace packagemanifests -l "catalog=iib-499623" --field-selector 'metadata.name=advanced-cluster-management' -o jsonpath='{.items[0].status.defaultChannel}') -make EXTRA_HELM_OPTS="--set clusterGroup.subscriptions.acm.source=iib-499623 --set clusterGroup.subscriptions.acm.channel=${CHANNEL}" install +make EXTRA_HELM_OPTS="--set main.extraParameters[0].name=clusterGroup.subscriptions.acm.source --set main.extraParameters[0].value=iib-499623 --set main.extraParameters[1].name=clusterGroup.subscriptions.acm.channel --set main.extraParameters[1].value=${CHANNEL}" install 2>&1 | tee /tmp/acm-install.log ``` *Note*: This needs VP operator version >= 0.0.14 diff --git a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml index 218a6099..ed4993f1 100644 --- a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml +++ b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml @@ -45,7 +45,7 @@ - name: Set registry allowedRegistries ansible.builtin.shell: > - oc patch image.config.openshift.io/cluster --patch "{\"spec\":{\"registrySources\":{\"allowedRegistries\":[ \"gcr.io\", \"quay.io\", \"registry.redhat.io\", + oc patch image.config.openshift.io/cluster --patch "{\"spec\":{\"registrySources\":{\"allowedRegistries\":[ \"ghcr.io\", \"gcr.io\", \"quay.io\", \"registry.redhat.io\", \"registry-proxy.engineering.redhat.com\", \"image-registry.openshift-image-registry.svc:5000\", \"{{ registry_route }}\"]}}}" --type=merge - name: Set registry insecureRegistries From 41dc747995ea49e3ebde79e114e670422a20cd47 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 21:02:06 +0200 Subject: [PATCH 0887/1263] Fetch the operator bundle itself in a more robust way It seems that the operator bundle image itself is nowhere to be found inside any OCP cluster object (it's not in packagemanifests nor catalogsource). Resorting to parsing the IIB via opm alpha commands to fetch the exact image. --- .../iib_ci/tasks/mirror-related-images.yml | 60 +++++++++++++++---- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml index 574b73fa..b8de3999 100644 --- a/ansible/roles/iib_ci/tasks/mirror-related-images.yml +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -21,20 +21,60 @@ ansible.builtin.set_fact: related_images: "{{ related_images_raw.stdout }}" -- name: Add the gitops operator bundle from mapping.txt when the operator is openshift-gitops-operator +# NOTE(bandini) +# The following code is here to fund out what the operator bundle image is and to make +# sure it is on the internal registry. +# This is all potentially hacky, but so far I could not find a single place in the cluster +# where the olm.bundle image is available. The info is in there in the IIB, but it certainly +# is not in any package manifest nor catalogsource. This is why we resort to invoking opm +# alpha commands inside the IIB image locally +- name: Pull the IIB locally + ansible.builtin.command: + podman pull "{{ iib_image }}" + +# $ opm alpha list channels /configs advanced-cluster-management +# PACKAGE CHANNEL HEAD +# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.4 +# advanced-cluster-management release-2.8 advanced-cluster-management.v2.8.0-130 +- name: Read the operator bundle from the default channel ansible.builtin.shell: | set -o pipefail - grep -e "^registry-proxy.*gitops-operator-bundle" "{{ iib_local_folder }}/mapping.txt" | cut -f1 -d= - register: gitops_operator_bundles_raw - when: operator == "openshift-gitops-operator" + podman run -it --rm "{{ iib_image }}" alpha list channels /configs "{{ operator }}" | grep --word-regexp "{{ default_channel }}" | awk '{ print $3 }' + register: bundle_channel_raw -- name: Set gitops operator array +- name: Set bundle fact ansible.builtin.set_fact: - gitops_operator_bundle: "{{ gitops_operator_bundles_raw.stdout_lines | default([]) }}" + bundle_channel: "{{ bundle_channel_raw.stdout }}" -- name: Print found gitops operator bundle - ansible.builtin.debug: - msg: "{{ gitops_operator_bundle }}" +- name: Fail if bundle_channel is empty + ansible.builtin.fail: + msg: "Failed to find bundle from channel: {{ bundle_channel_raw }}" + when: > + (bundle_channel is not defined) or (bundle_channel | length == 0) + +# $ opm alpha list bundles /configs advanced-cluster-management +# PACKAGE CHANNEL BUNDLE REPLACES SKIPS SKIP RANGE IMAGE +# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.0 >=2.6.0 <2.7.0 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:f63d0a9a0e3dc9d86e84279c50e9c613d8430e71a3821d418e168250ca3b747c +# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.1 advanced-cluster-management.v2.7.0 >=2.6.0 <2.7.1 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:a81a574f2f22d37681c44fe0c3b958074408705415de333de54d120145537533 +# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.2 advanced-cluster-management.v2.7.1 >=2.6.0 <2.7.2 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:8a2c758689eaebe6a287315ca18fd9122f323e195ea3410db005b6a449060fad +# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.3 advanced-cluster-management.v2.7.2 >=2.6.0 <2.7.3 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:208f4d9473a923817c102bb7e5f138d3e1e8ed3057a23a220ffa8fe9c0c27128 +# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.4 advanced-cluster-management.v2.7.3 >=2.6.0 <2.7.4 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:75b6438e08800b2e3608aeb01c1c0a68810108d9905fff35916afd21e6d32685 +# advanced-cluster-management release-2.8 advanced-cluster-management.v2.8.0-130 >=2.7.0 <2.8.0-130 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:6c385aa69256cdd964ae9e79e52ce52e1048391f0557af59843326c4ebe9bec0 +- name: Get bundle image + ansible.builtin.shell: | + set -o pipefail + podman run -it --rm "{{ iib_image }}" alpha list bundles /configs "{{ operator }}" | grep --word-regexp "{{ bundle_channel }}" | awk '{ print $NF }' + register: bundle_image_raw + +- name: Set bundle image fact + ansible.builtin.set_fact: + bundle_image: "{{ bundle_image_raw.stdout }}" + +- name: Fail if bundle_image is empty + ansible.builtin.fail: + msg: "Failed to find bundle image: {{ bundle_image_raw }}" + when: > + (bundle_image is not defined) or (bundle_image | length == 0) # all_images will be a list as follows: # [ "registry.redhat.io/rh-sso-7/sso75-openshift-rhel8@sha256:d5829e880db4b82a50a4962d61ea148522a93644174931b256d7ad866eadcf40", @@ -48,7 +88,7 @@ # "registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-gitops-operator-bundle@sha256:e463314596098a4e774e0dda..." ] - name: Set all images fact (related images + operator bundles) ansible.builtin.set_fact: - all_images: "{{ related_images + gitops_operator_bundle }}" + all_images: "{{ related_images + [bundle_image] }}" - name: Print all_images ansible.builtin.debug: From 2a941fb54e2e9ef04fef075f4be1037d3e906207 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 May 2023 21:40:58 +0200 Subject: [PATCH 0888/1263] Add more mirrors --- ansible/roles/iib_ci/tasks/setup-internal-registry.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml index ed4993f1..db30627f 100644 --- a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml +++ b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml @@ -45,7 +45,7 @@ - name: Set registry allowedRegistries ansible.builtin.shell: > - oc patch image.config.openshift.io/cluster --patch "{\"spec\":{\"registrySources\":{\"allowedRegistries\":[ \"ghcr.io\", \"gcr.io\", \"quay.io\", \"registry.redhat.io\", + oc patch image.config.openshift.io/cluster --patch "{\"spec\":{\"registrySources\":{\"allowedRegistries\":[ \"registry.stage.redhat.io\", \"registry.access.redhat.com\", \"registry.connect.redhat.com\", \"ghcr.io\", \"gcr.io\", \"quay.io\", \"registry.redhat.io\", \"registry-proxy.engineering.redhat.com\", \"image-registry.openshift-image-registry.svc:5000\", \"{{ registry_route }}\"]}}}" --type=merge - name: Set registry insecureRegistries From a4e232b7ab4d3b2236271356f7c9ee18da087f18 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 19 May 2023 15:36:52 +0200 Subject: [PATCH 0889/1263] Some more work to support MCE --- .../roles/iib_ci/fetch-operator-images.yml | 95 ++++++++++++++++++ .../iib_ci/tasks/mirror-related-images.yml | 96 ++----------------- 2 files changed, 104 insertions(+), 87 deletions(-) create mode 100644 ansible/roles/iib_ci/fetch-operator-images.yml diff --git a/ansible/roles/iib_ci/fetch-operator-images.yml b/ansible/roles/iib_ci/fetch-operator-images.yml new file mode 100644 index 00000000..c0cd8fce --- /dev/null +++ b/ansible/roles/iib_ci/fetch-operator-images.yml @@ -0,0 +1,95 @@ +# This task fetches all the images given an operator name +# the operator name is defined in the variable "item". This +# set of tasks is to be included in a loop that goes over the +# needed operators +- name: Get default channel in the IIB for "{{ item }}" + ansible.builtin.shell: | + oc get -n "{{ internal_registry_ns }}" packagemanifests -l "catalog=iib-{{ iib }}" --field-selector "metadata.name={{ item }}" \ + -o jsonpath='{.items[0].status.defaultChannel}' + register: default_channel_raw + retries: 10 + delay: 10 + until: default_channel_raw is not failed + +- name: Set default channel fact + ansible.builtin.set_fact: + default_channel: "{{ default_channel_raw.stdout }}" + +- name: Get all related images in the IIB for "{{ operator }}" + ansible.builtin.shell: | + oc get packagemanifests -l "catalog=iib-{{ iib }}" --field-selector "metadata.name={{ item }}" \ + -o jsonpath="{.items[0].status.channels[?(@.name==\"{{ default_channel }}\")].currentCSVDesc.relatedImages}" + register: related_images_raw + +- name: Set related_images fact + ansible.builtin.set_fact: + related_images: "{{ related_images_raw.stdout }}" + +# NOTE(bandini) +# The following code is here to fund out what the operator bundle image is and to make +# sure it is on the internal registry. +# This is all potentially hacky, but so far I could not find a single place in the cluster +# where the olm.bundle image is available. The info is in there in the IIB, but it certainly +# is not in any package manifest nor catalogsource. This is why we resort to invoking opm +# alpha commands inside the IIB image locally +- name: Pull the IIB locally + ansible.builtin.command: + podman pull "{{ iib_image }}" + +# $ opm alpha list channels /configs advanced-cluster-management +# PACKAGE CHANNEL HEAD +# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.4 +# advanced-cluster-management release-2.8 advanced-cluster-management.v2.8.0-130 +- name: Read the operator bundle from the default channel + ansible.builtin.shell: | + set -o pipefail + podman run -it --rm "{{ iib_image }}" alpha list channels /configs "{{ item }}" | grep --word-regexp "{{ default_channel }}" | awk '{ print $3 }' + register: bundle_channel_raw + +- name: Set bundle fact + ansible.builtin.set_fact: + bundle_channel: "{{ bundle_channel_raw.stdout }}" + +- name: Fail if bundle_channel is empty + ansible.builtin.fail: + msg: "Failed to find bundle from channel: {{ bundle_channel_raw }}" + when: > + (bundle_channel is not defined) or (bundle_channel | length == 0) + +# $ opm alpha list bundles /configs advanced-cluster-management +# PACKAGE CHANNEL BUNDLE REPLACES SKIPS SKIP RANGE IMAGE +# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.0 >=2.6.0 <2.7.0 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:f63d0a9a0e3dc9d86e84279c50e9c613d8430e71a3821d418e168250ca3b747c +# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.1 advanced-cluster-management.v2.7.0 >=2.6.0 <2.7.1 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:a81a574f2f22d37681c44fe0c3b958074408705415de333de54d120145537533 +# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.2 advanced-cluster-management.v2.7.1 >=2.6.0 <2.7.2 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:8a2c758689eaebe6a287315ca18fd9122f323e195ea3410db005b6a449060fad +# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.3 advanced-cluster-management.v2.7.2 >=2.6.0 <2.7.3 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:208f4d9473a923817c102bb7e5f138d3e1e8ed3057a23a220ffa8fe9c0c27128 +# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.4 advanced-cluster-management.v2.7.3 >=2.6.0 <2.7.4 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:75b6438e08800b2e3608aeb01c1c0a68810108d9905fff35916afd21e6d32685 +# advanced-cluster-management release-2.8 advanced-cluster-management.v2.8.0-130 >=2.7.0 <2.8.0-130 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:6c385aa69256cdd964ae9e79e52ce52e1048391f0557af59843326c4ebe9bec0 +- name: Get bundle image + ansible.builtin.shell: | + set -o pipefail + podman run -it --rm "{{ iib_image }}" alpha list bundles /configs "{{ item }}" | grep --word-regexp "{{ bundle_channel }}" | awk '{ print $NF }' + register: bundle_image_raw + +- name: Set bundle image fact + ansible.builtin.set_fact: + bundle_image: "{{ bundle_image_raw.stdout }}" + +- name: Fail if bundle_image is empty + ansible.builtin.fail: + msg: "Failed to find bundle image: {{ bundle_image_raw }}" + when: > + (bundle_image is not defined) or (bundle_image | length == 0) + +# all_images will be a list as follows: +# [ "registry.redhat.io/rh-sso-7/sso75-openshift-rhel8@sha256:d5829e880db4b82a50a4962d61ea148522a93644174931b256d7ad866eadcf40", +# "registry.redhat.io/openshift-gitops-1/gitops-rhel8@sha256:5ff915a399c1cc12d4f932652b410bf7399850934833e755267bdd409f4ce11b", +# "registry.redhat.io/openshift-gitops-1/argocd-rhel8@sha256:81e0574159c6aaabe7125d27782a5e6e5e72383a4a0ba76b44d465f3a3098759", +# "registry.redhat.io/rhel8/redis-6@sha256:53598a6effeb90e4f1b005b2521beffd2fa2b0c52d0e7f2347ee2abd2577cab3", +# "registry.redhat.io/openshift-gitops-1/gitops-rhel8-operator@sha256:efbfb010f24894f715a50832a4b3d2cdc221f283cbbdca05e388850586e9d792", +# "registry.redhat.io/openshift4/ose-haproxy-router@sha256:edf7ce748b703e195220b7bd7b42fa2caa4cdfd96840445e096036a0d85f1ff2", +# "registry.redhat.io/openshift-gitops-1/kam-delivery-rhel8@sha256:10c5a1b6a0858a812117e6fb2b28d37617d9eb83da5e4fb647059ff740a14461", +# "registry.redhat.io/openshift-gitops-1/dex-rhel8@sha256:6a3eaee6a4f8cb9a35363bf4c7f83a7fa2042ae62bdaa700ecd0893dd52276f5", +# "registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-gitops-operator-bundle@sha256:e463314596098a4e774e0dda..." ] +- name: Set all images fact (related images + operator bundles) + ansible.builtin.set_fact: + all_images: "{{ all_images + related_images + [bundle_image] }}" diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml index b8de3999..876bbb92 100644 --- a/ansible/roles/iib_ci/tasks/mirror-related-images.yml +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -1,94 +1,16 @@ -- name: Get default channel in the IIB for "{{ operator }}" - ansible.builtin.shell: | - oc get -n "{{ internal_registry_ns }}" packagemanifests -l "catalog=iib-{{ iib }}" --field-selector "metadata.name={{ operator }}" \ - -o jsonpath='{.items[0].status.defaultChannel}' - register: default_channel_raw - retries: 10 - delay: 10 - until: default_channel_raw is not failed - -- name: Set default channel fact +# This is needed because some operators like "advanced-cluster-management" +# install a second operator "multicluster-engine" +- name: Set operators list ansible.builtin.set_fact: - default_channel: "{{ default_channel_raw.stdout }}" + operator_list: "{{ [operator] + (operator == 'advanced-cluster-management') | ternary(['multicluster-engine'], []) }}" -- name: Get all related images in the IIB for "{{ operator }}" - ansible.builtin.shell: | - oc get packagemanifests -l "catalog=iib-{{ iib }}" --field-selector "metadata.name={{ operator }}" \ - -o jsonpath="{.items[0].status.channels[?(@.name==\"{{ default_channel }}\")].currentCSVDesc.relatedImages}" - register: related_images_raw - -- name: Set related_images fact +- name: Set all images to empty list ansible.builtin.set_fact: - related_images: "{{ related_images_raw.stdout }}" - -# NOTE(bandini) -# The following code is here to fund out what the operator bundle image is and to make -# sure it is on the internal registry. -# This is all potentially hacky, but so far I could not find a single place in the cluster -# where the olm.bundle image is available. The info is in there in the IIB, but it certainly -# is not in any package manifest nor catalogsource. This is why we resort to invoking opm -# alpha commands inside the IIB image locally -- name: Pull the IIB locally - ansible.builtin.command: - podman pull "{{ iib_image }}" + all_images: [] -# $ opm alpha list channels /configs advanced-cluster-management -# PACKAGE CHANNEL HEAD -# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.4 -# advanced-cluster-management release-2.8 advanced-cluster-management.v2.8.0-130 -- name: Read the operator bundle from the default channel - ansible.builtin.shell: | - set -o pipefail - podman run -it --rm "{{ iib_image }}" alpha list channels /configs "{{ operator }}" | grep --word-regexp "{{ default_channel }}" | awk '{ print $3 }' - register: bundle_channel_raw - -- name: Set bundle fact - ansible.builtin.set_fact: - bundle_channel: "{{ bundle_channel_raw.stdout }}" - -- name: Fail if bundle_channel is empty - ansible.builtin.fail: - msg: "Failed to find bundle from channel: {{ bundle_channel_raw }}" - when: > - (bundle_channel is not defined) or (bundle_channel | length == 0) - -# $ opm alpha list bundles /configs advanced-cluster-management -# PACKAGE CHANNEL BUNDLE REPLACES SKIPS SKIP RANGE IMAGE -# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.0 >=2.6.0 <2.7.0 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:f63d0a9a0e3dc9d86e84279c50e9c613d8430e71a3821d418e168250ca3b747c -# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.1 advanced-cluster-management.v2.7.0 >=2.6.0 <2.7.1 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:a81a574f2f22d37681c44fe0c3b958074408705415de333de54d120145537533 -# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.2 advanced-cluster-management.v2.7.1 >=2.6.0 <2.7.2 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:8a2c758689eaebe6a287315ca18fd9122f323e195ea3410db005b6a449060fad -# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.3 advanced-cluster-management.v2.7.2 >=2.6.0 <2.7.3 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:208f4d9473a923817c102bb7e5f138d3e1e8ed3057a23a220ffa8fe9c0c27128 -# advanced-cluster-management release-2.7 advanced-cluster-management.v2.7.4 advanced-cluster-management.v2.7.3 >=2.6.0 <2.7.4 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:75b6438e08800b2e3608aeb01c1c0a68810108d9905fff35916afd21e6d32685 -# advanced-cluster-management release-2.8 advanced-cluster-management.v2.8.0-130 >=2.7.0 <2.8.0-130 registry.stage.redhat.io/rhacm2/acm-operator-bundle@sha256:6c385aa69256cdd964ae9e79e52ce52e1048391f0557af59843326c4ebe9bec0 -- name: Get bundle image - ansible.builtin.shell: | - set -o pipefail - podman run -it --rm "{{ iib_image }}" alpha list bundles /configs "{{ operator }}" | grep --word-regexp "{{ bundle_channel }}" | awk '{ print $NF }' - register: bundle_image_raw - -- name: Set bundle image fact - ansible.builtin.set_fact: - bundle_image: "{{ bundle_image_raw.stdout }}" - -- name: Fail if bundle_image is empty - ansible.builtin.fail: - msg: "Failed to find bundle image: {{ bundle_image_raw }}" - when: > - (bundle_image is not defined) or (bundle_image | length == 0) - -# all_images will be a list as follows: -# [ "registry.redhat.io/rh-sso-7/sso75-openshift-rhel8@sha256:d5829e880db4b82a50a4962d61ea148522a93644174931b256d7ad866eadcf40", -# "registry.redhat.io/openshift-gitops-1/gitops-rhel8@sha256:5ff915a399c1cc12d4f932652b410bf7399850934833e755267bdd409f4ce11b", -# "registry.redhat.io/openshift-gitops-1/argocd-rhel8@sha256:81e0574159c6aaabe7125d27782a5e6e5e72383a4a0ba76b44d465f3a3098759", -# "registry.redhat.io/rhel8/redis-6@sha256:53598a6effeb90e4f1b005b2521beffd2fa2b0c52d0e7f2347ee2abd2577cab3", -# "registry.redhat.io/openshift-gitops-1/gitops-rhel8-operator@sha256:efbfb010f24894f715a50832a4b3d2cdc221f283cbbdca05e388850586e9d792", -# "registry.redhat.io/openshift4/ose-haproxy-router@sha256:edf7ce748b703e195220b7bd7b42fa2caa4cdfd96840445e096036a0d85f1ff2", -# "registry.redhat.io/openshift-gitops-1/kam-delivery-rhel8@sha256:10c5a1b6a0858a812117e6fb2b28d37617d9eb83da5e4fb647059ff740a14461", -# "registry.redhat.io/openshift-gitops-1/dex-rhel8@sha256:6a3eaee6a4f8cb9a35363bf4c7f83a7fa2042ae62bdaa700ecd0893dd52276f5", -# "registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-gitops-operator-bundle@sha256:e463314596098a4e774e0dda..." ] -- name: Set all images fact (related images + operator bundles) - ansible.builtin.set_fact: - all_images: "{{ related_images + [bundle_image] }}" +- name: Fetch operator images tasks + ansible.builtin.include_tasks: fetch-operator-images.yml + loop: "{{ operator_list }}" - name: Print all_images ansible.builtin.debug: From 45912f16a3742d5f68b196e252d44f2914c405be Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 19 May 2023 15:44:23 +0200 Subject: [PATCH 0890/1263] Cleanup spacing --- ansible/roles/iib_ci/tasks/mirror-related-images.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml index 876bbb92..821e4be0 100644 --- a/ansible/roles/iib_ci/tasks/mirror-related-images.yml +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -2,7 +2,7 @@ # install a second operator "multicluster-engine" - name: Set operators list ansible.builtin.set_fact: - operator_list: "{{ [operator] + (operator == 'advanced-cluster-management') | ternary(['multicluster-engine'], []) }}" + operator_list: "{{ [operator] + (operator == 'advanced-cluster-management') | ternary(['multicluster-engine'], []) }}" - name: Set all images to empty list ansible.builtin.set_fact: From 86ac7c2191679f2f7968a7648593b51d7ec49e9c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 19 May 2023 17:49:42 +0200 Subject: [PATCH 0891/1263] Fix super-linter --- ansible/roles/iib_ci/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 35087753..277d991c 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -27,6 +27,7 @@ make EXTRA_HELM_OPTS="--set main.gitops.operatorSource=iib-492329 --set main.git ``` To install ACM from an IIB we can run the following: + ```sh export OPERATOR=advanced-cluster-management export KUBECONFIG=/tmp/foo/kubeconfig From d713e7b5e010a7fb71d6c16c9261149bad02603f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 19 May 2023 17:55:28 +0200 Subject: [PATCH 0892/1263] Move task in right folder --- ansible/roles/iib_ci/{ => tasks}/fetch-operator-images.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ansible/roles/iib_ci/{ => tasks}/fetch-operator-images.yml (100%) diff --git a/ansible/roles/iib_ci/fetch-operator-images.yml b/ansible/roles/iib_ci/tasks/fetch-operator-images.yml similarity index 100% rename from ansible/roles/iib_ci/fetch-operator-images.yml rename to ansible/roles/iib_ci/tasks/fetch-operator-images.yml From 09e53899ab70e38f28f1db5291c3bbe431ea5879 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 19 May 2023 17:56:43 +0200 Subject: [PATCH 0893/1263] Drop last mention of operator instead of item --- ansible/roles/iib_ci/tasks/fetch-operator-images.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/fetch-operator-images.yml b/ansible/roles/iib_ci/tasks/fetch-operator-images.yml index c0cd8fce..3cbe1941 100644 --- a/ansible/roles/iib_ci/tasks/fetch-operator-images.yml +++ b/ansible/roles/iib_ci/tasks/fetch-operator-images.yml @@ -15,7 +15,7 @@ ansible.builtin.set_fact: default_channel: "{{ default_channel_raw.stdout }}" -- name: Get all related images in the IIB for "{{ operator }}" +- name: Get all related images in the IIB for "{{ item }}" ansible.builtin.shell: | oc get packagemanifests -l "catalog=iib-{{ iib }}" --field-selector "metadata.name={{ item }}" \ -o jsonpath="{.items[0].status.channels[?(@.name==\"{{ default_channel }}\")].currentCSVDesc.relatedImages}" From 45a5e25f42be16de7d197cb4a5942b9af16177f9 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 22 May 2023 09:53:53 +0200 Subject: [PATCH 0894/1263] Improve the grepping for the operator bundle Without also grepping for the default_channel we can end up getting multiple results, which breaks everything. Tested this and it fixed the issue I was seeing with the openshift-gitops-operator this morning --- ansible/roles/iib_ci/tasks/fetch-operator-images.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/fetch-operator-images.yml b/ansible/roles/iib_ci/tasks/fetch-operator-images.yml index 3cbe1941..10f083e9 100644 --- a/ansible/roles/iib_ci/tasks/fetch-operator-images.yml +++ b/ansible/roles/iib_ci/tasks/fetch-operator-images.yml @@ -67,7 +67,7 @@ - name: Get bundle image ansible.builtin.shell: | set -o pipefail - podman run -it --rm "{{ iib_image }}" alpha list bundles /configs "{{ item }}" | grep --word-regexp "{{ bundle_channel }}" | awk '{ print $NF }' + podman run -it --rm "{{ iib_image }}" alpha list bundles /configs "{{ item }}" | grep -e "{{ default_channel }}\s\+{{ bundle_channel }}" | awk '{ print $NF }' register: bundle_image_raw - name: Set bundle image fact From 3c29969dcba84f93582a9650abc29a3f1392be11 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 22 May 2023 10:07:32 +0200 Subject: [PATCH 0895/1263] Drop display_skipped_hosts display_skipped_hosts=False has a horrible side-effect: When a task takes a long time, it is always the *next* task and not the one printed on the screen/log. That is because ansible has to wait for the task to finish before printing it as it does not know before hand if the host will be skipped and hence the task should not be displayed at all --- ansible/ansible.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index 4cceda11..652feb98 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -1,5 +1,4 @@ [defaults] -display_skipped_hosts=False localhost_warning=False library=./plugins/modules:~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules roles_path=./roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles From ff1eacfd7c8023f3ad0877dc47d4e4968fa63c36 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 22 May 2023 14:00:46 +0200 Subject: [PATCH 0896/1263] Be more specific about the steps in the README --- golang-external-secrets/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/golang-external-secrets/README.md b/golang-external-secrets/README.md index 6db62db7..7f3a1a8b 100644 --- a/golang-external-secrets/README.md +++ b/golang-external-secrets/README.md @@ -3,3 +3,11 @@ When updating this sub-chart, please remember to tweak the image tag in values.yaml. That is because we want to use -ubi images if possible and there is no suffix option, so we just override the tag with the version + "-ubi" + +## Steps + +1. Edit the version in Chart.yaml +2. Run `helm dependency update .` +3. Tweak `values.yaml` with the new image versions +4. Run `make test` +5. Commit to git From 44f6d5791f128bdbb87939fd30ea2f20a4198465 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 22 May 2023 14:01:03 +0200 Subject: [PATCH 0897/1263] Upgrade ESO to v0.8.2 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.8.1.tgz | Bin 72021 -> 0 bytes .../charts/external-secrets-0.8.2.tgz | Bin 0 -> 77831 bytes golang-external-secrets/values.yaml | 6 +++--- 4 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.8.1.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.8.2.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index a0e1afe0..4f30d0b6 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.8.1" + version: "0.8.2" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.8.1.tgz b/golang-external-secrets/charts/external-secrets-0.8.1.tgz deleted file mode 100644 index 73fc4a59ef1b4fa97ea5b661208f14704efa7940..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72021 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 pH+HAAk={W4- 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 Date: Mon, 22 May 2023 16:24:01 +0200 Subject: [PATCH 0898/1263] Update README.md --- golang-external-secrets/README.md | 7 ++-- .../charts/external-secrets-0.8.2.tgz | Bin 77831 -> 78582 bytes .../0001-runasuser-comment-out.patch | 30 ++++++++++++++++++ .../update-helm-dependency.sh | 29 +++++++++++++++++ golang-external-secrets/values.yaml | 2 -- 5 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 golang-external-secrets/local-patches/0001-runasuser-comment-out.patch create mode 100755 golang-external-secrets/update-helm-dependency.sh diff --git a/golang-external-secrets/README.md b/golang-external-secrets/README.md index 7f3a1a8b..e12d58f1 100644 --- a/golang-external-secrets/README.md +++ b/golang-external-secrets/README.md @@ -8,6 +8,7 @@ we just override the tag with the version + "-ubi" 1. Edit the version in Chart.yaml 2. Run `helm dependency update .` -3. Tweak `values.yaml` with the new image versions -4. Run `make test` -5. Commit to git +3. Run `./update-helm-dependency.sh` +4. Tweak `values.yaml` with the new image versions +5. Run `make test` +6. Commit to git diff --git a/golang-external-secrets/charts/external-secrets-0.8.2.tgz b/golang-external-secrets/charts/external-secrets-0.8.2.tgz index 880eaa6a3b8ece00153a32d32e2463f0dd4948ad..2b2d939798577aa57e08f21ab4a2110b6395905a 100644 GIT binary patch literal 78582 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 Date: Mon, 22 May 2023 17:29:05 +0200 Subject: [PATCH 0899/1263] Update tests after eso 0.8.2 upgrade --- Changes.md | 4 + ...rets-industrial-edge-factory.expected.yaml | 641 +++++++++++++++--- ...-secrets-industrial-edge-hub.expected.yaml | 641 +++++++++++++++--- ...ecrets-medical-diagnosis-hub.expected.yaml | 641 +++++++++++++++--- ...olang-external-secrets-naked.expected.yaml | 641 +++++++++++++++--- ...lang-external-secrets-normal.expected.yaml | 641 +++++++++++++++--- 6 files changed, 2789 insertions(+), 420 deletions(-) diff --git a/Changes.md b/Changes.md index 80da55fd..3902b67a 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## May 22, 2023 + +* Upgraded ESO to 0.8.2 + ## May 18, 2023 * Introduce a EXTRA_HELM_OPTS env variable that will be passed to the helm invocations diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index d7e88e41..df88573d 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,8 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -209,8 +208,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -500,6 +498,9 @@ spec: engineVersion: default: v2 type: string + mergePolicy: + default: Replace + type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. properties: @@ -664,8 +665,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -705,7 +705,7 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -844,6 +844,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -877,11 +894,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -1114,7 +1127,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -1854,7 +1867,7 @@ spec: type: object type: array controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -1993,6 +2006,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -2026,11 +2056,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -2114,6 +2140,9 @@ spec: type: object type: object type: object + externalID: + description: AWS External ID set on assumed IAM roles + type: string region: description: AWS Region to be used for the provider type: string @@ -2344,7 +2373,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -2784,6 +2813,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -2799,7 +2841,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -2832,6 +2873,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -3258,8 +3387,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3387,8 +3515,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -3586,6 +3713,14 @@ spec: type: object status: properties: + binding: + description: Binding represents a servicebinding.io Provisioned Service reference to the secret + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic conditions: items: properties: @@ -3888,6 +4023,9 @@ spec: engineVersion: default: v2 type: string + mergePolicy: + default: Replace + type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. properties: @@ -3957,6 +4095,14 @@ spec: type: object status: properties: + binding: + description: Binding represents a servicebinding.io Provisioned Service reference to the secret + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic conditions: items: properties: @@ -4006,8 +4152,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4038,6 +4183,9 @@ spec: spec: description: FakeSpec contains the static data. properties: + controller: + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + type: string data: additionalProperties: type: string @@ -4065,8 +4213,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4174,8 +4321,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4253,8 +4399,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4299,6 +4444,9 @@ spec: remoteRef: description: Remote Refs to push to providers. properties: + property: + description: Name of the property in the resulting secret + type: string remoteKey: description: Name of the resulting provider secret. type: string @@ -4424,6 +4572,9 @@ spec: remoteRef: description: Remote Refs to push to providers. properties: + property: + description: Name of the property in the resulting secret + type: string remoteKey: description: Name of the resulting provider secret. type: string @@ -4468,8 +4619,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -4509,7 +4659,7 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -4648,6 +4798,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -4681,11 +4848,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -4918,7 +5081,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -5658,7 +5821,7 @@ spec: type: object type: array controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -5797,6 +5960,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -5830,11 +6010,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -5918,6 +6094,9 @@ spec: type: object type: object type: object + externalID: + description: AWS External ID set on assumed IAM roles + type: string region: description: AWS Region to be used for the provider type: string @@ -6148,7 +6327,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -6588,6 +6767,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -6603,7 +6795,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -6636,6 +6827,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -7062,8 +7341,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -7092,6 +7370,9 @@ spec: type: object spec: properties: + controller: + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + type: string method: description: Vault API method to use (GET/POST/other) type: string @@ -7117,6 +7398,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -7132,7 +7426,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -7165,6 +7458,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -7360,6 +7741,10 @@ spec: - auth - server type: object + resultType: + default: Data + description: Result type defines which data is returned from the generator. By default it is the "data" section of the Vault API response. When using e.g. /auth/token/create the "data" section is empty but the "auth" section contains the generated token. Please refer to the vault docs regarding the result data structure. + type: string required: - path - provider @@ -7386,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7437,6 +7822,15 @@ rules: - "watch" - "update" - "patch" + - apiGroups: + - "coordination.k8s.io" + resources: + - "leases" + verbs: + - "get" + - "create" + - "update" + - "patch" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7444,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7553,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7573,6 +7967,19 @@ rules: - "get" - "watch" - "list" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "acraccesstokens" + - "ecrauthorizationtokens" + - "fakes" + - "gcraccesstokens" + - "passwords" + - "vaultdynamicsecrets" + verbs: + - "get" + - "watch" + - "list" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7580,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -7601,6 +8008,43 @@ rules: - "deletecollection" - "patch" - "update" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "acraccesstokens" + - "ecrauthorizationtokens" + - "fakes" + - "gcraccesstokens" + - "passwords" + - "vaultdynamicsecrets" + verbs: + - "create" + - "delete" + - "deletecollection" + - "patch" + - "update" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-servicebindings + labels: + servicebinding.io/controller: "true" + helm.sh/chart: external-secrets-0.8.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + verbs: + - "get" + - "list" + - "watch" --- # Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7608,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7628,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7664,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7703,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7724,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -7748,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7768,9 +8212,19 @@ spec: spec: serviceAccountName: external-secrets-cert-controller automountServiceAccountToken: true + hostNetwork: false containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -7798,10 +8252,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7818,9 +8272,19 @@ spec: spec: serviceAccountName: golang-external-secrets automountServiceAccountToken: true + hostNetwork: false containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -7836,10 +8300,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7859,7 +8323,16 @@ spec: automountServiceAccountToken: true containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index 4b957a45..09f472bf 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,8 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -209,8 +208,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -500,6 +498,9 @@ spec: engineVersion: default: v2 type: string + mergePolicy: + default: Replace + type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. properties: @@ -664,8 +665,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -705,7 +705,7 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -844,6 +844,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -877,11 +894,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -1114,7 +1127,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -1854,7 +1867,7 @@ spec: type: object type: array controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -1993,6 +2006,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -2026,11 +2056,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -2114,6 +2140,9 @@ spec: type: object type: object type: object + externalID: + description: AWS External ID set on assumed IAM roles + type: string region: description: AWS Region to be used for the provider type: string @@ -2344,7 +2373,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -2784,6 +2813,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -2799,7 +2841,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -2832,6 +2873,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -3258,8 +3387,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3387,8 +3515,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -3586,6 +3713,14 @@ spec: type: object status: properties: + binding: + description: Binding represents a servicebinding.io Provisioned Service reference to the secret + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic conditions: items: properties: @@ -3888,6 +4023,9 @@ spec: engineVersion: default: v2 type: string + mergePolicy: + default: Replace + type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. properties: @@ -3957,6 +4095,14 @@ spec: type: object status: properties: + binding: + description: Binding represents a servicebinding.io Provisioned Service reference to the secret + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic conditions: items: properties: @@ -4006,8 +4152,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4038,6 +4183,9 @@ spec: spec: description: FakeSpec contains the static data. properties: + controller: + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + type: string data: additionalProperties: type: string @@ -4065,8 +4213,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4174,8 +4321,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4253,8 +4399,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4299,6 +4444,9 @@ spec: remoteRef: description: Remote Refs to push to providers. properties: + property: + description: Name of the property in the resulting secret + type: string remoteKey: description: Name of the resulting provider secret. type: string @@ -4424,6 +4572,9 @@ spec: remoteRef: description: Remote Refs to push to providers. properties: + property: + description: Name of the property in the resulting secret + type: string remoteKey: description: Name of the resulting provider secret. type: string @@ -4468,8 +4619,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -4509,7 +4659,7 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -4648,6 +4798,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -4681,11 +4848,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -4918,7 +5081,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -5658,7 +5821,7 @@ spec: type: object type: array controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -5797,6 +5960,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -5830,11 +6010,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -5918,6 +6094,9 @@ spec: type: object type: object type: object + externalID: + description: AWS External ID set on assumed IAM roles + type: string region: description: AWS Region to be used for the provider type: string @@ -6148,7 +6327,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -6588,6 +6767,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -6603,7 +6795,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -6636,6 +6827,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -7062,8 +7341,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -7092,6 +7370,9 @@ spec: type: object spec: properties: + controller: + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + type: string method: description: Vault API method to use (GET/POST/other) type: string @@ -7117,6 +7398,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -7132,7 +7426,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -7165,6 +7458,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -7360,6 +7741,10 @@ spec: - auth - server type: object + resultType: + default: Data + description: Result type defines which data is returned from the generator. By default it is the "data" section of the Vault API response. When using e.g. /auth/token/create the "data" section is empty but the "auth" section contains the generated token. Please refer to the vault docs regarding the result data structure. + type: string required: - path - provider @@ -7386,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7437,6 +7822,15 @@ rules: - "watch" - "update" - "patch" + - apiGroups: + - "coordination.k8s.io" + resources: + - "leases" + verbs: + - "get" + - "create" + - "update" + - "patch" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7444,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7553,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7573,6 +7967,19 @@ rules: - "get" - "watch" - "list" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "acraccesstokens" + - "ecrauthorizationtokens" + - "fakes" + - "gcraccesstokens" + - "passwords" + - "vaultdynamicsecrets" + verbs: + - "get" + - "watch" + - "list" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7580,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -7601,6 +8008,43 @@ rules: - "deletecollection" - "patch" - "update" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "acraccesstokens" + - "ecrauthorizationtokens" + - "fakes" + - "gcraccesstokens" + - "passwords" + - "vaultdynamicsecrets" + verbs: + - "create" + - "delete" + - "deletecollection" + - "patch" + - "update" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-servicebindings + labels: + servicebinding.io/controller: "true" + helm.sh/chart: external-secrets-0.8.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + verbs: + - "get" + - "list" + - "watch" --- # Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7608,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7628,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7664,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7703,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7724,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -7748,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7768,9 +8212,19 @@ spec: spec: serviceAccountName: external-secrets-cert-controller automountServiceAccountToken: true + hostNetwork: false containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -7798,10 +8252,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7818,9 +8272,19 @@ spec: spec: serviceAccountName: golang-external-secrets automountServiceAccountToken: true + hostNetwork: false containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -7836,10 +8300,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7859,7 +8323,16 @@ spec: automountServiceAccountToken: true containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index 4b957a45..09f472bf 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,8 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -209,8 +208,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -500,6 +498,9 @@ spec: engineVersion: default: v2 type: string + mergePolicy: + default: Replace + type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. properties: @@ -664,8 +665,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -705,7 +705,7 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -844,6 +844,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -877,11 +894,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -1114,7 +1127,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -1854,7 +1867,7 @@ spec: type: object type: array controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -1993,6 +2006,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -2026,11 +2056,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -2114,6 +2140,9 @@ spec: type: object type: object type: object + externalID: + description: AWS External ID set on assumed IAM roles + type: string region: description: AWS Region to be used for the provider type: string @@ -2344,7 +2373,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -2784,6 +2813,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -2799,7 +2841,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -2832,6 +2873,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -3258,8 +3387,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3387,8 +3515,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -3586,6 +3713,14 @@ spec: type: object status: properties: + binding: + description: Binding represents a servicebinding.io Provisioned Service reference to the secret + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic conditions: items: properties: @@ -3888,6 +4023,9 @@ spec: engineVersion: default: v2 type: string + mergePolicy: + default: Replace + type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. properties: @@ -3957,6 +4095,14 @@ spec: type: object status: properties: + binding: + description: Binding represents a servicebinding.io Provisioned Service reference to the secret + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic conditions: items: properties: @@ -4006,8 +4152,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4038,6 +4183,9 @@ spec: spec: description: FakeSpec contains the static data. properties: + controller: + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + type: string data: additionalProperties: type: string @@ -4065,8 +4213,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4174,8 +4321,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4253,8 +4399,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4299,6 +4444,9 @@ spec: remoteRef: description: Remote Refs to push to providers. properties: + property: + description: Name of the property in the resulting secret + type: string remoteKey: description: Name of the resulting provider secret. type: string @@ -4424,6 +4572,9 @@ spec: remoteRef: description: Remote Refs to push to providers. properties: + property: + description: Name of the property in the resulting secret + type: string remoteKey: description: Name of the resulting provider secret. type: string @@ -4468,8 +4619,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -4509,7 +4659,7 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -4648,6 +4798,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -4681,11 +4848,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -4918,7 +5081,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -5658,7 +5821,7 @@ spec: type: object type: array controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -5797,6 +5960,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -5830,11 +6010,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -5918,6 +6094,9 @@ spec: type: object type: object type: object + externalID: + description: AWS External ID set on assumed IAM roles + type: string region: description: AWS Region to be used for the provider type: string @@ -6148,7 +6327,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -6588,6 +6767,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -6603,7 +6795,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -6636,6 +6827,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -7062,8 +7341,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -7092,6 +7370,9 @@ spec: type: object spec: properties: + controller: + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + type: string method: description: Vault API method to use (GET/POST/other) type: string @@ -7117,6 +7398,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -7132,7 +7426,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -7165,6 +7458,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -7360,6 +7741,10 @@ spec: - auth - server type: object + resultType: + default: Data + description: Result type defines which data is returned from the generator. By default it is the "data" section of the Vault API response. When using e.g. /auth/token/create the "data" section is empty but the "auth" section contains the generated token. Please refer to the vault docs regarding the result data structure. + type: string required: - path - provider @@ -7386,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7437,6 +7822,15 @@ rules: - "watch" - "update" - "patch" + - apiGroups: + - "coordination.k8s.io" + resources: + - "leases" + verbs: + - "get" + - "create" + - "update" + - "patch" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7444,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7553,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7573,6 +7967,19 @@ rules: - "get" - "watch" - "list" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "acraccesstokens" + - "ecrauthorizationtokens" + - "fakes" + - "gcraccesstokens" + - "passwords" + - "vaultdynamicsecrets" + verbs: + - "get" + - "watch" + - "list" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7580,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -7601,6 +8008,43 @@ rules: - "deletecollection" - "patch" - "update" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "acraccesstokens" + - "ecrauthorizationtokens" + - "fakes" + - "gcraccesstokens" + - "passwords" + - "vaultdynamicsecrets" + verbs: + - "create" + - "delete" + - "deletecollection" + - "patch" + - "update" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-servicebindings + labels: + servicebinding.io/controller: "true" + helm.sh/chart: external-secrets-0.8.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + verbs: + - "get" + - "list" + - "watch" --- # Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7608,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7628,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7664,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7703,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7724,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -7748,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7768,9 +8212,19 @@ spec: spec: serviceAccountName: external-secrets-cert-controller automountServiceAccountToken: true + hostNetwork: false containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -7798,10 +8252,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7818,9 +8272,19 @@ spec: spec: serviceAccountName: golang-external-secrets automountServiceAccountToken: true + hostNetwork: false containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -7836,10 +8300,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7859,7 +8323,16 @@ spec: automountServiceAccountToken: true containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 5e43d029..11f2ecca 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,8 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -209,8 +208,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -500,6 +498,9 @@ spec: engineVersion: default: v2 type: string + mergePolicy: + default: Replace + type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. properties: @@ -664,8 +665,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -705,7 +705,7 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -844,6 +844,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -877,11 +894,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -1114,7 +1127,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -1854,7 +1867,7 @@ spec: type: object type: array controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -1993,6 +2006,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -2026,11 +2056,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -2114,6 +2140,9 @@ spec: type: object type: object type: object + externalID: + description: AWS External ID set on assumed IAM roles + type: string region: description: AWS Region to be used for the provider type: string @@ -2344,7 +2373,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -2784,6 +2813,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -2799,7 +2841,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -2832,6 +2873,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -3258,8 +3387,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3387,8 +3515,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -3586,6 +3713,14 @@ spec: type: object status: properties: + binding: + description: Binding represents a servicebinding.io Provisioned Service reference to the secret + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic conditions: items: properties: @@ -3888,6 +4023,9 @@ spec: engineVersion: default: v2 type: string + mergePolicy: + default: Replace + type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. properties: @@ -3957,6 +4095,14 @@ spec: type: object status: properties: + binding: + description: Binding represents a servicebinding.io Provisioned Service reference to the secret + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic conditions: items: properties: @@ -4006,8 +4152,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4038,6 +4183,9 @@ spec: spec: description: FakeSpec contains the static data. properties: + controller: + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + type: string data: additionalProperties: type: string @@ -4065,8 +4213,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4174,8 +4321,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4253,8 +4399,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4299,6 +4444,9 @@ spec: remoteRef: description: Remote Refs to push to providers. properties: + property: + description: Name of the property in the resulting secret + type: string remoteKey: description: Name of the resulting provider secret. type: string @@ -4424,6 +4572,9 @@ spec: remoteRef: description: Remote Refs to push to providers. properties: + property: + description: Name of the property in the resulting secret + type: string remoteKey: description: Name of the resulting provider secret. type: string @@ -4468,8 +4619,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -4509,7 +4659,7 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -4648,6 +4798,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -4681,11 +4848,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -4918,7 +5081,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -5658,7 +5821,7 @@ spec: type: object type: array controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -5797,6 +5960,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -5830,11 +6010,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -5918,6 +6094,9 @@ spec: type: object type: object type: object + externalID: + description: AWS External ID set on assumed IAM roles + type: string region: description: AWS Region to be used for the provider type: string @@ -6148,7 +6327,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -6588,6 +6767,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -6603,7 +6795,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -6636,6 +6827,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -7062,8 +7341,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -7092,6 +7370,9 @@ spec: type: object spec: properties: + controller: + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + type: string method: description: Vault API method to use (GET/POST/other) type: string @@ -7117,6 +7398,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -7132,7 +7426,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -7165,6 +7458,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -7360,6 +7741,10 @@ spec: - auth - server type: object + resultType: + default: Data + description: Result type defines which data is returned from the generator. By default it is the "data" section of the Vault API response. When using e.g. /auth/token/create the "data" section is empty but the "auth" section contains the generated token. Please refer to the vault docs regarding the result data structure. + type: string required: - path - provider @@ -7386,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7437,6 +7822,15 @@ rules: - "watch" - "update" - "patch" + - apiGroups: + - "coordination.k8s.io" + resources: + - "leases" + verbs: + - "get" + - "create" + - "update" + - "patch" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7444,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7553,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7573,6 +7967,19 @@ rules: - "get" - "watch" - "list" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "acraccesstokens" + - "ecrauthorizationtokens" + - "fakes" + - "gcraccesstokens" + - "passwords" + - "vaultdynamicsecrets" + verbs: + - "get" + - "watch" + - "list" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7580,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -7601,6 +8008,43 @@ rules: - "deletecollection" - "patch" - "update" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "acraccesstokens" + - "ecrauthorizationtokens" + - "fakes" + - "gcraccesstokens" + - "passwords" + - "vaultdynamicsecrets" + verbs: + - "create" + - "delete" + - "deletecollection" + - "patch" + - "update" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-servicebindings + labels: + servicebinding.io/controller: "true" + helm.sh/chart: external-secrets-0.8.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + verbs: + - "get" + - "list" + - "watch" --- # Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7608,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7628,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7664,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7703,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7724,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -7748,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7768,9 +8212,19 @@ spec: spec: serviceAccountName: external-secrets-cert-controller automountServiceAccountToken: true + hostNetwork: false containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -7798,10 +8252,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7818,9 +8272,19 @@ spec: spec: serviceAccountName: golang-external-secrets automountServiceAccountToken: true + hostNetwork: false containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -7836,10 +8300,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7859,7 +8323,16 @@ spec: automountServiceAccountToken: true containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 4b957a45..09f472bf 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,8 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -209,8 +208,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -500,6 +498,9 @@ spec: engineVersion: default: v2 type: string + mergePolicy: + default: Replace + type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. properties: @@ -664,8 +665,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -705,7 +705,7 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -844,6 +844,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -877,11 +894,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -1114,7 +1127,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -1854,7 +1867,7 @@ spec: type: object type: array controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -1993,6 +2006,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -2026,11 +2056,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -2114,6 +2140,9 @@ spec: type: object type: object type: object + externalID: + description: AWS External ID set on assumed IAM roles + type: string region: description: AWS Region to be used for the provider type: string @@ -2344,7 +2373,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -2784,6 +2813,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -2799,7 +2841,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -2832,6 +2873,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -3258,8 +3387,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3387,8 +3515,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -3586,6 +3713,14 @@ spec: type: object status: properties: + binding: + description: Binding represents a servicebinding.io Provisioned Service reference to the secret + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic conditions: items: properties: @@ -3888,6 +4023,9 @@ spec: engineVersion: default: v2 type: string + mergePolicy: + default: Replace + type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. properties: @@ -3957,6 +4095,14 @@ spec: type: object status: properties: + binding: + description: Binding represents a servicebinding.io Provisioned Service reference to the secret + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic conditions: items: properties: @@ -4006,8 +4152,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4038,6 +4183,9 @@ spec: spec: description: FakeSpec contains the static data. properties: + controller: + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + type: string data: additionalProperties: type: string @@ -4065,8 +4213,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4174,8 +4321,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4253,8 +4399,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4299,6 +4444,9 @@ spec: remoteRef: description: Remote Refs to push to providers. properties: + property: + description: Name of the property in the resulting secret + type: string remoteKey: description: Name of the resulting provider secret. type: string @@ -4424,6 +4572,9 @@ spec: remoteRef: description: Remote Refs to push to providers. properties: + property: + description: Name of the property in the resulting secret + type: string remoteKey: description: Name of the resulting provider secret. type: string @@ -4468,8 +4619,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -4509,7 +4659,7 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -4648,6 +4798,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -4681,11 +4848,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -4918,7 +5081,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -5658,7 +5821,7 @@ spec: type: object type: array controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' type: string provider: description: Used to configure the provider. Only one provider may be set @@ -5797,6 +5960,23 @@ spec: auth: description: AlibabaAuth contains a secretRef for credentials. properties: + rrsa: + description: Authenticate against Alibaba using RRSA. + properties: + oidcProviderArn: + type: string + oidcTokenFilePath: + type: string + roleArn: + type: string + sessionName: + type: string + required: + - oidcProviderArn + - oidcTokenFilePath + - roleArn + - sessionName + type: object secretRef: description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. properties: @@ -5830,11 +6010,7 @@ spec: - accessKeyIDSecretRef - accessKeySecretSecretRef type: object - required: - - secretRef type: object - endpoint: - type: string regionID: description: Alibaba Region to be used for the provider type: string @@ -5918,6 +6094,9 @@ spec: type: object type: object type: object + externalID: + description: AWS External ID set on assumed IAM roles + type: string region: description: AWS Region to be used for the provider type: string @@ -6148,7 +6327,7 @@ spec: type: string type: object gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider + description: GitLab configures this store to sync secrets using GitLab Variables provider properties: auth: description: Auth configures how secret-manager authenticates with a GitLab instance. @@ -6588,6 +6767,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -6603,7 +6795,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -6636,6 +6827,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -7062,8 +7341,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -7092,6 +7370,9 @@ spec: type: object spec: properties: + controller: + description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + type: string method: description: Vault API method to use (GET/POST/other) type: string @@ -7117,6 +7398,19 @@ spec: roleId: description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. type: string + roleRef: + description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object secretRef: description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. properties: @@ -7132,7 +7426,6 @@ spec: type: object required: - path - - roleId - secretRef type: object cert: @@ -7165,6 +7458,94 @@ spec: type: string type: object type: object + iam: + description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + properties: + externalID: + description: AWS External ID set on assumed IAM roles + type: string + jwt: + description: Specify a service account with IRSA enabled + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + path: + description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"' + type: string + region: + description: AWS region + type: string + role: + description: This is the AWS role to be assumed before talking to vault + type: string + secretRef: + description: Specify credentials in a Secret object + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + vaultAwsIamServerID: + description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws' + type: string + vaultRole: + description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine + type: string + required: + - vaultRole + type: object jwt: description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method properties: @@ -7360,6 +7741,10 @@ spec: - auth - server type: object + resultType: + default: Data + description: Result type defines which data is returned from the generator. By default it is the "data" section of the Vault API response. When using e.g. /auth/token/create the "data" section is empty but the "auth" section contains the generated token. Please refer to the vault docs regarding the result data structure. + type: string required: - path - provider @@ -7386,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7437,6 +7822,15 @@ rules: - "watch" - "update" - "patch" + - apiGroups: + - "coordination.k8s.io" + resources: + - "leases" + verbs: + - "get" + - "create" + - "update" + - "patch" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7444,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7553,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7573,6 +7967,19 @@ rules: - "get" - "watch" - "list" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "acraccesstokens" + - "ecrauthorizationtokens" + - "fakes" + - "gcraccesstokens" + - "passwords" + - "vaultdynamicsecrets" + verbs: + - "get" + - "watch" + - "list" --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7580,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -7601,6 +8008,43 @@ rules: - "deletecollection" - "patch" - "update" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "acraccesstokens" + - "ecrauthorizationtokens" + - "fakes" + - "gcraccesstokens" + - "passwords" + - "vaultdynamicsecrets" + verbs: + - "create" + - "delete" + - "deletecollection" + - "patch" + - "update" +--- +# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: golang-external-secrets-servicebindings + labels: + servicebinding.io/controller: "true" + helm.sh/chart: external-secrets-0.8.2 + app.kubernetes.io/name: external-secrets + app.kubernetes.io/instance: golang-external-secrets + app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "external-secrets.io" + resources: + - "externalsecrets" + verbs: + - "get" + - "list" + - "watch" --- # Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -7608,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7628,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7664,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7703,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -7724,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -7748,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7768,9 +8212,19 @@ spec: spec: serviceAccountName: external-secrets-cert-controller automountServiceAccountToken: true + hostNetwork: false containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -7798,10 +8252,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7818,9 +8272,19 @@ spec: spec: serviceAccountName: golang-external-secrets automountServiceAccountToken: true + hostNetwork: false containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -7836,10 +8300,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.1 + helm.sh/chart: external-secrets-0.8.2 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.1" + app.kubernetes.io/version: "v0.8.2" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -7859,7 +8323,16 @@ spec: automountServiceAccountToken: true containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.8.1-ubi" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" imagePullPolicy: IfNotPresent args: - webhook From f6729507ae60f51cbfd7f8059bdc9c363a140223 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Apr 2023 11:51:49 +0200 Subject: [PATCH 0900/1263] Move to new spec format for dex/sso Via https://issues.redhat.com/browse/GITOPS-2761 we are told that the dex configuration has a new format. Old format: spec: dex: openShiftOAuth: true resources: ... New format: spec: sso: provider: dex dex: openShiftOAuth: true resources: ... This format is only supported starting with gitops-1.8.0, so we should merge this only when we are absolutely sure that no pattern in no situation needs an older gitops version. Tested on MCG with gitops-1.8.2 Note: with this change gitops < 1.8 is not supported. Starting with gitops-1.9 the old format will be unsupported. --- Changes.md | 1 + clustergroup/templates/plumbing/argocd.yaml | 20 ++++++++++--------- ...roup-industrial-edge-factory.expected.yaml | 20 ++++++++++--------- ...tergroup-industrial-edge-hub.expected.yaml | 20 ++++++++++--------- ...rgroup-medical-diagnosis-hub.expected.yaml | 20 ++++++++++--------- tests/clustergroup-naked.expected.yaml | 20 ++++++++++--------- tests/clustergroup-normal.expected.yaml | 20 ++++++++++--------- 7 files changed, 67 insertions(+), 54 deletions(-) diff --git a/Changes.md b/Changes.md index 3902b67a..758128d5 100644 --- a/Changes.md +++ b/Changes.md @@ -3,6 +3,7 @@ ## May 22, 2023 * Upgraded ESO to 0.8.2 +* *Important* we now use the newly blessed sso config for argo. This means that gitops < 1.8 are *unsupported* ## May 18, 2023 diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 949c77cf..84643644 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -82,15 +82,17 @@ spec: requests: cpu: 500m memory: 2Gi - dex: - openShiftOAuth: true - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 250m - memory: 128Mi + sso: + provider: dex + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi initialSSHKnownHosts: {} rbac: defaultPolicy: role:admin diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 2d3d7a96..c95c7f79 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -513,15 +513,17 @@ spec: requests: cpu: 500m memory: 2Gi - dex: - openShiftOAuth: true - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 250m - memory: 128Mi + sso: + provider: dex + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi initialSSHKnownHosts: {} rbac: defaultPolicy: role:admin diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 1982541e..1e25dbf7 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -1146,15 +1146,17 @@ spec: requests: cpu: 500m memory: 2Gi - dex: - openShiftOAuth: true - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 250m - memory: 128Mi + sso: + provider: dex + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi initialSSHKnownHosts: {} rbac: defaultPolicy: role:admin diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index cd66eaca..5afe1d74 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -1322,15 +1322,17 @@ spec: requests: cpu: 500m memory: 2Gi - dex: - openShiftOAuth: true - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 250m - memory: 128Mi + sso: + provider: dex + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi initialSSHKnownHosts: {} rbac: defaultPolicy: role:admin diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index b01ed5e0..548e300e 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -336,15 +336,17 @@ spec: requests: cpu: 500m memory: 2Gi - dex: - openShiftOAuth: true - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 250m - memory: 128Mi + sso: + provider: dex + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi initialSSHKnownHosts: {} rbac: defaultPolicy: role:admin diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 641a1850..07e6a772 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -934,15 +934,17 @@ spec: requests: cpu: 500m memory: 2Gi - dex: - openShiftOAuth: true - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 250m - memory: 128Mi + sso: + provider: dex + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi initialSSHKnownHosts: {} rbac: defaultPolicy: role:admin From 8ed17fcc93acedf8aadc193388ba79d93ce93aed Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Apr 2023 13:08:10 +0200 Subject: [PATCH 0901/1263] Disable ArgoCD from kubeconform The reason is that most of the tools we used to generate the json schema, seem to be unmaintained, so it is getting hard to update our schemas in our GH org. We'll need to revisit this in the future. --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7159835f..83871b7e 100644 --- a/Makefile +++ b/Makefile @@ -125,7 +125,8 @@ helmlint: ## run helm lint @for t in $(CHARTS); do common/scripts/lint.sh $$t $(TEST_OPTS); if [ $$? != 0 ]; then exit 1; fi; done API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.10/ -KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition,ClusterIssuer,CertManager,Certificate' +KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition,ClusterIssuer,CertManager,Certificate,ArgoCD' + # We need to skip 'CustomResourceDefinition' as openapi2jsonschema seems to be unable to generate them ATM .PHONY: kubeconform kubeconform: ## run helm kubeconform From bf564402d7024058f3e4f5521f0b1c8be484ff56 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 24 May 2023 15:57:51 +0200 Subject: [PATCH 0902/1263] Add a short line about username/token for the iib role on OCP <= 4.12 --- ansible/roles/iib_ci/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 277d991c..b6e2c9d9 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -58,6 +58,8 @@ registry. Run `make iib` with the following environment variables set: * `REGISTRY=quay.io/rhn_support_mbaldess/iib` * `REGISTRY_TOKEN=:` +*Note*: For the REGISTRY_TOKEN go to your quay repository, add a robot with "Write" permissions. The robot created will have a "username" and "password" fields. Set the REGISTRY_TOKEN environment variable to that value. + ## Useful commands * List all images uploaded to the internal registry: From dfc504e6ec0c514823ff81367d8f134033107a64 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 24 May 2023 21:23:42 +0200 Subject: [PATCH 0903/1263] Drop https:// from podman login Seems we hit https://www.github.com/containers/podman/issues/13691 at least with older podman versions. If this turns out to break podman 4.5.0 I will special case it later --- ansible/roles/iib_ci/tasks/setup-internal-registry.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml index db30627f..82ee7ac4 100644 --- a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml +++ b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml @@ -97,7 +97,7 @@ - name: Login the internal registry with podman ansible.builtin.command: - podman login --tls-verify=false --username unused --password "{{ kubeadmin_token }}" "https://{{ registry_route }}" + podman login --tls-verify=false --username unused --password "{{ kubeadmin_token }}" "{{ registry_route }}" - name: Set Mirror URL fact for internal mirror IIB ansible.builtin.set_fact: From 1edf4da39851fd95fa17a165700da4175dc279c8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 25 May 2023 10:04:28 +0200 Subject: [PATCH 0904/1263] Set the mce-subscription-spec annotation We set it by default to "redhat-operators" and if defined to .Values.clusterGroup.subscriptions.acm.source The reason we do this is the following: 1. In a default deployment scenario MCE has to be deployed as normal from the redhat-operators catalogSource just as ACM is 2. When we deploy gitops-operator from an IIB instead, MCE would be installed trying to get it from the IIB because https://www.github.com/stolostron/multiclusterhub-operator/pull/975 made it so that it picks the latest version looking at all catalog sources. But since we only mirrored the gitops operator in the cluster, this breaks as the images for MCE from the IIB are not there By setting the default to "redhat-operators" we fix this case 3. Now in the case where we want to install ACM from an IIB we need to be able to override this and we will pick whatever value is set in .Values.clusterGroup.subscriptions.acm.source, which will need to be defined for this to work when testing ACM+MCE from an IIB Note: Currently point 3. works only if you set it in a values file. Setting .Values.clusterGroup.subscriptions.acm.source via extraParams won't be passed down from the clusterGroup app to the applications. It's a bug that we need to fix. Note(2): We surround this with an 'if kindIs "map" .Values.clusterGroup.subscriptions' because we do not want to break things if subscription is a list and not a map. If we ever manage to drop subscriptions as list, then we can remove that if --- acm/templates/multiclusterhub.yaml | 3 +++ acm/values.yaml | 3 +++ tests/acm-industrial-edge-hub.expected.yaml | 1 + tests/acm-medical-diagnosis-hub.expected.yaml | 1 + tests/acm-naked.expected.yaml | 1 + tests/acm-normal.expected.yaml | 1 + 6 files changed, 10 insertions(+) diff --git a/acm/templates/multiclusterhub.yaml b/acm/templates/multiclusterhub.yaml index f925d5a6..79ef9339 100644 --- a/acm/templates/multiclusterhub.yaml +++ b/acm/templates/multiclusterhub.yaml @@ -5,4 +5,7 @@ metadata: namespace: open-cluster-management annotations: argocd.argoproj.io/sync-wave: "-1" + {{- if kindIs "map" .Values.clusterGroup.subscriptions }} + installer.open-cluster-management.io/mce-subscription-spec: '{"source": "{{ default "redhat-operators" .Values.clusterGroup.subscriptions.acm.source }}" }' + {{- end }} spec: {} diff --git a/acm/values.yaml b/acm/values.yaml index b7c27d88..1f430370 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -9,6 +9,9 @@ global: clusterGroup: + subscriptions: + acm: + source: redhat-operators managedClusterGroups: # testRegion: # name: region-one diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index f9772238..77080dbd 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -10,6 +10,7 @@ metadata: namespace: open-cluster-management annotations: argocd.argoproj.io/sync-wave: "-1" + installer.open-cluster-management.io/mce-subscription-spec: '{"source": "redhat-operators" }' spec: {} --- # Source: acm/templates/policies/acm-hub-ca-policy.yaml diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 5e064480..fce91f4b 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -10,6 +10,7 @@ metadata: namespace: open-cluster-management annotations: argocd.argoproj.io/sync-wave: "-1" + installer.open-cluster-management.io/mce-subscription-spec: '{"source": "redhat-operators" }' spec: {} --- # Source: acm/templates/policies/acm-hub-ca-policy.yaml diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index 86d7277d..cb73d733 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -13,6 +13,7 @@ metadata: namespace: open-cluster-management annotations: argocd.argoproj.io/sync-wave: "-1" + installer.open-cluster-management.io/mce-subscription-spec: '{"source": "redhat-operators" }' spec: {} --- # Source: acm/templates/policies/ocp-gitops-policy.yaml diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 79a9dde4..5b98b4b2 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -399,6 +399,7 @@ metadata: namespace: open-cluster-management annotations: argocd.argoproj.io/sync-wave: "-1" + installer.open-cluster-management.io/mce-subscription-spec: '{"source": "redhat-operators" }' spec: {} --- # Source: acm/templates/policies/acm-hub-ca-policy.yaml From 53968719c91679c6363e2d8bba410cd8684e8993 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 25 May 2023 15:49:54 +0200 Subject: [PATCH 0905/1263] Fix typo in README for iib --- ansible/roles/iib_ci/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index b6e2c9d9..b6ac5c37 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -46,7 +46,7 @@ make EXTRA_HELM_OPTS="--set main.extraParameters[0].name=clusterGroup.subscripti Since 4.13 supports an internal registry that can cope with v2 docker manifests, we use that. Run `make iib` with the following environment variables set: -* `INDEX_IMAGE=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329` +* `INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329` * `KUBEADMINPASS="11111-22222-33333-44444"` ### OCP 4.12 and previous versions @@ -54,7 +54,7 @@ use that. Run `make iib` with the following environment variables set: Due to the lack of v2 manifest support on the internal registry, we use an external registry. Run `make iib` with the following environment variables set: -* `INDEX_IMAGE=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329` +* `INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329` * `REGISTRY=quay.io/rhn_support_mbaldess/iib` * `REGISTRY_TOKEN=:` From 058d5c68795e6b52c329fbf9d3d6ee1f8db7612e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 25 May 2023 16:02:17 +0200 Subject: [PATCH 0906/1263] Simplify the README a bit --- ansible/roles/iib_ci/README.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index b6ac5c37..f36caec8 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -13,30 +13,33 @@ that the images used are the ones pointing to the internal cluster. ## Usage By default the operator to be installed from the IIB is `openshift-gitops-operator`. You can override this through the `OPERATOR` env variable. -For example, to install openshift-gitops from an IIB on OCP 4.13 you would do the following: +For example, to mirror an operator into an existing cluster you would do the following: ```sh export KUBECONFIG=/tmp/foo/kubeconfig -export INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:iib-492329 +export OPERATOR=openshift-gitops-operator +export IIB=492329 +export INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:iib-${IIB} export KUBEADMINPASS="11111-22222-33333-44444" # This will push the IIB and all the needed images for the default openshift-gitops-operator into the cluster make load-iib # This will install the pattern using the gitops operator from the IIB -export CHANNEL=$(oc get -n openshift-marketplace packagemanifests -l "catalog=iib-492329" --field-selector 'metadata.name=openshift-gitops-operator' -o jsonpath='{.items[0].status.defaultChannel}') -make EXTRA_HELM_OPTS="--set main.gitops.operatorSource=iib-492329 --set main.gitops.channel=${CHANNEL}" install ``` -To install ACM from an IIB we can run the following: +Then in case of the `openshift-gitops-operator` we would install with: ```sh -export OPERATOR=advanced-cluster-management -export KUBECONFIG=/tmp/foo/kubeconfig -export INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:iib-499623 -export KUBEADMINPASS="11111-22222-33333-44444" +export CHANNEL=$(oc get -n openshift-marketplace packagemanifests -l "catalog=iib-${IIB}" --field-selector "metadata.name=${OPERATOR}" -o jsonpath='{.items[0].status.defaultChannel}') +make EXTRA_HELM_OPTS="--set main.gitops.operatorSource=iib-${IIB} --set main.gitops.channel=${CHANNEL}" install +``` -make load-iib -export CHANNEL=$(oc get -n openshift-marketplace packagemanifests -l "catalog=iib-499623" --field-selector 'metadata.name=advanced-cluster-management' -o jsonpath='{.items[0].status.defaultChannel}') -make EXTRA_HELM_OPTS="--set main.extraParameters[0].name=clusterGroup.subscriptions.acm.source --set main.extraParameters[0].value=iib-499623 --set main.extraParameters[1].name=clusterGroup.subscriptions.acm.channel --set main.extraParameters[1].value=${CHANNEL}" install 2>&1 | tee /tmp/acm-install.log +To install ACM (`export OPERATOR=advanced-cluster-management`) or any other +operator (except the gitops one) from an IIB we would call the following as a +final step: + +```sh +export CHANNEL=$(oc get -n openshift-marketplace packagemanifests -l "catalog=iib-${IIB}" --field-selector "metadata.name=${OPERATOR}" -o jsonpath='{.items[0].status.defaultChannel}') +make EXTRA_HELM_OPTS="--set main.extraParameters[0].name=clusterGroup.subscriptions.acm.source --set main.extraParameters[0].value=iib-${IIB} --set main.extraParameters[1].name=clusterGroup.subscriptions.acm.channel --set main.extraParameters[1].value=${CHANNEL}" install ``` *Note*: This needs VP operator version >= 0.0.14 From 04325363e34cf8b8ab057f6cc87e1a99627970d9 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 30 May 2023 10:43:15 +0200 Subject: [PATCH 0907/1263] Add support for extraParams being passed down to all applications Via https://github.com/hybrid-cloud-patterns/patterns-operator/pull/74 we add the extraParams in an extraParametersNested dictionary that holds the extraParams key/value pairs. If they exist, let's add them as parameters. This allows them to end up in the applications. --- clustergroup/templates/plumbing/applications.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 2593942f..8ed7b4b6 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -111,6 +111,10 @@ spec: - name: {{ . }} value: {{ $.Values.global.pattern }} {{- end }} + {{- range $k, $v := $.Values.extraParametersNested }} + - name: {{ $k }} + value: {{ $v }} + {{- end }} {{- range .overrides }} - name: {{ .name }} value: {{ .value | quote }} @@ -208,6 +212,10 @@ spec: - name: {{ . }} value: {{ $.Values.global.pattern }} {{- end }} + {{- range $k, $v := $.Values.extraParametersNested }} + - name: {{ $k }} + value: {{ $v }} + {{- end }} {{- range .overrides }} - name: {{ .name }} value: {{ .value | quote }} From 48126e7988160bc45ab77eeb61b6af0a24de4a1a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 1 Jun 2023 16:54:28 +0200 Subject: [PATCH 0908/1263] Add a lookup playbook to figure out IIB numbers --- ansible/playbooks/iib-ci/lookup.yml | 46 +++++++++++++++++++++++++++++ ansible/roles/iib_ci/README.md | 13 ++++++++ 2 files changed, 59 insertions(+) create mode 100644 ansible/playbooks/iib-ci/lookup.yml diff --git a/ansible/playbooks/iib-ci/lookup.yml b/ansible/playbooks/iib-ci/lookup.yml new file mode 100644 index 00000000..f7e42ca4 --- /dev/null +++ b/ansible/playbooks/iib-ci/lookup.yml @@ -0,0 +1,46 @@ +--- +- name: IIB CI playbook + hosts: localhost + connection: local + gather_facts: false + vars: + rh_url: "https://datagrepper.engineering.redhat.com/raw?topic=/topic/VirtualTopic.eng.ci.redhat-container-image.index.built&contains=%s&rows_per_page=20" + operator: "openshift-gitops-1-gitops-operator-bundle" + ocp_versions: {} + tasks: + - name: Set url fact + ansible.builtin.set_fact: + url: "{{ rh_url | format(operator + ':v') }}" + + - name: Fetch URI + ansible.builtin.uri: + url: "{{ url }}" + return_content: true + register: jsoncontent + + - name: Setting content + ansible.builtin.set_fact: + content: "{{ jsoncontent['content'] | from_json }}" + + - name: Set messages fact + ansible.builtin.set_fact: + raw_messages: "{{ content.raw_messages }}" + + # The when clause is because if we already have an IIB for an ocp version we do not + # want to override it (combine will always override existing keys) + # Reason for this is that the messages are sorted last first and we only want the + # last entries + - name: Set output + ansible.builtin.set_fact: + ocp_versions: "{{ ocp_versions | combine({item['msg']['index']['ocp_version']: {'indeximage': item['msg']['index']['index_image'], 'bundleimage': item['msg']['index']['added_bundle_images'][0]}}) }}" + loop: "{{ raw_messages }}" + when: item['msg']['index']['ocp_version'] is not in ocp_versions + loop_control: + label: "{{ item['msg']['index']['ocp_version'] }}" + + - name: Print OCP versions for "{{ operator }}" + ansible.builtin.debug: + msg: "{{ item.key }} -> {{ item.value }}" + loop: "{{ ocp_versions | dict2items }}" + loop_control: + label: "{{ item.key }}" diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index f36caec8..4cfdcbd6 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -65,6 +65,19 @@ registry. Run `make iib` with the following environment variables set: ## Useful commands +* List IIBs for an operator: + +```sh +ansible-playbook common/ansible/playbooks/iib-ci/lookup.yml +... +ok: [localhost] => (item=v4.13) => { + "msg": "v4.13 -> {'indeximage': 'registry-proxy.engineering.redhat.com/rh-osbs/iib:509435', 'bundleimage': 'registry-proxy.engineering.redhat.com/rh-osbs/openshift-gitops-1-gitops-operator-bundle:v99.9.0-106'}" +} +... +``` + +Override the `operator` value with the desired bundle name to figure out the last IIBs for it. + * List all images uploaded to the internal registry: ```sh From b1070a16a7c3a677f2e48b2d52a423d4cc63f1d1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 1 Jun 2023 17:34:08 +0200 Subject: [PATCH 0909/1263] Allow overriding channel and source when installing the patterns-operator This will allow us to test the patterns-operator using a different catalogsource (potentially installed via an IIB). So we can run: make EXTRA_HELM_OPTS="\ --set main.extraParameters[0].name=main.patternsOperator.channel --set main.extraParameters[0].value=slow \ --set main.extraParameters[1].name=main.patternsOperator.source --set main.extraParameters[1].value=patten-index" install --- operator-install/templates/subscription.yaml | 4 ++-- operator-install/values.yaml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/operator-install/templates/subscription.yaml b/operator-install/templates/subscription.yaml index 381e185f..41d0d211 100644 --- a/operator-install/templates/subscription.yaml +++ b/operator-install/templates/subscription.yaml @@ -6,8 +6,8 @@ metadata: labels: operators.coreos.com/patterns-operator.openshift-operators: "" spec: - channel: fast + channel: {{ .Values.main.patternsOperator.channel }} installPlanApproval: Automatic name: patterns-operator - source: community-operators + source: {{ .Values.main.patternsOperator.source }} sourceNamespace: openshift-marketplace diff --git a/operator-install/values.yaml b/operator-install/values.yaml index 5f7cecc2..d5b0b13f 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -7,4 +7,8 @@ main: channel: "gitops-1.8" operatorSource: redhat-operators + patternsOperator: + channel: fast + source: community-operators + clusterGroupName: default From 8f3c4071b90f4d92a3e3ec600e02eeee37c9af84 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 6 Jun 2023 16:38:06 +0200 Subject: [PATCH 0910/1263] Fix small typo in iib instructions --- ansible/roles/iib_ci/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 4cfdcbd6..1d8b447e 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -19,7 +19,7 @@ For example, to mirror an operator into an existing cluster you would do the fol export KUBECONFIG=/tmp/foo/kubeconfig export OPERATOR=openshift-gitops-operator export IIB=492329 -export INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:iib-${IIB} +export INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:${IIB} export KUBEADMINPASS="11111-22222-33333-44444" # This will push the IIB and all the needed images for the default openshift-gitops-operator into the cluster make load-iib From 611696439b09ff16fc72c97c8900545181a51a86 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 7 Jun 2023 15:23:29 +0200 Subject: [PATCH 0911/1263] Drop a redirect and up retries when pushing the IIB to the internal registry --- ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml b/ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml index 54d875e3..4b39184c 100644 --- a/ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml +++ b/ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml @@ -23,12 +23,12 @@ - name: Mirror IIB to "{{ mirror_iib }}" ansible.builtin.shell: | oc image mirror -a "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" \ - "{{ iib_image }}={{ mirror_iib }}" --insecure --keep-manifest-list > iib.log 2>&1 + "{{ iib_image }}={{ mirror_iib }}" --insecure --keep-manifest-list 2>&1 args: chdir: "{{ iib_local_folder }}" register: oc_mirror_result - retries: 3 - delay: 2 + retries: 10 + delay: 5 until: oc_mirror_result is not failed - name: Template mirrored catalogsource From 6e6f2581b9fd2596e3fea8dcd0f1a3d054920478 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 15 Jun 2023 11:03:57 +0200 Subject: [PATCH 0912/1263] Update ESO to v0.8.3 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.8.2.tgz | Bin 78582 -> 0 bytes .../charts/external-secrets-0.8.3.tgz | Bin 0 -> 78591 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 74 +++++++++--------- ...-secrets-industrial-edge-hub.expected.yaml | 74 +++++++++--------- ...ecrets-medical-diagnosis-hub.expected.yaml | 74 +++++++++--------- ...olang-external-secrets-naked.expected.yaml | 74 +++++++++--------- ...lang-external-secrets-normal.expected.yaml | 74 +++++++++--------- 9 files changed, 189 insertions(+), 189 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.8.2.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.8.3.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 4f30d0b6..74b1c051 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.8.2" + version: "0.8.3" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.8.2.tgz b/golang-external-secrets/charts/external-secrets-0.8.2.tgz deleted file mode 100644 index 2b2d939798577aa57e08f21ab4a2110b6395905a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78582 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≪0Q1jt^qXiICVFi0H8ob zaAEY~O|j?PUPTGhPo?i3z+Z#C(#{^7go8bm1uVCh72PJI-hkc>*123Zx$hn4;!Z*yGM@|g2Ibt|_CFYzAr+$DQ3HN0NMkb5!>dKp5XKV@U zK$+j@Fi(0pv0g=^5^q#!Q=bx%6P_2E8ze=femQeYG~v|i9p**M^0`*6XahZL=L-N| z3Fd^~gaA|{&uWNf_Pcd}HUd3#N;z-yukj0&FO5!aH9fv`;lRu?#=7-1{zM)0ljI5) zCG98Tz<6k)gTQ_vkLa|ft!&sP?fYEK0%2UmUY^&wEC3DzE`#Wfs58O}udL@n- zGQ_y1`P*7n%*chsZCElR?+LE);jlKyo2wl7K{U;(v*MB=o2xSWh+t86TNdRtQ8x$d z&=S)ifaLl?20Dr2+Bt!~FSpT`+vv+}6gsR^ZbLiI?L{{9U~4-SlDPa@1*`joR8y=H zjAQ)w%k#Iodhh|PdwBfr|rIBcPUyDG*u zh(}-eVOx_ng^7FZ-371`Mt0Ui(Kuf&6Ofz8z^>#joT|()E942XPVcCqubh0%_e>0w z66ROG9WMP}&t4llgey>9{e~r>oL}Rhm;^ju4Tj>>3=)AG8IiDnCm%|iTJ(olN>sx) zMQrGn39XPa)kw!W%3_{fII(9I1%$-PGg=FWY5)=)43QdbVC5%3Jwp{wsW zW}%3@0`le5hOb~9Z;c$KtY(my9FI!)-!55rkg+#x%oBDwwjk%C^KyIazka*m?3|bR z^OO}Pmj?BTn_fM+H-Pwjq_ zp+pGfHgX_YxGa|Jf@nLHMYTe#(k)JD;e6gtoDawj2?;?^SelS?9?|~7yB-y>l9A_z z;+7OhL~OwaBKQJa^L{E{OSSaYk}KJG`!!Y`D~4W8Jltm_SH;9ZY;_7PwvmQ;WQx#2 z+LDSHI4ARvDfXq57*o0Jg??a2Dgjvh{NHk&lak3I`z(*QB{W%iJ0>|ovLWH3q40Vj zqEw<_dI+DZ-OB}Pro3{B1L_28ud+oVgO>5iMWU#a9W&xsJ*;p6)3P@O8W4i!=f8&3 zz;m}k?A+DNM-7It%XEPuuRA2hIZmJ_%AY4uwoL|T=-X~V>&+n;D)GUfsm8Onv^Iv-n29cC!(qKH zd%w#36*;eD=4IjE>>#cSb>@O6K0A@cE)=hID27n=Hi}nS)SM8)9%y`ep1SqvdFpS2 zd|UCy9h$;klgMo%9QDxRjHnuoZBoCJ)ZV78LYsxNjW+!PCb@2avX@&jk z#l`74(wH=wMgghnNEPRaD9t-q&jr^w(LTd&(j~~S$HnV&6bwT^P)iK}5VjU>`ofvq zHRgOXTJx!U+F*{aMEZqhgjbrm4DFoe-W>5=m{i*-cO}JEVP{Bpv6i(sU21lSLNTE| z4n?D-Bhh@oG;B~G!IC}3$IpboHWF6hGerJp=uwuIC)7qWtbjElrIirNkPxkI8ll^- zi*#QLMw9FU<-k0I0}z^_jrVl2!X&|oTKYXy91o6Uo~J<@)a|Jp@fkV>72mm``gAww zGE>PJC<7b9x^#dVuLF}Dxkbm7rlz{HNKLy|sDPkN+(~UZyF4SzZS7opM4zJ|TFpA~ z>}hc^se)KK$gqmi)$B<~?BU;J9C52-Ol^R}Cyo=l?0|>5j+7FHC#k|4WAW_l{IE-f zY2?qQl13fIMs+)^_$?ft>QVgU2i~bsDK*=i_EF$%1M-BUzD*ruEXKC64iwhMutyH# z27Lq$4O(hg`Lreo?LaHi=UhdBDjk@~>Kr?+XqMR_$H(6C@UwS!a%|+cS2-!XpbxWw zdJ{yiBiP0idL4mLb1SVP99ZE*-_7Y4JEW%oJq6gp6nY9^)O<_M9(gXordxr;>`;Fh zdvjR-X3vUB8S#udY6o5E)mmi04782w%^>A$crE9)w&;9o7!vc_xt+)i?FF59L&X;2 zoy*PlNn+Mx$1rXVb+qK_@8Q05{4#)}I zAvb;;g_P_i@w_ZhZx&6H5ezeO7w+#-IGRR_F^o6_poVh{66ZiYM8j~t0#uD9#sQ;4 zXgRt{7r}RIxx)-CLk)nYS|z5J=|68<)0r9w{eU*!1C?%}ok}j7yCs+GqBV|^y?<`+ zpIhe8QH&~1HsLHFAr#+DImIWn}_=`Yf>o&xj~U<*^|DS%P)ojb&_(#2ez z1Z>i_RY`12HU{Y|3Osh;rGFis{i5Xt;Z0vu+pqxVCdsOir8R=JBC};9xI#Ko${dDO zO0FWs)g<%a%j#~|=P{L!Ng-EJ>qDTVr7<~Zdzh9?Wu>crI?@pGZfs~E@U*sy@b^{h zUEi3g39;?Z@9b2C*vG+Zw7bkXyGucXz>nZ~OE-9ECUz_4_nZ1lH=us4K7OWb0H+T- zX%#`sA`9VJc9#@d9-<{f8`K2BH@m*tz>kk13-cAQFIE?tDciDrmc<%uV^-yhXHzUt zW6YlLp*%OLB3N^-kF+8Bv)!NV4#Dovc5Tyc}_Gv&-DW zCfYJNjditQm+p1#X(|*iyY}?ebcGU(t`5vz?oW}+8gry8m4Mtp<5iV_U4IP7rn!)3 zB;r|6U_wFnBffE$1|9)c)j!t)#>!4{Q(Y-y0{~2YR@$r2Tgg047ploYoz+)LZ-rw% zT_&}>eD0J2&vO38N?rXbim*m2$AX>)Zi+sopW4@TLswV8LI=F7vB!~}i)U}Q{}PpB z`1E#r+j)FxtoNE|+J?X5Z9y9Ek_ZX(y%y!8I_D3>_*)3rR$~KqgD5o1FKQWW-aFxc z1Bw^<^iKHw?eVQU;oHPb7VZx79tMdjH1D2_{O-_jwKTa@ibblqhwLk=Q70;9T;#~w zOcT^DA^K_HPRv9^KYi^^>|{jl^3IKYtRtO#8hNnb%ETZJ7eYj>aAtBP;8INJ!lGd; zvDwFQP_7tzZX@g4AOQBMOnV7%{VDW!xKZ;iB|ua2j!6cBtzUkbxZqY^gq)C6T!kB( zZdMjn8_cF!ut60r1#B{MjRicGUmEziWV99C7jL^D%{=`f@eDb56av0lxqAY=H z-T1h?2(z4pXxhcSzfEoM((hHlJtI($qn;7y8G%+tAnA$`5E8hJZ-me^iPYtY=9jWW zJpM**XjvV@sazZphosDmQf(;D^5=c&W~CgB?q02qt<4pjD}XCD`xkN>=Rh}+!3}Ds ztETNW)NQbV52lN^axA-YMUDt|^*w_Alp=!v#9P>vsV3-t=W?2z+HvG}H@8vr1yp)w zqh~h0T4uw^s8UZWT4&tT3Nsw_wBl=|6&5+UbX%Pj??l2uBFKqB)QYtpWBt5~y}w8A z@1dUF-=n9*9i!>f`+FESO-(G)xizE$RvZn&6r!r&c!#8W;<>RGuW32&`P!bZt;A8! z*M2e7^poXGTSTe%i^&S9R0rZ8rb^c{`h;G^up$!}#TjmDP)GL7qBLj0o~|i*c_8O- zHiMU)RBkgV)>;Da=#V=xIp)%>L#=eHRv5HpSi>Ty_!#e7vtp>FN!`yS-}gSk84TTE zDE7g~kH$CqqbH+-G2UY1?7Zch zT7a*Y>*gXy#}U#sil=mXzWqqZ;h&G(OGY?Nsa8+(lYXB@l zFGu%s^!iii)wdu^RfeFZ_t*5-4(rl`zmKef$dvtOvVqJ(+%tAgpOM8do zT7mkj0K^SNWFbRYbiWnW+8yVkGx1X!LPdtpZ~4|K3_bU$0+GRE)RNG*Tso?)v+$-@Zs;eAy=+1;y$xq_-_l}{ zES~c>FEAEeioQ#?dF3)32u&8?457AVmG7Uzhr}T*;U;5&orycW#ypy^a~DX^85@Mz zJ@mAB;S2->gEnDmbY>p# zo~rN=3o63fp^7P&!GtaY1)GUaAp_NmV7&;o{uFu<%&2+iA{bs`2F;kh8#FXy>urk2 z*oI?q{8gF7xOja|?}WuaaVB@9E5|*;K@qw`z@Dyi=5Q`oe_#3xUO5Xdy^7kfw4=zb zqbwMW+#ht)`$)?UM+M`#`$n4@j!<^eZ)r>GA#i!p*#oOFyQw1f)C)XB)0*Hk_0CbPpnfg5!RE z%X_eoc|%s{QD``4>HqDA>9Z$R3j#g7w&L3ISu5o@K9yC)6%nB`rtxyAc9pvt-7C;4 zQ{31R5qzu(NokhfV8|*ymUIN4HE2oJ&77X#W5ZqIv)eG%Y*v=Yu;w^RbNf=c&NWeH z7ut%Cad>M6k)fDuA82cvdfXZ$Yr`RM-kTZ?k+-(D7s7|PV=G+CQpk!-oC%2x^;%3N zG;F5H+>td|TZ3vUFy=$DC)nuLO9S8mscRHKPUfkPrbEeiapZzksN`mz_EJS9{# z%@J?-$^ZyiyFH6QSUyU)uvj+SV3!f99~wY_lze}8=VIiCGAnnnQzy~3-d8v}+@&V1pEWkdFG835BreN-1@B}14v zCJ;iU2dREMPRg0w!7465_=$3HVO+wtG!yKB0cP5O4Y)E)Cu_9YW@%(gr&cA&l$>+6 zOd@Tj(LyzR(v&6}(Bn^=D%SYWnUK(Jr-!uy)`)O+pAyy>Mw3*qJ4*v=mIBr|{i}KE z*NzkKwDN`s<-Shp1wkSR5oSQ=CNOJvHgqrX z!o7|C)RT_o>b~V30W`xafmQanJraMh1d;$t72ioK0jV2;C|ZiUWa@^{1(wr={R6~| z=r$_W#ibMxytwJT(47R{Opr(KrFAr%P^&cUzAi8=o?K`E~^*b_;D02JW&z~tuRrX;)#NhoHx+MABucXBca3! zlY_y?d4q|1+zA@D!wei;K46(I$y`m^veqT>6nx`spe7xe(cx^<3RYmv-MS@;l6Y|FK~m+60d%V%Qf5AI_<&1qrt0x(N0_SLP`z8nbK&9sN7zJIj8KiGT=iKEaw9igeed1N~2cjADFNvjiEc?`9y23FYfQQ&t5 zHwOb^Pi`K;L6+m`q9na(0H~c?89Fyj<&vpE)08_(0oRb~Tsq^|UV5#J$gMZIilS?y z9B{vJTg$54b1(E_%%$kT-X!`VzFG506RBtt%^F3b9N0fXM~(Yk0`v2EM7ZQHh;RBYR}?WAJcwrx8V+|*jT-FxnC=gXXb z;2raAWAy&?VLorFot|*!OR8XwcoQf00Fa#&1X*f`5-Q!;v34&CMb^q_beIiA3yUH_ z-daE*M{f^-m_|%hMFyhf%rd5pBw}@_K&zG?XO7t3_}VRaLbPirNGLmNa2@Ek@|zZ> z6Mc#%+Z41bK|)%C#Dv!zhNZetW3`m}BZx8uw%YXl+#=XrGqllZjp6s|8zl7bGA;IT z4QGe_q6Ux~6j?Uiwp*tx4lhzEyiyMeQLfqobb;#em1(Wbq#9E7q}?`@ZO@tajv`QP z!h#=fD@utiytN+F0JMYZP7}3+@PT7dnwLx*wUQW#Ay2$ib z@wcM+R&;7yJ~|G_oTaEpOwsnXJHG+ai^1$1@=gpoN8zlL9=H4b>(_vcYle3?KpvM5 zdB3Q$j?1Y52Eyc$vU7&hNm>;*4Soku4ss9T(AUlOrP+4 zhcTZyW#9a)Z(fJqH!>TEZ60-=jA|QAP>d~G_cPJ8V=HG_k;lroSBAU5*=$3Wi8BB& z0SH5Nzf)`Q+*CYfSOFV;RE|4(7xh_0TizNb!4|UBB8c2PBH+01cDd%u0MMvqx}!ST z@iH8kEfG}`XJzy5X@z-?(q#@-o{U)&R-=(bZisx|A*8t`@wC?D#ph(G#k>;h9!@|u z2|B~naYZqL@%bk*fos6d)==`2m5JLcF?aujiOxMMQNzYe=C(+5Xoap>RUzigNlh^G zLVh6tqpJ(eg?I#q3dP|?=c?Cqwl^rU=wyEY6JKrhJTZX+A8naKm?8o#{GGRh8BcDM zE$-kXV%~^=CBdENfl12!C{*2ZLM}$ZT=_O%gV|u)ox+YC4zH&MW~9xLB_JmK!pqIi zPd?m_6L8PZ+ksy^%h#ZA-wgX#YVV8g4sDbz{6{aF#awqlcyQJhtA*#O$AN5eB~4?m zm5<$TPMf#K`hb-LI%x@K$u0JcCP0kHxH&nUo?g0c3Y3XLAHkwzbCI#xsB4+YUUFTr z6r%GW)-9=TwDue1I6-2q$^S)|V6)7)& z1~AN~4bN@iYfRlZ;8~Gtt4hY5`m<>A+3kq-;u6`%RGRjQ>Dbh#h@MO4xQS&TV@uK) z8W4(|iI8eW5&wu?=a1Tr;JWkvSiZoP3Ff--bV+6hn{r|+J7lM`?9r%^DyOBcKMv=S zoT}e|G53RQr+(J!Lq3*V?XGG@!N?tY91F3&(7IRQ&?T1$WCU(t^WgnDoD?u)>_x6O zY^|$^tHG-hd9px}8QlzBQ<6*A?>(Rdn2g{tj zV7TvM&>Vv1QgNEDh{FDv&&w^*ZZxE953;io=B77kR&ru$fmieC^0Wpe__PRXsRu3v z-)BE?lAYPEY$rdgDpyMSAK=&3v!#hxx81~c8YLjTdMYFw>6oC*RKH0?b%=p~|sCfz$X0<23(N_37QoZdYJ3pTEBzQWg&cI*AHI~?(m+A4kCP%M> zQ+MGi8c9XO2rYQixm~EiYPlH=QfGq+Hf7etB~=1y@r} zwLAHoDlmP2&xtpke8Lq!W+q~Jx5ZjzN8(d^YagXf63y(ZN~gEXwL++4<%QFJN0U*i zWC*+QFiY{K!kEe!$8Dz)(kFsN?<|M`td%9r zT16nw6D?qYgU)Vyv<@xb{)Z{7o4J=&A#J}!)`&i{XD2RxXf^r+xD{wLIXhE-BJg6& zC{L7bXQMoGzZB{i5!I=IAHWC8Dpht|p9K4EWS%qWVzq$R>_1xFa~!3*Q#LnZkaF$_ z{)!RtnIH<~yFOgYT;)YnCpGj<9<|fWnG8?|R9uFkr9dm#HQB1;D!$Lv?V>>Iw zC$3=W>3%J(4NQkPMgy|rpoWsa9QI%m2+}0NQ#*nX_KG>VTs2wPf1Iq$SwpuOi%H^G zK{$Zwv$|Xqz2&$!2=;?Earr_E>XRE|&xslRTz4;tM7^Fi_U6BzE}PrPqJfW=><(`q ztg!32>4ZxwC&$vJGLY>eMK#SAgXdDcGTu@JY?HD!;ap2|0XX1oi3rG$St z4c2?C@}~OcPVD4K{X9H)cfb|C8XVuG?)rpXGwv>RC`6;vPX!+xr>8XNDcmRE9~y3N z@XKWnZ3*4>8<8|F1@AON4Z&rN8f3G=X=Yy^xm_+69u{{%-~Q~EzxC>&8x!=MFhwo* zmXe{`$jMP#pUfde-X|hNDoG12D@841ydV!i|Cfb{w#?N(OJI(eC;L1P5~~@RRvQ~h zN&Oif2vBL_urtbaujj>YL->cLRwldBmQsOkS<>#u!XApSk1H9sT{4DdJwWx0#zh{l z%x1{Cue_p8TSZ8!W)0NdRbJ9Dd)}m@MHxmw0=5^GU1`cKRv=O0dcwIVg1tt`W>}rH zxWw@1LXmi!_rQ#kMM8UMw=C52&n%b8Ix(AyTy)^y*<~%~G+>NX0P(`vhWFRj-V9RD zmqv4CNG!Obu#jpeMy9cFhf5`$_}=`@62|A>q10hqvEs-sDXh+AjTrt!)8yCjhVVF| zvz-#zo6jl^jF+;gn}cyu8M>i>FeQ^KNjz&W@Xt{<3dO{BW$&BaTpzBoFG*V{x(VQ! z0zH97>K4{TQ#@jV6dtQ`iYG(Y7B0~J7MBC?fUFCqctq|kdrQ$4y4MbnX#osh%m2gl zd|dZ6<%6EgxR2qo2-`m2-#qGvVZKpgLHs}5i2wiNMh8D(q)4v#OqH?&jJnTbj(50+ z&(&F$E=P$AlER(4WGA<0sREFtTjU5P{h2{vB@=iy+)WPKgep{VaoTb({4Wtkx~R2Z zMgE=zDi(pJY{FF41D`O7FZ-akOe#E=Ol0XgOvmgL;iOac)C)N9MqTzq&W{a)gzDk8 zzynd6!GArG4bT4kmoz20yUl@lEA*tx>$!iu%Ed4xE0KGMXKiWc%!AI%0Ek^{AXz>Rj_1YvLvRr z+em}qFOCT}!^V-1yI?>*-A)1^3gJ>+9R+JqeN0m^y0HZ`+gO4AWx@y)KL4#aC^giP zzn$&SMo}f`U~0Cp0HWfTY5|9xZxO92edNNXRGVFr!lx_`PfsK1pMO9ooDFIYGaLG}Q~!6+bP zR1$zPl5Ns5OhC_FA9NZB;8bYRnwoL_WDK5O7*3JSIUZ_F>0?wk2Ap@WFMTUVV&Q{7 zhTLby`Z*2sH?HaLHxp(jT;iOQ)@y5UUuf^SdxOM^QE7O3QC?QM$-LrbXHj}{+a?7T zJKTdUm-Y!+AIJnrQ7WRP;6J+2y8rZZxzYM9D;U^$hME&wg{JxLFoWT0Vws%Rj zN{BNmh^Ra6{>~Qu_5^2ch=MVM`S>F$gYZn;9yVa_J z1Vr<#K{Ny#XCVSc9QbRct)<`2OtP$Y-GChw&Z{nKq~m=QS(R@&S2hzArmmO&)=j@WMatBH4w#mvz+JKAFmY%2f1J^8HjgoB0);;fgy|hIeMW zXkC&OThN)=z~>c;zQgtAIq#l#&kY$sO0^^@#Fl@&R{|X^o^J|yD7T0Wj&{y3ylxv8 zCci3hTGvq0I;xYDPU{Vyk4FzKlb2COQg`Ylm8g*vqRYu_eiqC|Lvy_;l6ekG`{!aR zoAI_+chEL%S5e(KcR|^*cV>vU2HZMl2=Q9sgKTbA88)jXhr?|W3FPY1^# zmMBBZe7lr7Dv`u4nR%Jha!njSSnx39)faMGAp-_{tNa4Ju!$iMZhEVo{Su;Osvp=jY2;$28VkA+ zae~NQTF5p>fJM!@*J~zFin)&drf840+L=a0JN#|G)H?>=CTW`+`ud0D@R$*c}rliHF%jZM&{jYjj}tD#QEs*~01j%(S~=to3r$6GBB*$==3M zp0L>qClMaaj&MIE;zJs!fx|h{76>e3sOp|_6Lz*X+00K|4>*yAOH$77 z=8Tg!bQFqnRGX$-OHoYO&n<>l#Hpy22ijw^P;Qb)6rCn$J5kCDtCreSZRJ>}$9EbE z8-f$>H8&O}Iob-5OK~_!Udm-V_@kraCwkvj)1rz=zhc^gdzljmm(`8aD1?6zN-Yxj zXSmsR+Ky-EcwV%%SYkhIZm>;uVX#DYi6nXSq*cX@Us8P;RA_P+%9(>#Q&tMnhGw2c zrE4{dM=RcPEGVaQ!!KusrbhdC4p)f%lh~|W)5ieZoJX_MunWIu8_9(59}wfFBs;~IPRn4a?a!SE$#>6oq(wjgZ(>?${7ic=tv!s&`XHAiQKZw zz?DuGq_ytLSEl1i051}&0m=B}RQWZwfSJyT76W-U^?;iDKuMX*XSRs(Fi`bZ)&1`@ zCQy^81QAiIh)iCYx3|Au3SY=>-@ zo7-L8I_7!!2!>ufB`%#R@$KJ(7ZF=X%dWV?X0|V}FH%)=s437VmMlqk%7`)v0J-2D8*No|Wxb;#+-iwL*xk@JegXl705 z6%KM6K9FA|b~K*b5k!QjQ^6<-4TbmVnV+U{iJ`&h-^u5`b!20hMiSzv?prw zJ?LcQ25}gp)u$CWS-)}E)07=WwKf}JR?mus*0JDmC7xrJSE4$Zc{*>QvWgQlcBUUY z8+S^Ra<+vV?mqbyek-m5XA^rfrlr^9mr_4XVnO1qpGaV=L*BA{@X2TjXOy@{|7*~p zDZ_~4nBG6pJ<-U%!}xZW7;qiI>eMJ-RC%}Aq0~{P-h`>U!Qz&3un!5%yM|YiI#1>yeo6?sGRyh@~F^TZnx=| zZasrmGg&Sqv!jM&Oxd~1MI5==>7rw#4md=4SjSgn}XZ+1bapopqpiu$Xy<^XtsD06GIRue^t35{vaO6 zV0YX`A_|OAop+mtnHpm0seoO?Orc{P048@LsU8i(rOhBH17`{++kKT=69$ux7rE0Q zgbN+7pB2r3Wu)9uVlSlMhqou5NYP+gM^|cLKLWXkJk6p8D#)VQG#){6XR%{K&kWC> z*@S{GY)L_->X3(|_Qb(PCdFl|TpsQ$>vVmBgA|P;pUf zUJ{3NS@YAjLzv1j^M(-jbTr+h_c$*TA~o8tEDkWh0#E^`;Vn0`?!U?-%a2rk8N#w@ zR`m^L>w1=XOeACgv4S^xrnwvjGr_|{uNP3>)$aq?Fa9oR{n0aq#~l6?CU4i-#30X* zEe20pj!9NwI)h_7PrvMDt%T!oOgLRgj=0pEwF=t=5XVCECwB@+a!?hqQQQYk4npOq zZpNo0$Ap?51MC!CQ=66qXUPuqe?RNdxfigg9f^Fr!CR*Ptt)kri!(j5g) z)c609HY8jx780*>oPVT^wsCEI{q#!ZR+Ow_QGAyS^%)h^GYL5f56Sr}WDiNh#mG{LbTOnKZG%3l!{6z3g98Tu&Ukh(S1Ex0GN8xkNtUNh^Xrcds`T-I z6>Ysz_%GnQ;?o#TLFZ}!c!Tvy5NVivvHI4rMPj_lqwkYyo({L^<|-=3P^T;9$;hi< zbvF;JB(B`&&0ATjsr*BGg~a&v$H}#6K)lMqlcmPjKZtn~BZLU;URAZQU6~q&&Tp%m znXi|$3LZZH42o{4e+I?P!jC~w!w?^904R(@C5IEL?x5vbC)x;gw!Iq zR^py%-Iue5$uG|VV@i#Q!@UAB+PkU?cmhaKcKbm#E6*`q5M&HXx=q5t(b~r{rl?>P zcd))|hc65nB5*S1Ofm0C`nXeOn)=K}B|JGG6^! zL%wpx_wmi<4m0GF`tTQEn z5IN!O5|Lc{I09GX19!gpgcPBn1r~7t$1v{2g;3k*uad^0F16ihQiXYj^PGXTP=|G}F;mBDEe35<&k}^rxyAO(fZ$ z_lKbKYR^dEF~b$BG!a~qN>SLAQ@D#NC$iOpn+iVcnL1 z4A;TvfVL)YYksZWkSr~96){SMjD4-$p$+QhZA=-kJ7^&r5z}I`G!c=zZ$EuHC+OCR zSkPin?~70%k1N<9iz6}oR$R57hdhP+v5PPa@BKx24@mLoqbFFkC97#T+zU$M5;P=% zGZn^@<8x~bk1*|%35v((3#;^nh4_-9tnltNZ2Yi$L5tF04cy=PN6vs4ncUzg_4F@W zqIlw6f+O%yMsRT|&Iolt9c{-cG%@{_yUC|u__jxi@fIL}YsewZGOvTIxqnjAgy1=# zU)ro9e_98xCH?Bm_he9ko!cbU90*wE`*=6U_8=$7(`j3);!pmgAxW+3!sC0kGtlIs zg`?n@ObJjfy_%~P)`DcLX6AS*hwx>)YYBe>hW3=RV7t5h z)z^Ey{SD~70{L)s`YS8ENpK|ylzh*)@zxI~y_6+gn1hQbRwR^#!PRSekeSH$#1x_? z*6SiWXC+tg=FxibLrJ(B!+Wa967Q9J5&G9+geQtXymopFm%ZEs!4F1*wxUB3J#1S6<+=fX7QX zjC5DwY~nwgs8IFQR518_XisR?fDc$VO9}GnLuiJtiZ3|Fm)_DA+ovr=w?5|oXA?~x z$Y=v94rbA|XEUnCV*Da3RR2GHA_L~v4`|MvJln^I#!+YSaIWRRXJmIQ^M)lx%yX_5 zVsgs4;k;^regPx$!1RtLL^3@gFd_j&9Ul17gW{^}F6U461=lWKUhb{-mVjnUG=zro zEOizpAP5A$tuE`<1#LLX>LYK_GW^=y6lX&nYiKVIjFPJ}6Z9YRnTV|<8JcLUgGJ>{ z#o72kK@{(_MUqe<51Olyz=mKbeNaWB zwS$==8$i`}agG0i2fq~xnF)5G{2wxr#<%G727N1%W{Naqh-d+Lfq0&;gM&wm8DDgX z0{wm+vC0_OMtBA6ATBMCK4y9Bc|eshI1*Tz1dDJ5o}}sp#~4o4K44HTd8&2%#9Ojy z01v%YA*()6O%d1LA*~JJlxQW7i<}4uz`;_V64eAO`80So#L5{47GQ8dmqD2SypvqK1e$J3p2whb{ zX%J|Ft`$OJVlXkY2>QJJiIu@1iW%Pdbio%NVdaHoG=Z$!(S#T z2bKY|-xI~0C!o-Z|H3dDOoJEgb_&FA_oh1rpY7_o+j_yyS#GDbd5VfU718B-iWFUE z56r>~h-)Dr{?;H14Fi~a7V0b>HhxWv36rzdsUe1H722Be40J~VHaWv?pB z?8IwV)C{7%FA^PnC^oXp7pE58vX6-Nl0O^J|@1F1!swATA81`8UR z%z6}+665C#F{DfZ(fycMUSfj$o^sWGoW*Z8t!?u?U^UaIPN%-1d_o8iwM7ajP+9@N zMB_1&BK^o1igcnv^JwnzBT8gM89`Un9ktU6U~OsfK?u%CZ}{8w>0FK_a)vgmNdO9sGuQaab%l>LJm|Yre zk8U@mMv{Oi9dYR{?I>v*v%B>oO;FJao8_Eiha->t#V_#v})jtf8|Mqw?WqNHCPf%cm*xo%kTEEa*|F}&3 z|8berycp5`;FO6tF}riMEBterR46%0sM}xsX*XxIF(JwH;gJ52wi)%1o!2FT@Zl}( zc%DBS*Wg~}OfIor1x(tQ3$Ppx1e$yBg!5|B@6@)9aALPyt~;$o+y5Rgfiix{a@knW z;|Os(tIewY7(e5<$Z5PD)0R8#c4TbPnQp&h*#%WSItS))_QzEfkcW0FK`KqN=`)RV zASdF1EjoI{W>cBsvL{gj`~Tc)TtapwC9I z?vq-nrM=x21w*?YVZtr3WoWkkj=k{G+-Ur}5R|D(gH`gIiOo9d9fzX|DaBGu=;lEM z0tY3;8FH2LN13u9%9zdOHsX8cCaHqHx6~{u?{Fsb*oiXpJ1o~%Iu}PbIF%vE);&xJc;GI`Meu}55GFaX{R75w17?px%9w@#s^ya`gp+2 z4$%$*-bH0S{E)>cI$H@y^!{mOb=-AnA#~*l6pG+V1L36sKPN2q9sygtb0QF(eB6Nz zAAnS*pFnrUQk3+AhX_tEw`+p{uwtqC8&@y!<@oN3@LQid zJQC{DnocWCM*O0`JfGov^8Gqua*q(lpxUWssCh*9NybDc%*={BnAQP4-dPPjSxY2) z-3dIAdwKf)X@c{3KR?oe{rr%9K0eM4PGaG{8oo~F_M8)yQGvzGMj);gUC$yHUIAT7TQvkZGYcTJq%}@Io?)C0n<0 zr~?*3=sdTF?LbkAlU4QkbqU``ZRXs_$H|Uvz(7Wdn(WDgp)eQ&Rm6mmEFOlpB_x4o zCWHc^$4*lXqOf;XxNV4g=5<PN$75OxG*5sgDO6XyfkB3H;-8wlWN`q zhug7cmzx-6>dor%^KwdkjmdxFXblO4Y$ysMlqb@>Cw-)r-j7wPj6Skt1wRF34jV_S zaHt<uD88vHp-&(SUF!K z>LgNeaz_Gv``{I>_&p5FUYBs;LXH6%5b2|@3Yk4^Xd=I0hBHxjMARy&Qlg|SHj^SU zMfpD92cJMT%_SvJJ`4LX5`idThX&kWhKDr)$Ie7J5fXv@^8V+~A%Vu0G~q{4r;ISr zXK44mn7b>?p?D&{H$pZe*rv&w8uV%CthVJ!wnug7gCM`!N;l2sR>cuI9IZO+&xG1? zcLAOmM-aRi@h`{u!Gxd2pf=th%c(=BSDR#nbF=JYh9`o*om5Zd+&~sHo~*A;JU~_V z0~P213zU~ggjfqB@A_{Zdm}hbuS*z|5M6=PM~`08elX8D@yd@QD(mNaIELRZM4d0- z0&qaa#BFV`Rv;9P!Oxk+SbwV0xRxc{4AB(x$q<@npVe<4x}j{Tl5KE*T>bd|GC~p? zh|nZPq+W`vaa4_3E2@F?3VB+^igv7si-#`P{YeH{ydPRJShi`jo0?0TIk;X2VYAco z{&pO+k5WuDm#ii8y@#{wMJ%M0zU{tTVwUM9yPXjM=@A=>P2oz`_z+70MOhL-MP3oM z#6Jl%vXQ}}TlsS}GXSVUvoFB~9qN9YbVm@5?uL{_42}omUUUo_DKp7j7!&w97n-%- z%zsmj%(iscKZgG-<(yUr!bX;9x>POygbp(1b0@!yRsGg;C$$@w6Vr($VNAWLZywqv zL&~_~v+PO-L4M2%*faq!8aRb0-U`m0csE_r-)iMKvambi#Yh!l;zG>NW;e|O+Xu5Z z0LS@?R&lfy6gcW~0*4DqaXA6j$6d^|66tEAasgA28DQ6uVfrI5wWNvi2htao$;VqC zg(wzlZRh0Y+>Hy-<*z5{kGGrCVedOpDYR0J zCnvgzlA$ND9CpOT_6vw z_6^7tGxyVgP-lx(roA`qx!zqUXS^6jwV9spk-%?kgC0B|bp79_9eo47QL6{_hJqk) zeGxZPvsDsjI8H52Ew~76TSQ7QPGlyF6t-s0p{LbXUCmuBf9z~+?AW6Z(~9uGDYVG- z-n~4r)ZQsurFOA}3bB7_Za;;mZhPQTk^3x1x+CGVJzpbo3Y6zOqws0oO@U@w0sUqL zoOb7b0Rm)x-?@G1;j0SyqnTu@1~{}0XzV9yqB*fReF$zD!_murSG$wMvo5WYETMU# zNN=T#SH}H3@)4Udj1teqjfc;_R*Lg>H9QCXgj!wEg00-25(oL2>72XZ+Mg3x<8<1& z_EX>2vGz+UJFtB8z%Iv@KKENBHE8(ymF9-ltH--3MhR^ zStGz!zX<2Rw%E-kAI(tt=(M1Fi9ahmm%fe)88eP*1(tOSbC0z7Ald@Xs!Yl?*%Cow zP-ht{TAXGz%PNwyo2|_WRkhg&tp>Fk=ksc+7zXmTTO9F)FU=|E0697f-+xRkFLrwpBd@7KZefeVEHi z3`^sDUve`FNo`ks`1lgV(N!|s?%At6Jy8LqW(4PSXLN|F#7MGyLY#Jt%JkvhPc%=R zt4X=@-T<~9uZa!M65pIhKqh1{Sh#AbUtq?u#F(TQ(7 zy3!P<7LfTibiPj)+seR4RFoo1O8b=Sj@P?&dMMuo?mcpdQsg3MPLw;o8fCRW9O(n# zQK>ae($$9LH<7(e^3kPiShs)12aFq)gXtN@--bFN5Y;*bCT`la6EQ^|=AW9Ee8ZG% zQK2Lyy3D&EvnrBnHpHf!)Z!wQPAxYB$S=0L(dq@3F*B=XohYxo)+pWR98NYEmA=~Y zZJiw3Gb1!F^uu|S2@6I2TzOr&20+6F^ZG|1yG!rK5-Nw;CHQo8pi2;o=F+{Q>x(7@DQVXX& z80~}TL3fHyJyx#)7YXoB_kWhjtXkBFw%rOn3?HX2lr-?GarIxF52J$;Yp^y42jCrG zvVN#9EA)*tfr>vfQ=M1*Vq~{_gT1QP`bbl&ukVk7kqzmDh|fd8FyE4YsPFXz=4j(lqR09=&FOl1;f3Z76#~%z zbW!=a?&i0B|GeZb={e>LVNep?@Vij0WNk>ZLuDh`Gnal;Nqf(0nY z6eG>JF?*D0!+W4@+>4bWK+dH+yn)ab!jKa6yvw*lNfEAHs>%aY+@kU02K{Exk-p}} zXk-Y#K|gXKW>4hdA?Y3sg?ky6WADYS*4s8Ha}n{(J#8+%lX(|bV~^ZRU$~**cC$ZD z^DBMBBvzg!9`m{py{y?g@tCg$%^HV0ajD?(7M;>WGQ63*DR)}K_0BEa7;KAFO&b1% z$`!I?5>50F<;>rxaeqvrmrgSJm}U-wxws=Utg6r`B~4OiTkW5mc}rED`J1g{&22i# zFo;Qa&5m}%%(k^0Ew{oC)m7Q4;M#%UavmP)#JS{VRZCfbMDBM;1gdM^Wj%c!GY1c* zUuB1&>CqTD!%6u+49>mb1RFceZUI*c5U^3urJFc}JP9q;tTg*s`zPE=NoXlC?%CSav-WJS1Jvy}vpw^3eZ34W;=L-MV10&O zyRe?i#vXc%YL!@sy4~af=yYTfm_~;WHBErcmSFH3Fj7_$_WUQfIbA`Lb{M6CMUew6 z=RGv@D&`H_s(XB6^7Zr3PwATcrWyT6jn2|yIUC`1bX9!I!~kvI75yFNPzo(#FOY{5 zJP!x4{*Cl}0pTn6dXe*6mJ1B|y0plU2r|8aY7dl^`O-uRA`e>2mAfZjUqS+As^0x{ z5CL22Qe=?Mu*R2kZ4VLH*lfBv!{G`wy)xAE|(81@L7>&|&^Z~S_i zM@I5h8HLQDxY|I^35qn&HaHw19Jy$|-z;%BA}-DCEwy$GKke0U<#m&HZB<#>Ho-hf zJt9VW{pVn=%XUFMbaj{K#=ZV9BtBy?a?WZ+|6f9gd!j~>*rW+&iVT+?Bu*!6RU*mC zfLVPS*gG>0l>z9-kBjQi2{jJF&=IjFM${crcs-~-X-nYe=1UdJ;3IdeTIs{7( zJRmDwIv(h3HuGm&x1GEOTb|h9uBAVK&04WqXE}gv{#L1e-F6MYA4e`Let8!C23Rpz zWZR{97YngZXCsy+`SYPn&m6n&Q8qNQ&lnp&Z|~thyhd8N^;bwH))$dWFGJ^l%{=8C zDx-^cK`)`Qa_@>gucCj%#JWDV=RNo5Lx``J-M0nr&z@KAE#HsR|GiN*|9<=G)64pt zHeI`|qUY{&_yXkqiD_i~_hQ~sex^_OS%m*=m24JKe=fNNYdMU@@!c9W-5}O$#HJrLWx- zBg|L0Lf21`J=OkP(`-TD&ArDzuINL%ousyFc+@(|Q6;S9K?bnk*!AGZEr^^bMv`vFdPT zXH5=(f5U9%I$Ot6P?lh?uorpaqB4EAXtTNe4LX{E6ic_AHQ2RTS_kH_3mDDLj>Bx@ zMav(nM?7`Ixg*$`^^}T!k(zjJ!nk0Lfkz=5!Ga@4M&qxh-&_k>#5uz`wB?D5|CbgSsHo1oQmu&6pePp(Lm25W+>O;G$MD2+QZwo_U0zH_RwPCn@;4qR>rGW^P?x{=TxmI@0-sJ zwASa`K4J{p!w(wG_-nqodq7ZKcDdT;th3}u2#)+N za)faLR@?D)Rk|}6z%GG2HgM-lK~lLkMj!`Vy z-O`3h+#ZW;hHI$|5Kuu`2D~r_#uYLRU<&n8CpcY&Gd~?=P0G#b$LEg7k}55N`K0%% ztU3lg5{X?SC6KYp0iX>#?{OfpLcapI2!O(6j`tnoRUh83j7u%!km!Rud~zSyIX82M zQtTFsjhTz~X?(l)gI80#N{UyStlun>ztzT2dNlQbW~smVJnoIZ9p=7ocGnJczjlU* zWZ5A>;V6@S!8Pi=-6}12E3G11L;1eKSVAm@(<{POl_HFG=Tm_ouSZ$}JQY--lSdUv ze4R(ugH(#-sD3kr_bV0nYiLTQH=qXI7#Pu{kVtyJo|~NU3i+3N=y&Ie zYV?Qy&$&bG&$&aE!DsBha|iYt!84_kA#M5kThm(<%3rSPRU@6+VHpZWQVW=D4LKb{vXvabpu5hB zsx8&xJS~>A%DMiFe)-SDGQ#4NJGOBlt<#3!$7|eaQ!FgZ5sq<}(7Nb5ZcqJsCM#k!<;M)wOd}MA2T8_+C?uu=+H`B|Gd=~I zBVm0xY@N2)?j>kikyn5STixD53VV&00aN7pBl=cBQBCDVH6xmcfBMSekNH=j2=J@o z;;g3JAqt4P1BV@L7J5!`;8JGnz90VjrZ+|mCbZ?5=x+V{M#*TZhU1vd!TZ|Xa(>@F z{oo^AzmModn&qaS36CUbBS=Dbyl{m7_WLhh^uMp2Y$me$!mpd35HMYT*``E~z z8@MLVu*9XoMy@ye;F3Yk2mWh(IXWsc@M6@`ZVpBr~>0gnd@loY_p5Zvjod#hmTvdtcFJJA%KfpBSBx zb9xn_8c{qPGIm!{q+@RXXjwtl)H@c(RIkslmlA8Rn-2T|3kscY`4Kk)NAXU&WYd{L zDa-k7_|b}jL~51zp*!0}*9uxeGE&LC@i8RraNM2Lhw0LFLa4_3q#!0(s8#oLUB~|q zW$(ZpiMqDk##YDd7#-WTophXzZL?$Bw%M_5+qP}?sa|WZ_3rPy<2z%VA5o*Ko_o$~ zdMr^W*n8ZI^#xh1$XFS{S+H=x@3CTi!ZNBEg-a%FF_L;@b|zqRrO0`erD!y-?RZeu zCqwpkEzV_BW!|)XIV9#V2Y!ZIHP*J_T7SQn7--5+ZZ}R`c-7&*>cuX{Gin_d^?4i_ z<6GK4Sh-cT7It`C2%2GOQP8BOU2AAbIYWWFpv~0KIj_e%TuioF)vXBM#$5Z+Xb@Oa zkC`gfVBCla>$@=hbwC z`TdN~yQ^M2)4BRcZ`(96E$L4j(_PLK}Z21O<%hHZy5eNuLs0K)xn~ zC>K+%EW$a}OR*qx$h`lm zlt@RP=y<#YQG+JV-_146aEW~i&wMT$P7VDq>}u8cNc3De&1sdeB1S>xTMt*}jmfF&i@1bDHwEkjAwQFkjJob-vP=-f6_ z=nGvPY?~Lg*59e5DU|~X#U~wXX?_a5E2DsU#ZZ$a;rJQ_82s}3h1*hMt(WsjQkW$3 zx8a4$0}vLRg~ISf=tUi)aN*amoy2fq1#&K9fDx^>gG_$18OA+~_TNj!5p}NLX_wg& zCNz1`brzo0=16g_%)L2D2JwtOCZ`I>M>q0D*581#7pZsLC?ZTdB7gg%;=`qxb9+cx zNSQ!;xMT``0q=yd-;o&~>+SL{XkASV-430SHLMxmc$SB&(Sgoz@);~XD%H%+D*j|0 zZbFlrfc(sxIb-ukv*@AFzVV6xCm{mI*`T>h=BXzmX#oWl+X{m;}ensEd}<3VPpCzELTeI2>&dC?b2gD8YdX!tyHsI4EY zAPFUm7Nj_u8Q%%yoe|OgRem?B(-s74{14^Dg%1#Agcf$6; zfUV&`6tU*z6%+R7W-zQrz#3bWpB@xc*^OCJG~8Y4df`Q4+v+JoYcc%cvt)s;siezJFLMx+?CkEL9i$!4>lzl-PJsyp1ph++VD$43!iM<9V@&u%GAH;*6@k(8rVsWoY z$otfEJ~fTpF-r1U+wcri82Wf_dWJHme+|cC4FtE}zw5%THNrF>Un!#?&m{nm6~b!H zV{am`5KBe02_5TS7!+P2oMlAEJn$;}b8< zH>H47X`Jc$Sl^tYEI^HCs3vk^{zyXdcx}TN)hs~1NpF`ipI3)1t}S*t`DIXfC$h%X z+1Y!!u$TGPL`fnaVQ;Xt=e9;_7Hm&9hxCk`?|N9f^^C6n?`dI_<`m9h%Ws^u|H};* zFoo2JktxjN>+r5B=bK)SO}<%@jd7;D|Jvkrm=8(q%`gm=wHIIs+Sov#oP=g>S@J z3St*IR2>@Oa4Eixc2SL^w>K*m98C=(N1A;xDO-~l!p5d_$Ne~Ljz>D}4lOUd665aY zn1d6!QAt`KJnPA{#V{^Zh2K;BvP+|Zm1G&)jQNBRZs(ruqN-a!?Ll0IBk{!ujTWT2 z>0sy60XtRy2;WgfGx~7E2*4#fxc*%n@`y9^8Pz~42BCJ{$4DAcRkI@3nI!|^U1mVa z10E|m=-EFv7s<3M`fVGg5l#1afYY0IIs~(;wx*QF`^c;|Qiev33=(7t z82o>X3zz@*xUl+z{7H}4i}XD=IXu4k(`RfJUnswXl!5*9UGuVsmHC=GOVsHxgEN=-t9zZ`78?R0HjtL-6^%rYxovNwIF+|*i{H52uU9#&(O zPFL0e5SGDnX#CN;!CJ9IsPV~%SXSYmjc;`ugv%c*RaUE!+`hpzGzeqov=?1*4Fhztq^CHAT0LfuF_;vLf> zw1#pw#2y?(&q$Ca&K_iylAs)U&pb?Qda506C1`=6#*_a2ceqQL@H^4DbQ-*twQ*_C zAcMn0hk;o2^-0dm{)eJCDwD0L-^Z=u;-yS{ja09zC~~n51{jN;rcY(m;~VZvQub)_ z$MHhLjg2=cdl=424{FQnBNS63+qvDs2gl0-sLMjAukr~T(JCd(7NO|rTyAL+!M(Kz*?HC3h9p} ze9&4m+I_50S9H`A`RR`koAp9pv2JXU4jSK@TBY^*?}r*FKuXz(kCYdxjB;~<=JxV- zV$~kvM{`DsV`E3M->#{TY%1m#s`H@6Djleg&9;k)(v+q>96!+N(ApNM~1Hu;J-`7OOa+{%Ra_v!{ zBgK#l8y-~CZ;U@A(4v3d5?Nv6542p&2rzO19^2Lqz++1Ql9}Kl!-8t`elgnh({%e{ zFQ{mTW^LK3uC8W9pKjg*q0)@{m%YjwqZ*1%s}g3h`fU1_yb%(7oKsSUT*h(_1 z(i)fKn|dI4Q)o9wYtBc}IEa=Po}gZp!k|7;Z1lbtrRBV3j+r@|55)ppPth3+`IFSh ztd(c;RCw}yNuEm6OglNMaohR1u;W;)VQ7q12u_MsY{$3zPnVYSLUPfVaXHKAQpV(OR^B;0rj= z!Ra_FwoDYLSwMH^3tjf_r${v+G|X0N=uIc-!H4JU8noL?gyklYIq@ZXZoNU7&^!9) zE<+OYVQ-yBXGI_kx`sz~m0Q|wLyS>9{cv=o5HKAj@SUnsJlGapm55dLbk060^7kvX z0PfVON)N}y6BA#e$TEkwS&d^XLRa=PgAG)^VW27k%!Uf6SX*i+qv69cuQuD`K%-ZR zs4@Y;)9#=z(Rj`~rLKGzyI>=KI)9EEEm{oo0EM$$`-(SkS^t?gPIyGa(oicDPMdjH zzwXPHg20+6_k=!nZyg;TtZSqwoit$<9hmr+tN1d@u0D!~WQkOp*_w>@PHU(QTEV(a z)*SW6Rb*6e7FGI9hQYaEq|+|7jH(l%R`)&SGvOielW5iUc9NT$r$?nlpc42OR0rd&}28%}U@w#~|p+{NotOm2| zA+MTyM*qfjU^r8midzJ~OpS2vb>`u>%J9{Y2AGJI;hvvP@l?NIk$tvG>xVY_1zN@q z)TPXl81L7MVT2)~6{G~W>`zJ>CwtR%T5_|gA8Vlp?pZTNyrjZKYF#I{+Pl>pzrE6K z943z05-_X#Fa@k8sdWB?!AB0R4@i#ycP6{M$G$V|ozx}}FtJ23ewv;;{M%le|9Dob zfnqoJxQUvbx5ajMo=)A5Upo|E2}hwTtRE;R-7;V~Mq)qKQn0Eh|0c(EWn(*`A7xKZ zFvQj^5oUINdyJH>tqMljqAU)Nal3RLMz9aAkj1E^u``qE*6mT&D@0uaWfM$Uqy>S} z3WsV*#v2HyOy~DNv<%pU7NO5S%nbkG8j8hi@M=9h9Z?DRgJXabHE{FY2z_t&fujQj z{da%rdTbT$6BMM#F#@nh>tt$Qi`IwN36tvqb^AS~dEaalQCn2)4`%zH(|UB9xL8{v z7pYj9Cf1F2eZ|E$m2z@>9@x6>iza*yhrNt%rM6UR@{#2_&{nEFQebpJ>GF==3A%lCXAcV_^%N!Ie} zDaEWnGE%XXnB6O$p8N(AAsj^!4dR$)Crnq!dJY=tIVi)M^X1_#oKcB=1f&J*i-=cn z#7SHkyjAO_KGs0WaTl-(mTVcwjIz%V9NT8FqWuiw%eZcp|66pu91;B`=t&$OLFwRJ z^*lw<(9<0(+Z`|MN6I8}%q>NRRddmbt?cO>PY4`L)Y}WqRS)kWyn3!!?oLMG?^1ngR$lI(!BI5lHPWqH8^CNs-s-g+?ZU4=v;C+^_L$v8t(x z`qF{j)K}IW1EIQORqlq43Z`}|ca!h=v)8AS-Otk2*L{M55qaXH<*sBlk0k0e?O*Qm zbjf(lkzum5TeOK4BgwvkBZNNJQ(PB!rlxeAjp?}FCj3VS0wplKc%<%}I9Tgm+ME9+ zQ7@UAx$M9@E$~KwCo%w0w5fKigQU3N6-}J7GXdtK07C7D@J4vOn-Nufgzx^T2N0&; z{S_Pxq;84|;aJKRV(n{We;1)tbc~|=*)k^kpfVI|SQkeu7}zTsi`=V@8RZBN9MrBC z$Dq1is#0iYPfjVay}qn`e|7ZULKpeGKY6zV6Cqv2tY-;Prw?LGX_2C`yJc~tM++vOJKnRERJJ4rsPckeP=&)}Zs(t>H*B z(mwH9YZ(=9G`tnpgc-=!$~bXUioQupaZRYT7`3z0SF)S#W0Ml|wsMoF6S=jAWwymVXa8hH!Q#d?65%0c*d zEJckC+k8lh$c!e+y5ek!o@w!fBou~c42k-%RWw%bS6A$VUeP?V#6YrGG&}q?m!SZjaJuR?L{E z7sk?LY9x{<*-x%8hSsNw!Y*7(D5Y8QNAp>gsqF#$B*CW4uZu8h#JwwCl4=H+GKxKy z)bJJRFlu}E)rY0JT(a$Rqlk?~?kufLK2__jH|Jp1!0cK4Qt2qdhbTfxZ=9tVV4(!U zDZrWO-4z=v(;fY=Pppqha-2!!Z%H0t({R;bsw;bs;_m~<;vm#2p5&Ea1=dh`rr$2( zN^t}+)MPOp5s{ek)=Jbck`;xocZ;3`U65Uw1c+jBBbB2r*Vfj)1R{pS9jEwt0OZUX zX%7LS0rtlvUnIK!m#_1kIe5`2Rrt$zBX4QcOwZI=y7!DJYB8Lme*_~hrQR1QwQV{c zwnAJMkeYS`y1oQtZv{n_92;hZmI@u$y&;9>YhekB`d(qXFmf1C`%QkFpA@$Okpak5 z(g+a5NAP5)cI2k(2u!xNFFr<{Bn~WQV;af*O4pHfzG;qzqQP}CRBE{!D57q&8wx)| ze~K$eVhx_GPC_l0$OzgK!cg^phqt4h73KHF! zHef~M)go_XaL2PG3d!A`Tu6yTCWz@uKk?L;?j2Ctj?}WDay-2TmJwCz-R$*3Iz^glt7$lURr@VLP<}z7CJG)?{1dvs~st!hh*H z9W%@t?a!YxpPxVOXh@<2?;Hb)J`ZD03bsN)$;c{YEe{yxtFYDdro?==u-0+mLU>x- zz#!G5io@gXi-2>St&NV?nG>aoAY_y-DZsUa${bE2bUjWQ7>SxT_bJ1wBJI0B2CZny z?Wa1X!x@tEAU)!bgP)Bj$Yl!hB$zEwL+yY3CG8M45pTgN-y?qpmDhnncj=Y7W9b4u z!(GWY9$32E)Um8tUO(oH2?LBs@0Zo-Vz49!E#6;Y_bXJ3$T=w{7k{6!* z!`(cIbUP!-qTi{)xf1O{yc3X)ipZ21VJf(c6PLzgI5ZjCqY<$#w9V|wB)QvRMch5z zq~br~Dt=|5TFxwxILM$@#)`FQ1q$X#98d1P!DL4JghPk23k0_yg6OQZFkkC4H#w$Z z-Aj@&7$qjWyhn0T!t5t8A=qjZ&kb1dt2p%XANq&_rrCt zZ(dJ8s`;gP5^5vsM?9IQUlbDm=Na!pEi5*@Mpr@~=eK-kW=9P5#A$`x7fvyWqYRCs z<$F`XDXRmHXMwDSIGwI8QY>m(f^+LTgwffQ0p*RtY`OW_34El1U2DjEEz5Eaw0+C!6O=~Eo8(*`%5LUl;&!7 zO*Z%GGMH5%AQC?8^nAa9hrZge#InT%S^9eZv0!eL9q&*T%o$#ODuvh@kgsZ8+TTLm^GN zf9|0NW$Pe`TMo&W{fM62zY6uD%XHHaH@Q=1$@Z#e-wvnj`qKTOD}o&LB#XXp_D&?h z56io{y5C;jxb2AWD2P1qJgOS9soHZs@D^bTV*KjSA#3fXDdrHp45hy44V97-yNGLp z;pq1!z(X_6H%4bkPArSEZk772-4Ly@Ie}1|<`!BiCGecGQ^{{CFI}5PIRzcx5U`sQ z89*+(OY(gttgdbS2UhfQy69buG6i?qZtpOA!mNsPGZD)2*Ox$|d_0f++g|`NqC_IE zMW|DrHqwC)Py1(f4*4Fl2DNH#_GaX_i3C1%V1)P^fDz>PA49_L%rvZO@NYxHY$@`O zApuX(3bUv*?ulu+Qw6K=$0FnMxR1j0^KP)V~cZ4voEHcMvjr3Bat3pY%9 z)j8b0^?e_+yr3@h+twI3@ouiZH=Wv3!?1$a|(O{5TD&gY`=;6MHK=PKZF|u5v zwK2~0RERgbK@U?qByOLgORLaUwXf6d&$&L2*`=5DQA8^W7^xtJz(IYnxpk6xfmn`T zfx4;f=7N$xEgP&EqQyEzGn_kk9- z%l~yC;7gRp#i%X}JW24~@Y^fislozMd$nvY&rveP!0-0=<2|o{Ge=xE=y%C3M_xCG zA@|eu$VQ`KKd3-IvnAI&`~Gjt0v65UJNWuhqu*G*2o3MH>Y*b13H6lY3gtk2PBYBW zXJCeTX1%~OS^I`(99W_;)%1FhJ{;QGQMbEBWr^{#NF!OtGFerQe~wY1miMqn`| zRoILaoN}ZUF4H9@G-Wc;coWk2(dFquFD@O7af$5mhA(by*7q@@W1eF8eV%J}3p%i! z7cR=DC+e5+bVwc}F}DQ&MaX*J@cAR_A$4>*uqC>W#a<6|I9VH)H^rRLnCR0uM9g~mQzb({tC&>mF z5XW4+9pfas{JVs@qjP9~z|YaJL!~yPnIF)mEAr~?l=BMrsp~%k#q0Gb1rYl~vq&s* zN~gAMQhWHsaLa=);-8-+-r-yCCEhEK?jn&6=%_-wQ1PW5i?OCQ6ZPi-Ma}@4qxW?1 z__!uG20GZ^2yv%ymt_B)UtZ+eZ)Y z>8?NXc;Daa{FSGMf)A^Pec;@HXW-A<8Ux^MO(~)}R^Jf^-6A{FE<*LhL{2)6+4xUS zv%bKLn_5jI9fkGr8^#%NEY6p$KnbUbf^c;`4j^6sZ`pzDa-({klndI$C$Ntp1W(4Lm^R`%Dpgn&V{;8*?BkVa)6(`mRbbIGBQp}acRz#^1v_Y&GCxE!JfC4 zOic|AV8~wVeL@)C{HiAo&u6olL1<}J#$LVU0%NL6;JMLz1?ZUz{HjKmv91QxS(%ym z&VaJW`-*a0V_!4FUaRMM`;ML`CBsuZzAG6Ab!C^2k!=&OTW?lv(Iqpw!P+u1%$dP5 zaj=~~FGw&14NHZmhJT=QPTgrBU{Fj6i!sq~Y5)%P?+en}x4$n)4&w=A|G6OjI%a|S zb3s!0b3yV~R=?)t{9pGY7LtGNN9J6A-;aX-y&utN{dGUO&=>}W=5Q4i$I0$#Jp%J@yc6%DUFN0nT!!}yA)%MonSMWP`OFPF;>`Y0_; zuL5DW*OR$NHe&7@upf5{v8zjR*P8;*kKIhJvA}HVEL1kzlX zVh|M{XF3Zv#JB;>ut>!!C`Knm?MHenN&;vXCTe2h_c+L1zvaGckETpx@QDRG`*Q#U$uD^jUbL_cLzFb=eZ#VR>vXi{< z%JtAa4l8~N{A^^Ole>cLu$tJkIYQUqF)Xd;vPI6tuPqYnE3w^x$MjZ7>CPf>HdhYa39oO7MUz4-l%DxXBy;wSUk_TlU4F1he{pD+seXcTJHl`1f$maYvt0tseHE1v;{=4mD8$|FwU5-{x7 z%<6n^Zm7R%C-Qe8R$FT{{>2y1AYNciF4C^tj&r(!m7Ly7iRv=A4TBax8^Tnf_(6V~ zCdZiUAlY3=erWzI-v(Od$SVUTse!E|92?x(#&VKzFc%ve=3*cO>Ra4UsoYhqfAu;z zt{7X37ICA{eNB6k8SLww`&cv0pJt|NPRgqkfk*;&`2^QSGd9*i1fSW$ zUg869fXRxs&evNc8m!eEG7Bk(iXiy+=X;ew%Iwb3WsGhC^_;;Jhh3o&s;0vh-r1nz zDd(S!TwrZ{YYj4OU!1P#8%e8>?mQKCY0o_+DI%n5XZp8MwZy^x4P{oK=G40ORRm$) zC;k)493Ldg_xituGMOh3{|aRqN1OdP(lbuh{a>NX(y;OW3T2vr{dLN^J7n0@E-shY zmix9i>T1w2{o$9SQJ^2BoPXJ(90iOxa<~!ap+?&6AZi5g%!Dy zPe#!5?)qBIE1YRuS5yFLW7h`@uB64$MvKpu95|^J26PE!Vu{fdrA-rlK_K3UrNgx$o2|bIZfPrA{kcvsAcx-EoZlv~5r%Ez| z(02w4ox%+k%RQivNv)nn+@jxwH$`ocDs1INMC|N$0oZ?TaX1O@z+DGr1hzLvl!VSX zT1YD!#pH_f+#o zkzE?DhvhXBx*-;570-`-nN&xp81sY<;1$xadDZ<1({Zu|DALVDyvAONP8g5APyqHY z-uhfZZaU(-$TJ}+r}7C*YZWV*9Z}4`T8~bLpqdx)e+p0d+U6n%7hFJuLIvUlAW&I^ zg|CaGp3Fjic?I^L1}h8I04bknVont?J1!LRPttLno86t%t}ngZ)^FQf{|1V9E^;0O zO&cik$RSe*@ve2g7uJ2KPQCMW;3qP?%t1G_)WaMVTZYtOycsxT(8RlxfU~QZWwTm2 zY0!&I4YHe*R6MJIV>lR{W3}d;i8cZ-hVfc~8zN>U-Zi{9NX1n1aZ|cbryeSxQrs3` z#1IGO?H9ypash+oqh?w$4d|wFVaEge!X{dG-+xC-1)MhV!ZA{4>wdmSUJY}3zwObEmw-5{(>LQ6GEcrzYc#%!t{`!tf>){eu`KA);q+&M*n{9#Y-KM@M6P-4A$)g}{j z8qDwcaZjECHZg^pNIsC2<_dF;j_hP|g4}>)I`N)u!h1e|gIEd!fh%3ze$voP0PelU z^9aa@tuhdAxRRAqoW!=y)K7)0Ki+o9fKUUd`N zqr4UKF)&qRyqlV+_B>{nY_lLChqzJ~@bh_YE)UsSTx0J|RvBvBhzvmT7wg`(YY=IFX2@mY{jw^jZvQgY!dC%8!xjO*ah-K+Ah;wD{H38Y0)zl~0wxS0)F__SzQjj8 zy`YLr2iM)~V`nm6X<g7x} zsu{(YU`&cX2VUM`Gl=HzZ!a4YUjEMBY*kEpp6;pT4<|-dbmvjpC^D)o8)z!0W+G=1 z9vXT<`B|!-k@dZTEER9V>Nm3LL@Z6`y_&I{qZ*kO2akq(74I}$l$z|{svCGFGUB-; z?N)P@?6XhnVds}ITi`OHBS+NCcdt@3p`?kw=|_qLr3!)()C%JvmjDjZXGQBLElpV- z<%0@S9bSFw=?Znr|HN*`I{~rVvH?KsHfXx6mU7>|Idw9nMJ0Tm0z8{ZCUFE*ufxrC z<+#bD!rc)t@>S=YoQmrs7Q7xh!tjd7#h`_zXJ+ZHhiEQfi1BVG-&SwA>Zr|}65aYS%hS8fGz;3;m*NVNd%{EFfg zOS}bjQi|{`i;NGKL+lrk0_L!07A1<}r#`Y+s2=Aom)}xo7`+qkL_KM?`vsgCkL-A=Qr=Ls4 zSn4>UR5jewAuQ3U$n;IRIKCW*QUyYXQs#D~zV3+HH=R=ncfI)Nud;QXHqW z$1ZW;(LZhBg!D@1RtNA6w{JDy{&o+pB3ytIwX3vWGSKqQA~{aX8ePlV!d3o>o);o{ zBZX~M2A8w{5IH~1>reP|8BwXa#TCGi{mirN?n&iDx1N8L<7Nv==D%!4YiAk8ZIk(w zS{rGOgRgW&x2UrN=j$UQ50{|wcda+p89OF@j?D1%>zPX?& zgv53Ce(TxuzMK;3|JDJYIhSk`uscv(EH1W2%C!aA>7*-Ufhvuc~gm+Gv} zGiF4*iI z6GWF7@SvJSGMH*AXmTaCYisQ~s7;3%R}^(sGF5sE(+6cZ;pKe!lp_YNxH#YS!Y#75 zbIIO$YAD(o4#Q0}`gpe5`*c#(37KXj(PYLWG97STbfrd*-vP3iHJbxXu=AL845AGd zFl=Dx?_Z*rQ;7hP5&JX@(MNN>_=?~AyD9Y2~A+Zf}~n~G)PWD zqpPS>64PsMl4^MAz{ZmSnz`}RQA31m{Lv*+bF<(rMpl)hzir3h6xl zJ6IIH{UR20&`7<+np)S{{d*&31*yZ6&(uk}nRQD0Bcu2fH#Qv~HsQGJV2mi=G}&rj zf}xXVt|0y6pt_wag<~sjDzq5OSbCBBW1IU=(Q4OMoDS&6Ptl{J@~)Xk%Th&lbJP6G zC7A$ek%J|4=8o%8nf4dTDjFZPoQ|@N#2b~j$>N6;|8%%0=`h1z$3&1|ilBA=XORXs z=^PZE;~f`f{5zrTiyJR)0ih2VmJ4&bzhr(rVNmlHahglGN2H-ajTRwS%MJ6%`Q=mJ z4^R}H&%(!%{e1J(IS8w!phP_L_WtC=@Tl`K8bleHZumZICe_y&VqAVGUxnLjQ{vVG zEZv3X)~+uIwjAidp1aGqp-fRPGc3!pDr4P$QQX){voLT7wW^P{C8cmJi#5xRz?AS${kenmD!VmBLkFDT&^CVnTYCMZA-JrGx|HLG zKYd%Ux|j|-C~!{>O&Kyi)mI=fE0ZuOr9QI^ToW#h6LV%CC+Hqe#5XM6zHK&umnyO2Y6|E+%bZT? z8*2IEpzem@!f~+@Izt4i#m zFc56V>^tZQ-Per69^92V_vM-&Lgt-=6snK*nM?I*9<=BA4xat zvV>T82^;83QHMtNJd!(Vc)GOAZH>m~&&}1k@FT6URw5VnCE0=YWu1_~;wFKXJF_YH ztNK}7NdycQtWue&j0@`P8c9zbK$@5exYd&yId^R6^>n%|l~go2dZHtA>Jqu|$iyfV z?yRkzxry$9R~yCX5F6zMlZ=0!kP#KgvhO-_B2R`@ws|(-1mj9wi^q;C!78n31pPd< z5*RFT?#E!>Opk-M4_w3rsV!}cAI#KG5ctGBYDw|KAG_Ld@%}hk@ziK65FAzTpAdot zKQJA$q?3sv*=jCm%5lBrqAKmdlZ<@~U+8fx>o}Lr#=-DmQyc6-bc@!*rdBajgP&~0 z7|%uLPOfxe6V!EL;<`%>IynZ|rx2HwBD2izmG{o_u_Zu4YURWuzMroww&rnKqa1B4WhThIyJqFE z!^mKAg=cyG_{~Hz(2XR#K^T)k7|m=L3p*!U+TJ~xc%*LzpBEd?r6-$U?p^8#X zvz+-Dhm7})w4c%W*BO)$gpJzUZY`&;rYv{wFFLg5yfAKcB3itZ>)zt> zZz~RmcJ&%$${mJS;Gd{yQz@Lgi=-2bdtbPe4jEI4CBbxEcW^JutrZORG7Dms6Klr0 zgW@#90r_%APWSK3&ZXBO03$>2_>9Sd4F< zyilrE0e{C*ETiTbg%M@G7aD1PB+Oob7zVcF4?96hDn@k7;3Z)4f6BQ9AM_quMp6G> z@~mKYK9ie9J8ML6RFsid2yA*0KD&Os=cV<^kjcLtmS@{|B0D_jp!k!}Z`*8p%1FsD zTQ_YEiQgEun|BJYhkirA1vqeRG0xnrTddlQ z!Xj>gVq~;4%d19CXvawl8_!db2}h;Q_?y1Wl=C#)nBntWYLm!3o$YhFMqg}j6XIha zq7C`9OC`N-6he2ZIF+M}sSmO&D=gx2teZx>L_!_Yt(v16io?*6v^3MLi*?fW>u}TN zi5mdRk)0Bq;1k(Tze#55>6UCOknkdtEP|1ZHd9NA5c-EIA|vY=3NoIlbT#)-H*sCN zpR5us+*!!NQkRE z@t6nNkSUkw;PQguJqm3Pa1_c<@!h}o0ob?~$o#{>on(t+w6hXbvC#p}k965zkCd9j z+?)M>V&x}!Yky+pzyyq8Xfv8L$-WT231jxz$fEvJQ#p6=r=om(;nKkg%?3QCa1J+YEQo93A!kuz(;pEYf?vH;RAjt&=@VB!p8~7R%$A&;jmqsh?_a} zMP@zN?6<@FbLAS16#OtH2_pytd;K4th!{aiG12o^X2~i4`3@scQS568?XYAJy`4|0 zvQWWmu3a)-?+%GT};@n$63f(uP%7NSz29e3uehFfOrc!G6+wL~W{2GBQXDDCYoR z*IgRD&99=gFtsF&W%!JJA@FYhp_SmM|Du(y{-Kqmx5wG)wr>ZVCsWKK+Q93{!!#GL z6k8GxNX-0*ukv9@*7}y0F}CZZft`4CNT&qC7VLJG~{$$Ycr++DD%M*9Qk*k72PU4Xfr{-jk z&=l1T`BBo~nq?Q}ERjua&y_=1s(^MxY>hqt0hvCiJ4Qn8A<1UOXp3Ka1v#Ef^mL>Mw1<&L`XGusXK^7 zm$fw-9OlB%!ljIpw-w2({jrTqLNAe|B?{AcoD#>u7`8Z^uT>>TI78xSa^n?PCD}a? zAe@oYw+;zheW8W@%$>F-GBN&>6f~_?55`PHOy$0s-bj}jMNK@7WM=u9KdHenz?L`O5qTN}ITOyvX^;6$#WJVY?hntD z%%)=)WcnA+bcm*u<^|xH^#0+Q?j!$;X9_O~U<$fJsw1prNI}bJW#zi3XQ3twr{vrj zF${eOx$^NJpcAe6-Nsfo(xly*QLA^1u1h!MeATP^a8GL~^Xq4*=K#5b-kPrlvsipn zKF+|-V!m==oWW?G2mW#I$9Q3`EyyFip2c4v69Sj|^_{{;N3r#PgG@o_%0j%{Aw^5^ z$NAuQA88&HeRgA2JjokSCTv=b{LWMAO*|io1|i9xqc5Y?8GvnJz`*eMgW)9=xx?B) z1&%JL^$(RQ*=Zq+V?8vm0Ur}iyRa2>%SOy##@Ri74cXtZclI^XSJNLx*)q%e1C(*? zH@{S17c#E11j#3P?0HKHY1}S^bMA5#rARPl{z!u^XdYiDLwIGpGpVw%y`y3=#3GkM}sM)-cf?; zB9XLKDVJSH?+xU|9;bqbF@dItxc+P~Oy51bXX!jzgHwsm`8vto^=5uo*J0gK{0O7hl6Ggt6(R2bU_04Wi>%I}j{37l)sAoqpJjJ|nYDKxCX`dJQhJxsw6rw1P z@Xq=2a3r3*DE(E_GDY;Ke+ zl%w3>b0XUUyFICeBe{nX;ogW z^b+*XzVPj;__im_`nk7{UN4~J=@LB!OLvOJWYxxMH}Yh+O#LZqmjfbg7;_RYdGW=A zXg`tVA%&R@mF-2mpPe+M7tLg4fD8OpI^RuP((bFw$buF@Dk(Qy1L@H&Pgt|ag2cH3 z{Rri}X!FioEO?r6qUez~CE{Ngn(g@|;H3RN0^>roM1)8F>3KXjN44!zjM>ivVF(Ys z93#7G%J3#@hc!SjuE84NY)o8bXAwqVe@g3FiY94f^p(f!cS3iN}rnn+Ig)n*ifYOoBt8 ztT)kobm`QWlssFX`4w{+iOS&Oqgh17b=<3{sm##uYZ0RGnv{EO6@7q*N0nu&)3Iyed*H~N(+O%BNa8nwh*tfwA~?wTHxPfK6c}8pz}My3hZDx zU>Oo$eE#Kh^tLM7XQkRmg3Ulv} z9Nu1~ebm2-nx2+4{^}|EZhcH1-eHMcrdp%6Q{gV1nI9#-z za?TbE+ZNEZdsfNuGm_<;eO02I{AX#@8lYySq!Lus=%(Gs^ zuAT)Z>p;eDz;ZBQO4N{1_7)20zMh2c(2~aT8)^^}Qe4czaBb1H9*_iCo{&gG*@`}S zM65~@B}~Ck6&#W!2Jd%o8;^6VX!svbjYF$N5Wot9xsiCW7g5_*9Kw z9*jzB^@-ytMSi9eRMzNrW{2z$Vk>GI-9W!66@cTERtZ7{=Z^JnS^O7jVXMlI;6Cl*LN*NFw_*C>}#FUG3aZZ z=AXXS=@)$0t%0UbU+dJkshN4M$=(K6jxalt{aq{HP_nPxCLP+=ym?Em@e=5K!Oz|t%`t+4dzm5CQ;^LA7N*&wHPXBp+ zu$}_+6kr=u=qZ3vb7KmSpf~u1RWV(B@{B5<|7@tblg~_!7z8SMO`WOo^zgqb-Z|u+BYlFPU zq2B|{L**Pkx=I&;Nk#geQnfujrLvtzPpQ^!N&?*BZF1r+IE=zlt$Y9f3`vk4g2*@o z(lO*whAh&!p;i|{RFPm>H@Oq0Evc&3oa7hKVXo)6n)#Pvp(sLv>kljgI9_g=y3=bJ z1T*te*9RJh8UT&RVf+OWozaz>T=~-|UJ{|BsdEiA1Yitwurn0^jos!`T|y>f1iMk- z$qj2LTo%Q-<-{Yb`chFTLm;>oBTRDP)3uW9arjw6l~I;a;7?J(dKaynTbDGbj63X% zrRL=pmYLkRc3C&*F!f+jNZZsH)1v#&SGBmUbWV9!G{z`5_5b?qLg=3}t1SFBetU9! zv^Aqk7JQM{vl91Uwa4DdVvZF96*Tdh_kB^vj;sp%4Z=o}mMK@@gmMC3rLtf8T55AtY*hn(GDK8aD3ry9=-`Kv5$MCnIo9qP zm%=N_zg}2OKG0G5fJ_DzI+K2#*l!toRB`GB7fLFQJvvm=6-9DQxHb#J#afq|nZd&Q zxb!&-$T?@f*63Zz8ioHd2lLcQ~t}vP~zhMsc;SLQ~09Rw> zO!;U@bDljhUb3bHI{LG@iSxWjeCU{J&NQjU$GRG=EYP^#1lpTGE1up2`fZ6(pWXy& z4#BWYK6h(g7158tQEy_2MV{F{=n+2JZNQ^-xFdUQBlOZ5TH~^<1c3G3wzq61)%bqp ztcrXN9k#^WaWxZvjQmxbC8IuSsCQbe2fKG#t!=uis3D`TWem5m)tP{1c2She)Sz_7 zkn_<9deJWv%#Kq`4UyX!d6lTh=^+%Lo#hqS@Di5UOp4QQnit-@m3Bhtr$_&upb$*d z8|!qakY8*Ptx~?`_nZ%OfIdi%FR@9fcNBV(2&(RT)C%8B02n3sX&HBPg-L&}_xE}| z*!{g;+tesT%~GFMM}az0s@3VBbmFsD*+LLOzR8ig<#ev?RIYkV%sdvwih!+_%Rqxb z%09VI2}StlVVL;Fbb_CUWuYT#+M)ihr>b3`#zxa-py%sY_);Q7OS_iDV@6N~Gk0k$ zp1p>~MuHi-4j|sf_`_`!&)|(8YxnZOVnm&1ow4@)}bn)@&<%6oHmk*j?|H^MNWBEXmWGf2stLVfAkL;||Z~t1*#Ec{GzF% zAvz4sXKRzG`y&;tG)mHZ`tl8I{Aq*&ZdUG6QbtwmQa3W>s$4p6!$4HQ^)qP^8vT0q znnS;EHU4^Wae9vDIE|)JFyinJmG7JKpNl0zwrm+-l5Fgwx*!V|!h`{4Qjw4f#N$lt z$sMfN0)&Yumsl;KV3E>Hum@&n(*~@;lXM@O+@*CSwRKh7E0Jzkg|v2IYCdN|OIzq@ zB@NBw2>@452ZwdcfWlppc0EsAVSZdV>5aQ=IzL8dr;Xw@Qae$6A&AH^(Q$cWsg*zg zIsvei&Q(Im1P38H)gj`t0aV?=iSQI^p;VNbTIc}bialbg)=>Shmz>gD5bnjK$Rw5xjc73nqLC?5B~6 zi@CkM9g!b7>@=!c<3}6~BQv}c6F5JClpOiE~OXV z_;D02u)+*gG+8Z|QJmr_!IGwLpp8F-4FG7r8wpiUm>dj5B$7KpQ)`%klOykR2edAO zr{EiB1J!AgS`S^5uB<}uCCtL4)#9=o0_TjLpWi9nm#nTM6<(~O8gLfsn@8AG7}l$h)8HV`biYfMOva3iMPNiH&afEOE&=` zV&((C4fumDf&q85Fv~r1QWO6pWQ~JvdcdRz# z+LVEbmf5Bi+_c!X8PkgKhgeuhafv!3D7UPf7)@&AeRP2Ydl)15;`>MY`-Anj%1w3Q zv1|%LH+@rAt2iGXC=QO8q&kU}$4k2^U==+dMQLYnb1)#b;pPz>P_5jV65i-d4KU4I zgjVdc@?~>5=2|2+vkDERyN6)F^Xi` zl5SZeM z83(C~pf`vMSpOB(MYF8r+*nJyN)>2ZV!N-<+gIrQ)}Ds?zcXMrqB~@Z?MwdVp62?$ z2L}g_pA_o<9z1%q-`D@$#Zw|ocv-w%^1W0p0lZAIlE24lf8>h%TGNbswlpGeCtjdc z5U&3aw;bea2J;}L(!<$yKP=WCuB=;JRgO3tT2Lrifm5*V3C~Y_QT%vBf*19Sfp#OR z|LWr{bNAX`_-Ua_xdC^T-f#>cF#pr5*BYMMFeSUcuE5wc{(__o;tPdhq$PYVqge@& z_>_>@fY9%=ZiN!h`CYKt{Jz((o~n6pr~C!g4si))y&P-lGyVdYic>fk=RyKj;}qX) zgtfd&=`ZbPwcJ6ST3XFj8j#qFm+ulAB#pH?_v;HB9l~QTdCxd8*9> zayO>(rgYr=*M`W_h?=O2P8aKL^FH;FdkbV2ryQR(ZUodRYb< z?jyK+9>@-?IDLud_hI%cTO-vCp}hg7e@gX*S;3F0{o1!OO+6nitr;vctN@xZwI5+6 zV)rnfClB*LyYSPC@+2xIkncBihC^okhumgJR0{CP<6&pYm|1$)NtQ zVgGCneGM)R0LX9AR!A&x(y+d5do2qFqB7oPsImqC8MM&1Tw`aGuj3q1^ZUk2V}Dw8 zJ8OuMjfV?rJ!HN?ho^lwLzD=LQHc%lu7HQi9<^VK-Ei(X|3*Pw|8|3eI4^#1O4O-l zi-{g3-~b{V5?&y1RjuldPrmmCJFD~~8ej_g_F;DQp2FMQodPEY9nid$;xk5M!YKgd zDCdb6&c)Gb;T_-Myi1Y-fVrES5dAW|$q(;^H{D&+rEe=k{J0&Af9NY^9p%ziz~Sm%is-Uiu-%AZ1d9_@&$Lq*36- zMe$VovFoobqad2!oiEV@2zvqbx_<6J)i&7}5^X0fV?QF%0`K0uoKkvKAhNX`d-d#> z`ZVZ&LHwTiNxUKp*JL%Dduu9!oAtl@2agV(7WBW54<0}5^}oA#io_wB9ji6}k=I-^ zPU*^E%Cm5nse@0;T{qJU50|sar_c>cHJt%!7Jhhm&^ ztz_z_jK;2`%q(0?HNA7UQ;x27a8<>tfTh2o11t54vcU6z!-`@2h;{S@rNK}-a*`4= z8OR@nMzQjp99z6B_NjZlTqRez-)_8kl7Ru&k!p%LCDb3cc{q-i4qw%z{wsFNS@SIeES1(j|66fj{Z(d(S9oB_jNx+@LWgq=F8AkILs z8J--&D=|=*S|jkAlHkTK*C2H`9DbGT33_jS@r((reeHs{^EtW)r`|A)XudC;0UEpq zHvPOA`bOFJb7vrStB&;?l-0(r-`X|YLGvBo$}Ht+RFN>{sMc3~1sdbwYo!)=3ce+3 z(Vu$#b}Vrj+7D9{amkrTAH8rmvb*)`Clgyi+^*@$kMJ)HjcFt=xkkOpB(E|7{D>4j za>F;Q+Gf+$u7jcrP5DYFOm(A0N>&uaBuJdWP>o)`wh=r$x4c8J-9gb1x7o&Jw@T?% zS>A!$wb6mC>7JMlruN z_n;)FU;I|IC4D*iwegJeM5&TUNNTeyYE@DbzG+JF4!_?`$EIuizqvVuYTv4~X(AUa1SWdAZZp9Pyz}Gze4RMW^fiW^fk7E2n z#Rx@Il)CFawN-Hh#rT1|-_-g}Qct#d)wR=qVR_o%8fc;a`_G>|D$xH&`_KCL|D8O= z7{Yv%ZjC2Uk-$s#-IC5Xu<@UO(pQG)=CDDr6#4M5W@=bL51YpCkoX#ti}v%sh(Zht z-rD&;IN0AWo&N*)sXzZac&g9;8?okJ3xn5sy&CZ+l40*3yFvu^FSkc@{bwDw#CDY^vsGo|bbyswACx9-Kyi_#nI=$_*D6yK>6@G6KuR+~6d z2B6*C0r*Wl?aTJ>f|@UW{&zwF@Xz&bD1!O*+!4lm^)tIGY<7HqXY_YQ?VZtE&ijX+ z2L1017u15yCWVAGRlqIwpC|iIi}^nuJ$=&W|Jcc6r8Ph&`UL+Odvl-C(pULBy;J)} z$ABSwmaT?X^d^`Nu#ai`7UbHG?kkv*V?NEH~CzO&E&RcebJeGv+jz|T3$PX2xmunAT>sgpk36>w$xEn{+*WsUkj*^>c;X7m`n)z`RWccpbkITRT zb2GgjO503b*CT9oQZV3Tg9ua55DW4CP_>*!bsBJ_E6)wmtKWH~%KOcoVU5+~G>#_T zNLK!>-b97Q_@aizUzNXRMkuqON72%Cmz%HrM-*qko}wKP*dpSZyI1e~nTf zh9Io@y)qHW@60?huu}CauRx8Ra@>;fE~nnCkifQgzvtdzeFfhQj=jLWQyPOu`?)qt zFZSu)%k&BIsl9nVMX{6&CA%t`Qdb5gyR4CXCWX}?X5*BW#FSPu{O;^+7~JJb5KGW1 z=vYe$7k%EXJuUE3C{9w33cmXCXyf17dBbM?TRTEDT$tfu?J6pAtX)@Ak>A}_RTcTY z-Bwr0zWRAoS;?-eHLI-{4A6x?hk^cY<2oR1@&8XA73)7gdHVcuum9V{Bet!c-KwAi ztdM*^pAsQzk8ZP6h>7~J#Zu>F++R(3ktXz7lo_!brWBrP3Xomd5!<11VC_m_nSJzA zpUGW52J~Opjkg#7|LjSj{>P((=g$s$`oD{3-Ka6LQGW$yA7c-TX5}h$?9e*2LzBDF z`QI=PRLl8)^yG2L|MTEUpZ{Yg&u0)d`b80dN)fM^r>Oe)e<3NM`pjz;>~vR1aJixsP?Wk9Q6*j6Ov5>*-R7j9mWlgd!(-G+~*-ud0o07FxLtQG+3 zyB5?ab*q(W>KjCZj^ePtEq^!j`+xmBU~T%J{U@dRuTKwp{(mQrz5ZvHDZ)%VKRzk% z&>$0RH+5K@3(FTNg!IoRdrI_w1XOO}&%-G0jMla4fA*gj>c2jFe9-Ivck+DaoVqD) zfdr_6!|U$cUU{K2S@{7b6L)}!Ub}OkIrqMEF0TB2*Z#-Dk%KmE2c$0Si6EiBffM+lH@Y`E zK7W5sAwSQ^+#AjPbWH!t;NKffevilWzw(Ex`56C4{+Zl_<7}V_ z%x|@HF8#nu?)|S(a=V28ow(QVzv%-19l<#F{@4Exdc1REKUyWu$?;40E^w=u(Ei@Y zpLy<>o5N4{MmNbcnt9`U_>Hq;=j`R-@tc>U#q3_jTutLyGR|Invh^>We+5%_-)KJn zkDnhrE!Ka23V-$Ie;3bp&P%Zagi<%Tckh2M9XEip^1~qTF4Muk|8IXWcanJe!vKAf zk{9D~?B3#eyIM_Fz<{8~6h?<&3X;-(D*do<{c!9i3B63BVGzyXe^7uNEyMW$$iXbV z`eAUezyERIjQ{`@7~|X578Cp{9Le>{+rfLV=_E8h!dq)eH(v$;3M_Cu}t&<`)8c;Rv` zlK)83yTJS563*~2`R|n*dk>xe@pr}a?vK77J#P7cXgs90t)t(&r;XUQOV&2P6fk4bbP;|JU)^y$>HgOx)z^9$w`bVh}m*Y*x~9ITFq(`qNgH_~H@# z5Y7k?`qBWci;a*FTqisPUMK05=a2{Qy^BZi1OF*EYS-#Z_QBY$|2pXKH- z0<8vG5G|ZD=^l3P$2;kCyqALzzEk@>AovNkhI?}A!sX&QDqoAZ>OP3R*z$uzZkHoM z(yyUkgQiueBdUxHCks+3;3ex>IqwUc6d`U3LYSZuj>?_bW(-W|vtc+TKI$HJ!@|CLN*pDe@9zwZ5Oh!6j-{7>#* z^cRe#yqf;yL{we|{_)|DKR>{Ka2ThM3Cps%5N`kFOrj{jrl^U<#^+0K>RNFVL91S79i;i-?Tr`5z;i*fB*h$9*ljSV>!yXEXe)V%k+#_7Y!&h*&h-L5 zlzzp|z_g0q<-tpV1UZ;aE`N#A)7VR3RchfYg+khg98&Gl>-WWs^GshtyZ^Qs{iqtCmM#Z>m?CzX9soB{sMA(@UnA2!hGg{H9 zCqLvstkvKJEz#aU_d^=IIG|7B;F*`9!)}Joka(O0%A_+dg)WFpPcbgn>W0D-GK;1O zmc5#KOE?~QV1vj7DGLBh#@X1(xMmhA(VQU;i}7EE9ZUY4<#k46$ac;Y8xj{(d1+Kw zlPb;xTrIIT<(r52mx(9tdvT5LkDibA_vp0b(~uFTbL&XMR}f3Gld?RiV*f&IbDm>@WRiaQy9HwC^h9Ilo_ z_78nnHJ?IE;s}`1==L=BZ~VZUdoPoz8?YX!VzZl!H+7fp#1G&Wf;r6MX!-a3f8-bE z@b&B3)hUi9BQnmSDE$fbNO!Pl7Zsb*Xs}gynEVojIM^mb{0b*Y-vaylrrqIhV7yK6E|Ie4Y$@Rj=j=dp1&)@)nbA&;icX| zYUWr>g3A-%A@MJ&THtRWu&5SsVaL;)NAl|eZsG@CY%A8?Raeh{2RIP+#KA}gX?BBKJEH3tC21&}ME z#exQNGPX3QPH+>=Z&HvYLF7jgnr7t~H8{$6fl6A^tTHpyk+mzuOtoO9YOi*c*E%|` z&XrnCS}bHd=)HsQC7Xf2%A6s7qARLZ<`8NG6v9OXgMWGH5n<<{E(7-lZe`d$QdhAD z%W;hj6T}$in?UwuHStHzDcdSgMtbF^e_c&7^F@T)5ea~TkpBdD67iK7_F-mgAV{^3 za6gy<%t{<+GZmd?XQ(DraJB3yO9stlIgj0$AX@2F9IfV84%rb1bNq2G>;5v}V^Viy z_sXUbI*$5aDn=Z#4v6%1=pY~);f-AS3{%AN zZQHqu@)PSMJ5xfMP!kh1h8>gli5?PGZVz`SPgZ)>4cFA5$L5 z5lMH87x2h=i7Vjv(j3Z;Lom*w3X(cL%mM&T&TR16ooGW#U)#3+lRvl{jVFCYfFU|1 zmZnf%H2OT8aFq3&!hn>;1XTmL$aR3U=vrHqq+h&Nl=2-JPp*cc8!B zs>1g3*U)yQlKnh%unqO4Dj<0ch}2XNxR|8ML2kjDy?59bn!t;~OQ3=D>H0};q~mR^ zb5#^3S%i?qrwBxU&bxBC1X_)sabc+e(G%|y88R5gOQ=kO^LNoZ`#|O(nU7|r3p4v^ zOpem&c&gV?DE##u1OHxt_FjPYuxg%R!zG%%yCr=93Td}49|$QQ=l4q`D&qzuo~?(y z?Vv4>uS#_7Lf7A_+_%)`mr|Y8u!Z$0O{>iFf{aWBhe*@FlZqWlf)UPRNDryfL+Kls z#V552Fm8@5uKWpzs-5f*Hi+RbC0&5x>-5elyA_ic}vS&QH%Uq`~O%lfu)g`N zv5tbbHj(qDir%W!&mAg!OP^>}qt<8fEdy4d73D6(U1*NNw8e;2X1fNZz>!Z&qd&+g#nw@jpek`ExY@RS3lNgI5Q=M()N%aY%>*I-9VPAy1HTP zRCcwa7^viVs%$}`9&v9U_8wdl&WRC~>dOFC8Y8D#(1T-8_ek2s*b*PUJBk3ZCX=M30E z4q2`rG0y|1C#z^7T&J~0l>^PPaCq6OCA|X{WWv?>^H2I`wx-axIYmO&b6f#Qmaw(E zYz~c|sY~t9*vWblUr@S-W^9?{erU(YZW2LMod*g-IA&vBA`=$-1Rr` zl7Ig6X>I;Dc<}yz^62T)gXh2t>>oUN3a_1~T?10}eB<|j)%xQdIuzkS>sNiqp0@md z`;Q(M_`m1Rp7!})ck*zFR!qO`&R;mV@;&wamY$0I&k6i*r{A!r|4$zuJn!lME}ok8 zfB!xS-DPqWrSIQwycz7*|M~N$kNWlB#bdJm=&$fTvm3p~Cd3$wuu=OrTJHZR2c`V) z&-R}@>hJ$uJi?GcG31V1bF>vmM;hh>kwZ9m;rxqIi0gUY-Q_aD0)O~BCw$L5#kgRb zmhc@PJpnrpwjAXxmom8Tg>&}u^~=Nam&0EU-@I(-z$r0j!^zzX=dU36HZ$RaBE2s3u_ z!Z}dClDYcL#6fb#0VW`D|7M^Fit5|G&MjZEo8}7v0bMD>&|lq%)Qj zCuz5nna;VYlcwG#smFGcyL0AD3`w-bHAU)>lpXin^WX3D0+8T~M1ZDbI{{{Pwy^*Z zcmO=_FD3=&X^Qnl=do3MAnJ=;=U(#4BP(&OzwSlSv}JTGY5F^!)%e}J+Aq!SB(=vc zJ8NDeo4TNL2x9POb$FGXK$=#{mA1;hAX+!gTeuFRdLb4A?0DZyj%#b15&z$v^{3Xt z|Gzjm__4zO|M2X&GcD4#P5Ig{Qbk`=kJSu9}J3oaz9sDr2FDi*8+#z zJu<3}*Q>PM^Lm@H*nLwo*KC^+|Ch|tZk!*tjr`vaKfZkN<3Y9l=Z^=j{`XGW-TA+O zayjC^(&5!l%rxozAiG&9FDpPnB>2ZsBX?LUSBb5VKL(e6Lo2LpwMazAQ0a z@>Wywn8pG}gy%G(UnDmzzK?#*nSTki^HW9tB=u1`;+=*85LV8RSw_ebomR25i8i=d z@3od&ESl8M@UC>kU+nV)m`DFc9%zkoxmH}sWfHHJb>=|9P_9NhGz3|kR0!)JpP+3kdH@`b0%OsSA5hX_*?3LHZ>uNF z1RkdF*M9`&%T|=Qm)Dp6=YKr?Kjf?W^Ur%2A$Pm?{eNNHQ!1Zj^|PlmLF1P>6hZr` z2vV#uH9Xn%fe4p#p|iUw;MRpGg9*SMjgOB$zBq085kHeV$d)b!ibu@crWCg`#RQUh zs;DsLvThXOk;*7$dFcDz%^Z-t^5aZ=N(TxjP6=euu@TVXIo*vD|Ki-c;L2{*7nfij zyJm(`L!3>_roN*mxOMJ}s2J6=(yDvwOO3^GL; z(8qLzxj9&?6iil_Ol36lul;b82A2boOy@xcv%|TK0AK@JY(&>~xR`yH2VXu}%k9># zBsan^t#C(7gJDiW1=-NP3h*jt-IF(`XGO#i9f1sDDlT9Fcmx{2gp!{@&x<5p&@6s5 zTgDjTN8YnUI=pl>0r?$I^uJfgjKP@!b8!$b6~-8R?}vgrUp zXb<=%5x+c;Ay*e~D$;o4iD_>iqrFP61v&-^-tG)Nj^RycovRNQ+os2ht>8qOf3IGq zKQUPzMtYG3M&?@vX&?Uk&FLR{?&YU&FCvh^0Dc(!Je2>R2ef}rfqa!gaFbXGl4*X} zg20CPCG@W3caP7Plg6hol{XQ%Jj(hUq-yWCO~+fD)S4rfz{9kHW}s1!QHhCsJu_jV z*8-M*q9-)L_{I7?;kFQ8j*Ij2>J}&2Er?9IwOs{l;>y3p6wT{ax> zxo!ET2Y{{6PvVN&`&YK11!rtnFh(&g0hK7~RaR%pNNT!-d6rPb7&8^WuLxb+#%(u` zjdQ>ry*BGz**>l`-(&?A1B}{@Rg&33Ff;ss@#Rwuh%5?xq#^6FiDKOYq2Eeos41*RP^xXH(>1Lm+bWl>ML8B>;e9A%ZYPd{94ZloaJ$V?uMn_fJ70ShL z2p`)Afelt~-nnuF$SJ^h74Z)+Om5YyuDs_bSg8Y8GbznTOwEM`PUA(9X+O;c4eltZ zu>>eW4n9Enp;FAwEq_K978x*8NMWrLR`yEw!@m8 zu6rs{M$1iY*p19mH^bZkQtHjr(~!xQ2b`~YSoya)|6Fd2>YwHLwhXe3VnkCb+ctVY zcIB)ZHLRCucTDJoYRVXplV~@j>Clnfeq#@bZ<&-MBGplq8A5HIR;Ef*xN@~zhT0+| zttfv@C9U5gf)uM+fZ7?0AvFe&XUGA|G~yg)KF0)M!9vw(S(ABY4a5KfDW&EN!@*S) z-$a^Ln`ZQfmjLdQ(=jGve0zB8eUjOkyrUVOPj-v*O0g{8XAWVpTynW1rK{0Zq3sQ% z{Dl?JI^r-jOeBdhuhKgrFL4(Rp+6pkt~dvCcnxT++o2bBs~O}XjbV39elQqewRS6J zzI5SEl>VD?QYoe=n~O}*HN{hN)=BK837~cYGq#<|jb}NZHl3H&bB4> zVEC0eMS?>x+uM#qFuOr8$0896hh?mH@34$Lyd0MKmavRPcjK;8@yjZjH9L*#t#nMs zZ{Cm4Yv9M1@Jy#MDxnU?-h?J%;RygMtEOmnIcc!c3tvbJKWhswN@hG|jrQ<=fwF@C zc6N4rnnwqe@)eU3j+1mJm~C784A()dtqez;y*ougHsZ{4Z3m7ui$(Uzo7`q?GP?17 zn2O^Tmx;zhIMW;@at&p144lx}{2=-?Wt&lfM(j_{ZRBbNI2_A(s4U)UrL9o1wGx9ZaDET;eNDx z)-=k&Cp1_?61k4jK+Y|e*?O|Zx`b5=yFSSGduP?bjYTNfr&BwX0rZzR=RM`SO72x} z@Psr5daNbJo}!MNLRNgd&jKh))hjHQ*kLaYW^!V8#I~YH*1uDLB{_!7;d80oXE! zMfe?CUT^Vl+5*BKS`A*Vdd6?}T`V*tZ$!SwdlV#~4+d=&**v8V+lGRaj$tN;Nx`8n9@)Qm3&q zfYtI&U;sOs(|?UmK4`gr_>ojqRV;*xG+ni_+Q#c%lUH-&(xqgVE3kOoN)nf8@+O7n zDCI8Fa~idL%*<;{d5onJOa=oe$iRmeR?d;x+#J{>WSEpkC z-#^-^RB?=lB)tF3TDwh4g&P?Hs!-t`&`sWOOhh^jmLjBaMt-I?>!KII zUrnJXf_j8YUU|4CfknH^w(VZ9S+G@K2}@;7Ol?@H-LO%ewqV6ReHE;eC77&OCVs?x zqIbXrhuSm_A2@vA@WG?R2gY6T0h`3;%e}vle1Vb zQ&zn_HKY7{VeZ4so|jQ!(p$ze?`i7$o)EJE{9_Qvp5M1wvdsk-tAoHHmE9POP~UldX9ZcAXaw#Z`potVYlx@8O-eTy#!PCYM0+pNabVX67DbT z(3pczbkr__ng_iuYm1Z+!oPCb{%BS%!_bq8#Tc-}g8Z3;R@-B3)_vq-H`OGnZQWH% zcppVi!IsOPpu_HOZ!o+gjjEjvGHpf;2mh~XD!#Y5qeW3fG^{&2;g$M zOgG9aB@6((*6DjI6T#a!z6^aCr+plX41Js*k{|XR-LT)*YOHo$IW{ZLHG9`%=g0B2 zC>gOiKMv=|@qqj|JnyEWrPu}%lVE)(%MfmjUysX!b~yZVfN2 z%<?WEl8ZO8Eu4?@bdG1Hl5A=w$hEkqvCNPIcjvn0R0 znc!BI);G*rU9l@x2oH7{dat$1rd4bQLQc6Wc5IPr$&zAp#=*o~6@T=LogyClKY(nh zoj4D?0=Ry)BiXf2{$m%K_hsPJ?v5ep1~12uIEJLL*)Sw&-wcV=MA-V*NDZAVPl3nq zCYmkd0FD*^My`=Xz22!hJQ?ekn~jwUR^fohNAQa>#>RACtNm_6{!2yUDdS`$Mk$lOpjM!G>2cTckl3vJ-i%#`IhjDWe!2z#%j&8kuZ}=G-MF@ z)k$}Ax7@SN2gvyVwKnGiRNIxq>e~g`!}0;L>YkZ;7?jSJ3N3Q<6l0fatN}8vTObD$ zUnG8V2d@anZ##b53SN%ie$<%xLF9m3#F_uW^yKoiC-OgR6x1j5DZPqsLl#4d{q4(9 z0@+E+Qq~ne2~9c9lX>IW41Vmj>Zobq^V(t$$2^eHJeNTqXl2l~qS^*iTND(>W_91q z3yaq_`J_}-#s32DI1=GN-j`tzkFK8$Uk(pONG(U-HFh`<;c_kxhKFOdjJB*Z*mj+o z3oqQx3XWsDzsy=a6(8@siSD_z&PwzGSKYA?d8f5!2C1pmE}Pc3rK056%nOravno!` z18fSEW3vRWZFqvQkYrS_9GewGVBiLbEhpK9ZG31(y?OK&h-0x;qq8xD$}as3;zUe0 z6YLV;{-Eww{snT#R`LMv$iq47=URkb_n!6>=eJ(=pK?(mk`cVT;;LJ?w7%bCK2B4=dJ?S}_y#=@X8q~St!pRA zx=HfR@C5RtdI!(`uC;)lyg+3IBK^uZgODpl%BxXY zkS_L%%_a0}G*fq-4w3W_r5K6|!?O)he<|8ew19`y{s9be2VGeGC&%zX`Os^yDp+`L zBBocEpBQfV0tpC1hakO=o?5TGJ-LcK6Y~o;YxV_aH!J>B+a_*F&%iuWe16-0Da(B7 zXOv!kwPYH)1~d{LTQd+?>9Fb`DEpsWvG|rdoRFg>z$e30on_F-N1>|I)!c2A77*nF zc1@A8k1XF@DE9D)&3xdO^CEaGbV=`*CZR$F>eHXEX~=wQ~FJMe5BswmTVDtk2nxWcL>?n_4pkADD>ZJ zpFuLV@UwYr0<|4SLLL@@oa73l@1shM3Z9SpGb7k_pI!Ef&Cv}2A6zFcGV&rfGE z5~e^r@VC()N3Rce#^M$3XXRM3?(s5zEV&-fmA9RZR`GG87EkzWdqm>_%sD>=7CHz zypZR$1k2~;Y=gqXdr2BL8Ay6gdiHZf_lPNS^^na|f7s!f{+DHt(3=C25J$5f&bgS< z%L1aW$Oz%w5AVnio?%{~6?&su>|4bs1X1?mOX~xIQT}DC4X|jP%RFFfO3+mx9eRfv z|CniPg3xef1-IG2steM=)(P>{cKnTogk$S~cd&KDJ;hKv{DaMwv&FXiFmY_1fzyfG z#rD;3yx2N|&pvb7G)m4*Ya=jC>YxLMta~)J6<{7D;HcNocYU=b)+uM%QKRr6D zsnsgX^6;v*E+4OnlBR$sP1tWrl5dhAG|>`Xbo9} zVkr-hQy5p0+*CztKH(F^L8R$w;SXUH{bdrMhNR-fSGrL$RdVW6Ae@ZJjKg3Wn5!@D zi>Z*FSv9b1+Anevje$BaPWR?ILS+R}mq3rr#4hnFW42}AJEsSEuAGYH<)+bbATIu^JqpM-Mo|LTXbdNfc+i&b4 zku7r|h(${(_jYXBJSQih&k^NnxeT=xD4DVJDMT5**eZ5(X+vGwP|u|e{aUg4!%7=! zE7ay9mtlXy*chu-hcmIX7JuKtAS1ySqZtw2BmBcnoXp^tAkp-D?_Q6Ocjn#EN#%_I z>O-z=+Nps=i=W?~EW2|T#QYv8nn_@xK;)LNoW3p%HYaB}Im=Pzt%A&2&QheJ6DImo z4rW7Kfm7&jjfka!O?GaN+hW*|f@I&U0*NzLl}yWzx-@(<`v7kPeVsrbUeol|J560}p0?U5 zWwl+p>MbSGGU8Q@(o-ALCcsrUBAFclrGaM-__{qIZ8;HbeY}_XVVFt;hgBoE3qV55 z2I2yV{Y?^Nz6xl_X+4h!p&ODOHi_UhN`u7`&2jcUb_F!~IlQ@PQZ%&3~j5x}Aw+|Y=2T2?)d|C1jEnabw;p>B$y!X&27|6VD zL#fP*(u2LB_Z|~#i3vI%M8$IOD1oKFAa?@N8nzyfpRaFJOw^_;G=9V;+R_G%r*xno zeJ0JJ&Vs@A`^Q}C>C5u(O4Z5A$UV-#bNw8imDT?P^S4KHpS71`bPMkrkE@n_Q2 z#YOOi*bu7Nf^SAI*ivMTi}RQ>OpH{umj%quNg0&llX9mK{LrvB2qS9v7f2JI5HGYc zHkBku`cME+O4m0?d*4}@Y*w{3T0Gt-SY(y zF^Rh571ol4%j@i41H@K>*keYE)7xC5ea9QFP1zfU=}>#;iIgYgH80q9PjFpT(;?+;jpFP{_?`p}X(!Mom<+y%s1B8$AAxa?RFT3%%YNQHI z-w#lU;q6@?>=7Gu{Tv>=nok#l>6ey8nQ=t}@A|-`QyU0+P6ydi;z0$2Y$T{g49|@} znaA;!aX!dT+}|oAr0_2N$WJgos}FaYaFG1&hR>Qxb(473D$--pEH?tRgAJr9?ZKZlbdqa9R-| zKp1ctfPicA#tpq^?JU;j#@Lz(Gq*0Q9QYcjjApd7CtCWIH5@BiJoS^+3alATY474? z<>2*|I0STZ?Xh$1+LdEh$BkGzb$u1i0bOglnmP*E_%<2)W7}jnwsvP|k3PU$Rq@J> z@}KdTzIYFVTB?m45RAjCGZkUDTMZX8OMSg#a3;~ewj0}-*b`1{+qP{#6Wg{X$;1=e zcAnU_ZQFSB-}~L?RPCyBzI0cAT~%GZ)_q-l|LC#|UEI{{H{f!n4Fpj-()$gf7j(b! zl&;*9lIx-*JgqIWT@?TJ;8|hRa$D=qTz_XwEY!b?ZD}@D;7C2e(2~vwBj6Yy$8;U04x1)bh%tyt+-C55Z1?km zb{lG~mJ@sLNv{ipV&PdDY&z7%13cYmwx&SuthSJnJHb}lY0O2>xemGR{cJ26PcE~) zR{+W8{gtUfUH$!uiR@?yrhhcMAh?~PvKY75-$qh%-3k;*7TxSq&TCGOg4(YZMmNF);oO2p4gTcbsDu5f9nL_>yFmeC1sh^u>!{2qLoDmh zDLjNr%#y)xWACM%)AjNUok3O{!;-HvTZU?inBK3Fv@dTZDzzR?DXM63zOO2-#9DNc z+(!63UqY)TDPGP+!G#wdaZpEOOc(`8$s zjAE71{%1$^j3Mkn_2N(^KSH)N@BqhdN#DuI>ex=bUkWJtQ-1ERpYQ#Rc4ZQ=~ zpQx|lzRx<3%HqI(KiA{y4)~G{O`b?@+_=w(q!D#GhxKuVu}*@a-6t`z>5IHiaV0)WQgxT0qbvRHx~rmtgaEq{4J=T%ayZ~U^FBmV z$Y1M8d$3c^U5_>>?(BnY?6SD!p;w2Q(tLIatrRDK9E)MrB zJYu!4^vs$YE(2yc)$nhLBw>cs3Xp#%mWKv!J5-4D&_B~A1Dxe&;SzD)n_s?nw&4;Ir*?;kE^AZTllhXBbxvR0hjAYEQ(790(1~{w!KO}5?EWlaC z!b-W!Ze%ER%oX%j7ecFSC=OvK&19AejWUI$%qkY_fuy7OTCw1Bu_rcO4jM+-tD8o5 z1LA$+d}}sao5^n)c(0v9`U}Hx((X)mi@OI*kn}n;#BI1PC$?_-HD{hm%zp@l(dO6y z9$SpxYr34j3GYs;R6{H4UJ;<;nyCm??|p}@6_52ReR7ZU;Q<8enY0^q;dK))!YvtE zqC3196-Jt)PXCb2B<(Iab5zwO1E-ytiaKK?>z@eIjR@P$;LG@ea(9WskzE&Yog{}| zDVt3tl0`wt%GPz>Tws)~&mR^&Ms@Ctw?bfi_SJKr4f?_#hpv{$Rr`-XRsX;5g}O@_ zrK4S=n~@MOC%FEa`&sDOdI5DFN&@Q2`OpOt1r<`I1ywNct{cgh6)%sF2P(47rKBp; zXALp<_vHNWF2G+z5Ss%YHA$;RNbM>eJreV7M=IByJdux`$PS@jPlq!8_efJ$F6YJ9 z8n0uEAU$KJ5Kx5D8XU$#QNmnoe@P~sEDwo;HU%4Y1onNMhAvd)bK#~X99XZh#?G4@ zkYFjE!;lIQ=SE7Y%>Dh0LYaOHZ%nGgl5!O&eRFP6%N@#;V+?5Q!5`0mJ;-=0aG58F ztTNo7f7ci(xcvB_)&@LElzb8VL`7$Dqx3TzLoZXI&?nq`4AI;@dnOXyaTfYY;iG3d zX{~51{-TJXd1d7UhvMi<6Q+#pjlDSlT_i&TtJgRoiFA@2N1dokhNM^y^1HZL(x2Z{ zY8dJ-d<=Dz7Uf+g{AhCHK(qFyiRSUe`SB724N@M^A*0m+Q$*3BJQlM&Or}&|Rqm96 z>dV;WL}7O@>B+1hbN9RdNt%TE4lx_(eVjO7G_`i9}A7wO=ODD+ygHa-UC?53g@b!8Lm;CF?EOUPHy!QJG)MdEJ$2EhIKNo0 z*_1k{V@vCa5{D4j$NJW~p^M8m*Qi7Se7C5Dpb%whx%X&8qhKeGH_OAD>( zk#!JcY86JClHg;lTCSbzTPrlx9R(Hs$L*5{sCM0M<$m3v+YySws*s^NHKeX z<$;g+d8eYA$lRn6;a1X#phzBfk;hodYX`*lG%z{E)`{XO23TDl$)R9aSv!~VVv zeJh4$fk-9?fBgq(MHLU6qZJ-TW7??auu590^+c?pq!?}gGS8!sW zW|i^|+IC8R7Po!lNoC}u6{fdnq_JV9v7jdn)99hCu|0s{?f|Jz-R=nE(@yNQ4ym6c zX;NlGzaQ3>Jgh$MP3|K!Qf}$T*bhGh>8vw!uSdg3J(R z)SS6t<6Z5S&fvo8A49+ppha6NN1o z3=x7M_6;sedZFz1Y}({G%Mq#HJ+P(f<`1pFEbvtPz7@mQ`>#6rjR6{^DwPTd%ybNt zk%Ygs^$NRx$WFu=IT%`g1*Ha3iahP_J9i*HIOC5O!TH1{E=J;)tw`8)yOs2xRtp_x z970^)lhcX-C$b98O96Z0W-o|3Jn{z!bmGbK{q~u`)kb!R+Q>te1tj5lyH~tO79LQ$ zx^q2{PLAG|v8**N^%x(QTZElWqu&)9#LOsr#>^J1j;ol(fS0xgI^3rCTNyuWkxjxd zHvC0<&Aswo+zs8}QT&LDX*ka|N;$_}gWeCzr#`EsAO*{sP%_7uqJT^tTBnqOmwDf- z3htI#82mS9>tbgAPqd>Btb~^PMEwUS511R+a9Y;^oxy`z+mg&lNGnveCb;(zylijD zMnmRJc^8gzj0|II{6B=fItt%s=K#r*z|)IepyXs2mky@&F|Lgsbnyn{qBi>7eyrB~ zO&~-FEaYWg%48LGdSDL4OXY*1+T&($@TMX(r@eKsvN*|`7-l*btV1waxyP`7eSjPZbHfQI%ZQQ zHolnCUeaf9hrmC29*O4o6ObAmk^nkelB`nH#8!;yV2%18^kk*;ztEG5W>CeNsW=dC z)V-H8KlL|3)3$dw8`4=`@-GoAm5MSv$`Xeek2ZD0k(uX@a3`C%PYAAix?kdfD;9u1 zQ=AVLns>wsE^=jm*t5c|k=nV=4{O%cg&slHU)03FbyyH!a;bkcLm$W7-ia%R@ZW*( zC1@z?oB&KxYcKjeBiifGYU45%ZiiKc#1YII7#p-0qu;cfRgJaj^vaeTX~3#=N=3{7 z;ZmslLa^`m{l0WWEKZ^7vD2jxi9H~Z4RXe4G>RvsZ>dV7f3W z{R2!&$F02_X7OVS`C6x;Q! zbo(wiyObj7(`VV>!txuU5=?>dTSRMF3#`<2?^!2BmEtauS}l2XI0z5EVYjJ=kk_V4 z>+%)TTzdtDvba%qG~dL#B=J&x_MN~RIoT_p{G@S@Y^(ZtDbB$bd^WtSoevI%FUFYr zA)lPj7P>LJzaIkF%H13LLR#y#u?q@kK^S$}fefUktB5!NO@2wkaG$@rwv4%Ex zyV0$3_X{d^Y*Ks|A{I3i1~sL&A4XJtk2m^8pe02eMYM?Tj^&uK^Es&zm(T!FUngGQ zEWfC758Zwv?5*wK0bIp7nkq4rNU87x|KxOr$4`(Igx*oe7#j>(~63I z9iIheWV1b1RZbbo)`A92t|Gm8Lc&ot$k-LGc{}gWZ7ZWvs*8x0AeQar`8ekk*g|Qu zp~$l7Uvu-(1dVq>WXuZ>SkiTR=CcqK1y@)`Q4t7!FR5`o|A9SCO0V5Y@^o)V4Z5G{ zA@j0KV2r*Vgj@0XNS(+oj^tvvZKX)+JwPXziElK4X?5s{6N_$P&d;aB~j5tM-v&cSy_ z4eokMN||l4Rl%45s(=evbeL-GTY&d%`Z@!q&(vvURsRV}R=+w;3z6CX4W}B|s~gWO zEhP@Pk_F>sYq5sqCW*JDcJ9dk8r94kDLpb9jF7IGDKm16b^oX739I z_nC5Fd>vv0_%G_*qT!L4;nY6J7B3X@_#GP_UXhPP0vnt0d2L4!h<_Io@A|Ze`Ip>_ zfU6oto4-f6oKE1tNu;P+qarAGXze$x4Ewc)v?3S~gF!vi?gQtv-vvL(5w0JvpYJ=9 zL-zpq2BBT!te)S5Q)-Q!CBj+R$63J(6vcx!9T|&bsR6dwvnD7-h0j~247&W114HGU zJw6rUtUVQVZ1EZ6C5x^IX%gr#E*$Ey(Szg>ulFb0Zl1Y1UFLSLWUjAR9+_q&u5zsbWBd3{ zbaWoB)G+TP>D~KJv(3oX>=YhN$%KxcZHE6Qo8?)_tgCcDRky^i7Hr_i>Q#X*<-uJnml4$=t;JlJ1e($79O+8)rrF~JguqvgYoX? ze*gYkzLjDm8@3Ph8dMz<3OG9);{7vxe5k26%F`@X(xbU2PNdkK35I)y;R6;}`lB=i@PHxaRyA14aQM-l z%}4f2hEit~P+{ac10$6N1Dk=SwxmLO{UGCDE#*u6#^eaoX_Fr( z$z;7~hvxc4-+>=ZXBr!jMn~sdnzgRQn~yAUEra7~bT$FYCRbfIiRPT(Kd6>1scL|< z^ayBjw8?L~+0(zi?ea}&Tj9ScZJB4*v)BJn+KXVU-6(T`N2Jv@=ma^p@&+7T!D)p- zD3CsNo>LLFK9DFhCt_l7XmqSB^5OIG>(cza1@@nN;I;0gLEz0ri2Ap;JW|4^v8sZ# zY}KGYT5VlE3L(pT8g6B!?BP{2yKfJ+QXIT$42ubtt>ga=Zm;WuoyM9Um~GpR%CBBR zF;D_{tloa>6-uwOr^}^B#$zF+LaWcGJexQ!Av$uIm4VRRjU9Z-jAE8-p6%^LRC^QE zkmcN}%^J5b^jx{&x>AQ}g>|WgK8-W$9EF#!Hs6`}fx&#h-UO&dbp_@<3e`Q#qIGRP zO&Sez6+4Xkgu!||m9laas2G6y)FBmIk|1?JbMNm0-#}uRFj~Sz1c@#pGw6u7+3Bu` z4c4=i9;|mM6S4u~C}yl#$hE>Vd93kQy`=LtNlix9?cRdgh{u*OsE5LfUpwa-VT{l; z@+a9pgepd15|%?>Om-iiGwDA)Pv1VCGdJMnF55h8<^MX@6(o{3Yb=otSt0>7uQqqE zyxh6oZ|QchWZ2p-Q>-THF-FM5HXb@i$n`0cTN=+YEBij0FKz8kEVo|ZGI*AsQl;B0 zhCEZ0J@E|d*tl`Ir*(3B$LE)toCQ;3H4ahZwW_I=nulWnpd)hL3G3wKse3;Ou&kgN zhQ#<0=2uZok-K-(o})PL1XBO${sZWJhQ+Kt1oIbywwF3%wYELxyIIJuGGnL=q$sJp z+?sB>vj-9Be27-6*6*gMv9dT7Qqw)!kAve(ry@Ik%8Cs`V zjAyLlx-_?4sW{MLd@g2qD^shNox&m|0e}C0;%S6L>caLaiN4KF_lr>riIEj1Jo@Xg zdN?VF*8TXZbFW(OXEjr7l3&GU3Z|njS!c-U`{Z476m2XY7UawU<@}2pF$|31p_?R` zs`w$1V>!~DNlyPn0mhq1X_z-JO1a&Y04|o2>{{3620G5>UFVi*u{N-1I^~M0K%Vb; z);ujK2EBZ98>t6bDsmw`nh3)w_a*zJ(oWDcCpt;Y_UA)?(Vu(yd0F!xcs=0@8kViI z%|G?p#0C<8Kdd~_De(j}0yl{g{AhF9tIG_30V@32I zlzW*`Y%Dv*FF*M>kl0j0=z! zV;LHfaMxa<;sGH#T9zF$|82(;JF#9wQ}j*i8&8IKb;UoZO%bbMmHMshaY&+1mWFwJ z@AT989`(5C!=&&jUeXyOD!a=ZFhWV;WEYy7{#l30^8}mSa)!L@vu+ZFNmLhlBycYM ziR#*=8;mKt&rY<$Yx)zM*SZmYx7t$v4Tz9!wu#WLKZP+K817(LQuy~c zikzt_li+An2pC>=d*Jo(mfin&sktBIV+RFjwWO=Zb2k%5$4xL;`Wvx2!NX z>m0ekb-OXn6)&)>BaQu{NiJzxL5Wq??%9bsZAt;^QYED3k8sxV*Dye246R<)I3Fft z>PB;#F?ClmT1T7FN(h$q5xNdAw!+Z`IKO;ws5IiImW)9q+^}?pymt|+C{nH&nBRg> zZ<1^Hh%p=+0kvL^d3TNtQ=#j*1EJ=hjYK~Wq{h5%^HB|tPB4g{$?$xJXmG+U*`H}* z-#1-TDbZ?VyT~NC=y#EHdJbE{7Y|!thQ#JqIflfr*M^e)mu4A?k9=U}xG-Al1urC= z5?)ivO-**T;q<45G;tOh?KXgNdL9NK55X2GB$1VE7GSpvf7eN+4tVh|G6~q*VSc1* zdTzKCtszs2&Y?av9fr4G@Z&pFv*WG#3+oN)(F~U`zzXC9amn@d{R|%Gthy5pw$eG5 z;@a}*TXKX0bKASW*ZcJs0IC6Mz*0S1!JQ%18Y9N5(-@}s_wVPwP}NLC7wK^)b@Za{ z%Ff^=I#_puUb~cDyldU@((B)IkS17A1~Qb(PkK0w>N?6n1S6qbZ*m29+n+0S@>Q`N z-;Je@y`hCDV+T^}B&HtYd+C42Wa#ozbIMbm9xy+@cZS4z=U5ps$4iz)^W@Iq%;U=N z9OdzQ0DBD?BX#QF9TEq+ZWOoa5M==_Oig7nWNi8#>8OG4cQZMTdOS}PQt&^X#!8I7 zhnFLU7X|9fa)k$4=0?yDkkN6M>ScUxr{nS5`pQ7t zv41_o8+fj4ZYYhqF2(O3nKeLq>#R_(d{kj&NurefFcsS`>&NtKxs&I5q>!|5R9k%@6unPHmMJf`E7BaN>>i_BI@oj@x!sVu`8m$i50xspEqU2e$804w*=+=8br?XmT0scv zK^jnV5ccqn;Xr%={&{@YiW@@9hzfJJnltbDHvi^LDSUOrHaIP3vnnu60M0zSQHpoCr?$4lMRP+q!Y*fyNhBvx2LsJrN2ND}aCftz#(n*3B`b+I zjPMJezh801W|0pH8Jrw(;96iG=fz zJ#i$J+?!+^=t)9UI*3aY67Y3~Wwqlmtb5=c3s`MtnwiE_5T&YWPjZ|e%P6e4izIQuj}ga%Gwn8d)vq%t62-87O8*IL}1#vjEqBHP}c$n z%kZic!j&~QPPu-7KgR@^duT}%tB7WF0>Qwf9GRTIT}gWSS09JBtqxK$V*FPJ6ZY%h zQuN=W16ehH{%vDr{Dg+>thq!Ui6Yn&>q~*yy}uC*EEGy49jEJk4qbZvCxURcEpter z4Lqm@JmBeHW;kT`$^qa}`C3HH`Kfbbfg!TgLWu%NQOfw7wX4=bnygQ4Fyif+X5|Hc zz%I6Nrk^cC4(@E}1Il`EMZdR&u__yr^oL`Em*^rZt?*|+*>GYm+ToZP7Jd;+RGh%kym3b(J z6^)kZ{nmN1Hrw1d(3m5?=T|1IQw#_zh`*Sj9qR zsR-3JB{Q`)+KXQpM>=CsbLc80ETZ$>bymP(Db`h>QRY9jU6^DHQ9ZEE{^HRA8QZZg zH+ke+T@T06F}eJ`VU1xl9d83aoyI6MF$+fnUG6@UwTnZK7{W_tzRC-DQb^ zUf7AJa$VIN->KwpU~O2Lj37RY@ywstE`kj9DI74tJu-=;@&o31^&Hc|+2VqL10%fN zE+vASz9!R$=z-=OWY=YHtmTfV&;{YEcC%b|tX70KJPO=KZ5K{-8Jn9Y0t99>?-@4e z9llm~TJn>QNgn0I^?&PslpY61d?p30plEFWc-0{_E;UTdTH9+%?NSt~vr{Lj zk2U@!a5{TEaSq;r_#VURf3Kw>H>5Uq(gpc_FKI_5WW40Y09NFsnQ^7;aa`>P$(g9? z;>UKvPQ;sB+n_?R^l!E?$xeN8-G7Gn>XhnH7kC|$MQx256I)me6^Ap_x#Nb3Af=4a z6dDO=t%NSmo576J#EgUy%iw{67)}(RktSo2_dIsqW6HaN_G;@&caI)u|Hmpy9Z}1u zfz%)Ebau>W%yr+>VZ<1!V~$*~+M&@PS96T*;$)})Zms(ty{Hql80!|-Rz!h@#)elv zzOgv^+-NIIb$z!}^HFzf1|Qt6zw2FFtJmksY;dV6q(ylIrBnxMaVNgEqGBeb>2b4JDy#iGzA2DN;brr=v494_TDp@@|A7!v z@Ztc=cFGjmw}DxjC4Bv--(ZbXSF~@BrAqEZcs;@N(p#SC_wV+i%si1bn(iPR)YdWW z)C5n}4>$UUVMf$5B$*xW5dUp4X$57F4SQoSBsf9w{@h0qvl7h3HSRHnwF8a)&MnyS zJi(3d8OTFK@G(X0*TF7va#ZrjELYFHb;ED18;$@#w`7T~OZX70Fj!#pC=Onz~#+y{$Z?antJrHA?Xq6q-kJqGAmcE4PC@ex&JGQ!GDMI*}HJD%(tiWZ3f-qj_ zW~@DPHOItnI3lMF`l#xdQ(`MZLg@1R=S3DZqEPIXihMu6UU^55#*385r8ctQM+(}7 zXx=hk z!gBx8n%!iA1A#pzpBut*vh#k>9?lMK71Yr71$(CY`B_+XHx0<8N)WgYvBYkypJ*u= zY#`*4A5j$3(pOzauobF?yb=1{ODc0zzsjK1QZ=FM3O4#rnXgYn!;Cu8jjDRrUw8Qm zYYHeCZ|oz1NI02&!&Ca5-Y>f&Pd*z4s_xpA9?>gWE2#b$6Kw+|w@d?U6dC2*fo1!Z zXUGK-Nccc}${vHy@uo|`7)DGLRF0X>MSrIZTboJ1tSeDm@{$M2??fJDsTBKn*N0h7 zfSaub4O^PdeKrWchjL4ss{R;1MXtQevGEp97|GH3gwo{T4(~WY*?AXa7Y^;@Vj~Y{ zUs8u7j-=+>SK{cVENFw}PmsC#4%0ae3arvmVsilPT-4=^Hx9^*Dht>))z(YSeKM~d z|2>m@iqlR#tOAe}9+|7+8Gj5U{UPssnn*gTZM#%Qa&KH5!RUgkyYgQ`6&4=)dK-#? zynCBmSe2TQN9~BlTE!_GXx@cs)CoI(_}{387ja~E*dRQM!Q*H((qUYDYAVg!B=ID& z5_e(V-e?oPQB601zu8)@{D87-Amg(0`Prg-Neo@3GfuG;BP>(d#8G?$s|AuJ*#P$V zB3kfbQ2&r5zoX64`tFj+phWP`iAgln!h1SaG&^yZ(@j!04ndxKbCE`zPyL({@0D9K z6R#S{HDfu1Vr{>|%3|v$AGY-2V0^^=HV)U7<2x(a%0&-q+f>5|h4*s<4=v@}R}t$F zto+BP1+wZJozM_omMg!{rz<}vIlIpk1oWq1@KShuBc=oA`xhtN2s7@u`l)OQ=GbkP zPu32MDEn}MbiX~E1|oy5{x5;&A>^t1uc61=p(AKN9`~2qfhfQC>s=xSq|e9mm$9R- z=b^0*i?ub^8Gkj5h@%exs_c>ParMR0;~Oc!zMlqdr0!&0#UhqW{l;zITzcwcJNfDB zSS`YgxNk?_NYN6>wvp^;S-a% ztiuP37EE)kryfYtFzS&QC6cq}r@@tmWHE=cKf0iyqkOQ*x{(!Y)6q9Dy(5oaS(myy zoFUz>ev&9Fr4#r zZV*-}kv$c1WGdgb7JmnLIu7xW@itc;@Q1|j(`aVdCZKnomB3K)cB4D7**ev>hFE^vkzei_+h)JwHB2MdPn(5AW|U>AQpFj-aL~!+HKlo)ZT&1>97PA01n- zG3NZEi`y~wp=-c&UyYp7FkxsVf8U4d55@H|=DN+h*KbD&c2{e2FHAb@2f6{o$^}U` zEWNl-LK6QTCu~&^3fUdUiE40QArKz5$YrxdE<7`!p;5ko5a^}x25A%|)JW-&`2lCM zeZ7eLGa*Z3hVuhOV%b9lYg{EFWk-uZA0*zYL$g;)A7WysZ#*p|0UezCODxjnXT zq1P|Yz53=7OU$#G|ngH(ehn{I9Ib z>+=m7z87fnX@~d~=k>08d=Iw>9OKY_{yGxqdd}_r67YNJ3-~THnVD6K&n1gFsJ)qX z5xcC|-oI0%?broB<)ZRLiu~s+^(mhc-v0B*ipsRcKB1^fToju3R2AtbY8NxzAy^5D zy(&~(AD+XQQ_;X&e=^Y_Jke~+j&lC{YR#(h`2@{4acJ7{n6;$nvM`sZSeC zt*lr}`@K~O89bY1RU`Jq8$UL2GqQfq&H~ zG-Y-gXBg&>dHl<)bhz!MgL6C%HWUU=_tY z)yqZAn0+qNV(|f?=Euaamh(K&8Fi?-BVxL&l?8R0@pHph;;!vQkMO4ihapt5;~fm(*)a+?*F|s+7*BJxqyPEfG{F}{2#cU BSDpX> literal 0 HcmV?d00001 diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index a8f88bb9..ea7db53d 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -11,10 +11,10 @@ clusterGroup: external-secrets: image: - tag: v0.8.2-ubi + tag: v0.8.3-ubi webhook: image: - tag: v0.8.2-ubi + tag: v0.8.3-ubi certController: image: - tag: v0.8.2-ubi + tag: v0.8.3-ubi diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index df88573d..c1a23515 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -7771,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8031,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8224,7 +8224,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8252,10 +8252,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8284,7 +8284,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8300,10 +8300,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8332,7 +8332,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index 09f472bf..7ae2a78f 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -7771,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8031,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8224,7 +8224,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8252,10 +8252,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8284,7 +8284,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8300,10 +8300,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8332,7 +8332,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index 09f472bf..7ae2a78f 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -7771,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8031,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8224,7 +8224,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8252,10 +8252,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8284,7 +8284,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8300,10 +8300,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8332,7 +8332,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 11f2ecca..518bda17 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -7771,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8031,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8224,7 +8224,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8252,10 +8252,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8284,7 +8284,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8300,10 +8300,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8332,7 +8332,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 09f472bf..7ae2a78f 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -7771,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8031,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8224,7 +8224,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8252,10 +8252,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8284,7 +8284,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8300,10 +8300,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.2 + helm.sh/chart: external-secrets-0.8.3 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.2" + app.kubernetes.io/version: "v0.8.3" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8332,7 +8332,7 @@ spec: runAsNonRoot: true seccompProfile: type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.2-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" imagePullPolicy: IfNotPresent args: - webhook From 279769915f94202dcaa7734ac0d07a23e70da8b5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 15 Jun 2023 19:54:11 +0200 Subject: [PATCH 0913/1263] WIP add presync for eso that waits for vault to be up --- .../golang-external-secrets-hub-presync.yaml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml new file mode 100644 index 00000000..5a682bb0 --- /dev/null +++ b/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml @@ -0,0 +1,26 @@ +{{- if .Values.clusterGroup.isHubCluster }} +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + name: job-wait-for-vault + # By placing the job in the vault namespace + namespace: vault +spec: + template: + spec: + containers: + - image: FIXME + command: + - /bin/bash + - -c + - | + oc wait --for=condition=Ready=true pods -n vault vault-0 --timeout=900s + name: wait-for-healthy-vault + dnsPolicy: ClusterFirst + restartPolicy: Never + serviceAccount: vault + serviceAccountName: vault + terminationGracePeriodSeconds: 60 +{{ end }} From ab5532a7e602235bd2ec02a7c9dff78f82309856 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 15 Jun 2023 19:54:30 +0200 Subject: [PATCH 0914/1263] Add tests --- ...-secrets-industrial-edge-hub.expected.yaml | 26 +++++++++++++++++++ ...ecrets-medical-diagnosis-hub.expected.yaml | 26 +++++++++++++++++++ ...olang-external-secrets-naked.expected.yaml | 26 +++++++++++++++++++ ...lang-external-secrets-normal.expected.yaml | 26 +++++++++++++++++++ 4 files changed, 104 insertions(+) diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index 7ae2a78f..cc3e6f4f 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -8364,6 +8364,32 @@ spec: secret: secretName: golang-external-secrets-webhook --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + name: job-wait-for-vault + # By placing the job in the vault namespace + namespace: vault +spec: + template: + spec: + containers: + - image: FIXME + command: + - /bin/bash + - -c + - | + oc wait --for=condition=Ready=true pods -n vault vault-0 --timeout=900s + name: wait-for-healthy-vault + dnsPolicy: ClusterFirst + restartPolicy: Never + serviceAccount: vault + serviceAccountName: vault + terminationGracePeriodSeconds: 60 +--- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index 7ae2a78f..cc3e6f4f 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -8364,6 +8364,32 @@ spec: secret: secretName: golang-external-secrets-webhook --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + name: job-wait-for-vault + # By placing the job in the vault namespace + namespace: vault +spec: + template: + spec: + containers: + - image: FIXME + command: + - /bin/bash + - -c + - | + oc wait --for=condition=Ready=true pods -n vault vault-0 --timeout=900s + name: wait-for-healthy-vault + dnsPolicy: ClusterFirst + restartPolicy: Never + serviceAccount: vault + serviceAccountName: vault + terminationGracePeriodSeconds: 60 +--- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 518bda17..5141cbbd 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -8364,6 +8364,32 @@ spec: secret: secretName: golang-external-secrets-webhook --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + name: job-wait-for-vault + # By placing the job in the vault namespace + namespace: vault +spec: + template: + spec: + containers: + - image: FIXME + command: + - /bin/bash + - -c + - | + oc wait --for=condition=Ready=true pods -n vault vault-0 --timeout=900s + name: wait-for-healthy-vault + dnsPolicy: ClusterFirst + restartPolicy: Never + serviceAccount: vault + serviceAccountName: vault + terminationGracePeriodSeconds: 60 +--- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 7ae2a78f..cc3e6f4f 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -8364,6 +8364,32 @@ spec: secret: secretName: golang-external-secrets-webhook --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + name: job-wait-for-vault + # By placing the job in the vault namespace + namespace: vault +spec: + template: + spec: + containers: + - image: FIXME + command: + - /bin/bash + - -c + - | + oc wait --for=condition=Ready=true pods -n vault vault-0 --timeout=900s + name: wait-for-healthy-vault + dnsPolicy: ClusterFirst + restartPolicy: Never + serviceAccount: vault + serviceAccountName: vault + terminationGracePeriodSeconds: 60 +--- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore From d4d3fe100681dd858601ed1934f0244333ddea49 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 15 Jun 2023 20:08:28 +0200 Subject: [PATCH 0915/1263] Fix image and comment --- .../templates/golang-external-secrets-hub-presync.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml index 5a682bb0..05e1c117 100644 --- a/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml +++ b/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml @@ -5,13 +5,13 @@ metadata: annotations: argocd.argoproj.io/hook: PreSync name: job-wait-for-vault - # By placing the job in the vault namespace + # By placing the job in the vault namespace we can avoid dealing with RBACs namespace: vault spec: template: spec: containers: - - image: FIXME + - image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest command: - /bin/bash - -c From 598bc74fefd2cb72fe90834989206ad12b130a69 Mon Sep 17 00:00:00 2001 From: jonny <65790298+day0hero@users.noreply.github.com> Date: Mon, 26 Jun 2023 15:14:05 -0500 Subject: [PATCH 0916/1263] Adding rbac to support the vault sa checking on the vault-0 pod status. --- ...-external-secrets-hub-vault-rbac-role.yaml | 32 +++++++++++++++++++ ...al-secrets-hub-vault-rbac-rolebinding.yaml | 28 ++++++++++++++++ golang-external-secrets/values.yaml | 30 +++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml create mode 100644 golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml new file mode 100644 index 00000000..1596c67f --- /dev/null +++ b/golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml @@ -0,0 +1,32 @@ +{{- range $key, $value := .Values.rbac.roles }} +{{- if $value.createRole }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if $value.scope.cluster }} +kind: ClusterRole +metadata: + name: {{ .name }} +{{- else }} +kind: Role +metadata: + name: {{ $value.name }} + namespace: {{ $value.scope.namespace}} +{{- end }} + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/sync-wave: "-15" +rules: + - apiGroups: +{{- range $value.apiGroups }} + - {{ . }} +{{- end }} + resources: +{{- range $value.resources }} + - {{ . }} +{{- end }} + verbs: +{{- range $value.verbs }} + - {{ . }} +{{- end }} +{{- end }} +{{- end }} diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml new file mode 100644 index 00000000..6f28d740 --- /dev/null +++ b/golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml @@ -0,0 +1,28 @@ +{{- range $key, $value := .Values.rbac.roleBindings }} +{{- if $value.createBinding }} +apiVersion: rbac.authorization.k8s.io/v1 +{{- if $value.scope.cluster }} +kind: ClusterRoleBinding +{{- else }} +kind: RoleBinding +{{- end }} +metadata: + name: {{ .name }} +{{- if eq $value.scope.cluster false }} + namespace: {{ $value.scope.namespace }} +{{- end }} + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/sync-wave: "-15" +subjects: +- kind: {{ $value.subjects.kind | default "ServiceAccount" }} + name: {{ $value.subjects.name }} + namespace: {{ $value.subjects.namespace }} + apiGroup: "" +roleRef: + kind: {{ $value.roleRef.kind }} + name: {{ $value.roleRef.name }} + apiGroup: rbac.authorization.k8s.io +--- +{{- end }} +{{- end }} diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index ea7db53d..bb40e179 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -18,3 +18,33 @@ external-secrets: certController: image: tag: v0.8.3-ubi + +rbac: + roles: + - name: view-pods + createRole: true + apiGroups: + - '""' + scope: + cluster: false + namespace: vault + resources: + - pods + verbs: + - "get" + - "list" + - "watch" + roleBindings: + - name: view-pods-rb + createBinding: true + scope: + cluster: false + namespace: vault + subjects: + kind: ServiceAccount + name: vault + namespace: vault + apiGroup: "" + roleRef: + kind: Role + name: view-pods From 64e9dc7a3ed3fcdd358cfbcb4d8c62121fc2365b Mon Sep 17 00:00:00 2001 From: jonny <65790298+day0hero@users.noreply.github.com> Date: Mon, 26 Jun 2023 15:17:43 -0500 Subject: [PATCH 0917/1263] Make Test --- ...rets-industrial-edge-factory.expected.yaml | 38 +++++++++++++++++ ...-secrets-industrial-edge-hub.expected.yaml | 42 ++++++++++++++++++- ...ecrets-medical-diagnosis-hub.expected.yaml | 42 ++++++++++++++++++- ...olang-external-secrets-naked.expected.yaml | 42 ++++++++++++++++++- ...lang-external-secrets-normal.expected.yaml | 42 ++++++++++++++++++- 5 files changed, 198 insertions(+), 8 deletions(-) diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index c1a23515..22b23f0d 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -8140,6 +8140,25 @@ rules: - "update" - "patch" --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: view-pods + namespace: vault + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/sync-wave: "-15" +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch +--- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -8161,6 +8180,25 @@ subjects: name: golang-external-secrets namespace: "default" --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: view-pods-rb + namespace: vault + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/sync-wave: "-15" +subjects: +- kind: ServiceAccount + name: vault + namespace: vault + apiGroup: "" +roleRef: + kind: Role + name: view-pods + apiGroup: rbac.authorization.k8s.io +--- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index cc3e6f4f..2de0030f 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -8140,6 +8140,25 @@ rules: - "update" - "patch" --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: view-pods + namespace: vault + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/sync-wave: "-15" +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch +--- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -8161,6 +8180,25 @@ subjects: name: golang-external-secrets namespace: "default" --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: view-pods-rb + namespace: vault + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/sync-wave: "-15" +subjects: +- kind: ServiceAccount + name: vault + namespace: vault + apiGroup: "" +roleRef: + kind: Role + name: view-pods + apiGroup: rbac.authorization.k8s.io +--- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service @@ -8371,13 +8409,13 @@ metadata: annotations: argocd.argoproj.io/hook: PreSync name: job-wait-for-vault - # By placing the job in the vault namespace + # By placing the job in the vault namespace we can avoid dealing with RBACs namespace: vault spec: template: spec: containers: - - image: FIXME + - image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest command: - /bin/bash - -c diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index cc3e6f4f..2de0030f 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -8140,6 +8140,25 @@ rules: - "update" - "patch" --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: view-pods + namespace: vault + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/sync-wave: "-15" +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch +--- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -8161,6 +8180,25 @@ subjects: name: golang-external-secrets namespace: "default" --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: view-pods-rb + namespace: vault + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/sync-wave: "-15" +subjects: +- kind: ServiceAccount + name: vault + namespace: vault + apiGroup: "" +roleRef: + kind: Role + name: view-pods + apiGroup: rbac.authorization.k8s.io +--- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service @@ -8371,13 +8409,13 @@ metadata: annotations: argocd.argoproj.io/hook: PreSync name: job-wait-for-vault - # By placing the job in the vault namespace + # By placing the job in the vault namespace we can avoid dealing with RBACs namespace: vault spec: template: spec: containers: - - image: FIXME + - image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest command: - /bin/bash - -c diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 5141cbbd..bf906863 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -8140,6 +8140,25 @@ rules: - "update" - "patch" --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: view-pods + namespace: vault + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/sync-wave: "-15" +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch +--- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -8161,6 +8180,25 @@ subjects: name: golang-external-secrets namespace: "default" --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: view-pods-rb + namespace: vault + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/sync-wave: "-15" +subjects: +- kind: ServiceAccount + name: vault + namespace: vault + apiGroup: "" +roleRef: + kind: Role + name: view-pods + apiGroup: rbac.authorization.k8s.io +--- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service @@ -8371,13 +8409,13 @@ metadata: annotations: argocd.argoproj.io/hook: PreSync name: job-wait-for-vault - # By placing the job in the vault namespace + # By placing the job in the vault namespace we can avoid dealing with RBACs namespace: vault spec: template: spec: containers: - - image: FIXME + - image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest command: - /bin/bash - -c diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index cc3e6f4f..2de0030f 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -8140,6 +8140,25 @@ rules: - "update" - "patch" --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: view-pods + namespace: vault + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/sync-wave: "-15" +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch +--- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -8161,6 +8180,25 @@ subjects: name: golang-external-secrets namespace: "default" --- +# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: view-pods-rb + namespace: vault + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/sync-wave: "-15" +subjects: +- kind: ServiceAccount + name: vault + namespace: vault + apiGroup: "" +roleRef: + kind: Role + name: view-pods + apiGroup: rbac.authorization.k8s.io +--- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service @@ -8371,13 +8409,13 @@ metadata: annotations: argocd.argoproj.io/hook: PreSync name: job-wait-for-vault - # By placing the job in the vault namespace + # By placing the job in the vault namespace we can avoid dealing with RBACs namespace: vault spec: template: spec: containers: - - image: FIXME + - image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest command: - /bin/bash - -c From 1895a7307d582c89b5772e66fc002450ee88209f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jul 2023 11:23:47 +0900 Subject: [PATCH 0918/1263] Revert "Make Test" This reverts commit 64e9dc7a3ed3fcdd358cfbcb4d8c62121fc2365b. --- ...rets-industrial-edge-factory.expected.yaml | 38 ----------------- ...-secrets-industrial-edge-hub.expected.yaml | 42 +------------------ ...ecrets-medical-diagnosis-hub.expected.yaml | 42 +------------------ ...olang-external-secrets-naked.expected.yaml | 42 +------------------ ...lang-external-secrets-normal.expected.yaml | 42 +------------------ 5 files changed, 8 insertions(+), 198 deletions(-) diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index 22b23f0d..c1a23515 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -8140,25 +8140,6 @@ rules: - "update" - "patch" --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: view-pods - namespace: vault - annotations: - argocd.argoproj.io/hook: PreSync - argocd.argoproj.io/sync-wave: "-15" -rules: - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch ---- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -8180,25 +8161,6 @@ subjects: name: golang-external-secrets namespace: "default" --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: view-pods-rb - namespace: vault - annotations: - argocd.argoproj.io/hook: PreSync - argocd.argoproj.io/sync-wave: "-15" -subjects: -- kind: ServiceAccount - name: vault - namespace: vault - apiGroup: "" -roleRef: - kind: Role - name: view-pods - apiGroup: rbac.authorization.k8s.io ---- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index 2de0030f..cc3e6f4f 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -8140,25 +8140,6 @@ rules: - "update" - "patch" --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: view-pods - namespace: vault - annotations: - argocd.argoproj.io/hook: PreSync - argocd.argoproj.io/sync-wave: "-15" -rules: - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch ---- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -8180,25 +8161,6 @@ subjects: name: golang-external-secrets namespace: "default" --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: view-pods-rb - namespace: vault - annotations: - argocd.argoproj.io/hook: PreSync - argocd.argoproj.io/sync-wave: "-15" -subjects: -- kind: ServiceAccount - name: vault - namespace: vault - apiGroup: "" -roleRef: - kind: Role - name: view-pods - apiGroup: rbac.authorization.k8s.io ---- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service @@ -8409,13 +8371,13 @@ metadata: annotations: argocd.argoproj.io/hook: PreSync name: job-wait-for-vault - # By placing the job in the vault namespace we can avoid dealing with RBACs + # By placing the job in the vault namespace namespace: vault spec: template: spec: containers: - - image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest + - image: FIXME command: - /bin/bash - -c diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index 2de0030f..cc3e6f4f 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -8140,25 +8140,6 @@ rules: - "update" - "patch" --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: view-pods - namespace: vault - annotations: - argocd.argoproj.io/hook: PreSync - argocd.argoproj.io/sync-wave: "-15" -rules: - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch ---- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -8180,25 +8161,6 @@ subjects: name: golang-external-secrets namespace: "default" --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: view-pods-rb - namespace: vault - annotations: - argocd.argoproj.io/hook: PreSync - argocd.argoproj.io/sync-wave: "-15" -subjects: -- kind: ServiceAccount - name: vault - namespace: vault - apiGroup: "" -roleRef: - kind: Role - name: view-pods - apiGroup: rbac.authorization.k8s.io ---- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service @@ -8409,13 +8371,13 @@ metadata: annotations: argocd.argoproj.io/hook: PreSync name: job-wait-for-vault - # By placing the job in the vault namespace we can avoid dealing with RBACs + # By placing the job in the vault namespace namespace: vault spec: template: spec: containers: - - image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest + - image: FIXME command: - /bin/bash - -c diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index bf906863..5141cbbd 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -8140,25 +8140,6 @@ rules: - "update" - "patch" --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: view-pods - namespace: vault - annotations: - argocd.argoproj.io/hook: PreSync - argocd.argoproj.io/sync-wave: "-15" -rules: - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch ---- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -8180,25 +8161,6 @@ subjects: name: golang-external-secrets namespace: "default" --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: view-pods-rb - namespace: vault - annotations: - argocd.argoproj.io/hook: PreSync - argocd.argoproj.io/sync-wave: "-15" -subjects: -- kind: ServiceAccount - name: vault - namespace: vault - apiGroup: "" -roleRef: - kind: Role - name: view-pods - apiGroup: rbac.authorization.k8s.io ---- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service @@ -8409,13 +8371,13 @@ metadata: annotations: argocd.argoproj.io/hook: PreSync name: job-wait-for-vault - # By placing the job in the vault namespace we can avoid dealing with RBACs + # By placing the job in the vault namespace namespace: vault spec: template: spec: containers: - - image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest + - image: FIXME command: - /bin/bash - -c diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 2de0030f..cc3e6f4f 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -8140,25 +8140,6 @@ rules: - "update" - "patch" --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: view-pods - namespace: vault - annotations: - argocd.argoproj.io/hook: PreSync - argocd.argoproj.io/sync-wave: "-15" -rules: - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch ---- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -8180,25 +8161,6 @@ subjects: name: golang-external-secrets namespace: "default" --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: view-pods-rb - namespace: vault - annotations: - argocd.argoproj.io/hook: PreSync - argocd.argoproj.io/sync-wave: "-15" -subjects: -- kind: ServiceAccount - name: vault - namespace: vault - apiGroup: "" -roleRef: - kind: Role - name: view-pods - apiGroup: rbac.authorization.k8s.io ---- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service @@ -8409,13 +8371,13 @@ metadata: annotations: argocd.argoproj.io/hook: PreSync name: job-wait-for-vault - # By placing the job in the vault namespace we can avoid dealing with RBACs + # By placing the job in the vault namespace namespace: vault spec: template: spec: containers: - - image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest + - image: FIXME command: - /bin/bash - -c From 08eee554754d3c9ac0e43404ccc1d9db5ba50071 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jul 2023 11:23:50 +0900 Subject: [PATCH 0919/1263] Revert "Adding rbac to support the vault sa checking on the vault-0 pod status." This reverts commit 598bc74fefd2cb72fe90834989206ad12b130a69. --- ...-external-secrets-hub-vault-rbac-role.yaml | 32 ------------------- ...al-secrets-hub-vault-rbac-rolebinding.yaml | 28 ---------------- golang-external-secrets/values.yaml | 30 ----------------- 3 files changed, 90 deletions(-) delete mode 100644 golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml delete mode 100644 golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml deleted file mode 100644 index 1596c67f..00000000 --- a/golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-role.yaml +++ /dev/null @@ -1,32 +0,0 @@ -{{- range $key, $value := .Values.rbac.roles }} -{{- if $value.createRole }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -{{- if $value.scope.cluster }} -kind: ClusterRole -metadata: - name: {{ .name }} -{{- else }} -kind: Role -metadata: - name: {{ $value.name }} - namespace: {{ $value.scope.namespace}} -{{- end }} - annotations: - argocd.argoproj.io/hook: PreSync - argocd.argoproj.io/sync-wave: "-15" -rules: - - apiGroups: -{{- range $value.apiGroups }} - - {{ . }} -{{- end }} - resources: -{{- range $value.resources }} - - {{ . }} -{{- end }} - verbs: -{{- range $value.verbs }} - - {{ . }} -{{- end }} -{{- end }} -{{- end }} diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml deleted file mode 100644 index 6f28d740..00000000 --- a/golang-external-secrets/templates/golang-external-secrets-hub-vault-rbac-rolebinding.yaml +++ /dev/null @@ -1,28 +0,0 @@ -{{- range $key, $value := .Values.rbac.roleBindings }} -{{- if $value.createBinding }} -apiVersion: rbac.authorization.k8s.io/v1 -{{- if $value.scope.cluster }} -kind: ClusterRoleBinding -{{- else }} -kind: RoleBinding -{{- end }} -metadata: - name: {{ .name }} -{{- if eq $value.scope.cluster false }} - namespace: {{ $value.scope.namespace }} -{{- end }} - annotations: - argocd.argoproj.io/hook: PreSync - argocd.argoproj.io/sync-wave: "-15" -subjects: -- kind: {{ $value.subjects.kind | default "ServiceAccount" }} - name: {{ $value.subjects.name }} - namespace: {{ $value.subjects.namespace }} - apiGroup: "" -roleRef: - kind: {{ $value.roleRef.kind }} - name: {{ $value.roleRef.name }} - apiGroup: rbac.authorization.k8s.io ---- -{{- end }} -{{- end }} diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index bb40e179..ea7db53d 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -18,33 +18,3 @@ external-secrets: certController: image: tag: v0.8.3-ubi - -rbac: - roles: - - name: view-pods - createRole: true - apiGroups: - - '""' - scope: - cluster: false - namespace: vault - resources: - - pods - verbs: - - "get" - - "list" - - "watch" - roleBindings: - - name: view-pods-rb - createBinding: true - scope: - cluster: false - namespace: vault - subjects: - kind: ServiceAccount - name: vault - namespace: vault - apiGroup: "" - roleRef: - kind: Role - name: view-pods From c5aa3d28b35039fd028cbb894424d75502b492a4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jul 2023 11:23:52 +0900 Subject: [PATCH 0920/1263] Revert "Fix image and comment" This reverts commit d4d3fe100681dd858601ed1934f0244333ddea49. --- .../templates/golang-external-secrets-hub-presync.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml index 05e1c117..5a682bb0 100644 --- a/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml +++ b/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml @@ -5,13 +5,13 @@ metadata: annotations: argocd.argoproj.io/hook: PreSync name: job-wait-for-vault - # By placing the job in the vault namespace we can avoid dealing with RBACs + # By placing the job in the vault namespace namespace: vault spec: template: spec: containers: - - image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest + - image: FIXME command: - /bin/bash - -c From 6d4a481068541143bda53f261fd675522f8d9cc6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jul 2023 11:23:53 +0900 Subject: [PATCH 0921/1263] Revert "Add tests" This reverts commit ab5532a7e602235bd2ec02a7c9dff78f82309856. --- ...-secrets-industrial-edge-hub.expected.yaml | 26 ------------------- ...ecrets-medical-diagnosis-hub.expected.yaml | 26 ------------------- ...olang-external-secrets-naked.expected.yaml | 26 ------------------- ...lang-external-secrets-normal.expected.yaml | 26 ------------------- 4 files changed, 104 deletions(-) diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index cc3e6f4f..7ae2a78f 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -8364,32 +8364,6 @@ spec: secret: secretName: golang-external-secrets-webhook --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml -apiVersion: batch/v1 -kind: Job -metadata: - annotations: - argocd.argoproj.io/hook: PreSync - name: job-wait-for-vault - # By placing the job in the vault namespace - namespace: vault -spec: - template: - spec: - containers: - - image: FIXME - command: - - /bin/bash - - -c - - | - oc wait --for=condition=Ready=true pods -n vault vault-0 --timeout=900s - name: wait-for-healthy-vault - dnsPolicy: ClusterFirst - restartPolicy: Never - serviceAccount: vault - serviceAccountName: vault - terminationGracePeriodSeconds: 60 ---- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index cc3e6f4f..7ae2a78f 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -8364,32 +8364,6 @@ spec: secret: secretName: golang-external-secrets-webhook --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml -apiVersion: batch/v1 -kind: Job -metadata: - annotations: - argocd.argoproj.io/hook: PreSync - name: job-wait-for-vault - # By placing the job in the vault namespace - namespace: vault -spec: - template: - spec: - containers: - - image: FIXME - command: - - /bin/bash - - -c - - | - oc wait --for=condition=Ready=true pods -n vault vault-0 --timeout=900s - name: wait-for-healthy-vault - dnsPolicy: ClusterFirst - restartPolicy: Never - serviceAccount: vault - serviceAccountName: vault - terminationGracePeriodSeconds: 60 ---- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 5141cbbd..518bda17 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -8364,32 +8364,6 @@ spec: secret: secretName: golang-external-secrets-webhook --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml -apiVersion: batch/v1 -kind: Job -metadata: - annotations: - argocd.argoproj.io/hook: PreSync - name: job-wait-for-vault - # By placing the job in the vault namespace - namespace: vault -spec: - template: - spec: - containers: - - image: FIXME - command: - - /bin/bash - - -c - - | - oc wait --for=condition=Ready=true pods -n vault vault-0 --timeout=900s - name: wait-for-healthy-vault - dnsPolicy: ClusterFirst - restartPolicy: Never - serviceAccount: vault - serviceAccountName: vault - terminationGracePeriodSeconds: 60 ---- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index cc3e6f4f..7ae2a78f 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -8364,32 +8364,6 @@ spec: secret: secretName: golang-external-secrets-webhook --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml -apiVersion: batch/v1 -kind: Job -metadata: - annotations: - argocd.argoproj.io/hook: PreSync - name: job-wait-for-vault - # By placing the job in the vault namespace - namespace: vault -spec: - template: - spec: - containers: - - image: FIXME - command: - - /bin/bash - - -c - - | - oc wait --for=condition=Ready=true pods -n vault vault-0 --timeout=900s - name: wait-for-healthy-vault - dnsPolicy: ClusterFirst - restartPolicy: Never - serviceAccount: vault - serviceAccountName: vault - terminationGracePeriodSeconds: 60 ---- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore From 3bf245b1b6c76c1b8183aee643c5500819eba36a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jul 2023 11:24:15 +0900 Subject: [PATCH 0922/1263] Revert "WIP add presync for eso that waits for vault to be up" This reverts commit 279769915f94202dcaa7734ac0d07a23e70da8b5. --- .../golang-external-secrets-hub-presync.yaml | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml deleted file mode 100644 index 5a682bb0..00000000 --- a/golang-external-secrets/templates/golang-external-secrets-hub-presync.yaml +++ /dev/null @@ -1,26 +0,0 @@ -{{- if .Values.clusterGroup.isHubCluster }} -apiVersion: batch/v1 -kind: Job -metadata: - annotations: - argocd.argoproj.io/hook: PreSync - name: job-wait-for-vault - # By placing the job in the vault namespace - namespace: vault -spec: - template: - spec: - containers: - - image: FIXME - command: - - /bin/bash - - -c - - | - oc wait --for=condition=Ready=true pods -n vault vault-0 --timeout=900s - name: wait-for-healthy-vault - dnsPolicy: ClusterFirst - restartPolicy: Never - serviceAccount: vault - serviceAccountName: vault - terminationGracePeriodSeconds: 60 -{{ end }} From 0ae561b62ae84c3d2ad0a110c79bb3f9098774a5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jul 2023 09:05:21 +0900 Subject: [PATCH 0923/1263] Increase the default retry limit when syncing ArgoCD will retry 5 times by default to sync an application in case of errors and then will give up. So if an application contains a reference to a CRD that has not been installed yet (say because it will be installed by another application), it will error out and retry later. This happens by default for a maximum of 5 times [1]. After those 5 times the application will give up and will stay in Degraded moded and eventually move to Failed. In this case a manual sync will usually fix the application just fine (i.e. as long as the missing CRD has been installed in the meantime). Now to solve this issue we can add complex preSync Jobs that wait for the needed resources, but this fundamentally breaks the simplicity of things and introduces unneeded dependencies. In this change we just increase the default retry limit to something larger (20) that should cover most cases. The retry limit functionality is rather undocumented currently in the docs but is defined at [2] and also shown at [3]. In our patterns' case the concrete issue happened as follows: 1. ESO ClusterSecrets were often not synced/degraded 2. We introduced a Job in a preSync hook for the ESO chart that would wait on vault to be ready before applying the rest of ESO 3. MCG started failing because the config-demo app had already tried to sync 5 times and failed everytime because the ESO CRDs were not installed yet (due to ESO waiting on vault) So instead of adding yet another job, let's just try a lot more often. We picked 20 as a sane default because that should have argo try for about 60 minutes (3min is the default maximum backoff limit) Tested with two MCG installations (with the ESO Job hook included) and both worked out of the box. Whereas before I managed to get three failures out of three installs. [1] https://github.com/argoproj/argo-cd/blob/master/controller/appcontroller.go#L1680 [2] https://github.com/argoproj/argo-cd/blob/master/manifests/crds/application-crd.yaml#L1476 [3] https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/application.yaml#L202C18-L202C100 --- .../policies/application-policies.yaml | 2 ++ .../templates/plumbing/applications.yaml | 4 +++ clustergroup/values.schema.json | 4 +++ clustergroup/values.yaml | 1 + tests/acm-industrial-edge-hub.expected.yaml | 2 ++ tests/acm-medical-diagnosis-hub.expected.yaml | 2 ++ tests/acm-normal.expected.yaml | 4 +++ ...roup-industrial-edge-factory.expected.yaml | 5 ++++ ...tergroup-industrial-edge-hub.expected.yaml | 17 ++++++++++++ ...rgroup-medical-diagnosis-hub.expected.yaml | 27 +++++++++++++++++++ tests/clustergroup-naked.expected.yaml | 1 + tests/clustergroup-normal.expected.yaml | 5 ++++ values-global.yaml | 1 + 13 files changed, 75 insertions(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 0cb10ae0..f1c7bbdd 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -95,6 +95,8 @@ spec: automated: prune: false selfHeal: true + retry: + limit: {{ default 20 $.Values.global.options.applicationRetryLimit }} ignoreDifferences: - group: apps kind: Deployment diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 8ed7b4b6..206e420f 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -35,6 +35,8 @@ spec: {{- else }} syncPolicy: automated: {} + retry: + limit: {{ default 20 $.Values.global.options.applicationRetryLimit }} {{- end }} {{- if .ignoreDifferences }} ignoreDifferences: {{ .ignoreDifferences | toPrettyJson }} @@ -239,6 +241,8 @@ spec: {{- else }} syncPolicy: automated: {} + retry: + limit: {{ default 20 $.Values.global.applicationRetryLimit }} # selfHeal: true {{- end }} --- diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 58d84cff..e64a8125 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -192,6 +192,10 @@ "type": "string", "deprecated": true, "description": "This is used to approval strategy for the subscriptions of OpenShift Operators being installed. You can choose Automatic or Manual updates. NOTE: This setting is now available in the subcriptions description in the values file." + }, + "applicationRetryLimit": { + "type": "integer", + "description": "Number of failed sync attempt retries; unlimited number of attempts if less than 0" } }, "required": [ diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 18212e4b..117e009e 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -5,6 +5,7 @@ global: useCSV: True syncPolicy: Automatic installPlanApproval: Automatic + applicationRetryLimit: 20 enabled: "all" diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 77080dbd..d54a8db6 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -246,6 +246,8 @@ spec: automated: prune: false selfHeal: true + retry: + limit: 20 ignoreDifferences: - group: apps kind: Deployment diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index fce91f4b..6e3eedff 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -237,6 +237,8 @@ spec: automated: prune: false selfHeal: true + retry: + limit: 20 ignoreDifferences: - group: apps kind: Deployment diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 5b98b4b2..7c7ac7d5 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -655,6 +655,8 @@ spec: automated: prune: false selfHeal: true + retry: + limit: 20 ignoreDifferences: - group: apps kind: Deployment @@ -747,6 +749,8 @@ spec: automated: prune: false selfHeal: true + retry: + limit: 20 ignoreDifferences: - group: apps kind: Deployment diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index c95c7f79..649463a3 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -145,6 +145,7 @@ data: localClusterDomain: apps.region.example.com namespace: pattern-namespace options: + applicationRetryLimit: 20 installPlanApproval: Automatic syncPolicy: Manual useCSV: true @@ -382,6 +383,8 @@ spec: } syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -428,6 +431,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 1e25dbf7..c4c7dddc 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -306,6 +306,7 @@ data: localClusterDomain: apps.region.example.com namespace: pattern-namespace options: + applicationRetryLimit: 20 installPlanApproval: Automatic syncPolicy: Manual useCSV: true @@ -713,6 +714,8 @@ spec: ] syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -759,6 +762,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -805,6 +810,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -881,6 +888,8 @@ spec: ] syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -927,6 +936,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -973,6 +984,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -997,6 +1010,8 @@ spec: } syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -1061,6 +1076,8 @@ spec: value: "1.10.3-ubi" syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 5afe1d74..dccc6e92 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -293,6 +293,7 @@ data: localClusterDomain: apps.region.example.com namespace: pattern-namespace options: + applicationRetryLimit: 20 installPlanApproval: Automatic syncPolicy: Manual useCSV: true @@ -649,6 +650,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -695,6 +698,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -741,6 +746,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -787,6 +794,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -833,6 +842,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -879,6 +890,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -925,6 +938,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -989,6 +1004,8 @@ spec: value: "1.10.3-ubi" syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -1035,6 +1052,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -1081,6 +1100,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -1136,6 +1157,8 @@ spec: ] syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -1191,6 +1214,8 @@ spec: ] syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -1237,6 +1262,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 548e300e..6a79b27d 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -66,6 +66,7 @@ data: enabled: all global: options: + applicationRetryLimit: 20 installPlanApproval: Automatic syncPolicy: Automatic useCSV: true diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 07e6a772..c0af256d 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -198,6 +198,7 @@ data: multiClusterTarget: all namespace: pattern-namespace options: + applicationRetryLimit: 20 installPlanApproval: Automatic syncPolicy: Automatic useCSV: false @@ -563,6 +564,8 @@ spec: ] syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml @@ -609,6 +612,8 @@ spec: value: apps.region.example.com syncPolicy: automated: {} + retry: + limit: 20 # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml diff --git a/values-global.yaml b/values-global.yaml index 8a890f3d..24feccd5 100644 --- a/values-global.yaml +++ b/values-global.yaml @@ -3,6 +3,7 @@ global: useCSV: True syncPolicy: Manual installPlanApproval: Automatic + applicationRetryLimit: 20 git: hostname: github.com From 66d456d6599023e49d4eb228d587191f95a48dc4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 8 Jul 2023 09:08:09 +0900 Subject: [PATCH 0924/1263] Add Changes.md entry --- Changes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changes.md b/Changes.md index 758128d5..0e1e8c47 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,10 @@ # Changes +## Jul 8, 2023 + +* Introduced a default of 20 for sync failures retries in argo applications (global override via global.options.applicationRetryLimit + and per-app override via .syncPolicy) + ## May 22, 2023 * Upgraded ESO to 0.8.2 From 54056c7fe138ea250ab47d7e17114d29f540772d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 10 Jul 2023 16:35:08 +0900 Subject: [PATCH 0925/1263] Split off global helm variables to a helper definition We can only split out bits of yaml that reference $.* variables. This is because these sinppets in _helpers.tbl are passed a single context either $ or . and cannot use both like the top-level domain. --- clustergroup/templates/_helpers.tpl | 25 +++++++++++++++++++ .../templates/plumbing/applications.yaml | 19 +------------- 2 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 clustergroup/templates/_helpers.tpl diff --git a/clustergroup/templates/_helpers.tpl b/clustergroup/templates/_helpers.tpl new file mode 100644 index 00000000..250eae68 --- /dev/null +++ b/clustergroup/templates/_helpers.tpl @@ -0,0 +1,25 @@ +{{/* +Default always defined top-level variables for helm charts +*/}} +{{- define "clustergroup.app.globalvalues.helmparameters" -}} +- name: global.repoURL + value: $ARGOCD_APP_SOURCE_REPO_URL +- name: global.targetRevision + value: $ARGOCD_APP_SOURCE_TARGET_REVISION +- name: global.namespace + value: $ARGOCD_APP_NAMESPACE +- name: global.pattern + value: {{ $.Values.global.pattern }} +- name: global.clusterDomain + value: {{ $.Values.global.clusterDomain }} +- name: global.clusterVersion + value: "{{ $.Values.global.clusterVersion }}" +- name: global.clusterPlatform + value: "{{ $.Values.global.clusterPlatform }}" +- name: global.hubClusterDomain + value: {{ $.Values.global.hubClusterDomain }} +- name: global.localClusterDomain + value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} +{{- end }} {{/* clustergroup.globalvaluesparameters */}} + + diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 206e420f..39d51cc6 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -172,24 +172,7 @@ spec: {{- end }} # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: {{ $.Values.global.pattern }} - - name: global.clusterDomain - value: {{ $.Values.global.clusterDomain }} - - name: global.clusterVersion - value: "{{ $.Values.global.clusterVersion }}" - - name: global.clusterPlatform - value: "{{ $.Values.global.clusterPlatform }}" - - name: global.hubClusterDomain - value: {{ $.Values.global.hubClusterDomain }} - - name: global.localClusterDomain - value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} + {{- include "clustergroup.app.globalvalues.helmparameters" $ | nindent 8 }} {{- range .extraHubClusterDomainFields }} - name: {{ . }} value: {{ $.Values.global.hubClusterDomain }} From fdee136e65e513468ce5b4aecfd5839f8f82502a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 10 Jul 2023 16:39:18 +0900 Subject: [PATCH 0926/1263] Switch ApplicationSets to use the newly-introduced helpers I only remove the variables that are defined identically in ApplicationSet and in the helper. Leaving the other ones as is as their presence is not entirely clear to me and I do not want to risk breaking things. --- clustergroup/templates/plumbing/applications.yaml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 39d51cc6..0fbe71ec 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -69,24 +69,13 @@ spec: {{ `{{ values }}` }} {{- end }} parameters: - - name: global.clusterDomain - value: {{ $.Values.global.clusterDomain }} - - name: global.clusterVersion - value: "{{ $.Values.global.clusterVersion }}" - - name: global.clusterPlatform - value: "{{ $.Values.global.clusterPlatform }}" - - name: global.hubClusterDomain - value: {{ $.Values.global.hubClusterDomain }} - - name: global.localClusterDomain - value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} + {{- include "clustergroup.app.globalvalues.helmparameters" $ | nindent 12 }} - name: global.repoURL value: {{ $.Values.global.repoURL }} - name: global.targetRevision value: {{ $.Values.global.targetRevision }} - name: global.namespace value: {{ $.Values.global.namespace }} - - name: global.pattern - value: {{ $.Values.global.pattern }} - name: clusterGroup.name value: {{ .Values.clusterGroup.name }} {{- range .extraHubClusterDomainFields }} From 4e1f360daab2159baf97ab6d5d97080efc9cad3a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 10 Jul 2023 16:45:07 +0900 Subject: [PATCH 0927/1263] Split off valueFiles to _helpers.tbl --- clustergroup/templates/_helpers.tpl | 17 +++++++++++++++++ .../templates/plumbing/applications.yaml | 13 +------------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/clustergroup/templates/_helpers.tpl b/clustergroup/templates/_helpers.tpl index 250eae68..c3a730fb 100644 --- a/clustergroup/templates/_helpers.tpl +++ b/clustergroup/templates/_helpers.tpl @@ -23,3 +23,20 @@ Default always defined top-level variables for helm charts {{- end }} {{/* clustergroup.globalvaluesparameters */}} +{{/* +Default always defined valueFiles to be included in Applications +*/}} +{{- define "clustergroup.app.globalvalues.valuefiles" -}} +- "/values-global.yaml" +- "/values-{{ $.Values.clusterGroup.name }}.yaml" +{{- if $.Values.global.clusterPlatform }} +- "/values-{{ $.Values.global.clusterPlatform }}.yaml" + {{- if $.Values.global.clusterVersion }} +- "/values-{{ $.Values.global.clusterPlatform }}-{{ $.Values.global.clusterVersion }}.yaml" + {{- end }} +- "/values-{{ $.Values.global.clusterPlatform }}-{{ $.Values.clusterGroup.name }}.yaml" +{{- end }} +{{- if $.Values.global.clusterVersion }} +- "/values-{{ $.Values.global.clusterVersion }}-{{ $.Values.clusterGroup.name }}.yaml" +{{- end }} +{{- end }} {{/* clustergroup.app.globalvalues.valuefiles */}} diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 0fbe71ec..e57f1199 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -144,18 +144,7 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-{{ $.Values.clusterGroup.name }}.yaml" - {{- if $.Values.global.clusterPlatform }} - - "/values-{{ $.Values.global.clusterPlatform }}.yaml" - {{- if $.Values.global.clusterVersion }} - - "/values-{{ $.Values.global.clusterPlatform }}-{{ $.Values.global.clusterVersion }}.yaml" - {{- end }} - - "/values-{{ $.Values.global.clusterPlatform }}-{{ $.Values.clusterGroup.name }}.yaml" - {{- end }} - {{- if $.Values.global.clusterVersion }} - - "/values-{{ $.Values.global.clusterVersion }}-{{ $.Values.clusterGroup.name }}.yaml" - {{- end }} + {{- include "clustergroup.app.globalvalues.valuefiles" $ | nindent 6 }} {{- range $valueFile := .extraValueFiles }} - {{ $valueFile | quote }} {{- end }} From 35e79901bd5d048662af87bbcc2e2f1de817a45a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 10 Jul 2023 16:48:13 +0900 Subject: [PATCH 0928/1263] Switch applicationsets to use the new helper --- clustergroup/templates/plumbing/applications.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index e57f1199..0c275a35 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -57,10 +57,7 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "values.yaml" - {{- if $.Values.global.clusterVersion }} - - "/values-{{ $.Values.global.clusterVersion }}-{{ $.Values.clusterGroup.name }}.yaml" - {{- end }} + {{- include "clustergroup.app.globalvalues.valuefiles" $ | nindent 12 }} {{- range .extraValueFiles }} - {{ . | quote }} {{- end }} From e85c3ab58ed201dc40376d24ca5308acec806ac1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 11 Jul 2023 11:18:45 +0900 Subject: [PATCH 0929/1263] Drop some older comments --- .../templates/plumbing/applications.yaml | 2 -- ...roup-industrial-edge-factory.expected.yaml | 3 --- ...tergroup-industrial-edge-hub.expected.yaml | 15 ----------- ...rgroup-medical-diagnosis-hub.expected.yaml | 26 ------------------- tests/clustergroup-normal.expected.yaml | 4 --- 5 files changed, 50 deletions(-) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 0c275a35..6e52e667 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -145,7 +145,6 @@ spec: {{- range $valueFile := .extraValueFiles }} - {{ $valueFile | quote }} {{- end }} - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: {{- include "clustergroup.app.globalvalues.helmparameters" $ | nindent 8 }} {{- range .extraHubClusterDomainFields }} @@ -201,7 +200,6 @@ spec: automated: {} retry: limit: {{ default 20 $.Values.global.applicationRetryLimit }} - # selfHeal: true {{- end }} --- {{- end }} diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 649463a3..ac7ddf1a 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -385,7 +385,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -409,7 +408,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-factory.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -433,7 +431,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index c4c7dddc..d4a208c0 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -683,7 +683,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -716,7 +715,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -740,7 +738,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -764,7 +761,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -788,7 +784,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -812,7 +807,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -836,7 +830,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -890,7 +883,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -914,7 +906,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -938,7 +929,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -962,7 +952,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -986,7 +975,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -1012,7 +1000,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -1036,7 +1023,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1078,7 +1064,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index dccc6e92..30442f2b 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -628,7 +628,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -652,7 +651,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -676,7 +674,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -700,7 +697,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -724,7 +720,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -748,7 +743,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -772,7 +766,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -796,7 +789,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -820,7 +812,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -844,7 +835,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -868,7 +858,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -892,7 +881,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -916,7 +904,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -940,7 +927,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -964,7 +950,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1006,7 +991,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -1030,7 +1014,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1054,7 +1037,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -1078,7 +1060,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1102,7 +1083,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -1126,7 +1106,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1159,7 +1138,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -1183,7 +1161,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1216,7 +1193,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -1240,7 +1216,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1264,7 +1239,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index c0af256d..33d0663e 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -533,7 +533,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-example.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -566,7 +565,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 @@ -590,7 +588,6 @@ spec: valueFiles: - "/values-global.yaml" - "/values-example.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -614,7 +611,6 @@ spec: automated: {} retry: limit: 20 - # selfHeal: true --- # Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml apiVersion: argoproj.io/v1alpha1 From 16fab03e098a6c1f333b2fc1eea0f41470203b52 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 13 Jul 2023 16:13:52 +0900 Subject: [PATCH 0930/1263] Tweak the load secret debug message to be clearer When HOME is set we replace it with '~' in this debug message because when run from inside the container the HOME is /pattern-home which is confusing for users. Printing out '~' when at the start of the string is less confusing. Before: ok: [localhost] => { "msg": "/home/michele/.config/hybrid-cloud-patterns/values-secret-multicloud-gitops.yaml" } After: ok: [localhost] => { "msg": "~/.config/hybrid-cloud-patterns/values-secret-multicloud-gitops.yaml" } --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 101b83af..a820f40a 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -80,9 +80,12 @@ register: encrypted failed_when: (encrypted.rc not in [0, 1]) +# When HOME is set we replace it with '~' in this debug message +# because when run from inside the container the HOME is /pattern-home +# which is confusing for users - name: Is found values secret file encrypted ansible.builtin.debug: - msg: "Using {{ found_file }} to parse secrets" + msg: "Using {{ (lookup('env', 'HOME') | length > 0) | ternary(found_file | regex_replace('^' + lookup('env', 'HOME'), '~'), found_file) }} to parse secrets" - name: Set encryption bool fact no_log: true From 6e318145f540366daaa2ed9e1ab926a3acb8e7a7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 13 Jul 2023 16:27:15 +0900 Subject: [PATCH 0931/1263] Check if the KUBECONFIG file is pointing outside of the HOME folder If it is somewhere under /tmp or out of the HOME folder, bail out explaining why. This has caused a few silly situations where the user would save the KUBECONFIG file under /tmp. Since bind-mounting /tmp seems like a wrong thing to do in general, we at least bail out with a clear error message. To do this we rely on a bash functionality so let's just switch the script to use that. Tested as follows: export KUBECONFIG=/tmp/kubeconfig ./scripts/pattern-util.sh make help /tmp/kubeconfig is pointing outside of the HOME folder, this will make it unavailable from the container. Please move it somewhere inside your /home/michele folder, as that is what gets bind-mounted inside the container export KUBECONFIG=~/kubeconfig ./scripts/pattern-util.sh make help Pattern: common Usage: make ... --- scripts/pattern-util.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 149e8af7..f55bbdee 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash if [ -z "$PATTERN_UTILITY_CONTAINER" ]; then PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/utility-container" @@ -14,6 +14,13 @@ for i in ${UNSUPPORTED_PODMAN_VERSIONS}; do fi done +if [ -n "$KUBECONFIG" ]; then + if [[ ! "${KUBECONFIG}" =~ ^$HOME* ]]; then + echo "${KUBECONFIG} is pointing outside of the HOME folder, this will make it unavailable from the container." + echo "Please move it somewhere inside your $HOME folder, as that is what gets bind-mounted inside the container" + exit 1 + fi +fi # Copy Kubeconfig from current environment. The utilities will pick up ~/.kube/config if set so it's not mandatory # $HOME is mounted as itself for any files that are referenced with absolute paths # $HOME is mounted to /root because the UID in the container is 0 and that's where SSH looks for credentials From 4193c08f816a1b928ea779dceafd9eb256da9190 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 14 Jul 2023 05:10:26 +0000 Subject: [PATCH 0932/1263] Include an example SNO cluster pool in the tests --- examples/values-example.yaml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 6f84820c..740da650 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -104,11 +104,18 @@ clusterGroup: name: aws-ap openshiftVersion: 4.10.18 baseDomain: blueprints.rhecoeng.com + controlPlane: + count: 1 + platform: + aws: + type: m5.xlarge + workers: + count: 0 platform: aws: region: ap-southeast-2 clusters: - - one + - One exampleAzurePool: name: azure-us openshiftVersion: 4.10.18 @@ -118,7 +125,7 @@ clusterGroup: baseDomainResourceGroupName: dojo-dns-zones region: eastus clusters: - - two + - Two - three acmlabels: - name: clusterGroup From ad39f4d5c6fb6bd15d38cd5d4e13fafddb67b450 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 14 Jul 2023 05:11:03 +0000 Subject: [PATCH 0933/1263] Enforce lowercase names for cluster claims --- acm/templates/provision/clusterpool.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index 31a22224..73bf4aa8 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -51,7 +51,7 @@ spec: installConfigSecretTemplateRef: name: {{ $poolName }}-install-config imageSetRef: - name: img{{ .openshiftVersion }}-x86-64-appsub + name: img{{ .openshiftVersion }}-multi-appsub pullSecretRef: name: {{ $poolName }}-pull-secret skipMachinePools: true # Disable MachinePool as using custom install-config @@ -65,13 +65,13 @@ spec: apiVersion: hive.openshift.io/v1 kind: ClusterClaim metadata: - name: '{{ . }}-{{ $group.name }}' + name: '{{ lower . }}-{{ lower $group.name }}' annotations: argocd.argoproj.io/sync-wave: "20" argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true cluster.open-cluster-management.io/createmanagedcluster: "true" labels: - clusterClaimName: {{ . }}-{{ $group.name }} + clusterClaimName: {{ lower . }}-{{ lower $group.name }} {{- if (not $group.acmlabels) }} clusterGroup: {{ $group.name }} {{- else if eq (len $group.acmlabels) 0 }} From b087e876f244dfb39a2c7af6bcad04173eba7940 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 14 Jul 2023 05:11:37 +0000 Subject: [PATCH 0934/1263] Avoid mixing yaml and json in the OCP install-config --- acm/templates/provision/_install-config.tpl | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/acm/templates/provision/_install-config.tpl b/acm/templates/provision/_install-config.tpl index 39aa03eb..b0336627 100644 --- a/acm/templates/provision/_install-config.tpl +++ b/acm/templates/provision/_install-config.tpl @@ -24,7 +24,10 @@ controlPlane: name: controlPlane {{- if .controlPlane }} replicas: {{ default 3 .controlPlane.count }} - platform: {{- .controlPlane.platform | toPrettyJson }} + {{- if .controlPlane.platform }} + platform: + {{- toYaml .controlPlane.platform | nindent 4 }} + {{- end }} {{- else }} replicas: 3 platform: @@ -36,8 +39,11 @@ compute: architecture: amd64 name: 'worker' {{- if .workers }} - replicas: {{ default 3 .workers.count }} - platform: {{- .workers.platform | toPrettyJson }} + replicas: {{ default 0 .workers.count }} + {{- if .workers.platform }} + platform: + {{- toYaml .workers.platform | nindent 4 }} + {{- end }} {{- else }} replicas: 3 platform: @@ -50,10 +56,11 @@ networking: hostPrefix: 23 machineNetwork: - cidr: 10.0.0.0/16 - networkType: OpenShiftSDN + networkType: OVNKubernetes serviceNetwork: - 172.30.0.0/16 -platform: {{ .platform | toPrettyJson }} +platform: +{{- toYaml .platform | nindent 2 }} pullSecret: "" # skip, hive will inject based on it's secrets sshKey: "" # skip, hive will inject based on it's secrets -{{- end -}} \ No newline at end of file +{{- end -}} From 38d83ec3cb2be3447672df19606aa4a015c95f37 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 14 Jul 2023 05:11:57 +0000 Subject: [PATCH 0935/1263] Update provisioning tests --- tests/acm-normal.expected.yaml | 8 ++++---- tests/clustergroup-normal.expected.yaml | 11 +++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 7c7ac7d5..b233b9d1 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -6,7 +6,7 @@ metadata: name: aws-ap-acm-provision-edge-install-config data: # Base64 encoding of install-config yaml - install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXdzLWFwJyAKYmFzZURvbWFpbjogYmx1ZXByaW50cy5yaGVjb2VuZy5jb20KY29udHJvbFBsYW5lOgogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIG5hbWU6IGNvbnRyb2xQbGFuZQogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhd3M6CiAgICAgIHR5cGU6IG01LnhsYXJnZQpjb21wdXRlOgotIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIG5hbWU6ICd3b3JrZXInCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF3czoKICAgICAgdHlwZTogbTUueGxhcmdlCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT3BlblNoaWZ0U0ROCiAgc2VydmljZU5ldHdvcms6CiAgLSAxNzIuMzAuMC4wLzE2CnBsYXRmb3JtOiB7CiAgImF3cyI6IHsKICAgICJyZWdpb24iOiAiYXAtc291dGhlYXN0LTIiCiAgfQp9CnB1bGxTZWNyZXQ6ICIiICMgc2tpcCwgaGl2ZSB3aWxsIGluamVjdCBiYXNlZCBvbiBpdCdzIHNlY3JldHMKc3NoS2V5OiAiIiAgICAgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cw== + install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXdzLWFwJyAKYmFzZURvbWFpbjogYmx1ZXByaW50cy5yaGVjb2VuZy5jb20KY29udHJvbFBsYW5lOgogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIG5hbWU6IGNvbnRyb2xQbGFuZQogIHJlcGxpY2FzOiAxCiAgcGxhdGZvcm06CiAgICBhd3M6CiAgICAgIHR5cGU6IG01LnhsYXJnZQpjb21wdXRlOgotIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIG5hbWU6ICd3b3JrZXInCiAgcmVwbGljYXM6IDAKbmV0d29ya2luZzoKICBjbHVzdGVyTmV0d29yazoKICAtIGNpZHI6IDEwLjEyOC4wLjAvMTQKICAgIGhvc3RQcmVmaXg6IDIzCiAgbWFjaGluZU5ldHdvcms6CiAgLSBjaWRyOiAxMC4wLjAuMC8xNgogIG5ldHdvcmtUeXBlOiBPVk5LdWJlcm5ldGVzCiAgc2VydmljZU5ldHdvcms6CiAgLSAxNzIuMzAuMC4wLzE2CnBsYXRmb3JtOgogIGF3czoKICAgIHJlZ2lvbjogYXAtc291dGhlYXN0LTIKcHVsbFNlY3JldDogIiIgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cwpzc2hLZXk6ICIiICAgICAjIHNraXAsIGhpdmUgd2lsbCBpbmplY3QgYmFzZWQgb24gaXQncyBzZWNyZXRz type: Opaque --- # Source: acm/templates/provision/secrets-common.yaml @@ -16,7 +16,7 @@ metadata: name: azure-us-acm-provision-edge-install-config data: # Base64 encoding of install-config yaml - install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXp1cmUtdXMnIApiYXNlRG9tYWluOiBibHVlcHJpbnRzLnJoZWNvZW5nLmNvbQpjb250cm9sUGxhbmU6CiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgbmFtZTogY29udHJvbFBsYW5lCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF6dXJlOgogICAgICB0eXBlOiBTdGFuZGFyZF9EOHNfdjMKY29tcHV0ZToKLSBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBuYW1lOiAnd29ya2VyJwogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhenVyZToKICAgICAgdHlwZTogU3RhbmRhcmRfRDhzX3YzCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT3BlblNoaWZ0U0ROCiAgc2VydmljZU5ldHdvcms6CiAgLSAxNzIuMzAuMC4wLzE2CnBsYXRmb3JtOiB7CiAgImF6dXJlIjogewogICAgImJhc2VEb21haW5SZXNvdXJjZUdyb3VwTmFtZSI6ICJkb2pvLWRucy16b25lcyIsCiAgICAicmVnaW9uIjogImVhc3R1cyIKICB9Cn0KcHVsbFNlY3JldDogIiIgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cwpzc2hLZXk6ICIiICAgICAjIHNraXAsIGhpdmUgd2lsbCBpbmplY3QgYmFzZWQgb24gaXQncyBzZWNyZXRz + install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXp1cmUtdXMnIApiYXNlRG9tYWluOiBibHVlcHJpbnRzLnJoZWNvZW5nLmNvbQpjb250cm9sUGxhbmU6CiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgbmFtZTogY29udHJvbFBsYW5lCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF6dXJlOgogICAgICB0eXBlOiBTdGFuZGFyZF9EOHNfdjMKY29tcHV0ZToKLSBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBuYW1lOiAnd29ya2VyJwogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhenVyZToKICAgICAgdHlwZTogU3RhbmRhcmRfRDhzX3YzCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT1ZOS3ViZXJuZXRlcwogIHNlcnZpY2VOZXR3b3JrOgogIC0gMTcyLjMwLjAuMC8xNgpwbGF0Zm9ybToKICBhenVyZToKICAgIGJhc2VEb21haW5SZXNvdXJjZUdyb3VwTmFtZTogZG9qby1kbnMtem9uZXMKICAgIHJlZ2lvbjogZWFzdHVzCnB1bGxTZWNyZXQ6ICIiICMgc2tpcCwgaGl2ZSB3aWxsIGluamVjdCBiYXNlZCBvbiBpdCdzIHNlY3JldHMKc3NoS2V5OiAiIiAgICAgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cw== type: Opaque --- # Source: acm/templates/policies/acm-hub-ca-policy.yaml @@ -87,7 +87,7 @@ spec: installConfigSecretTemplateRef: name: aws-ap-acm-provision-edge-install-config imageSetRef: - name: img4.10.18-x86-64-appsub + name: img4.10.18-multi-appsub pullSecretRef: name: aws-ap-acm-provision-edge-pull-secret skipMachinePools: true # Disable MachinePool as using custom install-config @@ -117,7 +117,7 @@ spec: installConfigSecretTemplateRef: name: azure-us-acm-provision-edge-install-config imageSetRef: - name: img4.10.18-x86-64-appsub + name: img4.10.18-multi-appsub pullSecretRef: name: azure-us-acm-provision-edge-pull-secret skipMachinePools: true # Disable MachinePool as using custom install-config diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 33d0663e..92b13045 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -123,17 +123,24 @@ data: exampleAWSPool: baseDomain: blueprints.rhecoeng.com clusters: - - one + - One + controlPlane: + count: 1 + platform: + aws: + type: m5.xlarge name: aws-ap openshiftVersion: 4.10.18 platform: aws: region: ap-southeast-2 size: 3 + workers: + count: 0 exampleAzurePool: baseDomain: blueprints.rhecoeng.com clusters: - - two + - Two - three name: azure-us openshiftVersion: 4.10.18 From fa6865ca8ee8c486f8d35dc8f9dfa78dd12649d5 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 14 Jul 2023 05:35:17 +0000 Subject: [PATCH 0936/1263] Sanely handle cluster pools with no clusters (yet) --- acm/templates/provision/clusterpool.yaml | 9 +++++++-- examples/values-example.yaml | 2 -- tests/acm-normal.expected.yaml | 17 +---------------- tests/clustergroup-normal.expected.yaml | 2 -- 4 files changed, 8 insertions(+), 22 deletions(-) diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index 73bf4aa8..a038492d 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -19,6 +19,7 @@ spec: {{- $cloud := "None" }} {{- $region := "None" }} +{{- $numClusters := 0 }} {{- if .platform.aws }} {{- $cloud = "aws" }} @@ -28,6 +29,10 @@ spec: {{- $region = .platform.azure.region }} {{- end }} +{{- if .clusters }} +{{- $numClusters = len .clusters }} +{{- end }} + apiVersion: hive.openshift.io/v1 kind: ClusterPool metadata: @@ -44,9 +49,9 @@ spec: {{- if .size }} size: {{ .size }} {{- else }} - size: {{ len .clusters }} + size: {{ $numClusters }} {{- end }} - runningCount: {{ len .clusters }} + runningCount: {{ $numClusters }} baseDomain: {{ .baseDomain }} installConfigSecretTemplateRef: name: {{ $poolName }}-install-config diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 740da650..4035c431 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -114,8 +114,6 @@ clusterGroup: platform: aws: region: ap-southeast-2 - clusters: - - One exampleAzurePool: name: azure-us openshiftVersion: 4.10.18 diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index b233b9d1..5cfc5f2e 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -25,21 +25,6 @@ type: Opaque # Source: acm/templates/provision/clusterpool.yaml apiVersion: hive.openshift.io/v1 kind: ClusterClaim -metadata: - name: 'one-acm-provision-edge' - annotations: - argocd.argoproj.io/sync-wave: "20" - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - cluster.open-cluster-management.io/createmanagedcluster: "true" - labels: - clusterClaimName: one-acm-provision-edge - clusterGroup: region -spec: - clusterPoolName: aws-ap ---- -# Source: acm/templates/provision/clusterpool.yaml -apiVersion: hive.openshift.io/v1 -kind: ClusterClaim metadata: name: 'two-acm-provision-edge' annotations: @@ -82,7 +67,7 @@ metadata: cluster.open-cluster-management.io/clusterset: aws-ap spec: size: 3 - runningCount: 1 + runningCount: 0 baseDomain: blueprints.rhecoeng.com installConfigSecretTemplateRef: name: aws-ap-acm-provision-edge-install-config diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 92b13045..b60d60b2 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -122,8 +122,6 @@ data: clusterPools: exampleAWSPool: baseDomain: blueprints.rhecoeng.com - clusters: - - One controlPlane: count: 1 platform: From c220a68be7ebcdc93a9863ee0d8660505e775c68 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 16 Jul 2023 09:06:13 +0900 Subject: [PATCH 0937/1263] Clustergroup Chart.yaml name change We currently have a small inconsistency where we use common/clustergroup in order to point Argo CD to this chart, but the name inside the chart is 'pattern-clustergroup'. This inconsistency is currently irrelevant, but in the future when migrating to helm charts inside proper helm repos, this becomes problematic. So let's fix the name to be the same as the folder. Tested on MCG successfully. --- clustergroup/Chart.yaml | 2 +- ...roup-industrial-edge-factory.expected.yaml | 58 +++++----- ...tergroup-industrial-edge-hub.expected.yaml | 108 +++++++++--------- ...rgroup-medical-diagnosis-hub.expected.yaml | 106 ++++++++--------- tests/clustergroup-naked.expected.yaml | 26 ++--- tests/clustergroup-normal.expected.yaml | 60 +++++----- 6 files changed, 180 insertions(+), 180 deletions(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 249163ae..38ece255 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 description: A Helm chart to create per-clustergroup ArgoCD applications and any required namespaces or subscriptions keywords: - pattern -name: pattern-clustergroup +name: clustergroup version: 0.0.1 diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index ac7ddf1a..be93aa88 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -1,5 +1,5 @@ --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -8,7 +8,7 @@ metadata: name: manuela-stormshift-line-dashboard spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -17,7 +17,7 @@ metadata: name: manuela-stormshift-machine-sensor spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -26,7 +26,7 @@ metadata: name: manuela-stormshift-messaging spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -35,7 +35,7 @@ metadata: name: manuela-factory-ml-workspace spec: --- -# Source: pattern-clustergroup/templates/imperative/namespace.yaml +# Source: clustergroup/templates/imperative/namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -44,7 +44,7 @@ metadata: argocd.argoproj.io/managed-by: mypattern-factory name: imperative --- -# Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml +# Source: clustergroup/templates/plumbing/gitops-namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -57,14 +57,14 @@ metadata: name: mypattern-factory spec: {} --- -# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +# Source: clustergroup/templates/imperative/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/imperative/configmap.yaml +# Source: clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: @@ -161,7 +161,7 @@ data: kind: ClusterSecretStore name: vault-backend --- -# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +# Source: clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: @@ -176,7 +176,7 @@ rules: - list - watch --- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml +# Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -190,7 +190,7 @@ subjects: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -209,7 +209,7 @@ subjects: name: openshift-gitops-argocd-server namespace: openshift-gitops --- -# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -234,7 +234,7 @@ subjects: name: factory-gitops-argocd-dex-server namespace: mypattern-factory --- -# Source: pattern-clustergroup/templates/imperative/role.yaml +# Source: clustergroup/templates/imperative/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -248,7 +248,7 @@ rules: verbs: - '*' --- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml +# Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: @@ -263,7 +263,7 @@ subjects: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/imperative/job.yaml +# Source: clustergroup/templates/imperative/job.yaml apiVersion: batch/v1 kind: CronJob metadata: @@ -337,10 +337,10 @@ spec: name: helm-values-configmap-factory restartPolicy: Never --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml --- --- -# Source: pattern-clustergroup/templates/plumbing/projects.yaml +# Source: clustergroup/templates/plumbing/projects.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -361,7 +361,7 @@ spec: - '*' status: {} --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -386,7 +386,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -432,7 +432,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +# Source: clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 kind: ArgoCD metadata: @@ -569,7 +569,7 @@ spec: ca: {} status: --- -# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +# Source: clustergroup/templates/plumbing/argocd.yaml apiVersion: console.openshift.io/v1 kind: ConsoleLink metadata: @@ -583,7 +583,7 @@ spec: location: ApplicationMenu text: 'Factory ArgoCD' --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -593,7 +593,7 @@ spec: targetNamespaces: - manuela-stormshift-line-dashboard --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -603,7 +603,7 @@ spec: targetNamespaces: - manuela-stormshift-machine-sensor --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -613,7 +613,7 @@ spec: targetNamespaces: - manuela-stormshift-messaging --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -627,7 +627,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -641,7 +641,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -655,7 +655,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -669,7 +669,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index d4a208c0..a759bdde 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -1,5 +1,5 @@ --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -8,7 +8,7 @@ metadata: name: golang-external-secrets spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -17,7 +17,7 @@ metadata: name: external-secrets spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -26,7 +26,7 @@ metadata: name: open-cluster-management spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -35,7 +35,7 @@ metadata: name: manuela-ml-workspace spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -44,7 +44,7 @@ metadata: name: manuela-tst-all spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -53,7 +53,7 @@ metadata: name: manuela-ci spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -62,7 +62,7 @@ metadata: name: manuela-data-lake spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -71,7 +71,7 @@ metadata: name: staging spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -80,7 +80,7 @@ metadata: name: vault spec: --- -# Source: pattern-clustergroup/templates/imperative/namespace.yaml +# Source: clustergroup/templates/imperative/namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -89,7 +89,7 @@ metadata: argocd.argoproj.io/managed-by: mypattern-datacenter name: imperative --- -# Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml +# Source: clustergroup/templates/plumbing/gitops-namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -102,14 +102,14 @@ metadata: name: mypattern-datacenter spec: {} --- -# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +# Source: clustergroup/templates/imperative/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/imperative/configmap.yaml +# Source: clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: @@ -322,7 +322,7 @@ data: kind: ClusterSecretStore name: vault-backend --- -# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +# Source: clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: @@ -337,7 +337,7 @@ rules: - list - watch --- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml +# Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -351,7 +351,7 @@ subjects: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -370,7 +370,7 @@ subjects: name: openshift-gitops-argocd-server namespace: openshift-gitops --- -# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -395,7 +395,7 @@ subjects: name: datacenter-gitops-argocd-dex-server namespace: mypattern-datacenter --- -# Source: pattern-clustergroup/templates/imperative/role.yaml +# Source: clustergroup/templates/imperative/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -409,7 +409,7 @@ rules: verbs: - '*' --- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml +# Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: @@ -424,7 +424,7 @@ subjects: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/imperative/job.yaml +# Source: clustergroup/templates/imperative/job.yaml apiVersion: batch/v1 kind: CronJob metadata: @@ -498,7 +498,7 @@ spec: name: helm-values-configmap-datacenter restartPolicy: Never --- -# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml +# Source: clustergroup/templates/imperative/unsealjob.yaml apiVersion: batch/v1 kind: CronJob metadata: @@ -574,10 +574,10 @@ spec: name: helm-values-configmap-datacenter restartPolicy: Never --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml --- --- -# Source: pattern-clustergroup/templates/plumbing/projects.yaml +# Source: clustergroup/templates/plumbing/projects.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -598,7 +598,7 @@ spec: - '*' status: {} --- -# Source: pattern-clustergroup/templates/plumbing/projects.yaml +# Source: clustergroup/templates/plumbing/projects.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -619,7 +619,7 @@ spec: - '*' status: {} --- -# Source: pattern-clustergroup/templates/plumbing/projects.yaml +# Source: clustergroup/templates/plumbing/projects.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -640,7 +640,7 @@ spec: - '*' status: {} --- -# Source: pattern-clustergroup/templates/plumbing/projects.yaml +# Source: clustergroup/templates/plumbing/projects.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -661,7 +661,7 @@ spec: - '*' status: {} --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -716,7 +716,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -762,7 +762,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -808,7 +808,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -884,7 +884,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -930,7 +930,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -976,7 +976,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -1001,7 +1001,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -1065,7 +1065,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +# Source: clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 kind: ArgoCD metadata: @@ -1202,7 +1202,7 @@ spec: ca: {} status: --- -# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +# Source: clustergroup/templates/plumbing/argocd.yaml apiVersion: console.openshift.io/v1 kind: ConsoleLink metadata: @@ -1216,7 +1216,7 @@ spec: location: ApplicationMenu text: 'Datacenter ArgoCD' --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1226,7 +1226,7 @@ spec: targetNamespaces: - golang-external-secrets --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1236,7 +1236,7 @@ spec: targetNamespaces: - external-secrets --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1246,7 +1246,7 @@ spec: targetNamespaces: - open-cluster-management --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1256,7 +1256,7 @@ spec: targetNamespaces: - manuela-tst-all --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1266,7 +1266,7 @@ spec: targetNamespaces: - manuela-ci --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1276,7 +1276,7 @@ spec: targetNamespaces: - manuela-data-lake --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1286,7 +1286,7 @@ spec: targetNamespaces: - staging --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1296,7 +1296,7 @@ spec: targetNamespaces: - vault --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1310,7 +1310,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1324,7 +1324,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1338,7 +1338,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1352,7 +1352,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1366,7 +1366,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1380,7 +1380,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1394,7 +1394,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1408,7 +1408,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1422,7 +1422,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 30442f2b..175f134b 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -1,5 +1,5 @@ --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -8,7 +8,7 @@ metadata: name: open-cluster-management spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -17,7 +17,7 @@ metadata: name: openshift-serverless spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -26,7 +26,7 @@ metadata: name: opendatahub spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -35,7 +35,7 @@ metadata: name: openshift-storage spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -44,7 +44,7 @@ metadata: name: xraylab-1 spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -53,7 +53,7 @@ metadata: name: knative-serving spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -62,7 +62,7 @@ metadata: name: staging spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -71,7 +71,7 @@ metadata: name: vault spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -80,7 +80,7 @@ metadata: name: golang-external-secrets spec: --- -# Source: pattern-clustergroup/templates/imperative/namespace.yaml +# Source: clustergroup/templates/imperative/namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -89,7 +89,7 @@ metadata: argocd.argoproj.io/managed-by: mypattern-hub name: imperative --- -# Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml +# Source: clustergroup/templates/plumbing/gitops-namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -102,14 +102,14 @@ metadata: name: mypattern-hub spec: {} --- -# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +# Source: clustergroup/templates/imperative/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/imperative/configmap.yaml +# Source: clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: @@ -309,7 +309,7 @@ data: kind: ClusterSecretStore name: vault-backend --- -# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +# Source: clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: @@ -324,7 +324,7 @@ rules: - list - watch --- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml +# Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -338,7 +338,7 @@ subjects: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -357,7 +357,7 @@ subjects: name: openshift-gitops-argocd-server namespace: openshift-gitops --- -# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -382,7 +382,7 @@ subjects: name: hub-gitops-argocd-dex-server namespace: mypattern-hub --- -# Source: pattern-clustergroup/templates/imperative/role.yaml +# Source: clustergroup/templates/imperative/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -396,7 +396,7 @@ rules: verbs: - '*' --- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml +# Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: @@ -411,7 +411,7 @@ subjects: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/imperative/job.yaml +# Source: clustergroup/templates/imperative/job.yaml apiVersion: batch/v1 kind: CronJob metadata: @@ -485,7 +485,7 @@ spec: name: helm-values-configmap-hub restartPolicy: Never --- -# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml +# Source: clustergroup/templates/imperative/unsealjob.yaml apiVersion: batch/v1 kind: CronJob metadata: @@ -561,10 +561,10 @@ spec: name: helm-values-configmap-hub restartPolicy: Never --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml --- --- -# Source: pattern-clustergroup/templates/plumbing/projects.yaml +# Source: clustergroup/templates/plumbing/projects.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -585,7 +585,7 @@ spec: - '*' status: {} --- -# Source: pattern-clustergroup/templates/plumbing/projects.yaml +# Source: clustergroup/templates/plumbing/projects.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -606,7 +606,7 @@ spec: - '*' status: {} --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -652,7 +652,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -698,7 +698,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -744,7 +744,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -790,7 +790,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -836,7 +836,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -882,7 +882,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -928,7 +928,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -992,7 +992,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -1038,7 +1038,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -1084,7 +1084,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -1139,7 +1139,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -1194,7 +1194,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -1240,7 +1240,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +# Source: clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 kind: ArgoCD metadata: @@ -1377,7 +1377,7 @@ spec: ca: {} status: --- -# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +# Source: clustergroup/templates/plumbing/argocd.yaml apiVersion: console.openshift.io/v1 kind: ConsoleLink metadata: @@ -1391,7 +1391,7 @@ spec: location: ApplicationMenu text: 'Hub ArgoCD' --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1401,7 +1401,7 @@ spec: targetNamespaces: - open-cluster-management --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1411,7 +1411,7 @@ spec: targetNamespaces: - openshift-serverless --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1421,7 +1421,7 @@ spec: targetNamespaces: - opendatahub --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1431,7 +1431,7 @@ spec: targetNamespaces: - openshift-storage --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1441,7 +1441,7 @@ spec: targetNamespaces: - xraylab-1 --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1451,7 +1451,7 @@ spec: targetNamespaces: - knative-serving --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1461,7 +1461,7 @@ spec: targetNamespaces: - staging --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1471,7 +1471,7 @@ spec: targetNamespaces: - vault --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1481,7 +1481,7 @@ spec: targetNamespaces: - golang-external-secrets --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1495,7 +1495,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1509,7 +1509,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1523,7 +1523,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1536,7 +1536,7 @@ spec: installPlanApproval: Automatic startingCSV: --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 6a79b27d..e15566b0 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -1,5 +1,5 @@ --- -# Source: pattern-clustergroup/templates/imperative/namespace.yaml +# Source: clustergroup/templates/imperative/namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -8,7 +8,7 @@ metadata: argocd.argoproj.io/managed-by: common-example name: imperative --- -# Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml +# Source: clustergroup/templates/plumbing/gitops-namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -21,14 +21,14 @@ metadata: name: common-example spec: {} --- -# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +# Source: clustergroup/templates/imperative/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/imperative/configmap.yaml +# Source: clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: @@ -76,7 +76,7 @@ data: kind: ClusterSecretStore name: vault-backend --- -# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +# Source: clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: @@ -91,7 +91,7 @@ rules: - list - watch --- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml +# Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -105,7 +105,7 @@ subjects: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -124,7 +124,7 @@ subjects: name: openshift-gitops-argocd-server namespace: openshift-gitops --- -# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -149,7 +149,7 @@ subjects: name: example-gitops-argocd-dex-server namespace: common-example --- -# Source: pattern-clustergroup/templates/imperative/role.yaml +# Source: clustergroup/templates/imperative/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -163,7 +163,7 @@ rules: verbs: - '*' --- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml +# Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: @@ -178,7 +178,7 @@ subjects: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml +# Source: clustergroup/templates/imperative/unsealjob.yaml apiVersion: batch/v1 kind: CronJob metadata: @@ -254,7 +254,7 @@ spec: name: helm-values-configmap-example restartPolicy: Never --- -# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +# Source: clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 kind: ArgoCD metadata: @@ -391,7 +391,7 @@ spec: ca: {} status: --- -# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +# Source: clustergroup/templates/plumbing/argocd.yaml apiVersion: console.openshift.io/v1 kind: ConsoleLink metadata: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index b60d60b2..ab4d4d08 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -1,5 +1,5 @@ --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -13,7 +13,7 @@ metadata: owner: "namespace owner" spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -22,7 +22,7 @@ metadata: name: application-ci spec: --- -# Source: pattern-clustergroup/templates/core/namespaces.yaml +# Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace metadata: @@ -31,7 +31,7 @@ metadata: name: excludes-ci spec: --- -# Source: pattern-clustergroup/templates/imperative/namespace.yaml +# Source: clustergroup/templates/imperative/namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -40,7 +40,7 @@ metadata: argocd.argoproj.io/managed-by: mypattern-example name: imperative --- -# Source: pattern-clustergroup/templates/plumbing/gitops-namespace.yaml +# Source: clustergroup/templates/plumbing/gitops-namespace.yaml apiVersion: v1 kind: Namespace metadata: @@ -53,14 +53,14 @@ metadata: name: mypattern-example spec: {} --- -# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml +# Source: clustergroup/templates/imperative/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/imperative/configmap.yaml +# Source: clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: @@ -219,7 +219,7 @@ data: kind: ClusterSecretStore name: vault-backend --- -# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml +# Source: clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: @@ -234,7 +234,7 @@ rules: - list - watch --- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml +# Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -248,7 +248,7 @@ subjects: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -267,7 +267,7 @@ subjects: name: openshift-gitops-argocd-server namespace: openshift-gitops --- -# Source: pattern-clustergroup/templates/plumbing/argocd-super-role.yaml +# Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -292,7 +292,7 @@ subjects: name: example-gitops-argocd-dex-server namespace: mypattern-example --- -# Source: pattern-clustergroup/templates/imperative/role.yaml +# Source: clustergroup/templates/imperative/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -306,7 +306,7 @@ rules: verbs: - '*' --- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml +# Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: @@ -321,7 +321,7 @@ subjects: name: imperative-sa namespace: imperative --- -# Source: pattern-clustergroup/templates/imperative/job.yaml +# Source: clustergroup/templates/imperative/job.yaml apiVersion: batch/v1 kind: CronJob metadata: @@ -395,7 +395,7 @@ spec: name: helm-values-configmap-example restartPolicy: Never --- -# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml +# Source: clustergroup/templates/imperative/unsealjob.yaml apiVersion: batch/v1 kind: CronJob metadata: @@ -471,10 +471,10 @@ spec: name: helm-values-configmap-example restartPolicy: Never --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml --- --- -# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml +# Source: clustergroup/templates/plumbing/hosted-sites.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -495,7 +495,7 @@ spec: - '*' status: {} --- -# Source: pattern-clustergroup/templates/plumbing/projects.yaml +# Source: clustergroup/templates/plumbing/projects.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: @@ -516,7 +516,7 @@ spec: - '*' status: {} --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -571,7 +571,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/applications.yaml +# Source: clustergroup/templates/plumbing/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -617,7 +617,7 @@ spec: retry: limit: 20 --- -# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml +# Source: clustergroup/templates/plumbing/hosted-sites.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -677,7 +677,7 @@ spec: jsonPointers: - /status --- -# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml +# Source: clustergroup/templates/plumbing/hosted-sites.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -737,7 +737,7 @@ spec: jsonPointers: - /status --- -# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml +# Source: clustergroup/templates/plumbing/hosted-sites.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -797,7 +797,7 @@ spec: jsonPointers: - /status --- -# Source: pattern-clustergroup/templates/plumbing/hosted-sites.yaml +# Source: clustergroup/templates/plumbing/hosted-sites.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -857,7 +857,7 @@ spec: jsonPointers: - /status --- -# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +# Source: clustergroup/templates/plumbing/argocd.yaml apiVersion: argoproj.io/v1alpha1 kind: ArgoCD metadata: @@ -994,7 +994,7 @@ spec: ca: {} status: --- -# Source: pattern-clustergroup/templates/plumbing/argocd.yaml +# Source: clustergroup/templates/plumbing/argocd.yaml apiVersion: console.openshift.io/v1 kind: ConsoleLink metadata: @@ -1008,7 +1008,7 @@ spec: location: ApplicationMenu text: 'Example ArgoCD' --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1018,7 +1018,7 @@ spec: targetNamespaces: - open-cluster-management --- -# Source: pattern-clustergroup/templates/core/operatorgroup.yaml +# Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1028,7 +1028,7 @@ spec: targetNamespaces: - application-ci --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: @@ -1042,7 +1042,7 @@ spec: installPlanApproval: Automatic startingCSV: advanced-cluster-management.v2.4.1 --- -# Source: pattern-clustergroup/templates/core/subscriptions.yaml +# Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: From 21c534c4dc54f39c70866039fa4777d37e45210a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 19 Jul 2023 17:22:15 +0900 Subject: [PATCH 0938/1263] Fix the clusterPoolName in clusterClaims Currently with the following values snippet: managedClusterGroups: exampleRegion: name: group-one acmlabels: - name: clusterGroup value: group-one helmOverrides: - name: clusterGroup.isHubCluster value: false clusterPools: exampleAWSPool: size: 1 name: aws-ap-bandini openshiftVersion: 4.12.24 baseDomain: blueprints.rhecoeng.com controlPlane: count: 1 platform: aws: type: m5.2xlarge workers: count: 0 platform: aws: region: ap-southeast-2 clusters: - One You will get a clusterClaim that is pointing to the wrong Pool: NAMESPACE NAME POOL open-cluster-management one-group-one aws-ap-bandini This is wrong because the clusterPool name will be generated using the pool name + "-" group name: {{- $pool := . }} {{- $poolName := print .name "-" $group.name }} But the clusterPoolName inside the clusterName is only using the "$pool.name" which will make the clusterClaim ineffective as the pool does not exist. Switch to using the same poolName that is being used when creating the clusterPool. --- acm/templates/provision/clusterpool.yaml | 2 +- tests/acm-normal.expected.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index a038492d..e2f9d3d1 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -87,7 +87,7 @@ metadata: {{- end }} {{- end }} spec: - clusterPoolName: {{ $pool.name }} + clusterPoolName: {{ $poolName }} --- {{- end }}{{- /* range .range clusters */}} {{- end }}{{- /* range .clusterPools */}} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 5cfc5f2e..b2f0ac48 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -35,7 +35,7 @@ metadata: clusterClaimName: two-acm-provision-edge clusterGroup: region spec: - clusterPoolName: azure-us + clusterPoolName: azure-us-acm-provision-edge --- # Source: acm/templates/provision/clusterpool.yaml apiVersion: hive.openshift.io/v1 @@ -50,7 +50,7 @@ metadata: clusterClaimName: three-acm-provision-edge clusterGroup: region spec: - clusterPoolName: azure-us + clusterPoolName: azure-us-acm-provision-edge --- # Source: acm/templates/provision/clusterpool.yaml apiVersion: hive.openshift.io/v1 From 4c05974a07130aef3e8e1b99e173cda335270a68 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 24 Jul 2023 12:01:57 +0900 Subject: [PATCH 0939/1263] Add some comments to make if/else and loops clearer Let's improve readability by adding some comments to point out which flow constructs are being ended. --- clustergroup/templates/plumbing/applications.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 6e52e667..43a820d9 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -134,7 +134,7 @@ spec: chart: {{ .chart }} {{- else }} path: {{ .path }} - {{- end }} + {{- end }}{{- /* if .chart */}} {{- if .plugin }} plugin: {{ .plugin | toPrettyJson }} {{- else if not .kustomize }} @@ -178,18 +178,18 @@ spec: {{- range .overrides }} - name: {{ .name }} value: {{ .value | quote }} - {{- if .forceString }} + {{- if .forceString }} forceString: true - {{- end }} - {{- end }} + {{- end }} + {{- end }}{{- /* range .overrides */}} {{- if .fileParameters }} fileParameters: {{- range .fileParameters }} - name: {{ .name }} path: {{ .path }} - {{- end }} - {{- end }} - {{- end }} + {{- end }}{{- /* range .fileParameters */}} + {{- end }}{{- /* if .fileParameters */}} + {{- end }}{{- /* if .plugin */}} {{- if .ignoreDifferences }} ignoreDifferences: {{ .ignoreDifferences | toPrettyJson }} {{- end }} From dd3cdcbe52c81d2f8ad8ed6862e732f315f31185 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 27 Jul 2023 17:03:01 +0900 Subject: [PATCH 0940/1263] Add some more comments in applications.yaml --- clustergroup/templates/plumbing/applications.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 43a820d9..075e1bdb 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -200,8 +200,8 @@ spec: automated: {} retry: limit: {{ default 20 $.Values.global.applicationRetryLimit }} - {{- end }} + {{- end }}{{- /* .syncPolicy */}} --- -{{- end }} -{{- end }} -{{- end }} +{{- end }}{{- /* if or (.generators) (.generatorFile) (.useGeneratorValues) (.destinationServer) (.destinationNamespace) */}} +{{- end }}{{- /* range .Values.clusterGroup.applications */}} +{{- end }}{{- /* if not (eq .Values.enabled "core") */}} From 5f33f3325de60b376e611433dce97585727bcb8d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 27 Jul 2023 17:16:08 +0900 Subject: [PATCH 0941/1263] Add a default for options applicationRetryLimit --- acm/values.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acm/values.yaml b/acm/values.yaml index 1f430370..7c4a19c0 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -6,7 +6,8 @@ global: pattern: none repoURL: none targetRevision: main - + options: + applicationRetryLimit: 20 clusterGroup: subscriptions: From 91f3ef0329d120e23245a3b8f13d40e18d934baa Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 27 Jul 2023 17:17:46 +0900 Subject: [PATCH 0942/1263] Split out values files to a helper for the acm chart Just like we did for the clustergroup chart, let's split the values file list into a dedicated helper. This time since there are no global variables we include it with the current context and not with the '$' context. Tested with MCG: hub and spoke. Correctly observed all the applications running on the spoke. --- acm/templates/_helpers.tpl | 13 +++++++++++++ acm/templates/policies/application-policies.yaml | 9 +-------- 2 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 acm/templates/_helpers.tpl diff --git a/acm/templates/_helpers.tpl b/acm/templates/_helpers.tpl new file mode 100644 index 00000000..fdd91273 --- /dev/null +++ b/acm/templates/_helpers.tpl @@ -0,0 +1,13 @@ +{{/* +Default always defined valueFiles to be included when pushing the cluster wide argo application via acm +*/}} +{{- define "acm.app.policies.valuefiles" -}} +- "/values-global.yaml" +- "/values-{{ .name }}.yaml" +- '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}.yaml' +- '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}-{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}.yaml' +- '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}-{{ .name }}.yaml' +# We cannot use $.Values.global.clusterVersion because that gets resolved to the +# hub's cluster version, whereas we want to include the spoke cluster version +- '/values-{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}-{{ .name }}.yaml' +{{- end }} {{- /*acm.app.policies.valuefiles */}} diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index f1c7bbdd..5bc5de6a 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -43,14 +43,7 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-{{ .name }}.yaml" - - '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}.yaml' - - '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}-{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}.yaml' - - '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}-{{ .name }}.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}-{{ .name }}.yaml' + {{- include "acm.app.policies.valuefiles" . | nindent 24 }} {{- range $valueFile := .extraValueFiles }} - {{ $valueFile | quote }} {{- end }} From 17697e546417fc3498e54a897298e40fd6f433bd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 27 Jul 2023 18:06:21 +0900 Subject: [PATCH 0943/1263] Fix up tests They changed because we made the list indentation more correct (two extra spaces to the left) --- tests/acm-industrial-edge-hub.expected.yaml | 16 +++++----- tests/acm-medical-diagnosis-hub.expected.yaml | 16 +++++----- tests/acm-normal.expected.yaml | 32 +++++++++---------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index d54a8db6..444b833c 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -206,14 +206,14 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-factory.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-factory.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-factory.yaml' + - "/values-global.yaml" + - "/values-factory.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-factory.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-factory.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 6e3eedff..f79e013b 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -197,14 +197,14 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-region-one.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-region-one.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-region-one.yaml' + - "/values-global.yaml" + - "/values-region-one.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-region-one.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-region-one.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index b2f0ac48..900cc291 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -600,14 +600,14 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-acm-edge.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-edge.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-edge.yaml' + - "/values-global.yaml" + - "/values-acm-edge.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-edge.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-edge.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -694,14 +694,14 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-acm-provision-edge.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-edge.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-provision-edge.yaml' + - "/values-global.yaml" + - "/values-acm-provision-edge.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-edge.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-provision-edge.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL From 669ff9208e16a9293d4a9be1bca8be572faa46a8 Mon Sep 17 00:00:00 2001 From: Tom Stockwell <2060486+stocky37@users.noreply.github.com> Date: Fri, 28 Jul 2023 14:55:28 +1000 Subject: [PATCH 0944/1263] Fix sa/namespace mixup in vault_spokes_init --- ansible/roles/vault_utils/tasks/vault_spokes_init.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml index af1a02fd..d4310e7f 100644 --- a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml @@ -182,8 +182,8 @@ pod: "{{ vault_pod }}" command: > vault write auth/"{{ item.value['vault_path'] }}"/role/"{{ item.value['vault_path'] }}"-role - bound_service_account_names="{{ external_secrets_ns }}" - bound_service_account_namespaces="{{ external_secrets_sa }}" + bound_service_account_names="{{ external_secrets_sa }}" + bound_service_account_namespaces="{{ external_secrets_ns }}" policies="default,{{ vault_global_policy }}-secret,{{ item.value['vault_path'] }}-secret" ttl="{{ vault_spoke_ttl }}" loop: "{{ clusters_info | dict2items }}" when: From 5cb41a368a2de1468a8b2b17a97ca9b13e241e07 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 31 Jul 2023 15:43:11 +0200 Subject: [PATCH 0945/1263] Update local patch Also set seccompProfile to null to make things work on OCP 4.10 --- .../0001-runasuser-comment-out.patch | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/golang-external-secrets/local-patches/0001-runasuser-comment-out.patch b/golang-external-secrets/local-patches/0001-runasuser-comment-out.patch index b4ea727f..6545881f 100644 --- a/golang-external-secrets/local-patches/0001-runasuser-comment-out.patch +++ b/golang-external-secrets/local-patches/0001-runasuser-comment-out.patch @@ -1,30 +1,48 @@ -diff --color -urN external-secrets.orig/values.yaml external-secrets/values.yaml ---- external-secrets.orig/values.yaml 2023-05-22 12:42:54.000000000 +0200 -+++ external-secrets/values.yaml 2023-05-22 16:20:02.748621794 +0200 -@@ -117,7 +117,7 @@ +diff -up external-secrets/values.yaml.orig external-secrets/values.yaml +--- external-secrets/values.yaml.orig 2023-07-31 15:12:18.815909938 +0200 ++++ external-secrets/values.yaml 2023-07-31 15:32:59.905360226 +0200 +@@ -117,9 +117,11 @@ securityContext: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - runAsUser: 1000 +- seccompProfile: +- type: RuntimeDefault + # runAsUser: 1000 - seccompProfile: - type: RuntimeDefault ++ # Uncomment this once 4.10 is out of scope ++ # seccompProfile: ++ # type: RuntimeDefault ++ seccompProfile: null -@@ -331,7 +331,7 @@ + resources: {} + # requests: +@@ -331,9 +333,11 @@ webhook: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - runAsUser: 1000 +- seccompProfile: +- type: RuntimeDefault + # runAsUser: 1000 - seccompProfile: - type: RuntimeDefault ++ seccompProfile: null ++ # Uncomment this once 4.10 is out of scope ++ # seccompProfile: ++ # type: RuntimeDefault -@@ -453,7 +453,7 @@ + resources: {} + # requests: +@@ -453,9 +457,11 @@ certController: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - runAsUser: 1000 +- seccompProfile: +- type: RuntimeDefault + # runAsUser: 1000 - seccompProfile: - type: RuntimeDefault ++ seccompProfile: null ++ # Uncomment this once 4.10 is out of scope ++ # seccompProfile: ++ # type: RuntimeDefault + resources: {} + # requests: From 9d2df973aa3e5882420da9a6fe472fde412550b8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 31 Jul 2023 15:43:53 +0200 Subject: [PATCH 0946/1263] Update ESO to 0.8.5 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.8.3.tgz | Bin 78591 -> 0 bytes .../charts/external-secrets-0.8.5.tgz | Bin 0 -> 78631 bytes 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.8.3.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.8.5.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 74b1c051..ab900162 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.8.3" + version: "0.8.5" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.8.3.tgz b/golang-external-secrets/charts/external-secrets-0.8.3.tgz deleted file mode 100644 index d738b9cc42fbb4891d382245ed4ac1fe98a092b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78591 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≪0Q1jt^qXiICVFi0H8ob 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 BSDpX> diff --git a/golang-external-secrets/charts/external-secrets-0.8.5.tgz b/golang-external-secrets/charts/external-secrets-0.8.5.tgz new file mode 100644 index 0000000000000000000000000000000000000000..af7525bf38dd4b6bea620632fd055a0e412e4f81 GIT binary patch literal 78631 zcmV)5K*_%!iwFP!000001ML0ldfP~rAd1#+KLv)_^Vyywl9F$k$9uB7m6Fq?PG9s% zO6Sy9r;ZJh5QP#5umMoAGAqwp?HAZjcGe{#5V%K>AbkU7y3!&d0xMQTtXTKueN4SL zbc12yO=B-j#((Ml*@x%*r%&;}2j4&4*Z(V@zZ^Vy^6cRI$4?I)!S4r84xW7f7w72~ zU@1MTBz0rQ`OCteUU`96+pg{X7y5X`^*_3D<8*ZAE`rX!feZZX$&=dk|NhYqz! z_Mbg|{21CDJlp^N*Um;O61PW&i*?%X`O=PsAopZlX9Mo;g}ykr{t z%ane5A-0Rd+r?S9p*!~$UYI(UQSAJ(n!sx>^^$v6(ZYM~T&3wUc|IP`{q$-z8BL?b zxU}PnKiu=D_$k>nc5g@AeU*4Iyh`C~W+ROkt{;xwB%zl{Gz_9S{Er*Yz0oq9-@Eqi zZlidXJij+|t`+FR5d|KERf4n_z6@6rBn|NcGvduZ}Afz{2e@Z zuDu}e!^PCSiSGV$j=$k-?m15ABFgy1^*#Qp^x^bw2|vR+1^(2(D*cJg=zLq~T=QpVp+1J4bItL1PR!RPKC-iy5(A2<77 z36Q&2{=!GF_wU{F7eI)f!zqFpMTw7ZpF8ubX^h`(r#X(ZTm``?edf7ya``4oPh&3u zDBu<0O=$hgU>SUC3L?b}M7*GlSH}fwq;opE! z9_})L1HXzB$Grhs;7$T>&k6i%Y?vTcyaZl_GiNpNd8kvsO5!<)=9hl@*VP2(%s+(@ zAetz#f|UORxDxTDIN?!#x>JB7)Y`|mFU$sJIK##;%yVZj5S!!V5oIerL7CkPm} zfOA7wFe2iB zlt2E&rco%#IsD}~UWof)CQ(K!a|#=k2ZCOI{|8+ZZ=qN0&pcYDOK4E~@m?O};sJ1O zeOSt+7X$nY2X@m8&;q+^iqJU|FTM4=ke3`zZ-V>Z3zJohd(K$^YGC~I$_ z8KnoK8{3(R1DNoNVJrDlak>J0{9qnEzJv@v&ASWEAn)fmnj%H38FS>kfMI}= zrQF<~hX4kSvl!hIeVfRO_4jF-7GKqgPVr{Gy~6zz1I~)m%7Do3Qe0hLIiTqLkGG;# z&GbM2OVb6vlh69-T8-MB@PN&3! zNa9*CQAad&Ty8754t;8p+U6|XGjg0XUBPC^JO$cVlBrg z46U7WAd2p6hX1Mf%BYgy(-?r!*md~mw-4fBYe^@vL68%{6DOfAPm(wmY7U1P03oVE zM~;s)@-MWJT^MX~QZat)P$MKPG-S z!(rj<0|$inO8b(+-(qT_cfwa??XD4rk3I}YnEN+gaRdOsogt?1uvUUl1mZhw;k@w1 zkE4)|*^LX=%p~yibyPcwh<)t72yf`(7K6NYm*S#?u|PIm;Xu@X3Aa(z0Eh9Mmp%u^ zO%y=aTu0csqRTseseiZBYa#auaj}JO@&K>t5S2&K4o!alW)K{yCZc=&={Z(p>t05| z4hv_eX@FBWvGC7-H9LgDeOnyqcdPb_+O3#u)lgm@`^iq3%B@L6s12S z8NGwux==(WUWJFrn<&KB8s(v%e}!#~(hoq0zx^6AFMs;|SO&j}I(x`lMYrgGB~Xeazwk9k^U$xEF!|PP#m`3Tv_V-@s9&i5zaa zTp@f5@#n%@kXZKk(QE(Sy^JMNjKA`8(gB4#E2>K zq`-g!fjlj;k4br+@`H-)!VdytaTl$eTbEC^l-cs1O#}hg*g|=`&1ODsYTzxHU&W2D ze$&sA=-`~d0f~XL#;+yQ*j;)W#J+e5TVOv?eJ)_H;r;|hN;HL|6W&jGQQ!yfhB;d# z-63%iY9w4;`id9?4(!uxLat)t+Luc^B~XA-nr0)vaQcj0=3WuJJL$lt-pa5PXK;QU?mKI7i-<{uBF!XyJ% zDNM4T#(3Q(>6kyaiizM9{`!t__+EhaUV!%So+GfEeU&LO%?K_(CSIrzLYAFRYz(CB z<8$Z1=zl#_ztH;yv2Xu&Ab#atD^MkV8)k^kBmqHYPz9S(^XHnFAd!2v9#*%5&O9C; z62-1UU=9V&-`~H7kGStMneaW_Ht)k|<`FjjAra>-UBQL_UwNGWe-Y{bR*5he1jlGx zk>YyZ6G!WLB|UsWqy$eOQVXPdE?k{RtQJfni1~}6fQ5XG9A(EGulww%SCOaO*+~?K zikl=Gr$kp(KaChE<~F4p38*U;5} z`her$BxRNt(7Zaaf=fBf8{E`_*xjHDP^w!j>%x znO0svTT}}(OPA9DJ5Hr)SObSiG5kZKkeK5D_5dtq^XS@r+ibx7BAk__*3I;4QR{V*J`e{ zcedly+}c{lo!#4bzAA6-io3h&_O8Ca9VahqcP0ea0hVJhyZu^`H!MrtTbH{VSlcRj zxn5#6l9?-{=5FNXI>}iR6slKp8^S_0PR#{|vdCaAG*pNULN5pOJxKumi7qR_h%xGk zFKOS1MtH&|o?LBwFUXn_Y4y=~1~>o`ioi?rLeIfHvC8WynFYmHYUXSxD^;@?CQ~*S zL8?1 zQJ14umCCwG^+zu>3Ch|_O}*6AOHJld6LtT*WR@>QXz8VnUh2qx%1a#u!u(+tCU5!f z`+#dc)4Zj^89Uk ziT^yx_mBJduU$N)@Qf=LeQSX6VX^|OP_iPwSQKjR{>Qz0C!rI?GiD4Y z-W(Vu7JTw<;mIERogvE_dLCU)C`_%E#73%!ntNFkj`nNDSw2A4()&nGKYPyPj(Ji1 zp6lGdL1KGfAMWAk-o4lC7leT1nBG^&WK~C{|XG7^C#S_@Dp%(&gdP-muN0Xf?IQj&`fgn2;r?8 zhcKt-_x_#4)XTt~J0HsZKH%MyNdyFOQ7GU55WxAzREC8iS0ZA@7XBQCRTg#;i-Nmb z6rCGk4jzpD=bxW?gZ9r>p9cE>9_ZcDi<42h+$HgE6{_o=X zMUCUPaDPvW*k-*ppx1XnnobH58&oOlpMb~$F@W{fgy+M z=HxPqzQsX=ESLzV9aa;jicC|49bB<40WY9!$FS5WW>|(BMk2MSu9nEFeQ$zzklp0J z|BWW`{73VZ&0tjM;6X-b;!2{v63*O@mcT7zKTI#3!T(H#|1%jB#^R5f@3ZQE=60X9 ztaxTbv@R|wEv?um+=`T0Cd*6Bv$ZYGNk=#TTR4Kje1w0mGx&cEoWc8n{hHEWpSh=FKmmQ%LCe^z$J$W1(`_Al>O)^M7KcIwn#1P$ zSgC+n=yIKb=r}Nk5sfax%sRUL6)%49t6cfQB<**A5tf>ELSca7jsQw3?Rn<3v&az2jU+CqXC-PwXX1|?SCUYRyVJ~ zXYJPpZR6Ra(qy0=p|pG~jZm9+YgiY+*kt{-=27W_Bja>~PKc zpM(7;kDnFvKTjS%e$?xKcJVZyt?V%Ac_QIzOU5?`_pbeL_FT7IW5nysP2K1Bq_b1w z?Wx@rRX1oZCcSuNa)7(WiaLVrV#_K`%a|Dq6chL_Cxl6%jo=9niUX0KO$~-BP4(PW zcaKm&rP1Hu?9_cp9D7tPJ$BnF@IY`xNuY>VbH z&z&LR8GlhyAMr(PKgrKEEk@q&fk!MlswkI(n$`Fr>!z&;{}ALR-{thRuNqBL_(N)) zOeR#ClH6z}UoE=NX2X^oYd67hnF(m7B1T-qNZSk2Vf8%M?gHIin@&mL-d4lpr_!=) z1$P^L))lTbYhp0x(OEwsJsMW^%^ZDo-yEjpW%_KoVUxOPH%`4_`Uj87TR(>bwJkw% zuSWs37-f#<(EoW`YYf%>S5Xt5Q>YAzFq~@s>21SjzU=wQxbZyEQ8T3Fs9cSqGaOFi z*-(TK45jnpk4;xVF4`dfX&g`N{vqy!A8jsmDcaW$M>qM8)%SY^9Qc*)z)+(ZL&3&< z^bJ?IjAzNHE@LPOFy*zOPyQ&OmOz(g;YYQ7MH6yeXz3_V$FOu}VmF+7&c9uIcYDsi zQ94XC^VDn-fu@|^^=a{Trtn-G(5GICvO53XHF)~@N zT;$9qY*O3jWoV7YI?7<1ZGKjZ0?In+p+VdIcul;K4oY1}(x!mb@6l4MQP!~{45^8j z*N#)IfBV$hne9Io`ah6gvalw_?Pwu?Ckr{n+a}!M#)~IeV<3zJ`EN|cr~bG_@~8e< zEdhI;`g18?R>C@4_z40Udvh%OqpwaK%vmBwp-syr%H0*@bqnaIqE$5iGzkOR$_viCi@PAVOZOsC<@PCgE z4)%-u-?RPwp8wm$Bly24b{?#+11JTmKHOk5>Dn!~n;NpfU8r1**jy(D1H059CV}Jk4 zY6sZ{vNEj2`xU?;Vs->1YhdYME~PiH%Y+k^M8GRfbw6T|7-hC+xoj8KT!H7$_Kob z`KRUnhg<)knE&O`_s{zLUpsjU@{p(xNtNtNQ9ot#hNhk+gT|Rq8XqfzM(&EQykPP- z7W&ebE~fgMsOlPYw0!HsIc!=C!=0@BVCKcS==Pia(ZRv!iQ+x-dp#M6)Xq^{?O-dV z%{gmzTUra2gy)TUY35O^rNbN+zOE@s50B0c>BWV5fy)11Dqg;V-&rsWRxzf&Eli2t zC08I$zHt{VAIJ1ytH6zmgP>4}c`=_mikZTkqXMhSD8MMaIdFsJl`EZ96F5T)70%;e zq4MzX^yJ;+^TG?vftpNXf61vt2l;7Yp59d)Viujl-!V(uk#y&E&e)!HC-Rr4S8nQv z^lg-%1=GnbSIHGjZj8S6n8}Ac?~{@9Cc-S~le?_^mdM+~pTp9j00(^HGWL=yhe6@O zWthuHl#hNe^I%q#{9PoT^Kq~G>D@yt?&T*E9*pc)934sOo9L%l#T;<)KQ!2aBG+K-aok;Y@lOURm z7l2ew#C>>pJbZh&O0PzXnI7Py1*RuA`d>0fg7y-7kkra9{Rtl(GrPouK&sDoiW!j z`KIrYG_-KN+P>^S!sW{A83*2Rf4!pL<76VF?p1bi3WpfKs(7BN;w4~Ql}-}0}` zeF)i{NajS4(dtG8biU5_!6_%=vJkHv^{$9CBI(q2RP(7ei1CqB|D<#(Yk@wwbQW;$ z_{*RIN<6p;?B~!^1u&M;jMJ>*jl$Ym`W*--XJ@55ui@*p`@7hrVn@qIXcbOjjH$l_ zro&`;&I_IN^^y63vX{U_5f)rg0;K!JVn$6^>g%4|432c?7v?SML6gZ=@z$E=rb7W- zl(p4$kUsy&v14^}F`W-SOmmyJUujyHewIO&-HT~Hmb@&1#!zXqR@F&2haaD7ulBe% zP5~pv1l#~vd?(Z5GIrJFb|15;0~!CoxgWJd1NU1*g&#O)PofwQZituOMAd2a;22_B zV65hlARi`P<2C2UvJ6l2V{jC+hnVYi@S^8!Dc}MC=7L3VmB+N3w*odRWo=>4^tJ-{ z2B<~7;2Fxb^gD7MuyV*`OxY$lWZrvJ_*<^0;t#{}XL-DiQPWCf<->=PS8mO7eWVT1 zpY8r^cL;WWwriWVo^69>a?90Yc$hIV>#}pQ(3Taq{ac}v&d$kq+vd0P)-4-W?!4HO zq%}?{U76qvl}cHyJtCF2Fr6H&#B%pIuOe!?;OyMh3}|>Mu(@TLhoYC79jjVCz-S+^ zxSPKqf?r<@@T5B58?CkL+n<21E%j+5rlgV z2-y)=nS^c9pzxYUL(J&}yExSpU+O298fmjmM)T;qR|iiY=?LC)em#GQsxfLJemc7H zLs$I!r57*Ukp3uY&Ujr{i|U;>Y*|NaQTldGy9}V31{##LJG15_Ni}2jmelqdRpf~f z$vN_eCJUviq>JM+iYBn8TmooB>p;bUZ6RZ$RU24?W6oAe7adtxG$4)9 znj~M6z@%AmMlx3r-0V00tinix^oy)Xn!O53Fp)(`SbW$ng?c_&NofgKgOQp{M|=2d z>@k_eZ_cJ}Jj+f7*@6UCljnN%<;C4l`wQo|M{{g!f}9<-}5&38PoUD?zN-8w~A{;uVQ$60+Rov`s+E>wk8oOK(;nYb2cY9-|* z0#6`TT|y0@*Izb68nfbOi8V93L&eX?yk$P90_&@5GNt&?JnKH3@%r?*Gx||0CG3e5SHa^Z!2EfBNWYG5_zAN8k7U zKf8D||KA4{PCyT{U(0Fk@)iF|0Xkp5!%*enFme`J%R{K}9r`^NFPdKvdY^d;RkkTP z3{_8wsdk0m5Zn&@PB{xz4scKT3<)Uil^uq%a^HBdLYpJZCYGo7|4gFrl<25%iOCWz zo_Uv}f>6eb#APOncVgi_E)OmEvJ<`Xw?3@&0;*_W@CCX@@xJ6aCx8Es2md+3en0*= zIKvcvgNOeB5M1a|F(K@{h3_TBf#po$ee>s}v)2U9?xwz8mHQOgSkjKLH<0AXt9Ppd zpV2w7-bbnTQR;n^3LVz*QK~$eIZq{$>b-ZoQx0+v+iu#!UVbDQC%df)aN|YCJH4q#LLzfny8Qibv)xF02-T;@d z;pF7kw=y*&PmCOVzj$|&CBxH0CQ#vqAf3=WGV3EjrQJ{5 zggZ~h9LC9v!BQUhL{_q2MoHphKEi0pLMGZ6e#QazXl+Mw2h-U!T5XFN%X4cvSGk3R zU#KgMIa4*DyxH}c$mJjz(HIS^9n17(rkJNfjH`RRi+EbDTzj8*~nueLJ z5JfRKBaLkgeMERe{?c|@3(gIqPwsBSLks7XFA8y43JnOmqzVxS(I=u-{9I4ENi%;{ zgW0trlzDl(yYXE1aa^U?wd2Ve2Ps-1wU0=(-7rd$BoWZd?c+ zYa3MG7DN}g6n6%lqz)NsBu1E$do>PQq}(y+W8CPu(QBjpEr}Ap+2WTgk+M z&3ZbfZix-k(S@pAtbr+g#%D4=rDAA z#~vt%C}-6@-SwL_FYtQd#q&%$%RF1*^2n79)1MYCj2S(kbT-qLFonGKS2FmbYc^k) z&6pXsrR&E6QyAtZlpO3L^Fnl+MIJjB3Z_3}Ek}x9LJCsxGyYNKnPoe9e0rTo!~A-k zNO!RIIuXOU_BxTTPA78N%57F(G1yNTgC;Kiis?FF<2xjjkUIlhL}*MTKLv5e{EYqj z3#PwdHl3Q8^pFD(_g+%5X@zXvwIBzF0QUbH5>6S;?qwPa0h>H8?J#}l#1Xn}yXe>( zp(#6NCjlZGh(vZbYe%_FjFCGxSQp(-%4RjKuv=QrYEo%B{jTIh+>^JlpSB#F%2O?R zN{QhYl8-J^xDe-FD*Z|pnQw?_r>@P{QWiNA17*8FW^|HX{uG>Y$B`(@W#G#M4}p8< z#ZvTg9$bPXL;mK0o1_mLXJi!6W^=mD&vqj+$rrQYvgGg__zRH+s6~cY#`95zl2y7~ zQTVwlKpCBR%fOv_htNo2)0{ni!%taipO9XF zWZ#+wz;q@&8j?KJ5b`Y6U6VDYW22)YHIKQjY$)btOeBqN6-&z27CO3_^-H(>BvL_| z>DXB*EQ(&sn_0LCQWfefEfnkwc9H{%HH{V%Q5%R)x>T^hkaQ_f zlY{UHOnDs`*9xIG^)@zf_Z*Z1Wo3(;8*Oe>sj7DulxG7M(O+Qlic30Z(m{yz z+Q=d~?i_>Fm3*kLw^EP4!y1`968igd??IRh_ME{g88{DMvk(5C|2r8xgdZ04GyTs3 z|L1DpyyoOmiAQqb?_L~f#zHt$qP~U?0Eg3pj{Tt4nYVR^<>ROfGdOOUW|>f`Hv05= zP$i=S-bR7CqB{n_nB8;N&FF_sK z+z?)H!Q!#*L|Kj`$On~NPyz;_8;Ziim>h%xP8#=-pqkBSD@Ok0 z##dONddR2@6uE~5c}jD-AAuYF}|Goi?5*g6Ah6NA{%EF}${*XSbA(x%0n zV06X{d(mW-8n-LA)ICjdAE;d>g2QB)`0Jal6G7F6Uz0UxMTXHT_>09V#q1QulqvnD8ow3LNfZTM!|t@d6Lj*_ln;^#l}N13{@tAX5*0scw#S92xRG=; z80ljd?(}Z%Ak6B*i71Q_a3MMSDbL9hJc^AH%m^p{%z=Dzr@W(4UQtOc zlY)M)n2g4+bPQ@Q?UhciUSxh_My7{iC(ajzneI>}K{FDwA)7QED^A%gywb8?$UbxU z=D5C&hfPA7!-kKGq*n3`7p9aBIc#d$g9bqPIJXz33|k7jIyH_x$^@q3OR$kh^i>O; z*_GCMf#oK7orSsZZ1D$zki90w1ljR1X-!mbcz7f(EC5yL%QAv7g6}7cOx!zH{!(~N zaWx#7I*ko^atPihCn1Kmr1+l~AAL*>hHG#fc?tYVzjQKH2Ek&Y!cd7DEM$^~Gtu*> z!GuKd289%8eG;OTOUx)|@x&n(tArAsg;6*Jt|Psx977-i{?PHzFq{~QRF3?HGv=XU zXXeaS1hgwt1fxqADvHo8u`OAV5yNMEpO{<_W?`$%%*;@svtARrxk;T!=Bq~4ik&*e z0FPyw%>XbZ6rOs2Ai<-J@+XIOj)00{HqpS#y%T|S`Y<>G425d|jXX}>4JQC75D{D$ zy?9gXIk#6)!t_(=y9e;sV6U{Z2PffRPh|ni?PW!`$*4D=cY}2x;!a7jq zH#*FdUQVo6(Wt~5724FNMC63$h2{oH5vgC!91~4A^?HYS5wm=*RV&&+58L?yz*mAf z;Wr@w)yT6NqM7|}9iWXs51mrZ+x%<%Lgh=NlUq%XFI_k=vy8EBJ&iw82mK_u!bM5@ zi8wGGn&=>~U&tdmt!XP8wn_UwSF=DESFxApwJr;Q!+^^mx+Ch0G=hi*XF0B>knQ`_ zpmaNP@T3q;IwWMtxq2`Zl za8Ssrj#DgKoR-t7UR5XNmfUk{GrUq67CrxY{%JTiEjX>8a$m)xui~-e$FTl~cuS!? zcEvyb(o+B9>ErL8e!pL+{{g=}>g#{(;@PDBN6s0(*B6%Jp%DBYcyvcG!ZtykihAw!I7 zn!l}O#f)57+=eAH@}A%t9}a7Syt&GOA4JovIx8+2vbid=j|diJw`EaY6LoXI4lOYa z0!XeOWT2BMuALL;`*It7xsATuMxnzxnB#6mt&qfn^{UUApSEaB{6FxgX<^ zyz~RSdtfL%9S)2n^LbVIt~bhOGL)+o9TFR=mLwOf72QY9TN&`7g2NUnxT|7pgLw3X zAGS4VQ<%8d-dzAIVPt1L6pi!cG6A`X4D3qo!l}v(vqGLA>-3H)`pU`Ie9y!{DPey3 z+u_px_3V|gL%0Iv!C(nwK;A5cdgojQmo}Kno`|R2%46SV z!uW9Y-jib!CzetXV>PkD{D*KMTtzc6_|P|rNVoEKfy)k6C3WSH8UfE>9lH9CV-||Y zDR!|Lu~62N`?A#ynw{V+(RFIxn}!{_D33&dzz6KTla< za(ST8;_V7_BwFrVY=!wwguliI+O;2~N?CY0QE$8eWEPWro?d)20eEJT^VIG)8A^mu zZX*Yhh09{eE{L{MSyU^;D&69g7S8AW#QA{ikdP1rg{28O=Mn8Myz5aBD;ariC~irC zM8p<+Ac8NzHSeeLwNy)QExD47w_juBv0~`e#KV0?a#c(m#8#)!VjF3gN2UlZq%EnK zfpansnPOi`i7}PiUg!sgq!NI|&;KpgIVqVevd{8(TSAkSw_}ncBpVVg8VaumB1$C+ zribvk+Pz$$X38t4IG|3j_9|N>GH4mETqKG**)b!I)x!!GFfDsipaCIhe*SAn4Lo-% z#LivKeAHkVyG$1tGVZOA$u#~0jQRogvg_YKchhhj-Z=-mXMa>Bz?19Fo=c!wto~Qmc$hQ?w z%?JL%AkGG#VwUba8B@CA`{?FRM2ZqG7xv2@S`(7ofdL^Nx_n?;h&%{2Z zp(hqb&8>;WsT;eknG^8oDMn8*3~;lcWMhjnMe=Y-;+C)`v!?7aXLgcSQ3wCemsZ%nUR<1> zBaKO;X%vvEj#P1;h|;`+^;~d`6YVqXCS8IIdtAIaN5L=z1hv!v0AXw4rq7+pU1QES zqcxwprw!)#N~B+CMtG%}%h1kg?#&V3g-NxYa#vDp6?TSn7i(FI)1_vIC=?Ug<4`nO zIugwXOv48C5iHqbeEduZY$IV6K11Yxh8|^Uc|vV8!wOglLEWCp5uc%BQ1P7`s!w-=E;E&! zfikcmtV;)|@j5Wcky~_JX=qseKc#QVsUF48e&C%Nl~S|KX&(jNHXu(p>f6*o#$s$6>p)?B4144-ZqP^I z(4eJ;l}~Ge&AlW5AS?%&ytbkc0d*ew^cL?tq-Y9dhHx zQAo*N63@#5^=8pD8No0kcj5jXg`;V-7{iD|0BShLAaM@VLo^KMD?rs)VjM6!gqEYL zbP;^VmOISQGSmQQs#Rionf~*(HJzz}&<|+iJy7Wu+NtESxm$9{E?VO#+56}A{<#%T z@1Of^@m8PSKX==@so%~m^is$aXm{WwLf=?T351c#x@#)@s`8=o#Z0k|Q7o)EOhGZ@ zn0B-DsxJNM1>M%C7j(Z3Vr&_~mLo%(o&F+C>nT7_0k$xOo&p#(-?>8^D_zXhNx&v; zTb0DdWMh!dqQGMZUi#PJ*&8i42ygnL+J*%Ese=R+rzYEDl1*}(~*XdcVj~Xfv2@qgukz1@A}3} zO^9uOd1t38#6AvQqupiB*NX{6$Fdig`GK+eCQ<`O$<)K`YF%| zr}2ikF+oFQVK7w=2xc`zkNZ3@1LuJN^dTmS6eX?lWC$3d2dNjj$c5}Feq=941Rvty z%x=%5RcRnc6u)QYCd{fg4>Oa}GMYIL63=s7cQ%AShQ4U|u*phlCI}cKADb6u(9&Ak z(F~d!ZB#kyW?nJ3Nlq@E1uP!j6(uyIyjaHc9$LLAYqFLe6?430l({2u(ag+SB{*h1 zwOn?UmQl&(WxPg9|I*|n#yrYn?Sbai0%a({|k)|ew*sRZN(8n3DZ?D}IsHqC`RBN5Mn z0uu_lAMv%jH1G(ps{XkaFjjVoo9apt8vtPHv(jFD-b&_ax=>9H>a4y}dMh0B=`yM1 z<#VSLc$V|mR_f}PQG_*GITrLZa8vXt{nWm$8@jpz7CPWvjXjR+Ts(Wb{g6~~ z+s@-lW4+fz(>DAaZwu0RmqbXQ@0BPY)j5A4#@|A~wi+9_8$_X5eo@P4^WF*n8&JH+ zr+32dZ;x-?3Ew7evT%2p_b^CQp?UXYrbJ-!;PAEDFK?AcT6%6Z2j`f#09tVBIJam;ws$ObhEOs z+F&--f(@#0DPWV4bF6J3tr9I&D0B zMVRF*MAI(r{cUQ4mwvAb?iqo49QBMq&j_?K0!delfRMm#d?SRWNu(}EG{2A~;_)|f zL(A$IPUYf=I3#6elxjnHmOt-HH!J06boXj?Y;CUKTmf9M*}st6I0w3k3~o?6T{Ug5 zp>Bf>d@xRZ6%I+zV?fu zrk^Zl+9FE5Hzq5jQXPnYm?~Y*=o5Ms!-`B`6lb`pK^@sQi_)A0d%C9N<$;{T*$iHG zQn}5fSZfKuqeJe*rrR23f!G}^dw8wnPzMOHHa z4KCpp*DZY1b(z^$N9{K!_w4wR%BI$edjkw?+a4Xo?zH}*%pvD3HMO!si!Pn!wA_I= zm<~3TPxltf^lp!fhhJq zj&Zb_V=`H|MpOSL7dqW#Uc5`#6kd#2vsak@ob{FdBHD2B&OAIl!Bx3ItpTtIy&T=k z(d$p4m!plE8`_e^v0R7YKjp5%M^zG7I3;-8&;5CbIHk9tbNb@7kiho(xR#zObU%fj zDi}5IJcv}g?G0LI)TT4L1|}ezwx05GO0%h=T4D(2?a|4xh;<=OF$?+ZEbSeRYX$1B z0uVP8k%bIp(fw9fYj>QF&csid#7;(VODn|9=DJ*~`WK@1h1dt&S?mDLF>AI3%(K)P zL-tO7p0#Q4$9=K%GK03<0SstOtnvc$iaVglPmuV5`f>S zw8!1VLAlH2Sxae_GW6W13Pc8vQARdyIOtqaZLF&) zPdo2#aLr%Pvd(UUwYPOKXc~7GW)4;D_BsCK1?gQzPKOm{b)34MhrpNd53pevVF8!F!5|HvJoNc(K*>Fzk(>;g`3Xc2vE$_iT z<_%e)N1@@IrT@1drq7;OEeQ1R+KOw-XRVau_*7OES44!)n8wSc+Ewmqbgw|GOmSmN zMDVdDB&At?gCVQH*Tzq3tPO|2d2eboMBduoUI-uFj;(MlOCc*VaV8`()N3)7(6E^% zb4S)dO;%8P8DyF0uqNg@noN8$Qq43=R=qW^*Uzj!gbUAbK&QjI1a1rCY$v?%bWzL}W6>&sqv@{~}~G)KJQ zD+3^8?e;9b&E?y`*W=W%Nwi9to!E0e&_Vkk9Yw_krrucSUJz2%eUBrU;mrhqQ35Q2 z4*MbS;+&4B=O23hVf`ue{DV<*V@vU-r3^Ky)sb?sPXDG8ti8$>f^agIfD|#OT5hL| z)njr}W<~T?%VnTJAmy^$r;rqQ?p7#WpU%%xH~RV6SNe2*-xfvw^y&O;1Vts<7_>Er zSuln&n7KaY)D%O+mKl6>iQ<}z_>zP{&42HHeczm-|-!3!|0P;YIQeBPxxg~-`(do z?1bX(D7)d8NM$Wh(`A-hlkBVc>^?7EJ6fXE38!I|)b_>={25YO-EgA^D2<%ADzm4b zNx{%al$H}Q_!k$a=XmzhXc`3=^a`(*ZVVVsIrD`tmJQj%WdKYg^-*1rl?-9xm_P`X z9;Ev5I4Ngx2dlUM;U~()g>ebn(oC=i2AF9BHsH!IovhJno28L0om!P7Q*zGPGKsXA zMhn&ONmH6^K#xCds#xPgXF@`^ogUT-SR=yOeM(qk7)?^a?ko+gSqfO=^snZrUpr2` z)5;IFrg}BHGk~t#y7BA;ZUH!+KuenwMCKDimis!X7X*nQM3@1co4~By+0ebj3->nm zQ%^dUtNWIF1kenx1XkJO_DKB25=a6pReUF{1f*^VqG&1ZlBpX)7g$ag_74ylNmVpp2zH@^h zx_vL^2++O1@!j`VG>6u8nM4uHwmzwj(ZSE_o8D+NicTvji<@#835c%mc}T4AC%#S;Z1Ij^COKNR_(MnZ`dCI^F& z^BNQNxDzyPhZ#7ye84halDV3+Wvxr%Dfq_OKutO_qr=&z6|B(i38Ose?x?@SKcniG zcS^(Oi4wa~t;Y(u0p|d@d4x@c$;=Dqeh9ze293fj3)EnY0?U9uldLW;{g1>JP?Z5Z z8+ws}EW1>s;(0J3OVNhoGdm?@If@5SjtKan(Z*QCqyIPH*nh)w-^gSXlCS9904WJ= zQ$L9Ux^+twCGp_UgQUtC1L#&kq|AKa@Bx?JOx5Gnjxbffp?bHD=fcG!$dcHqS~6a- zZM_@7(#j>4lu6$7PHAj6+{xOI8&eO<8WHye-!8Uu#vVH^=iOHPr_N%^tt=-=6C8OT zT_D-sXc{e^|L|ykf3W!$5=WtVIzm%_^T>44@5BKUlU66T@)&AY4Xm)|qrmSBZVm>- zp4>cwgDl6>MM-+o08l%(GIVa5$|X~SrYU!n0_`QrT9&Ej1aAg6vZXI`=j_qW}wr$(CZM$RJwr$%sJGR})m-pOz?mb`C z{j=8pRkf@3oX>d1OcBSP<*oDJjf5%nl0oPt>W4SYA zXmL)%dXgrlHAqf)&tX|=iZ#~BX}m(IP~&P&KQ1pqKQzIcoirF-);*wNL{;msPwBY0 z?w2(}+@i^H>UG_@XLEW3WC=@rsYH2eOE872r#7c`H`8hXn(4b;=v%&vA3enox}>GR zKK4}7+XQPprlFV#p%6gR9TDkV=(Z!V1YkOH1+k*%>AecEYFmSn5`#Rr>&=l_|D?V{ z<~p$H2!$9pe>O`|5jYav-4Ff)fa}4$LW&+-21n8SR9?UP!@GZBnb*u8NTB>~pYp-c zryb{WgN#IJmF1_*Co>Eh9-92Fzql!UNhAL4cCXXzY-iM%spYW3Zcnn96lscHZBJF? z>`YfdJ=4W2y&Bt^a>+X%pWeDQWwUR2CY{qunULN8mc+jD$%A=eA`2OST62;WBpb*5 z;8FMvu)cpA``F2BA#-}te=)9Uu|PMr?L5lG)=R8i$WBt= z!s>{uk~}S&^Uf|Ta+0lfvi4-opR^u{A@e{I^obxVu!yI3penzjLN61LS@(4TvrW() zqD?N25lkw+kPF-bcXvQgkgiSL-;BKnAx-rdT#XtsWwUTZXTT`%%&!hLXH9R0pA!uT z1sT~`Z7v}uK2R=>DnC=dW3szHk;S3{1)cb6tK*Li6#mziF@`NJ)WYBMFq-?~PTAoB zO(Efp0$v%?a}}1ZGK5LfwIt$d9L$^V_&tyZv)v=+*6Z|oFJb{`i>>@++9$f+{_)|* z{W^>I{C+nI>{+!9hxcVPv|0OD{&3)^VjD1V(=O$9@QW93ZJ|zVnQr=*U5>Ox;+@*r z4-?h4Kivpxo8^LsNQls@s*;A zVAgEu9(49*7C(t!gd1GF+BI)>LDADOC% zo;Oe_l0!pWQgfy(ifSTM%-%SVnC-45#&XDPSSw+fXyBrq^mk;m2dQ}`7u;N& zvB5C8O^8Ir-(rJz+L}Tw5l{I58q%++Z*MSRhFe?hQxRI(xr(jT7&)u_05M0)g8g8m z?=q-D;-(5IhVF=hp~ZjK`(l0As9C;*rzOm7AJXhpWOO2Lmh;uwjmp2XBCTbgxD|ch zL!iiaXZo_;18}NbshMn`ZtE8-(edy4$Xv8aAbJed0Gw$!P;7L->7;c?Q`>>%6T``= zA0gV<2)j6~7Ti$_6sXK0r8ofQoA@al9_m<8@7rUWcad z!c{bqibxP!@n>?o(L&U6Ga97Mh7xSbtVv3$1k}i9?zqZKr7`x~R%zisW1q%k6f~_B7LQoNH=Sm_SE4Y4&*rZhmTFJDLcV{KXl}+zqfJxk#`D*PCBE1s-qwWBZmKl%ZUs>eb3tD(1sevplVoYlXO!rpMER;tGe1`m>yxU1;bV$nz( zD)9y|eAZSABew1K2cYUu#Idh{QdgSN3P$p3f6oiK2n`_iT=@ zKPjeq4tl|&%CYg3sZ2zx2r&(_C6V@sET5?8X~Row=uPD*t6VPCJ?prKD-wB8*{MO~ zq9WAe@c^hL17`)#T=Cw%tb!^pN6WDhBIkeQl{ixb5cu!e=w0n!g#D)}Y!~VsOzL4Z zJ%58!( zar!4lLKJ`87e>%|1=cW`->;Q&!%{Wx$asRPg`ly}K@aAQNz`;!1=UFI6FZ2mcXxI5 zT`Trvxi?di*ns~#GIViY{}38>SH7_IlOClPH8g#Js19yJ971_bWOOX5N7)!jkyphU z#Nv#=y+h()H7(WMTfm`h1k6NW=!*19lTIin@T@Y+Gf!T|{WY>HeI4%P;$W3!QOZEk zoeNv%@9r;HCAT($?iTGYYfpw(BptjseT>9iQ1Z_Ku{MC_0jIa@Azh8*PSszm<~MXL zyNc@OsPb#X7cGu*7sYK3s%f&PVbq7%XtBZ?Z?C4|+{J>J`*gJ>V-)fdAk*#qktiL= z9-5+_yF*y_dc=GUdh@Y5%hYNwc#faCc^~2AbW1elVdV%qKwPG$=~uuWj0ACw$|Nj< zK#h|oBMkM5ABQkt`!n0iHBs8kU51($yR`Kh-0N-u`Ic0K`5copT8-e4kN9(7>ciQ{K3Q}*gh9ZUmE!pEm+rhKfL)sSXdxK9FSsH>Iht4@V zC({PrtVu?gaE4(4$t>?iQR!~6n%dJEv}u}vnEypqJ`3C|-)e1#aUX}!`J1%yA^6fW z_mefj{IhW=$=yNs+|20y$(oux#}qC8lQr=f%Y=j~>~~<(e^xMhh$rGoCe+u3Fo27g z3U_LFisXMiS_ZC^sv&!053Mn3(u;+%a{Uv@Sot4c+g2KQ%v17s{VDd_hxdD3-i0jJ zDJi)vdr}nwYWu5hOdqAtRq&)%vl)sktadcZ&GnON%!N)Od~bH=hwB*4-vyl#N`^>T zf|hv)eS1u*xH_XqzT)~KqfUYm;50@@Nu(R;Fv5WCPuv(355WWv(hT})f?V-j1GfV4@3MA+l24=#L zcRJf?^Zn6UQM;g$q~&fo(Yvy$>77}!5c-e?Fyx4(y`YJ!CB%>kFk{kb^WjwHiaTRj z{g3X)pa1H9U~aoJupsi`>b&Jsef?XAM7I_9v+f9(UP0GN(YLh71>Rba&zvsd($2Xs zwIFZRlKV;Gtg#M4C4<3Oh)gsa!e$&$XsS)BMw>b@Z|36#ho-QrBv}%kmKI>tIOPp2 zTLMRd1!F5`G~-?Q5TZ8)UArT)seVOQF!%81z8 zA49RU4Lazuk-vfZPO1p(9n7i-Uh=n_{$z3Pe$iGi(y1jw?-M#HLG<&#CV#THn{1Iu z6B*o8B#4;y>f|1DM3Rpy?yO$X;g1%9;mScTch7}>RP)PS^FlYoNzu@M%_wlA8$fWj z0_q1#lbQF!rQvj{zA5TTkDY;f0XQ;S<87!S4np^M;_b5Ksl-{0IO?fyNRU3EO*@ah zUnri7y$TOAg_uvo+tC@*Ne>E1Zc5K#H^2XSk@?ASA}HsG3CtN6Yg(KFmbkZ)@)0SP zqys+FGeGwRasP;K!XY6-b3x*&uArc|M=~xJBM?1;LgYg!BR7MgW!qUWMlQ@rg+b7C z-abW7X4)biOHbL~hIgP!M#=ZU3`c zO2%3~2u>8ImbpKKO>G)b;M=jU8#-9Rh}8EA zRmM2sFmCrXVYXbV1kLhH_Cl&y=}B#M(bG?|nHIbjqaIPL; zsw(gIA>4mgchzLqb@Nwf*YFQ`5aj{Q`YBPLu1@(_ELs`bly3{T4Pgt=4lJRMBPI?T=`R@n42Jwxpn~nJ0n?6v(rIJEl(K$q z*IQw_!ZY>Btm^0nTa)s)`oCeOCbx`ZPsXs38KAs*N?r5&rHS&7XHRNYVdC9QZV!(C^~w=iraDKFDaXO_%lp=Th(81Q^Vh>8N@JTfNkOlUaS8By1gPxq8I| z%ee4;hq4fv$mj*~+fBP}nFPxS0k7|8v-D6Ixg3;o5j-q+C~Gt%=Q9hWmV|Q|Z+x@f zYmsbtim0&cX}TVZRt^HtpDkXOG6j$=Ry>uRP@8GfL)FHF9&g~t?e)J7CkEaSFNRRH zIbbePSTQ+9vCftc$PXEI0)!3$&vJf-rs^DT<1JX*ZFiRU%mh$q&4_y#AHBRU!~tO(PcmXCNHi1r@$Zf-c54PFW2T z9I+)mfiALhzrD&(VBkWUFAVEE<4DLm}}-twh%-2nn%Ne~E8A$Zim}v|SHrC2~n>=3Je) z8XwVk({fdAu~*c!^2*>Kb4xmC5-L69UA90kt7mY;WXH!&tX~@1WoZ4KCaO4u6$#4} z0?BV2&PgKw6GyMgv@bf@@w});7v^|68u1}!b#y6ye&WO*%{{Ga=6X*Le z?CS5_lHuUD^gWL7qGI4lA#IX3T~*xtc_G6_C(A=DxqILh&Li?w~tOV zX#B(fIK#VaDR%%l@sze?rCWja;350O6YoNvmBUly0Qhh?#OjC?FvhfUD3KMv43Wgb z4LA?D0$0N$=v{cHn@Dp#uV5`G>v-2G?e8Cq!IPF{=nHuTdDaI1vB+av>oW#DZ^_)p z8vut!qhjPXYYR{m>|N-Dmh{i--2b8ge+(}?DMBBqVymrNKrbk07hE7yt^>R!ljLIr2C(3~FH z3}!Z3XEZ0(>2%IUYbIu;PKXqK7soE|cSs(7{*jcNk>2Ru>-s@=AKEN+eLRAz2p3_; z94H-@4Yz-?NKO&6#y0S_b5(w!75tIBlfts8fXzF6ie8%I4I=bgLr|)1cemnb^((ON z>rdxIvt4?Y<7Nv@<-cx2?P3|j>6H1B+8l37fU9;#vubpx=c-fjG(T%Ex zYzw4i!9PR7h3^=q?_9;Fjb7$L#wtI}J-_C3J&C}WV_s+{s4o0s#S~l#akS#up)-1> zXT^jE<;M}LRW0_><5#K+*r#_j*=nh@u-Vdl9`hWW%RcE_qQY z`MIz(uowa*Ba*=tkD z`=R=`<%_G1$fE_Zgy3(-Tx_R{FB$XbgS9pZZdp@ijP7)ijtlf-#7-aa?#&qfvQIW` zq|v7h!2VBQj|W^>^D?|JiS_9Ci$0I#48bi9JhXlV09{K3MW)1dYpc`vQ{A0+N7hgU zsM4dGKPkfsuNBItoiK1EBm`^~?*cw9B?p$MA!&cwayzK>iEIsznWU=IGHt&_QyEVH zy5M?fN-bXB!Lk_jJHxFoOBjs|qRm#&Y+z`gh%wCRL{<j^Zeq?!%x`^3YT=|qTF!=P7N^ov54&@Z(vkN* zn1a9HU0ZNuk4ckA=K^9?bL1Dvr3?5UV37F^OIXlAqYaYl8{8KTA5EAQq>j(N(r4)A zwt{@a*%sZbzfQV?}vA-Y`=K~hd-fPFE8rV$<)1| zQ}dQ^T1t3EWuQV%lpxj0jq%9^7E(VBlb2jBz$F0uKD>30B5KJg5iWdvzbMhY8vRX1 zkjLkm)y6EO27CUPRvydO;I!42d-MZK_o8}q847}}g*dV2A24n!lQ+qX$+B$7*bZHl z_6)5?6@)_2_qt%_i*kvCL=S|%y;Wvvzv;zIV$RZAJaMsgk8B^^Q#ZhRaUb%k5HPxH z2tzQtC~KMR8|-`XXsLS<~R_T6Yjz0w{(-< zVgv-+ZPUFNpGDshgGyvmviU`(RYSvh~;pB0{7?9R3PC~;Q@)+m_pJ&R%7bcua;b5p!qne!P#Q_V3b73rcVdk5rvTJ8I{hmT9d?#e|7rBj{k zs0?gQeyqx!yR$ze;hQfHL{g^jLf)PWOV5u*fIEcRO~pIXQ8UTE@~CvnF` z^Ck+%2g~h+XLLx^RbdMBZI4x%^DY=(p3G1ud9F^JF$ZRG%{uO9DpuJAxW$FM6O87G zE9zn0?u5qoxXotycG>OgGYGjk&aJ#WXV(&LXBxScQ{W&e?UEr{{Th~FilyOC+9vTi zKg32U$+m2MDT(NG=d{dKXEjH~y(+Pr!f==abHIo<)Llu~5?3-L|8KoN*JZ@Qt5`sYCEc2ROTRr)A~U6B?&~$b{B|}P zBTuv^JBZxaS7nF0)^z`bl(q`AKUmDd-83!WNW!DDV3y0wW?fO=)=PTp@->mTSOBnDw?CsL!bKycK+eL)Bo>0>zONT(7-v(;bGRO0x`#Z)_jr7Tc&mycTMdw&P zD*wGG#F7B{(;z1vC6^Omnh_7DmYI;2CV1vc`0y8W#sv-hDoz9>IKGa6i>?sEN#Vp1 z;XDNYKgcn#9Iqq#^^JnKL;Zg?Ccu`*3k;?K9_{yLJ8QY!s}LTsLcPDz!W<(>{=*%o z*+0KNE>)M>^0;hLPPCLW6XZYKvT`_}XEC|MvAq8KW+EBx`z^dp7?(vD%WNDEvnX5M z)i;xTVrT(Z5TD3pAe&_ATkZ_xO_w*QJ=@IpKsS7_|9Ltzvcu?Kcv@1<)h#PJV}(*? zrhER*kF8Ls5YMjf9C8P`NUn<%P;`cThC>Wwl$V=b$$W|p;Qb&SVsx#$fE0qTQ~x+< z;0(}`RsmAPDe*5S9{Yvokg2{^GlhxPD z(Ywi|P*wGjq&8Bw7VluQn7tb{2(=0$1cai)2SkJDnJ#1+Q0-sShuED&2*5W#WU{>n zUTm`-EMD43NF?)wrbsUquEQ+W#(aCDRILG4!&I!G=9z#NWquSIZ+j-pU4|F~w&ssG z`<+sX;GD%vz!db7cMm?|JGq9ep;q>);BYygpFz7|LU2-&RZt9UeigZ}b^F&x`<)@1 ze=nlIzU54IY{W^?k1%l0VsF+&$v9H#vIYz?5^cv=fIqd;85LcT825588>gl&D&Z_} z63fFkjn~VtIrs`3xSk>E6vYW$%5gb?40E2g;G16`kf2In2CEW$N#f>@1S-0Z5iW%f?!$ zr$h<;H&X-v^pA#`&i|~py)?|+Hy>weM2q*AvjgtZxAww9tnEeOnUxS1|B1|)5TAiY$3Sp~^T7VfQQmVE#RdzagVsp0VCOHP|wW z0qT)|MiNeQ{35s9wZ0`TXdVf1l@}h%5IcZ!nJx}581A#s-Y`e8{4C$&=im<+_x7vs zcw|4->J;^&OjT@RnDbxef3p3;B`7$Z>UwP;cUAOeym9dnUH zgJx&*9^lSJ`S>EG!;;#Jc+BPETLS=EG==#+PEvsdNH(w=SR^>i|39W^ei_t(Q+Hxx zU1~L!#b>qh@8Dv!7nORYD;*K%EQu-?7oSp91&Fu7 z^otGe;z8e0w<5Sgx#wi!_X)(G$UB+X@(04SC^S`iDOF)_B)9ShzZPhTl~3d2bOtPG zDQsf1-I|G8xC}<;JlP#~!3A;Un~WFfo0CKkM1Z~jUz&&*UP>|9yDq!zod0s45vU~o zy^MBDGL+uIKV4a<=sn-KIVW+epOTjUHQaF=tvyT60p<-z*3zUfPE>KPmYq-Ja07l+)RHX%7+?_?VcxT~nQ)cpd#+ zv27+cjWc3{(fsd~wdK;Bt11}pNNU;J?X{dL27dxOXs9!PKLE7Z1Hwk$Yrld89MjR9 zyGuam>CAS{M3bQ`*$Za@ovquzAA0)-TOt`csNubgo9p8IoIUE1JqH&c8bhlp!@x7W znhCuo)t&t*{d+Fh>1@wz5%0~>=I@nyvV9psePE?f_9U``Z_Emy&D8$m$HbCsJJjpi*%;@XEE`P2053V%K{72HCnD3yBR%1U9ERI1m3d-P+iw&}1 z^TjX*X!`?u*JpO$qWWS%b$0BJ?qRc%9rMBn=Hn7UVbq1BIsx+YyVR^qIZiY`b?so} zZ&gdo4|nW*F&r6X+Di1DR|gbY{dSADIaB0eiX3u`aSu3~PeN-5^}x<}d5@6setzNA zc4X#|A5f{?D(E_8h4c=^`>%CgwAEwF*>5W6fPrAS4Bm6PyVad$jdW3T`RS6$aK>A&p3I zw?7rAIV#$At|U-2D5%^WX_r2G*Ak_;^=02&}t; z2jt9hVy91bc+3faA1+=9iKzHQE#QQ%OlW&Ud(Ndf$MgdVP+1G@>H79-xFqV?WI762$6E0Q+ zlZ24T#=_`qS$muLf~j_T!kHgH!5j%UUXB+eJ1H2t)DSgHy0LTpUKEC`jnU{Z8-^Ah zRf4>&Kvsj^9ttUqM2eOeT+3N{90yax>QI4JEkDj2siX0=M^KG)&rpy^cFy2B$bZ$9 z7S0=Q`l`V6xHkbwB}7eOcp*PpMvsTaLi@tr6`ptGnny3dV|`

;qgpJ=3ODcBtr5@G$W%|!K^y#z)InGN7Q97=u`v~ zmm?~Pl2v}c?LRcrBsLwR5c7X%CfJ7vis_yonu-3uX{Pu8Ni&Hk4`2>@{8|6Kjxhx- ztCNZAnTCayD1w@OYuqs8Ipo&Ye}G=N_Pw3Cez-}iBfC!T99f%c)ahnW>FJ5y0$A%6 z?m0+oue;)>$tWD(oR2=Rzfh=99A_|@??!Om_dQ;aX9xC5t7G{uj43Re`t_~sYe%W| ze_~8RFcpOOc*2Vp5|8tNZ@$AkEBkFGt2vXl!Hw9oS_GXZRhv0q6AVLAen;L%YO=E$ zFIg^f$DWKY$%*Y(jw{i%z-)DtYGkMRFpqW6frfod+3dsCFsxcpLYQX`1T>}JV;=SD zC2pr*4YL62dP7u+9rv}05KCEixqRdkJPv$i#j+HMJU2SHz_TcQ7~KwEmw*>m&j09d z?&w`2%S}G~v`-c{a<+2;TTPbh^v#*=21ZLaV+PrE%3N5UxwSvY6n&9W-wH?bj0E<4 z@bw|)NBs$ffk~X&B9Y8?G1nbvuPxNY9_PaQQJ&V15J(Ors_%~78=#QN@I>Nwu0d*G zt4+YoeORjuWftTo`k{l{CgF+LX(0Hw5V1bzaqM42*23?r#HW!-*Nt|51&5tm7Gkgb zOy;+7Y*>YE(jTzu8P)oye1C`l9@SiH<#QZm8$|i@KWH=}_l=#`Z9&~bb)Uu`CiBCF zMWGp~(F1ym%kR_UCYCQ$+*<5rPKE+g#;A>2e|6Fv40G)HB=_Z%`H+YNsI`vp#BG8= zrWY;ivQgG-C;DbP;uIVXi{@S*>$Q)hF0}}PmP~wcH5`NEB$kgJk4}A>+icHAngLam z9!0Zc{)iH?ZoLl^_;s)W-*-f1pTCfauCK30VTAGDGf| zOV%c}T}&tY*J&yHz!d7t`akFty(8?*4+aI2%mKnJ8p$Le5p#E5Y<00SAfc4uwc zPBgqfQk!_;6VqWneK=XQYhLgIbh)PyD+QX1 zF_5aOX)6frOKBQ<&>t-Z&iABBgsC9QQr*pOk;bfAw8-gxsNL@&PMPtry7sZ2S>Ip= zk~-N)MkyPCiTpFz&-*gP>KOppq{thf$K z)L}4cO)?{USProuks;pCYGv~5qDq>~G&iE3plgQw67GZyRFp}CUH?Pg2iiXUL3Eha zoH!sYwgF>^9lx&gYEf|FotH#f)+hLMh;&qnxm?Nv;)sGn*x72MOe~s6STKvT{$8#5 zV^Z0T)ROee5T4^)H0mC|qwJ`bZSi>Be8b|*2u)}q#gH74el4maR5NX@sfI8p;BS>I z-mxYr8^72Jp%`TMd2P*L-J;iNYp!VL;dvcF%+D?c-F4JHO*PeAvymLo6wXqIUbMuGcazX%p~z*PY~!|Xf|A#KhurQbadLkM%z^K%xnF^MN?=# zSFd)XjASn6OaC`s1=P%!-fmh=#w3x1HTAB(bK;Z^r{G4!u`3%6(`IdgLmvdMiC2K( zqwLZ{eAgv?S)<64jnk7NNv4b#A8LO2KhTQe6#^Y)CC<8>z_HR&0v_=7DK87%M8?{< zT(C@3W`r$t#Qqp;T^T_BNcze$<#gwp7}Y|9)3U-+veLZ54N|fn%nQ~k`e4~|1;5?> zl(2xrDHk=N)&1^%_{+%m&TH>>>GBud&V!ZIV6S_|)%Pynrr&*;{Xnt-AQjD_RBCfjZT*7@@zMC<^z>zb<MnIu*gHiRc2{i(W-KMr@wmpRpU1OzLrhbs#(zfh* z+=AAYmzC$8jh^=A?tSJ2qXb`qBI|tbvxh4i?X$`~+5jimXh$G(k5w{V&kK*LyjKyr zV;SeejZUdkuwu^*wNLY5Hf-w}xV3di_7mW31jxBlzro$_uR5#^ON9;{C=@T4IAE+~ z3mPS^MB*}zW1zo7y}nA%=1eLo0E=v?AKimb-u)W>1D__An(+0lw?B|cuIphdwiwfr zR#U}}qdb(BAmyXof{*Mjlox!*O#Xu@*=@K>J+NXyOy&HALy;qWIjCZ0@c29E&sGb_ z@68_J%|ijIn2~eB6AwZJA*z({Mc|0K17dqfk#Fe7JGsNpGE%ibC*xHg#E&duW~oKa zPN`LeYd^p?HfukNy-lR}A@7#`eSwP+s7z?JN)qSm6P%j?P z^sly+`ue-a656eqWV~&-)GKh8iE&>O!r1DAu5~kW#Tb=;Y85v~{otc|qY+Uw3Cz4U znUTEUb2*{`eAV5uD9s*T*el1>lhgmm*=jOex}fL!@FjnFIaGzbVPY2B(>mne41GNA zbE1c>@SM`2RiadTiDKUgx2Wnz63N{Dnv>f%q}*;^UW@KyQAug!B7BFiyl3019!f7Z zyPO<=M^*0^oxW|;&%u{?niI$GXcUxcoxAUcP=L0E zh+Ab>Nph=~T^a5J4j4UIJkPh7mBD*UJl#Axieq(d4dSHKs7oaRd;~ps#-acG91>hW z=&FC2&#V~|P!lpXgsVd@S}v&cCBe>neR}a@eha}cXp-I6;QQv@Mw{zG8!Yo7A3ZWk zUUo$BlDAoqCDM-=83`>}`9R9OZv1U96B~~1&2Wpwve;^h90vi+0rOJGYEasabu@r5 zjh$&Gn>hiZa|PL2iEV@tXS}|I2JIVVwRUTyAqI}Wh()lzSXEXDGPc)s#MLxvduOCO zJOa(c%8q#tptrP;7a4{&nVNq3(Bt{{Wrz1tx$&)ohGZ!%XOgpTNi(_yxLNF~DJ=ko zSqec<89&N^KORD;H}$OS*(0+pZKurZNW>K!#vtZ16e+c5K%8%UQ@4iKtSO&g7Zr#K zng8hM5#?}D^WfH|VhO95No!}`~1~dJ(|F~`7!G& z`_-6!gNlnTlIe~!E`dm#^1%L^4i8e1BSn_`@BC@Lqrj<-^(20_D7moK%n6*fD7Lhy z?^EVIT8ePPdUYO%@;;qEH{_LJN7|MLqp1{F2z9D6eU?1i$| zdsIdAy5S}`Ho z%$f9VeQQ~uWX>flD$OXvRB=<}OY1(0w3N}R!n4Hh>?5mg$e4Fm^jmg)~LqDZ^>YHZlGc`I( zi{)&T*U?S!EfW)>V^7rek3%V}h`m4_ZpZ=x)W$c{^97Wj-0MY-t1QQCxaoS2AIBz+)TPJ}oy%Wkh@j~v=&EUR7Px;7l~zz@ zvz+rtBN)^)Dl8mK>E|W)NY}NSYqhb6l^o#(sfRUwgzNiAV8&)M%^40?XzAseO-gDj zR^zR?;Q>upokYKt?1`8tgGbhLwvQNZO~4!bx*rTFQrHn_11?JYWSy}+)wwcN+3Xh+ zEnI1_HN6mDZ?8$ZunHSNomzXpdDf`CM7z za7A32J6db)7=Fa7;mR8(@7k)evh9L-lzPOB^!m>sT$de!dKl_1&yD*7e~|f%#mG6U z6$5~TkoHB5B5?o-W{M1#o}^AEY*ix3${<<&8aTVN4wZoz$B#?uunD*^+SGxNim)L4 z1W(7g8>MXDdyAQVo)2<%UuV;NR)H_;>3;9V@;;1ZyqHUDsjv~TCP$;t;i*`RRMTC3 zi&{AzGR@AkA8_ zT4y;R?EzM){ylaLV4p`WtNwWw0|wYJ*kn7U_!o<@PiLc+CHV`nJeL1Vt?=mB3$JFt zvFeGQPL6Z07Q%vZB^ahgXnU<_AX{ixnE0kfzlUsT!ej4fwtdde4aV>-lJ@2=AJP( zf8IVLExblrx%F4bCe{~`%P+&{A7);14wccxdk~i}S-E$`URTlATOxJ+Y|r~1&xcT7 zFMA)QAMbv59v$DW^Lp?(+#c`dKbK}lWAkx8|9*`6IXrV^nhZ7$+=7k1!Q3*d*;`s} zHg|su=7LN=3h|5m*PQJB%rYx!%;V4)e7QoqFb>@Ocj8IxaEsz5*+_W}xz$y!6+FrL z{MF@tczWKtI=x73jXwGd;^Q~@UT@a#o^Z_jpRpLc9m!eqFdTEH+dkO(()j2X6CEJ! zD9ZL(?ANbCvKJDlk?x$R3|yg`@g?U6c1@biE%D(w!xr^@AG~3#Qm!Jq1^lRy+@sup z+@jvHT-|=X#|K9B{GLdGqsR-&h()$_F~=Z)JxyWQm(!aPWAyF2$_q#XEnB;m^_tDP zN3gEbNRq&xiZ6Fuk^Bj_U$KfzN>6Uj#o3GQQ#E6PI$(Gk>a3R_9)qP)G-c^8P# zy4b#J$0LFk8c%zq`xz5(|=XzR0ve`x1cqx7Bt_Wqm#tpuCp5ob2kM8AZ z*6gKe-0#8SQgKKw0+H`RB5?YCx1ad+8~p8N{zb0$=BQVPyZbYB-0!Ws$4Y|!+eHT8 zoetAMB_8&cO3G1BU#uBkfABZ`Qr)xNiGq<&D%|ps8g75Pd~lBg41Y3g07}nP+exkO z2BMiBn;}YE?G4f<@bn8gIAaw!NBzA}1+ht}TZD*ti;( z48z^k18S7HAr}kux;h_P7^IZC<*z3!_0(d!DUr~Q&f@aOEG$g#_-s*?duBcXZp92Z-MXK#OVbUx>7_> zT7Ekj)#4)OmLt(-fUpAayc&S!|D5HVp(v+ZSno{_^#~I9ffvn;b4T6o_WhF>MN2O* zoN$O1>NqiAc<`HaQEHV_|EjW2I)lK4*io&PnR@{I*;ulogsoAQa?=E9Pd4vwzA ze-wts`n~`yrNl2}F;q>FmbR4s(>fr#+(t7gYkcF+t9+rd!z%)KS6>yl^4k+Oi%PrO z@_J0Ed#dPqg^w;adRS-{c3zWgGWu|nhc?|XC3jB^;TR{2?-MB@C{TuA@v9YzdCg2b z%&2}N`Bc8Dji*olx_f&4dJ;-yzI##Hds***t?qtmOpoh{fj<{())U3Lzx>eUYQJY6 zza49DG#}`CE6+aVo^ly&dOzEXPSo;NEnl7R$DKbmX1P|+C)ZEky~$h$F;{5gZXCzk zIB30wJv8!VVE5wWLh%z|*&M&ns>NS&%{_oY>9R`Iyk;IJhJ&-_bdw;Deq*zl*i@!I zLIms(%wdCZx)LUqYPZJ&n;bXN-_>kk<3}>r?R8Mk1vOkK6cz(tQ0I6^&Du;+A8U6A zm_Yx-nG+|3Zv;M54+s`aK>v!u9x+N37F9XNEk1XIn)X(|k!+UUafr-7ZBpL1WVnNL z{dd11lp$z;N&i8I18+3bm_N|P_Ck%*;RNx{jqIrm5*5Q`^i^T1@!1`A$ zxegL0D8qm^+Tf^cx-N9ScG4u9t6lKbV~@|Cqy&YxiALBua!kem8Fl^bZOS_W-?;FKY-!ofbM@jaDiG-0e zm`x)rIVmbPRf}@8 zSkNnG`7Qh8y%GRKB*}Jc;)6>$2))&c)?cD27PltRR_cvZ`n(`#zLib95uO543M&~$ zK_@f*jjOSDK|x(9_ItdMc)2a-)NU!~CQo(|&C_HTG%W{0)&XyO>Sw zmuPQA7~0_{e@I&0o_%uq^k#ojlsH5BmVgk;DDT%#cXetRd5{o||2{4p>i^MRRd)Vy85jaj3QT3KNBFxIE8<5c z8d^XtxyeyghN0p|7OkX|XH7oL3s`Ts)sjcKPL##Sloq5k;R7)^Ut@R&WjggKzC2@-#}~+n~!u`!L!#; z7+BI$nZfMS3+i5g!psT|;z?d%asw8)2zy^L-rq0c_|8w%qj)fA z=q4}0MALp>v4pIoawI^gRspsa;x98F_TIyUQS5xn4!q<&4zpInA580uU4AZt7ADP& zEmMx|x3ZOcEuk4KDw5h48HHbi{(ro^Q*@;b+in@NV%xTDyJFi(#a6|(ZQB*wwr$%L zRIhr!@5lbf-rb}7ppSB%ku_GH=f38g2n?_uQkS4#yT0bYTpa=5)jqM7 zTAP2^2E9qfXaVsD{?c68faehKQM#j}K&#a>ZSKaH?Wh~Jl1Qg-M9TBIzn@|A@c7l4 zj5C+z(`@K0XS;|VIpJzUfBYLvq(i1O1%t;XwC%|>^BLoUm~)st?MlOtmQMUwsXCo* zSlEEAX~fwe{)|^|ZIIsBEd7kOwQZZ<_%=KR0!@|BIX|@VDi4HJ4Luu4`B&BY6W2v` z@Myn)8M{NLgJdI}MRw-7PMs1t43>uRxJnECK(<{*RA$Fm@RMCtVO7~5KVrC)C&_)-hHDcJTTrcne()c464}I}S0{gpYX1tP)3H0w?t8d5M?oC2_Q^$hBjLCt-TUn^@ZL6FZM++-Gnk(Q&s^kMcJ} zg?Z;`m3xRvWmAEOk;q#t#wO%WK|02%YDpOqmSm#z)_nhGnI%?sU_TP!tTd_*W2=CV zPwe2fs$}UO_*o+D*lSkrh*Cxsnc?8rShyYH4n3!PQ(4yk!x!I}4I4zj?9%eLE8K*nQrIW3K(2gx| z`EafIR;Co$(#5C;ch6`*-gMm&^?B9z(^WAWTe}2A9TFycSI0kwHV{Z(ZjL%RCYQis zt5yL8i=rh*{;e~@CIUMIuP%8Na2}6Yyx=52QdvlPt500j%?>lu6_IztW$1zQN^3KhyMsgAVA2m<#ZJ{ z7dHlVb;#$3f#`#`-I5=h?(GOE>0XVC-VT~mG-w>#dQ*mI)PhZO3mz;yELJPbDEs0Z zZbXzGgZj>$K4tPvH5(+=yz&o&AjbzpU!%YLKZ}N`O3tpS#4n<}A`C+cbBw&BQ3vYE z6&uUn7hqV1F*okz^*iwkRe!IcaJnu)B{NBcP6t6KvIW^O;s8slq&Fu-(@mp}4UmOv z_h(FIG)!czNok4UeF*4l8UFEfaPW*_C66vq6s+J@8uB1+8^EhN=?^E-xx8e-_15`s zrs1Vm)(MG_H`(j?=W$&d7s#yz2q$l{c?MRJ&iP4Giy153bfRw9#Pf`1B~>u4dEK-a z^w$1H5!49iD+d)WI=t{>&=SMI-;9lyYr5sidn}?~=BJ7&DHM<46IZ&2r&waJ$Fp+s z)Y*NjxEC6r_LFAtBvrII1`TVr{VVyJ`hmciR9nPtEo!)(G+ zBZ2!N{niu4KIQE%V2^%i9a%h+U#NiK7Qp~MR}{`85+|!gx#7`524NFuwPY@U1!)x3 zDoL)Ib_n|aK`YEDp>6v1dfA+{v0m^{Z74Zucj8LS(uCo>-;C4l#PGip3#TdvcO=i# z$o9=Fj`&egr0X{35&^=DADkmAo4{QDJcq-vg>em5IVTMTPQjS^)upE`NjRA9af-<{jMM1BWDEctP|Do*%svVLpKb^e(Y(|v6%zXZGuLKB&J8ZTV?M~1=sZ3y+cfn0RjId*ElbE!u?K7ciFC zX?xqG8mvq#qYbJ@t=6Dw;$o7RLk*J?tkw8bJZ}~GQTS%0213`ne2Y#Lh`-Ozr{Om7 z(H1RhR93UV5&av%u7vXa7lNIE_!q&JR}J%_^m1@ZcXCY)fbb{iyGyaX{fl7BUUEF8 z+6sPZ`FzN(0m&1%&Qk}Zh@tm4Ue=7Og^4toSOjJezM8G50|0EzQmc*Bv2G1ri;^D` z&zk7gb^?#1B(YzRp57vDKj~@J zc1cZFsA5LKps-!Q)JRRF;?UK;dmU}lzf(cCIt%ebZfwJ*8wb+g#q40qk#aTF)#-*k zYW!3@^(E8ek=%`Q+FG8}X~vEV+=X+Z^uMWvK@X_v+09eiLgkuF9kDO02I#jg))$g8 zzh-8?>&vNA_MM*hULJj+wpB4nv@5Zlz_IG7La#oJ`2EO$^$;h{0S6~Xbr#sScC*|? zsuNeVlQF2eMCEu&Zd(THT{JK5lI}>JTyW0Mz*X6tyTG4+b538pzi(59;hlK1rg3?i zpzL?Nz=ie1Zl(I2^wUaz1x{BN7#8ogW*A?>l|H9y>q|x@W^ZBR3gGxCuHUvUu*CU_ zX#uzF{0GC8PKrIx5BW5CAu_Nuf2cH88j=X`3aiwPC{YCAd_Nprmql5mC;Dx{RjqOplJ3dGVk0C5tZExmCwyPw2+FxKuTfa8q@Vy4p%a-I~gAnMs zT-FQP9?N_sMi0P-ny%%7nBvJq7#ghX{&~=hBH~BRVRc-5B`JM zF{T-=NVOnXCV79|&0x%OG-|f}=hvVtpZ9%Ywj<_-Zvm~p_10@^Csboru*LdNSC5>{ z?fi{uA0Ns0*uos7Q!IZqal*3*Bt+!aXuF_U*6_unuw#q{QQj|cTMJFRfpQ&Oq|l|nCdCCEE+!=_IC_NoD)Hg_&zwR(#n!6wndvde}t0A7Dw6+ zN3j?v*C)P!uO#AtKZDw<$>rpyKTjMpvez9B-8fnlAHh{T%?i!gyS2;)#>$iwXtc~X z0bj|uPfh#QMQG?;ZO+=+Xg8w$fCX;2&|*Y0zVT;gWLJ;G%}svJ_D*5xAYS3w!n+qH zhnyVkd>dO4h#D?jKWah6u6t;a*M3_)My(~+&D(lTZS|ny%*s!u#mB@WS#8nsmtm7& zl?u>11m)rjEFa-oBaL|IsQqsoY-J?xW{Me&1_~=loxE{QoSA(S^3*NqsMd7A?#`px zX}C^j%lT^Usx;r$nX3$uRJ@~Jsx_Gw=KI{2Gxq44dTWNSPK$_7eKaQH;YzSZ%rY{?h zmW!;|H})6DQMV=OD`+0Ig2?M5+uUytj%}xBc|R}vJU-09(^pzs)N-)%^S9Y3EL_cA z)=%O(>?|%(bG4W&UdT$}xrJEhE4e@Whg5X9yRDRs7;E6Vs;_grh(w=d*2kuhX#)*_ z6aJ?~B9h(%LM6???Q!W@kN;g$s%cMbIOTl8_+XN7hN*~#s z0snZRBd_WatjzxxsR$Ww)zT$&5w-4Yw$ck`vaqbICvVBFsv`4kual3P3%#CxkjjiG zH@moNz@b|3Qx#}Ggt#FtpSFDm#`-GeVJ`iZDsgoT%GNTtbxkMLTJ?|cVmIQuA5SD+ zFPGWWFF%hIZsXDIv6)78xUW`*6g{)q6dpg|>TuOUm=iNp5*7&OioI-HePG0DW#B{cNPE9T_!83Q*C;@rDaX`1saaW#34QQ{Iu;5 zmNq-fdP($|w@eJoNP4lI;*Cy6EchUDSo@Ig8x7+x3VR~^aVhqR^}W-B*?PwKa;t5y zq^D7E{g?Qj>%49dAOS*|?F0lg=)e*-fXzb&w!Fv;Ja?kE;|4C3m&1N>(l7VsL$4|b zJP^oinu=%_wj;}vPq-S*Nkw>w!&eASDW+&$d_1GZi3mZmRG9KT;jau`q@Yg1ab+`U z8eVP|1a}2h_b_m@y{qp-NfQRbsr~^|gQao)=VEnwVA=qP7Rqd~(+TMt15==x><%1u zM*S1Jn8}ZUdd{zkNh3fw10Y@B&*es$VqVK3C>9XsHWLrc5_=?qgH*l)b0&siCK?jEFJ%5WsvCNkPu&O zKAsO>EiS6mj?I!{b2S-CcCQjD88`vxPeBz7y_Dw%>N=O0fBY!Yk)oeGOU!BUVu(*( z{7mZ@Bx}LXpNeKM6qB;QP8obIoN zj=}xZkPK5qmGVHFIh@KNtqkS}(0iO1l#mndYouwh7Oj{YEbJvh%0&nNTI4`^aPwJ% zRmef6&NvR^@HPbss@lpKYH9PvdG*Y~W)D}RYWxotsIUyc0^z`J!3tNj-#CD=03L}M zrTxTa4F$mTvKHIBwqS}c7Tz@f!a(oRxzjfr1*5tA*}2hqWuiEM zwLcT=@OKCnH%l`4?f`i408(*3BKeWm0UOkr-Dm>q-!!D3xYr zghczL9jS0`=P4!s!hm3Ue?Z5d8X4~{OfcFgFbu3tAK`_Gy2QghuhqHt=#6h;!)!iY*$5bzyAeh!!Ii|sT=IJJ}!Srd6slFyZgYKa{?|ATEg{pyy~kRWu)f{uFCZdaiDUZ+R2=`HX8Qcd zLWPMPKJQ2j@n?Vk5qIV%wp<}lpx)lnd^GR?m3jB=Py>-y>o-5kBK`{4h6Ey6TokDM z`9Ku`e2OF4YXdY4!Vl55Rz`Y5?F{JWULY2mUI-nml0-Hwn?}}#V&%#N6X|I#ZSr{b z1&e%aeoN^@xYN5*IW%kUQ(4K=Ukd|Tz}BLi9SYFa-wLIuSF?-#*<^sNv%Z#ipR7m1 z-L&5NmQ`xS-5_R@wK;OKd+C5iP%JE+0!F=!MjiDjo&pGwf}mLPoKVu5d2rfO-S@VK zf1XQ?IJ)dO#sX#RCHGQ$ZQ_UbXn;uh6-V3@CHpPH{iSqtnO#IqK_<@nzkrRzopI1E z_LU-_^fn*$PUS)D$R$Y=3ggG;e7wAP3T}ZFk@1x6X8;dvDV7Y9MVKSo%!vC#ZnlV` zEjL5B!0j&_-S1x!$FuG9iIf3r%M?pY+^-%!3pml@4&#Juqm&1Q$msP!;9v7j%HxV8 zh!YpL#IjU-7-Xv8zr|6KC(eU8e&PnhDkKF7)5xNhx)&Ibp0G9Dg%T4iN>bMM9~v+^ z>KPZ{y9EqiF@i#-B4`}TFsmp2s&bLy1>fPsDU*^gbLQM=3P4-uGfbk`!h1@6HCOq( zm$!W7RWwqsc+vpo*u^E}h^;GUO!wH^MA+ll%u`G=^~a7?idri#7_;nBo#G(I=fB-!uY-57G6UNs&$>fDISZh9PMv;jPSv9!alDYLDL7rHgxmxGI}Ow?o3LG&PE?+C9N|C1?3TUfMX57H8^z-( zA)ggy-JP%;dG5Oe#TfSNTDf`RZgEm7Qtz5eQ}4X6dQ$I(29PZe)*#tu%$fH72b6P; z^=~K#0s!TN%O&ivSkcpGG-0WinEcL_Ld_WDIb84-q#(=hFV`3%TDY|R)j8J>T{DU4 zaS{+IB8Ci8V=<2MO(y`1d<^D7st;XPj(J6N4%;JXLY0yssx>Vx48=L^ zop84~TLzRt+)1W1U8vRsSy@dg9YIS(?!p57nF>}QKcZNqGQHbV%P?no?^4G#3eUA0 zB(T02HngpBnjY6>8ECs->I7ibsZ|c08KfFqv>O^dSz-+=OZMnkv&MN?5~X>_dJ#d`gMR;jYS+t{xZ!_a9rVF;W*urF5uc`foIg>>;qCrozmRRF zZv=E2Ll9Aiq^pkSsUZ&L#Da=8 zN@b#~D^qi6p(CBG?~BY4CU?Z#+7+Ra#VURgYJKt>|=B5L*un%%OW;uH$_u;*Htt|Je)@JWWN zDv{YrLc}dz6&epMqt+?OdAi&sMlP1zIelvNj#NRoOyN$Hl@6KQmaN(52!ySSYNIx$ zS8hIXwYXtXF|5J)Q!1_lh~b(j;_Qjp`(05l?~vKYq!7o#woBDho|0etCT!$ z|1PWR0m|xvvCSQQ1HMu2@5%fwdO9`Zbl>0j+|J}bdPhLDv7gI+7r*UJB~sZie^GbY zec^I+a;5cJNn(_kcplMh{#f(-77Tc(xWUiG&|5@Nb>1x+GxrCfoG-S6jQ*_cSKR+$$c-&EaFFV@0w691{O>0LD zipAQ734$0Rtt`LKV$_tOgkP6*-zSxKM62fPn1D;U3+u9EMUV>aFQlT6iV~sH9SWjh z_P>hPr(|BQm!LvjT^58c{?!me4oUX

;qgpJ=3ODcBtr5@G$W%|!K^y#z)InGN7Q97=u`v~ zmm?~Pl2v}c?LRcrBsLwR5c7X%CfJ7vis_yonu-3uX{Pu8Ni&Hk4`2>@{8|6Kjxhx- ztCNZAnTCayD1w@OYuqs8Ipo&Ye}G=N_Pw3Cez-}iBfC!T99f%c)ahnW>FJ5y0$A%6 z?m0+oue;)>$tWD(oR2=Rzfh=99A_|@??!Om_dQ;aX9xC5t7G{uj43Re`t_~sYe%W| ze_~8RFcpOOc*2Vp5|8tNZ@$AkEBkFGt2vXl!Hw9oS_GXZRhv0q6AVLAen;L%YO=E$ zFIg^f$DWKY$%*Y(jw{i%z-)DtYGkMRFpqW6frfod+3dsCFsxcpLYQX`1T>}JV;=SD zC2pr*4YL62dP7u+9rv}05KCEixqRdkJPv$i#j+HMJU2SHz_TcQ7~KwEmw*>m&j09d z?&w`2%S}G~v`-c{a<+2;TTPbh^v#*=21ZLaV+PrE%3N5UxwSvY6n&9W-wH?bj0E<4 z@bw|)NBs$ffk~X&B9Y8?G1nbvuPxNY9_PaQQJ&V15J(Ors_%~78=#QN@I>Nwu0d*G zt4+YoeORjuWftTo`k{l{CgF+LX(0Hw5V1bzaqM42*23?r#HW!-*Nt|51&5tm7Gkgb zOy;+7Y*>YE(jTzu8P)oye1C`l9@SiH<#QZm8$|i@KWH=}_l=#`Z9&~bb)Uu`CiBCF zMWGp~(F1ym%kR_UCYCQ$+*<5rPKE+g#;A>2e|6Fv40G)HB=_Z%`H+YNsI`vp#BG8= zrWY;ivQgG-C;DbP;uIVXi{@S*>$Q)hF0}}PmP~wcH5`NEB$kgJk4}A>+icHAngLam z9!0Zc{)iH?ZoLl^_;s)W-*-f1pTCfauCK30VTAGDGf| zOV%c}T}&tY*J&yHz!d7t`akFty(8?*4+aI2%mKnJ8p$Le5p#E5Y<00SAfc4uwc zPBgqfQk!_;6VqWneK=XQYhLgIbh)PyD+QX1 zF_5aOX)6frOKBQ<&>t-Z&iABBgsC9QQr*pOk;bfAw8-gxsNL@&PMPtry7sZ2S>Ip= zk~-N)MkyPCiTpFz&-*gP>KOppq{thf$K z)L}4cO)?{USProuks;pCYGv~5qDq>~G&iE3plgQw67GZyRFp}CUH?Pg2iiXUL3Eha zoH!sYwgF>^9lx&gYEf|FotH#f)+hLMh;&qnxm?Nv;)sGn*x72MOe~s6STKvT{$8#5 zV^Z0T)ROee5T4^)H0mC|qwJ`bZSi>Be8b|*2u)}q#gH74el4maR5NX@sfI8p;BS>I z-mxYr8^72Jp%`TMd2P*L-J;iNYp!VL;dvcF%+D?c-F4JHO*PeAvymLo6wXqIUbMuGcazX%p~z*PY~!|Xf|A#KhurQbadLkM%z^K%xnF^MN?=# zSFd)XjASn6OaC`s1=P%!-fmh=#w3x1HTAB(bK;Z^r{G4!u`3%6(`IdgLmvdMiC2K( zqwLZ{eAgv?S)<64jnk7NNv4b#A8LO2KhTQe6#^Y)CC<8>z_HR&0v_=7DK87%M8?{< zT(C@3W`r$t#Qqp;T^T_BNcze$<#gwp7}Y|9)3U-+veLZ54N|fn%nQ~k`e4~|1;5?> zl(2xrDHk=N)&1^%_{+%m&TH>>>GBud&V!ZIV6S_|)%Pynrr&*;{Xnt-AQjD_RBCfjZT*7@@zMC<^z>zb<MnIu*gHiRc2{i(W-KMr@wmpRpU1OzLrhbs#(zfh* z+=AAYmzC$8jh^=A?tSJ2qXb`qBI|tbvxh4i?X$`~+5jimXh$G(k5w{V&kK*LyjKyr zV;SeejZUdkuwu^*wNLY5Hf-w}xV3di_7mW31jxBlzro$_uR5#^ON9;{C=@T4IAE+~ z3mPS^MB*}zW1zo7y}nA%=1eLo0E=v?AKimb-u)W>1D__An(+0lw?B|cuIphdwiwfr zR#U}}qdb(BAmyXof{*Mjlox!*O#Xu@*=@K>J+NXyOy&HALy;qWIjCZ0@c29E&sGb_ z@68_J%|ijIn2~eB6AwZJA*z({Mc|0K17dqfk#Fe7JGsNpGE%ibC*xHg#E&duW~oKa zPN`LeYd^p?HfukNy-lR}A@7#`eSwP+s7z?JN)qSm6P%j?P z^sly+`ue-a656eqWV~&-)GKh8iE&>O!r1DAu5~kW#Tb=;Y85v~{otc|qY+Uw3Cz4U znUTEUb2*{`eAV5uD9s*T*el1>lhgmm*=jOex}fL!@FjnFIaGzbVPY2B(>mne41GNA zbE1c>@SM`2RiadTiDKUgx2Wnz63N{Dnv>f%q}*;^UW@KyQAug!B7BFiyl3019!f7Z zyPO<=M^*0^oxW|;&%u{?niI$GXcUxcoxAUcP=L0E zh+Ab>Nph=~T^a5J4j4UIJkPh7mBD*UJl#Axieq(d4dSHKs7oaRd;~ps#-acG91>hW z=&FC2&#V~|P!lpXgsVd@S}v&cCBe>neR}a@eha}cXp-I6;QQv@Mw{zG8!Yo7A3ZWk zUUo$BlDAoqCDM-=83`>}`9R9OZv1U96B~~1&2Wpwve;^h90vi+0rOJGYEasabu@r5 zjh$&Gn>hiZa|PL2iEV@tXS}|I2JIVVwRUTyAqI}Wh()lzSXEXDGPc)s#MLxvduOCO zJOa(c%8q#tptrP;7a4{&nVNq3(Bt{{Wrz1tx$&)ohGZ!%XOgpTNi(_yxLNF~DJ=ko zSqec<89&N^KORD;H}$OS*(0+pZKurZNW>K!#vtZ16e+c5K%8%UQ@4iKtSO&g7Zr#K zng8hM5#?}D^WfH|VhO95No!}`~1~dJ(|F~`7!G& z`_-6!gNlnTlIe~!E`dm#^1%L^4i8e1BSn_`@BC@Lqrj<-^(20_D7moK%n6*fD7Lhy z?^EVIT8ePPdUYO%@;;qEH{_LJN7|MLqp1{F2z9D6eU?1i$| zdsIdAy5S}`Ho z%$f9VeQQ~uWX>flD$OXvRB=<}OY1(0w3N}R!n4Hh>?5mg$e4Fm^jmg)~LqDZ^>YHZlGc`I( zi{)&T*U?S!EfW)>V^7rek3%V}h`m4_ZpZ=x)W$c{^97Wj-0MY-t1QQCxaoS2AIBz+)TPJ}oy%Wkh@j~v=&EUR7Px;7l~zz@ zvz+rtBN)^)Dl8mK>E|W)NY}NSYqhb6l^o#(sfRUwgzNiAV8&)M%^40?XzAseO-gDj zR^zR?;Q>upokYKt?1`8tgGbhLwvQNZO~4!bx*rTFQrHn_11?JYWSy}+)wwcN+3Xh+ zEnI1_HN6mDZ?8$ZunHSNomzXpdDf`CM7z za7A32J6db)7=Fa7;mR8(@7k)evh9L-lzPOB^!m>sT$de!dKl_1&yD*7e~|f%#mG6U z6$5~TkoHB5B5?o-W{M1#o}^AEY*ix3${<<&8aTVN4wZoz$B#?uunD*^+SGxNim)L4 z1W(7g8>MXDdyAQVo)2<%UuV;NR)H_;>3;9V@;;1ZyqHUDsjv~TCP$;t;i*`RRMTC3 zi&{AzGR@AkA8_ zT4y;R?EzM){ylaLV4p`WtNwWw0|wYJ*kn7U_!o<@PiLc+CHV`nJeL1Vt?=mB3$JFt zvFeGQPL6Z07Q%vZB^ahgXnU<_AX{ixnE0kfzlUsT!ej4fwtdde4aV>-lJ@2=AJP( zf8IVLExblrx%F4bCe{~`%P+&{A7);14wccxdk~i}S-E$`URTlATOxJ+Y|r~1&xcT7 zFMA)QAMbv59v$DW^Lp?(+#c`dKbK}lWAkx8|9*`6IXrV^nhZ7$+=7k1!Q3*d*;`s} zHg|su=7LN=3h|5m*PQJB%rYx!%;V4)e7QoqFb>@Ocj8IxaEsz5*+_W}xz$y!6+FrL z{MF@tczWKtI=x73jXwGd;^Q~@UT@a#o^Z_jpRpLc9m!eqFdTEH+dkO(()j2X6CEJ! zD9ZL(?ANbCvKJDlk?x$R3|yg`@g?U6c1@biE%D(w!xr^@AG~3#Qm!Jq1^lRy+@sup z+@jvHT-|=X#|K9B{GLdGqsR-&h()$_F~=Z)JxyWQm(!aPWAyF2$_q#XEnB;m^_tDP zN3gEbNRq&xiZ6Fuk^Bj_U$KfzN>6Uj#o3GQQ#E6PI$(Gk>a3R_9)qP)G-c^8P# zy4b#J$0LFk8c%zq`xz5(|=XzR0ve`x1cqx7Bt_Wqm#tpuCp5ob2kM8AZ z*6gKe-0#8SQgKKw0+H`RB5?YCx1ad+8~p8N{zb0$=BQVPyZbYB-0!Ws$4Y|!+eHT8 zoetAMB_8&cO3G1BU#uBkfABZ`Qr)xNiGq<&D%|ps8g75Pd~lBg41Y3g07}nP+exkO z2BMiBn;}YE?G4f<@bn8gIAaw!NBzA}1+ht}TZD*ti;( z48z^k18S7HAr}kux;h_P7^IZC<*z3!_0(d!DUr~Q&f@aOEG$g#_-s*?duBcXZp92Z-MXK#OVbUx>7_> zT7Ekj)#4)OmLt(-fUpAayc&S!|D5HVp(v+ZSno{_^#~I9ffvn;b4T6o_WhF>MN2O* zoN$O1>NqiAc<`HaQEHV_|EjW2I)lK4*io&PnR@{I*;ulogsoAQa?=E9Pd4vwzA ze-wts`n~`yrNl2}F;q>FmbR4s(>fr#+(t7gYkcF+t9+rd!z%)KS6>yl^4k+Oi%PrO z@_J0Ed#dPqg^w;adRS-{c3zWgGWu|nhc?|XC3jB^;TR{2?-MB@C{TuA@v9YzdCg2b z%&2}N`Bc8Dji*olx_f&4dJ;-yzI##Hds***t?qtmOpoh{fj<{())U3Lzx>eUYQJY6 zza49DG#}`CE6+aVo^ly&dOzEXPSo;NEnl7R$DKbmX1P|+C)ZEky~$h$F;{5gZXCzk zIB30wJv8!VVE5wWLh%z|*&M&ns>NS&%{_oY>9R`Iyk;IJhJ&-_bdw;Deq*zl*i@!I zLIms(%wdCZx)LUqYPZJ&n;bXN-_>kk<3}>r?R8Mk1vOkK6cz(tQ0I6^&Du;+A8U6A zm_Yx-nG+|3Zv;M54+s`aK>v!u9x+N37F9XNEk1XIn)X(|k!+UUafr-7ZBpL1WVnNL z{dd11lp$z;N&i8I18+3bm_N|P_Ck%*;RNx{jqIrm5*5Q`^i^T1@!1`A$ zxegL0D8qm^+Tf^cx-N9ScG4u9t6lKbV~@|Cqy&YxiALBua!kem8Fl^bZOS_W-?;FKY-!ofbM@jaDiG-0e zm`x)rIVmbPRf}@8 zSkNnG`7Qh8y%GRKB*}Jc;)6>$2))&c)?cD27PltRR_cvZ`n(`#zLib95uO543M&~$ zK_@f*jjOSDK|x(9_ItdMc)2a-)NU!~CQo(|&C_HTG%W{0)&XyO>Sw zmuPQA7~0_{e@I&0o_%uq^k#ojlsH5BmVgk;DDT%#cXetRd5{o||2{4p>i^MRRd)Vy85jaj3QT3KNBFxIE8<5c z8d^XtxyeyghN0p|7OkX|XH7oL3s`Ts)sjcKPL##Sloq5k;R7)^Ut@R&WjggKzC2@-#}~+n~!u`!L!#; z7+BI$nZfMS3+i5g!psT|;z?d%asw8)2zy^L-rq0c_|8w%qj)fA z=q4}0MALp>v4pIoawI^gRspsa;x98F_TIyUQS5xn4!q<&4zpInA580uU4AZt7ADP& zEmMx|x3ZOcEuk4KDw5h48HHbi{(ro^Q*@;b+in@NV%xTDyJFi(#a6|(ZQB*wwr$%L zRIhr!@5lbf-rb}7ppSB%ku_GH=f38g2n?_uQkS4#yT0bYTpa=5)jqM7 zTAP2^2E9qfXaVsD{?c68faehKQM#j}K&#a>ZSKaH?Wh~Jl1Qg-M9TBIzn@|A@c7l4 zj5C+z(`@K0XS;|VIpJzUfBYLvq(i1O1%t;XwC%|>^BLoUm~)st?MlOtmQMUwsXCo* zSlEEAX~fwe{)|^|ZIIsBEd7kOwQZZ<_%=KR0!@|BIX|@VDi4HJ4Luu4`B&BY6W2v` z@Myn)8M{NLgJdI}MRw-7PMs1t43>uRxJnECK(<{*RA$Fm@RMCtVO7~5KVrC)C&_)-hHDcJTTrcne()c464}I}S0{gpYX1tP)3H0w?t8d5M?oC2_Q^$hBjLCt-TUn^@ZL6FZM++-Gnk(Q&s^kMcJ} zg?Z;`m3xRvWmAEOk;q#t#wO%WK|02%YDpOqmSm#z)_nhGnI%?sU_TP!tTd_*W2=CV zPwe2fs$}UO_*o+D*lSkrh*Cxsnc?8rShyYH4n3!PQ(4yk!x!I}4I4zj?9%eLE8K*nQrIW3K(2gx| z`EafIR;Co$(#5C;ch6`*-gMm&^?B9z(^WAWTe}2A9TFycSI0kwHV{Z(ZjL%RCYQis zt5yL8i=rh*{;e~@CIUMIuP%8Na2}6Yyx=52QdvlPt500j%?>lu6_IztW$1zQN^3KhyMsgAVA2m<#ZJ{ z7dHlVb;#$3f#`#`-I5=h?(GOE>0XVC-VT~mG-w>#dQ*mI)PhZO3mz;yELJPbDEs0Z zZbXzGgZj>$K4tPvH5(+=yz&o&AjbzpU!%YLKZ}N`O3tpS#4n<}A`C+cbBw&BQ3vYE z6&uUn7hqV1F*okz^*iwkRe!IcaJnu)B{NBcP6t6KvIW^O;s8slq&Fu-(@mp}4UmOv z_h(FIG)!czNok4UeF*4l8UFEfaPW*_C66vq6s+J@8uB1+8^EhN=?^E-xx8e-_15`s zrs1Vm)(MG_H`(j?=W$&d7s#yz2q$l{c?MRJ&iP4Giy153bfRw9#Pf`1B~>u4dEK-a z^w$1H5!49iD+d)WI=t{>&=SMI-;9lyYr5sidn}?~=BJ7&DHM<46IZ&2r&waJ$Fp+s z)Y*NjxEC6r_LFAtBvrII1`TVr{VVyJ`hmciR9nPtEo!)(G+ zBZ2!N{niu4KIQE%V2^%i9a%h+U#NiK7Qp~MR}{`85+|!gx#7`524NFuwPY@U1!)x3 zDoL)Ib_n|aK`YEDp>6v1dfA+{v0m^{Z74Zucj8LS(uCo>-;C4l#PGip3#TdvcO=i# z$o9=Fj`&egr0X{35&^=DADkmAo4{QDJcq-vg>em5IVTMTPQjS^)upE`NjRA9af-<{jMM1BWDEctP|Do*%svVLpKb^e(Y(|v6%zXZGuLKB&J8ZTV?M~1=sZ3y+cfn0RjId*ElbE!u?K7ciFC zX?xqG8mvq#qYbJ@t=6Dw;$o7RLk*J?tkw8bJZ}~GQTS%0213`ne2Y#Lh`-Ozr{Om7 z(H1RhR93UV5&av%u7vXa7lNIE_!q&JR}J%_^m1@ZcXCY)fbb{iyGyaX{fl7BUUEF8 z+6sPZ`FzN(0m&1%&Qk}Zh@tm4Ue=7Og^4toSOjJezM8G50|0EzQmc*Bv2G1ri;^D` z&zk7gb^?#1B(YzRp57vDKj~@J zc1cZFsA5LKps-!Q)JRRF;?UK;dmU}lzf(cCIt%ebZfwJ*8wb+g#q40qk#aTF)#-*k zYW!3@^(E8ek=%`Q+FG8}X~vEV+=X+Z^uMWvK@X_v+09eiLgkuF9kDO02I#jg))$g8 zzh-8?>&vNA_MM*hULJj+wpB4nv@5Zlz_IG7La#oJ`2EO$^$;h{0S6~Xbr#sScC*|? zsuNeVlQF2eMCEu&Zd(THT{JK5lI}>JTyW0Mz*X6tyTG4+b538pzi(59;hlK1rg3?i zpzL?Nz=ie1Zl(I2^wUaz1x{BN7#8ogW*A?>l|H9y>q|x@W^ZBR3gGxCuHUvUu*CU_ zX#uzF{0GC8PKrIx5BW5CAu_Nuf2cH88j=X`3aiwPC{YCAd_Nprmql5mC;Dx{RjqOplJ3dGVk0C5tZExmCwyPw2+FxKuTfa8q@Vy4p%a-I~gAnMs zT-FQP9?N_sMi0P-ny%%7nBvJq7#ghX{&~=hBH~BRVRc-5B`JM zF{T-=NVOnXCV79|&0x%OG-|f}=hvVtpZ9%Ywj<_-Zvm~p_10@^Csboru*LdNSC5>{ z?fi{uA0Ns0*uos7Q!IZqal*3*Bt+!aXuF_U*6_unuw#q{QQj|cTMJFRfpQ&Oq|l|nCdCCEE+!=_IC_NoD)Hg_&zwR(#n!6wndvde}t0A7Dw6+ zN3j?v*C)P!uO#AtKZDw<$>rpyKTjMpvez9B-8fnlAHh{T%?i!gyS2;)#>$iwXtc~X z0bj|uPfh#QMQG?;ZO+=+Xg8w$fCX;2&|*Y0zVT;gWLJ;G%}svJ_D*5xAYS3w!n+qH zhnyVkd>dO4h#D?jKWah6u6t;a*M3_)My(~+&D(lTZS|ny%*s!u#mB@WS#8nsmtm7& zl?u>11m)rjEFa-oBaL|IsQqsoY-J?xW{Me&1_~=loxE{QoSA(S^3*NqsMd7A?#`px zX}C^j%lT^Usx;r$nX3$uRJ@~Jsx_Gw=KI{2Gxq44dTWNSPK$_7eKaQH;YzSZ%rY{?h zmW!;|H})6DQMV=OD`+0Ig2?M5+uUytj%}xBc|R}vJU-09(^pzs)N-)%^S9Y3EL_cA z)=%O(>?|%(bG4W&UdT$}xrJEhE4e@Whg5X9yRDRs7;E6Vs;_grh(w=d*2kuhX#)*_ z6aJ?~B9h(%LM6???Q!W@kN;g$s%cMbIOTl8_+XN7hN*~#s z0snZRBd_WatjzxxsR$Ww)zT$&5w-4Yw$ck`vaqbICvVBFsv`4kual3P3%#CxkjjiG zH@moNz@b|3Qx#}Ggt#FtpSFDm#`-GeVJ`iZDsgoT%GNTtbxkMLTJ?|cVmIQuA5SD+ zFPGWWFF%hIZsXDIv6)78xUW`*6g{)q6dpg|>TuOUm=iNp5*7&OioI-HePG0DW#B{cNPE9T_!83Q*C;@rDaX`1saaW#34QQ{Iu;5 zmNq-fdP($|w@eJoNP4lI;*Cy6EchUDSo@Ig8x7+x3VR~^aVhqR^}W-B*?PwKa;t5y zq^D7E{g?Qj>%49dAOS*|?F0lg=)e*-fXzb&w!Fv;Ja?kE;|4C3m&1N>(l7VsL$4|b zJP^oinu=%_wj;}vPq-S*Nkw>w!&eASDW+&$d_1GZi3mZmRG9KT;jau`q@Yg1ab+`U z8eVP|1a}2h_b_m@y{qp-NfQRbsr~^|gQao)=VEnwVA=qP7Rqd~(+TMt15==x><%1u zM*S1Jn8}ZUdd{zkNh3fw10Y@B&*es$VqVK3C>9XsHWLrc5_=?qgH*l)b0&siCK?jEFJ%5WsvCNkPu&O zKAsO>EiS6mj?I!{b2S-CcCQjD88`vxPeBz7y_Dw%>N=O0fBY!Yk)oeGOU!BUVu(*( z{7mZ@Bx}LXpNeKM6qB;QP8obIoN zj=}xZkPK5qmGVHFIh@KNtqkS}(0iO1l#mndYouwh7Oj{YEbJvh%0&nNTI4`^aPwJ% zRmef6&NvR^@HPbss@lpKYH9PvdG*Y~W)D}RYWxotsIUyc0^z`J!3tNj-#CD=03L}M zrTxTa4F$mTvKHIBwqS}c7Tz@f!a(oRxzjfr1*5tA*}2hqWuiEM zwLcT=@OKCnH%l`4?f`i408(*3BKeWm0UOkr-Dm>q-!!D3xYr zghczL9jS0`=P4!s!hm3Ue?Z5d8X4~{OfcFgFbu3tAK`_Gy2QghuhqHt=#6h;!)!iY*$5bzyAeh!!Ii|sT=IJJ}!Srd6slFyZgYKa{?|ATEg{pyy~kRWu)f{uFCZdaiDUZ+R2=`HX8Qcd zLWPMPKJQ2j@n?Vk5qIV%wp<}lpx)lnd^GR?m3jB=Py>-y>o-5kBK`{4h6Ey6TokDM z`9Ku`e2OF4YXdY4!Vl55Rz`Y5?F{JWULY2mUI-nml0-Hwn?}}#V&%#N6X|I#ZSr{b z1&e%aeoN^@xYN5*IW%kUQ(4K=Ukd|Tz}BLi9SYFa-wLIuSF?-#*<^sNv%Z#ipR7m1 z-L&5NmQ`xS-5_R@wK;OKd+C5iP%JE+0!F=!MjiDjo&pGwf}mLPoKVu5d2rfO-S@VK zf1XQ?IJ)dO#sX#RCHGQ$ZQ_UbXn;uh6-V3@CHpPH{iSqtnO#IqK_<@nzkrRzopI1E z_LU-_^fn*$PUS)D$R$Y=3ggG;e7wAP3T}ZFk@1x6X8;dvDV7Y9MVKSo%!vC#ZnlV` zEjL5B!0j&_-S1x!$FuG9iIf3r%M?pY+^-%!3pml@4&#Juqm&1Q$msP!;9v7j%HxV8 zh!YpL#IjU-7-Xv8zr|6KC(eU8e&PnhDkKF7)5xNhx)&Ibp0G9Dg%T4iN>bMM9~v+^ z>KPZ{y9EqiF@i#-B4`}TFsmp2s&bLy1>fPsDU*^gbLQM=3P4-uGfbk`!h1@6HCOq( zm$!W7RWwqsc+vpo*u^E}h^;GUO!wH^MA+ll%u`G=^~a7?idri#7_;nBo#G(I=fB-!uY-57G6UNs&$>fDISZh9PMv;jPSv9!alDYLDL7rHgxmxGI}Ow?o3LG&PE?+C9N|C1?3TUfMX57H8^z-( zA)ggy-JP%;dG5Oe#TfSNTDf`RZgEm7Qtz5eQ}4X6dQ$I(29PZe)*#tu%$fH72b6P; z^=~K#0s!TN%O&ivSkcpGG-0WinEcL_Ld_WDIb84-q#(=hFV`3%TDY|R)j8J>T{DU4 zaS{+IB8Ci8V=<2MO(y`1d<^D7st;XPj(J6N4%;JXLY0yssx>Vx48=L^ zop84~TLzRt+)1W1U8vRsSy@dg9YIS(?!p57nF>}QKcZNqGQHbV%P?no?^4G#3eUA0 zB(T02HngpBnjY6>8ECs->I7ibsZ|c08KfFqv>O^dSz-+=OZMnkv&MN?5~X>_dJ#d`gMR;jYS+t{xZ!_a9rVF;W*urF5uc`foIg>>;qCrozmRRF zZv=E2Ll9Aiq^pkSsUZ&L#Da=8 zN@b#~D^qi6p(CBG?~BY4CU?Z#+7+Ra#VURgYJKt>|=B5L*un%%OW;uH$_u;*Htt|Je)@JWWN zDv{YrLc}dz6&epMqt+?OdAi&sMlP1zIelvNj#NRoOyN$Hl@6KQmaN(52!ySSYNIx$ zS8hIXwYXtXF|5J)Q!1_lh~b(j;_Qjp`(05l?~vKYq!7o#woBDho|0etCT!$ z|1PWR0m|xvvCSQQ1HMu2@5%fwdO9`Zbl>0j+|J}bdPhLDv7gI+7r*UJB~sZie^GbY zec^I+a;5cJNn(_kcplMh{#f(-77Tc(xWUiG&|5@Nb>1x+GxrCfoG-S6jQ*_cSKR+$$c-&EaFFV@0w691{O>0LD zipAQ734$0Rtt`LKV$_tOgkP6*-zSxKM62fPn1D;U3+u9EMUV>aFQlT6iV~sH9SWjh z_P>hPr(|BQm!LvjT^58c{?!me4oUX

00CiES)-Dfi9=72@ahIJ2A|y;IXiN>w1GT>0;2Kz?i9qUn|`Q>zvP)guMV1_l-xp~Y9NnVJ*+kr5{y-k4HQMcv4|WoJsMpRPb#fKP7d*wbpe^vbKTSd;j=# zkehZ~J|S!a;bi7IqCZpJ&Jj#C48W6y>AmqxD*j~B$ z9$OKz>*Ioa&1F}B`p<#(1?hhrXq`CLQ;1-RQDI7S55mWhN@u~U_lSdi!?>tR7e&7j zMP7s`ay|kgJvENs{IOp}Mq~%}7W0xtJvdv|}toD1qMN^Ut8Yb`g%9f9sJ4kjER2$L|kpNZJ|LxijhqdNVI_@bv;0 zbZj;xdSg2Rk_)G`D~W*@Rp-po88qn3LtdV6L?xBSx*_dyFi)o;;ATubG`v%#Tl-Q< znp|A%hkB^&3=P9Ckr*SyK!nd}nk}6_aRwR_nJ%)MSXCnGI}ZI_lSkwJj#dMybulgC z&~7~ukDup)Pgoy45V1(!zb{jNJ-j0GNi73@8+|*>fxa=EY6u$?cQ=WzIL>E@ui7&w zI7H+6YRJA+{As81EIC~?LuE`Tx=`BFZyd1XWOi6afD|Vl=@Qna9GHT)D{-VQrQXH& zZ+&kv%g~JV;Mjl!r>&1?&hHJv9W`Dyf>+qUN0L|X5JT)&fKmABEIb(CPc>5f$!z2h zu{Pov@i*FK^f%gtsZTO=%1*n?&vSrTJwKC^Mrt`(mHgv}rPPAEMqLCu#g9v6`eg}X z4&r}~QBoL<{f+cc6nR%7#Mg}F}gMv zxRrI((DDVI*tG?{58^YG+p?q`{#8$gx;#=R0e=AD0|pPLWu@`f1Oa;`XubqnTVPPa zY(hPj6NyEcVd^@1SKp%ivA>m#i*u4jB?rTBI`AWJ+hEQb^%qmHUZKI_rZi-E!>)_- z?;-fJ|K3$hu$Ih`sXJ36Wa3$L9p1)W?pZ@9R1)Vcj9C{_*J?$_Vg$9>7>YXFTn30S z;Xo~Z^0c6&=Xd3y)NajYdpS@zHo~~@g8tg!<@#gTO@Py0qE2twW%9_=?FcxZkZOI0 zQ^@B8eU_DFRE2Oj=*vDJAG7igI!ri+*w-h0=ud+a#BQa~T4J?u6=|C1-rQldX0Qbk zP+l6~cS=}eGmc42u$ccg!n_5{v`oV;EWsc{8$fm{&Nw-$+u9>RwEP3-Uk%IHVGhVF z4KR*5`|bc!r6EROClL#-c4>@0w;yCfzq3T({7M?^hKuQKag;ey8Fi|eft&82E1dhT zRIWER!8=U?YaFDneDb~YPXWiK;ICht3jo7#m(A2R;Mk-!FOLzHld4!O6cyt6-&Cb|4qe`qXfu=kL=;{_sAdq z+&@3c+7oBE#-%r>O=$vyY<)F!86y;V@*mWz*MkxVR*a{4c)rjM+f$1~Z_RI-g|-i7 zZ-9^Ur9ow^g2_Mqy*;Iuo*$FO0S=~Ot;TXimh|$CVP_!WY+02Hpzs2n)co%VGq{9I zhxPM%>aER9H=V?RZX_BTzs%|c5}726Y$?P#RXTCbx3E$(`>9dghIe7m66eEMs+2w{ z?lTmaQ=O!Hiz$w+UcYyMRygy^fk|s&D~ra5c6YFyWgRWV$4C6TmszdyuU_V)Qt;ni zrg?QgKYRniR~MT8`WJ=R;0sTz!A>fOQCL zG|P1$x!f}TBCA1s@YdU>zx0--jFM@X8{9?Jkp#ouW8FsNAh!ssLd|P%AE*k!d`yNk z+vP>{;~$(HCNA{J`35ZVNcbd-(H6%y6nl9Twkv|0wRV-xI)!i;w*N9|7Hes6VrZ7C zFV1ozwC@pfF&%O{GK)b+z+N6P2?lI@X#CLsO-oD094zoy97pxxm}y(Lv{GtUfogf& z-MDMiw{&-jXiYw9d;T-hgs4bV99fAp847OLClFk;;vRx|zg=nYp-PEV9=XIe%WAQ$0Cqex-I~Y=XGF>} z^Rn^!R@BR>B$3fEoLVvVn5WfQFb!TABb&C6v2gx~s(H0!e0!XJwC+f^YATb6I+L|t z@fO>U1E9}ooxWeVC4Y+VN;;xdIVwwtIXNGKus8QOYjKi3fP0QA2<`7qsEJ(jbXbH5 znL2xho0E%Wl#P6j=qH5IP6LlvmzQ3q3yvB}Vq~B}M>dzGcOsz@K3(yr^$YiXHuCnD z_V{@D&URr~pY59FI>Jmz2EMc+LX2(vAX*4%9nO#B-u4#;F=zy2s;GYJ_SFi;kYAf@ zMijIXc_0+&lrBynO>4qeO?X2G@r!8L{niJ(Gd|lX^EjD{`W=5QK4mukMmgR=^*-Ph zcGs2ILy-+hJyS?%(V$e#>WpHAZ#zC4hH6zJ_$52#Z=Vk@T66^w4i$_S1W#if5xFUr zel`z@^cy&E7OWy%3#4+Yg*9Ew^0ZhaFh$ROVSayFx2gPk*QjHAlL{2!QtUDWnm$jk$kkoqN2^99S;(7Q zJ+g<4Od`9Gb0ippbFKD+xh^~~hS^4u2Lfg_-YuLYNYza1X-lR^w;`$%O?5|*DN_R2 z&yWyq^D7uEKMe~QBZ2RjkiA2!*x_fZ`!eT4uNFM>M(9UM(-tVx^$u_#p*=7k#`ivmx8sMV#_zKq^Y#3?jN-O>>I&-?>VXir*sH}T zBmT3a?R#_vog96#_Zj?}2;gvnEmF|n9AN~JY3aXCCMp288J-??T3Wjqo*gx+yA3U7 zdas%0f;G4&99D)FqN@%o`f-5;*<(wxi8356-SdG-6q8Uzg& zUbX8ewkvnrjRi-&n+>mGw$x61nac0#CgA8^&eSUHox?*KI8p>og~KYA*{3Ql*2&a%GP$CHMbIi|J>kv zCRYU8XwF&M3bL6r+gEFd>--1^-&_TCG~N3MbJ>Tf$pUwU$&AV}j()UqK)uJj=_PVR zc`p@UVyVjdFfmu_d(JH@nA7THrf2A)Mc_b?P5?~+=V*|3txkTkjElDfb>9L(R?#C9EO#un<` ze}s@J6q*UkS)<62Od=J0F&2n#2Mt;$L>A_nBrk&ti`kMNMH1V{+<*9Ne;hTR0Gom~ zbVT4pwQ})(3hPdbVO-2+3ctjxDpc~yh`pC;&A$tJ-=O4*vDw2GnIe$#QH;$k^PPY? zzx9?+CiRdZtt)6czD=|VRDsZOM8R#{=A0S}uLy-AHFrmW5CTuZM8JoeCbB!0`Dtbr z)sXAqdRl(&O(!ZZZAK)ux5ac0>xa?if^H2yKZTQdSeUpZ)aoeC__K`T3i+yC&*h?8 zQceiRr3G@~6&$yMX#b&l+nVwTbp6Ru!(!;|nO^yHW?I8=8Ka9Lr`EQGrh0BE2H1yb z{oRMDc}F+(3$sX!8w3$BO9KEy;I7f?EsA{4)a&5|*Pi|I-StSL`q?e2z`qypqcK&$W zU{T|17pU&n?3j>+|5xo6^D{Ju4+|tcHm;V3o79JV-lH{ED+KM$D{D;GTKT|-$3-DH zVckmU#R+`V#HJ+wG5)de4&dAvJ2i-ES$*vohxo^afgmmsiX8N7+@_;~rxzd4Zn(b? zk@95{kW<_(ZZ9TDiV|s6y`*73B`)wx!nfYs*pAo}O$5){hPE9*wsPdQ=HT zRY)X=$s*B+r~GzEr;rRy<;xUaILL^1hu3!@Dwf8bYqp-C(B&G!il&kK1C8^|qXA_6 zoIFjDARqs7?+d#Oj-4@G$m^Q?ZL|((U>GK9Mynh|K5`ui2Yn1MfGmn$+-)5t3rIg z|1EB1k=qw1a2Z;@qSFbOkO?Sq?(WLs!E#u=Q{>zRQP6ze#2C!p!^@3QQPNxP&{w6bhX+nTZ>?K^6!}fixSV(fCsN z&UHak2-~!GnC;^38BpmM_`z{2!w0+4Xb&g>o9_YCrR`(k#%(bvFG(E{l%i4wdr7+v z0xu^-`$^j&(`S(@!dKlZrQ>Ji!Y|6Fr>cv546*YkFXKCWbjR_I`< ztGLWJFll2rn!l%zE04)_pbBNuolt567$T`Qx_pHxqBL&yH^8l;)-Vg#*@AIEV85Uw zGGyb~0)4pT0(sQ(O6+Uem=P=H3RKl-!xi4?1M~fX){Clvh?HbB>mxw3;~Lxm1teB? zfrJ%^65)-<{S?dNNtyfYnfpoT+fOWEUq~Kpc!~$)apcop2^xhmOH^{jytm+R0>=gH zNdMuwaScsgOUK{L8D(S-&VOW2QY~!J+8!FDCIN31dvS=zW(HtK1m#Ls1mg~zKk!7D zoCnr!Unn2hbCO{se@Uhhdp>e_`WCA5enxMDdHEJTJS^>;ivLof!ewhxe7PVOE-ART zhRx7(KO*1uMq5D_q@C4U+8%$c@HtuVoa~c>kRTUh)^Lso1u6Gm>w6lmf1Scb}=HVNOu%1g{??AP{byzUeQR+=5zw@q_NecL*#-ULh65iTOL8?I45LR; z=4nm8vorM$t{pwlHADFD9doG>u{vpqK{L23svB?X?7RLm{4BKuM3(|}Gv99vvtpR4 zGdXXgfHT91*o63ZpRt|rqM$2F2MAU>wayvA0n*RwB1_sSi@0?{XQX$z9BuSm+LesL7SYTU*1w zW6-~Sy!kwbZ7*2{Qa#qli%-Gf5k{-tygG{}3|_r^#FMdn67lm{T6uZSoAAWuu%Z!E zVsqMBf6+OG^1)J8J4rgtpEgiB-JNgFJz_wkFAq>+>Ub_sopML<@nr-#eedAHlel9U zU8nDStZ0^>iFvV(;vaTA?>qKOJi&<}+ZJFNuaT2Pa(5?;83%?X~JXX!99)kc+ z=oI%?@6xh?lrIc=)-*^ecuE6y8@2%9ICA0f)^W-6)-tZDM`e6R+5P}$hM;kGkl`j= z*1*p68I(I=Ao*7oa*!d$2VSMyx&P9Ai%p{eGl8B{c(ybcVHks`+agkdHJl&Rf*zfL z6%=2@kX#A*n+Dw<0lTZUA*UKs-FTiX>rx}V9}I*23mbWxZ%rp5Zd#V`WTBB6QHv$! z)2`^R8f5&nfbl>wkS;%Z`0SCfkIU)SBy!=z$no}|xm)=BLt}$z$dj&C#zv}VZcR9| zQ*^lZHx$BiL@XGfUxu&4W^?2?6grmukC895H94(lQ==i3a#FIg`OZCYZ+E~a+>rsU zqr|}dk{WT@sftl;Wln97PyGmu78y}~6a;OsFE5lut*d}HCI>)lPG^{ny`4I5EO!6h z8Pq10FsIrMCClmM?Rmbe+>+C7gJ!y+f*m*O;hve*3OkL}8G`-s^#?1#$N;L~8c}jO zP6E4OG}y9KMbF@5+^N0^Vs3OSn~_4Atw)6$2td;q)|{;8zNH%7+5dDlGPc9&Rdilh z%+{kIIbnlYWMOde$%`(VE0e@!Z0~>dw?t(K7o2|#caA~wW15|rR>gjVL&^O_Hp1dr zbqOL0^}0l9%JFrUoKo)kA=R5jS2J4R(i$`yc?wAbNaC zZ^r$J)5FKbzuvA$L*tpSB37%8aCfDYwH?t1qZTFXkGjS0PmA=P+HaPqCaAs}rZcbn2X2q}a8)qd&xjdo0m?GzfGlj_kS9uSj&@JojNdr~G5SgnwNRSw;T?1j>glc;PYwh!Ix;P&HYW{%fFW0o4j zu}^)8Igdq%Df7qKG&F!2Q7D1c-w&!c?s4V4~LAs&!P6sV<(_QRBB#$oJ)K^^*WJmU{I_(U&N0> zrVL&>!b&SMOtfOIw!Ua|CySD}`+t)wXKyW#%qR~#`yxJR_*bmF0w2aw)3JlRmLC0R zGsv;^p%V{J=b8AtD=4CbYadSEGBNn4XVC{V%(F9KPo&9_PLjA94tONNYeJasQ21eR zju)ImT2%3l-`vlELao98Pw?+fxl-AG@01_hPOv>jxTsW;92sSQ$x&!{CDWPU+wK3I z6FJZQofAz0Az}?hoK~ky2nGs{o3<%H7xtZ)&%Qyp6z1lNm54}h*5fyoj&2AhR;SE; z-)SQgQVeH@uz^E{#}>hw+$>J!e1qY}gibg;_vgf8Ful4gz8J~qzF4~3J6rBTt5WR< zi<3JVtVqX_40}DhHXX=5_WW*5cf%qK?(x}FgmUr!VpBvlO}gCP-mP3o8Thd`>0;A8 zZ`gCdGaoWX@ji~^a}?%T2l&}6r#;j34=_WWrJ2Om3-Us^AwfAwfCorcR+n8xo^3EuVq`@YLlgt~jEr1x;(fPdjSfHxB`cTaUzJyJA$+#S0$Pyt zQNb`F6G`Ljm8}4DHNQ7#%}D*nl*lAfXS$XZ<o@@)6!p#!Jl3nYn+>4qT z3bPhIM@!RH&;d{@ci@t047PeLqLO3J8%b{BO!NhE<})oS!*Z2Ui{On9X~S16cwM<} zXk&}1uVjyXOQT>&Kt;H)@8qx5%7(R z6@NP~|F<*8DRVXE;y54tS!CumqhPS@mb)vmk%KyctE7b<-q*>$XGWrAFt5Jsfz9}Be~gw|!zUlI`d zh#o1zG1SzB?JhmcG78BV>_n@0uUuVSg&ARslJ&+DIH;sn*$aV;Vv-C8G2Un6fX!Wb zF-iDce@5SQpWHSryI5A5n}{;pua|RRTONG#w2D(4b|IsUgZBC?J}FU&5hqMp*%O1J zX^!&gjGiTlA*W7Vg0=hLgu$rWZ1T2f4BtmpK#e-=3}OF5WCx|~+mbBn1dNpk3obUH zGx`U8Ly0}|aT{QPMa*(TjhVIVPq^3jo!d>02GZaL2u}_{Y2l4Ug#thN3uG}V zUd~6t6~}qoc2z_w7J@_IUNUDSkbpuwL!caYnY5PB_{ad-@apr>Nk$`)?O)q)29`(2 zpcABao~4f+IM*gNqtWG%D4Y*dw5{It*(_$*2e!ah;rnVXAu6_}Tv8dkqwl96V}Q`h z^BY3!^9y9A9gzcSMk&eTZ;n`5icfi5MkW9QNE4gyy3`Bf#a{uL^}no@cK>s&G$o`B zsG#4JpyJwvWk6I%jaGBCMHF1L-<+sjaoAd`-cGq310v{xB21DoXQ8iVQxtM0Wm2px zwZ~s5tlK*Kn=$Nyc?BzwKLA6eSx8peC5lBT#)0sqUVo?1F-dYt3aQAm%lbTNSc!FW zs?+9hmeF{tv z=|%a>HfIOsi#j^2mMk^0V~+xfl&s+~qZ9=G^3uY=%JmT*rS@+cOa%QJgZq2=!X? zq(bn=9=j^CJm`=4mHbE()|yjX;LZqUo9M4BGdi0cE~(8)VXblD_O>rKl>)`=O2qW!$aEjaMz?Qn`pxe7ZrGV~Zv5l8Yxg;G`=yez z?)d+>Vp2?G7Ay87k(=5nYgCiJYo>IvD{S8rdMt>F!4;QA!zr`hEdC>WdK8V3PK1pI zK4%r;F_dJs;~{8>`C>Rbhq@Dk=~Fl>lPds_uzn55y%1CHMeXqp{s{Z=%?!M>W(uB9 z7A|tEAVS`qS<{GbpJ&(LE>iLG&-K)gdz)1H(jS=ZLn{;YTOD<(d|j~GD3|)g`gdbG ztFC$IQ*(z`r{WH+Pn4?VhiMI`+mP+{0#25cS$=%7s~LwepE+d%^5`GlD!w=68;Nb6 zbzaQs8%?mxE&1R}aR>+nXHTTRE&9BB_VF+5Hyf;HOgZ}3;8N-0ww#u26nu{`_ltWR zyYFdAPpZ78+bNv*(%;9&HeIG?FV(9%sQlHNb_KcGKmvjDCS<(hcWFJ#xmS zEchOFYS1q5IQy=dqx{mSk07<<{jns>M$uV18}1aFg+NWEGC?}qkqXi0EM5oBpxe{l z>xF}-(@HSop4L9J*zhS(^I&N+bFF}4tJVA?ZGCF9j_KU(uufj10uQchMxD%A1#_Ot z;37-wq$t`#*zG>*bM+yqj(N1wGQQ+`KKs>Qza_}U9?$aAah9%-^o{~peAgY9Ke22g z%`-vZsVaeygB}qd|6aW!sIgC=@UsSfle8;ri!uLhE=)tg^YEvbyk*$)nCF2h&y`MI z8Ry?+RoARs#>XLCNZEFRnh5JV%r8K*{XX=cW;?%=c#BXnT6>A{(2O^3E{|muZ{;m| zZ}8OTuZ%LS@HJlW>uiCq&X*Aa5z=3UZl@(lQ6)?|=yeyStO4Il-XCVXoUt8|ko~k* z;~scS<4W{l<)1Z6S#E}3Y{eb}AfnMd7~#APkXL1tKvPkQ&MriwvK~eVFk>;Q%yL+6 z`AgDZNHAgO7ET?}z*c|kMge^ot|15`)q~e;s|iPyl6?BRUZ&TzU0MqiD_J7Hk#p;_f^zFOb* z%~$YujvK`QlQ18F#s_vgi!A)YMQd!;6y|;{ydXgSCKD*)dlf3!9|IISw=Wx4Zrr(Y zCg0g|tAtT3%loQHEG@)!dvUmuOQNgA;_;*25c?C(6(2Fcmy5_71w4~t-P*wlpizpt zwZ9G~nY8=s7QWT`FSl@VK^k)Mizjn|1|de6BQrG;g+O~KH&_hD zBB#~?7^qXU!Zf4ln788Ns+czp-*U@a>EmM^h)T@YjS8xJI(xJsfLpi~!r=SOU$?Lo zqlI995qTr=gKuI7eF*Z_KQB{{n&W%lv|86bfd+#9bHc`Aw(e@PpKxe+F9@DC zO1MCgyiW2gI!a@G&SYszX?q_Z<{*t2Of%gO=?3$dF8&agw9lK7P}wHPEaQ%8B{Mzb zh3XJlk+@oDkf2%?Z9h;-0?Rr@9X(Ez9ynwi`Iy7%YVGhMwbF) zMel`ej3LP%$zADiqjnnFxGfXoyWdMh{ zBB>Q-kpC-b?>P7@hRAZ!5wsV2XlNuF=c5Ji^O_^&kz$r13k9zrbKPU9^6~{lW8o5k z*8i8Emz#>ttt!{e> z7e^}O_be)~r`N)7jOZ`J^}W3x8hU-adK*exEL2?{9-gVJ8+Ao`S7959H8uhLN(DYh zdx0z)Kq$0_5`^YVqz>bUZw%TM;r6sY4ylYwkV23G&pJFr!P~a7wg7LSYzHHZB2Obv zdX8W#dcW6c1=?mL>)uAa-P2o!kuO;!|M`^`3U7LpjUM1*@*gdmi zeiur_hilq%)Dufi(ADxm> z-+`V@N+?*G4PH!2ep%9_Gs%I^WvFk`?$1v;e2ZzQZ}I~+P%Qu^>R)Q*KM=dUfC2J5lFm7~kwiw^h2U$9V;gb%(S2h0}`i`;*hkOA+9` zqLdBFC13fGIbV!z^gTyF?-}b<+5VK43Vd!3wF)1fszpFoDa`PccMDUl20WmZS`b0d zPPHtCk^;wJ5C4Aw#6Ua0e><0!Dkt&hp|pMA?QM|rLDwKECRR2ozGOm#?UH?QwnieRWI zI@#AcrDM?7I?X?QtTHozM*7ayG=T@t$Fj7 zT;nCs`GTLfN5ongzAS@w{X}LmCvYN;=R=AoF6oJPNm1W!a82?^#5_w^Ag?KWiHlq3 zfpqE1Xj9tUd~nZrN%D(#cXDhTbXbBK$>(73t zI_rS8up%+R$`%;36UCT2MrVx@&&!;bv4GGBc;At`aDR_Nthh5iIea~4Z}c%1V1Bvs zXWrPKz1IeLk3+u)n1{+ad~}sA0+WjLJ*8@UdP-$GkDgMk-IN5l!`tM=wN3m02;f^ zr@Dkp#t3$!z>^!+P`E6LbIXZGSoNi%QiecqEk>B+!l!E`+2ioDges#fqrjh{g7q$1 zIkzrpP#JgF8B5K}Ei5y+aqY5h&|&JqqL8+!F{VZLpRa0hTj`wgu4s%=ZtDN_+lA0S zXI5GGZT$A+_-JcJmn`@quV*Fh!D^4amBkz@1}bRcHShbPjvZMQ_8WwaBrQ{}zze;U z?)@KVJU`l1b^tIIHsMx9o{A7rzvQQ|C4bj05 zB_q&>k#nrwH7H8X>S_i^cS7Laq!ezU=nV!UKc33T*la}(!zk@(Ot)tqTkjgNITT3MiRy$Q58fmS@d z3G~|%qdvU})Et6gnSAcnyegs}fur8U5{o>ueb6I(wA+A3>u^W*+D7Q5HMGWMTL}Q` zyKQgTOsety%2^fp96D@?x#Mak{uueIHcLi*)KKrVS`T*bv|8JAS5ZSoU&|P7W37uO zIhiP5D!41AL0Pf@xdyY+W)%x$k}LrqQ<_7!mRS*1afxPl+T<>+MxEi6nt|{MZ`Q*? zdhDVom#IPNjv?ox5%i*8CYT+km>MFtGx91?k<&vcKs(DTu;C>vvzZj9-!w10c`NON z&`*#4JwYLus5jQ>P$9qABwD3>&F?uM=m33?9$#XUQtv4ABoS2I_ox-VnE)_K@Y6Ex z=n9kmUhnVqda(O@y|$@Qh?=E7t&Re9q*SZZLFvS2ud;<8f_#%Bb<62o+o@djn3#Di ziWLD{Eti1?fs}o6pAw4j&%-eBjp+nG56ePF)U-qWUr$xLK#h&2%|OrBvGAoth?aIO ziN}nf3TE!oSUh_Ljg15|bR9svjq!)uD4xL^Ki2d=uZ|B-w`LW$Qeh$jHj%rkW?~?5 z;b*&(wRRStuGUlDH8g$l6lqC6wCUpG)5`}{PcI)d!Ty!sWXAG=B*|73;#bj$4IbH9 zr{5|MeBqRD-R{w08YGFKO|R6{*b6*=$a3A9uh}dews5ZMLUUCIMrKF6i*rje{$W^&?-iCpw zg6n6}A~gE->=lQ8;cEQ#;^Ooi&v6<}qhQ40A1dEB}YdQ7*AsLct=XnP3mh(54MogD2@eHn~gdNNVeM{m;r^mB<*^hxWfFnaMEjc*>rx4&Q2S}YovCf_(Bko zW1{2o#!@SR0CWOiE1j!^k_iq%bgDzdWdo?XgA?H?)IzB!HMP(I!WDbORIQ=8x-pcuDqJUan;9wt7lczs#<-17Wf?&qXl5F~=Uz)FdsVTSH(=w9L#$%PiQ zLL-`yGvJUIp@7C^PyEF)z=AKpWJ#Sj?ad&Hmf}W0TH%LNtkvgX%m-0mR2hq_B_eqB zd>2gk64*~86&G`RdpjaOa@c89wZ@M)8b)S#CnkQ3kv4*%|IQ79==QysBS825#&_Ry zSWn|xOri*8S{=yTdfxL|nKv3a;*42pfKkTnB%-J`F0-22a7g%d&8T)us zpeGbfPA;Vv-uQ78F0jH3RWw;GmrYGQ{R2X_Vrbt#)%F4D3#waoj_%q4s^3wlEm-?lbPOtC`$e<*u=)(YDB_I{Rfi(t- zRtTS&A0a`Jd%rS;st1pkn&sa0*FXxJNii!&_!CJ7KyjO zDmPP3ic2>EB4XwPzYX|72LGgwi(lk@rPJgNO6feBPh45oES}N3Sx5`a*;jwHALN|R=SF1Q59w-iun4~(1mB&lFDqs~oA4O?raC0yqw&CUx98j&? znG)XUO${*3T!dEav+`wgIp$gkt<#{G6=9+02voGY@1rLV<1_T?{mjK zA+`WHI$2YWqvnv{IaEXrE+%9|Rk1QvEpX)gbSFQDDiE>i1E!UU*xos+jJ2i!{3V<{ zxD0>0QZb5T+T|!$(jtwKcMw8eVL)UWsG|5}#oYY>yJhcll?eo}Bs!vU$~mP+ApS;wcd(ye!@>`CclQ0A40p$=_qOKXOHWt!c(R zTN)9#6EDyz2-kmzTMqIygL#lr>EUd<9~SEmSJo}ADo30REhv<%z$sYwgy$!|D1JO5 z!HasvK)VsufA#T}xqIa={It-e+d`ie{KMx;5YN>-uo-26Br)h`cg5%Z*j@zXotRZ;*= zpGWS+MmV{OlJt$2-bV3t4rJ~3tg&E>kGRgTelSJP)v6<0S%{`a$=4v%UluB^+rPD8AD9!~whi+{XygT!m@Qy^ANy=f-! zQyvaA8Pp#(?4Qk{ufe4O0QoK23W)_y8rHXMuVukNRK~juRn`C?gBJRhYwT?Db(|w= zeqVcO>`$w1XALp3@o+({hs-zV@U#zSh!R0DDzPEn74T5mqxNgD8_qrF-zcc--)?Xa z=lPFLi8|G6G0~$096*Fa!V3hhs#V?b$@ku1XO(_L1582RKFqG(Q+S)ZQ{cp)1Ddx| ze8z}OI0c{_qs$U#qO0K*P3gbVLiiM`sgf7SZkKG zP(QWIKI>}6l-4hUHdn@ePQ_;S8;N6>-Q%bK2 zM7FkLub%xfqCI*Uj|8!{u!9DRje9O=p0bg&!W?xPE{L;_vIfk0$oNe;!ZcT{PELb|9krQU_Vd) z55E8Y$w5#5ckvWx46V8F7#S7_KD|Q^5#R!UCk}aZ{X>EtVQ>emtrXSoVVZ|_h#5($d z(qJeZIZ26`4CIeOqgeS)jxAmm`_#Q&u9BX|2=uf?VJC?W%?T0Cfxa3Tvk6t(&+1>i}lZmY$Zr60>C-@hJ#x#Tizkq?x1Lh z+ic^qTcz}>EbqYWTay}9bf!YEY(ZewN?qCxXcK8|^d;*_>doTlG>R}dP+#}D&(H$3 z%IM89qnO{Cdr*?oFMcc9lD-`M+IYr!qEtyFB(>QUwW_jyh`}ek=rSY*b(`+2tq~+a_sUaHZ&M$f7&!}y{}Q1PlNqedeUxe0&esFdG_pa!T)Ff>HdCi z|J}ut#oJW)QcKSt`AYakD`pD0W;=R8?L&YzVLqFO^B#vIK) z7>lV4Dw-r9@M8J_jaC=NFm-w_i%&}GC^@ATSJdq;^tCb$mQyT>+~&D!p9*@fo?7{OksOp_f;FRB77%L`GG5842T%b$)n`Y2L` z!ci27*fjO!7Ad&+*!C%T@k{MdJfE1NOMpL9%C5lsdMJGBUi`c$z0r>DDSl7!ojL)p zg7{;#i6dnI+RYt+-_+B-Z2vB(`Qqn)ClmnxTQNLW(^++zQEvj4Q0|Kriq$9?{fojg`r19YNK@Sm|a_bDxX zmCw^VwQoee`Xjx_Pz9H%dBP9Hrt=DnN`;Hj7&x9~6kmlJ&b@0Bi2{~*XR{6N#c4{i=7Fuw!wGFqx!QaH-d*^9%A7lS=#@ayTZ_}B4^ zS1;gS{}A+DI!w`5b0Gr%5VvK7yemHgbx+R_{Bm0VLP~%8RQzj;-;#6YH2!iCirFcX zp&tze%7)r((e@?xTA`i+;TRF8V;ZYny|}z2??UGdYzk!ZnNc{${FgKM`RToiO;L`; z_)_*pRTN3p77Mis_B(z_j>(?0^5LkwxUcDGO@@G{y#%R9H^<%Vo?V^s1{M?S}$<)+{w)9azM&D3>0!d52*15P%GFa-^<5buvw%V|`n0Y|#>+#tRBokyy?-`pA2 zSWQmjXyT1z<=^T}RA^lOVhs}%!2h^d^1Ob=s!1d2S_Y~-JNRRBy$?D17h3njvc!kg z7NYUjDD`0o!kXVJ6OsJR%p(IURloEK)YvJdguXY zU9JSN1g(OOwUltt=iS=V0xyN)B=xA^t1pi>{;i!iY}UWEBSgc686MWIq9VuIbu|_F z-Cb2xk>A^Gb(QR^pGTFI?7CXB+KRydUHEet=>ImZ1JV}%|Kw4z{_~Tk&z|=Bzg;|H z+v?e^3Oc|F$@lXq5u*0!HcN$=s1I8#bxy|p)sz=$La#-c5xZeZ;i;wo*_9o!9V!Rb zt`wHpM?dwM+~s3H|ApOnd-4Czo)qeTJc8Gcd-}hNXWghVvQd8pW*=h@i)Q62bnMVN zv_q4-(fQvn4^+$ffAr*W$^Y}1WGY*`h91tg4m)t$tx!k(5hRWxQXwc|}etL#1~cKAL*xcS8dV zP5H4}0I2U;P@~kXR;H3{a0l7|GrTF_1WY7UjM(7 z=R4=rO>qk(KouNbcjxxX3!TZz4=|ay14Q)NodeCe_nmWbP zTg8O-_eTEAbI05qe!4fhNv6@v8{flkoEc8qbn(_TrPRf9d=y zn7aE$^Z9@L{lU{>{pY8s66(+Y4xaCv7h(qprEYTX-v3@YZUATH$3fs-rh|X~-~M9m zB=Pje0s15*&&T7~y~Xo(wVJGe0YQ%`j1IvRB&GdS`eEVv;n+OqsZ|xi`l4L#_GH z4=4h63!|5 z(^i)F;t~81&Il0t(g3ZCjgSyrCp-jRC+U^vkO%L*i%1QPKN1*x8{nTYGxEgWJ0FT8 ze|Vms<>oK~tp-{UEu1sy9(M1iJLz=1mxB<#Q~Nz2_zAX#dvfZ+<>EOiUyHcvK8U{9 z@`FQemm@;buc2Rqrd6mTs*DUL3sNcICF@x^?+ct2T!Se8#oi*i@e)UVCfiz$QisCZ zB#EXzojbKuj29e(E2t&<0`lBgY`J&uU(PSy9mpMc&fq`C!lxVml}uxwEW^&f?)__s z5C5qA0+osENhK=Sy$u zU-~dp;FVyqvEx!-_-%?kvK&3y($Hq05oe>SuT@7Hl_AyNrhK;HKUp|d)!3Pnu-VL& z+vdL?N=&weXcfA#y{^uck5H9<6k&WqgjI zFu_+Ta>!(DnydvCJ6L7+lEFYuu0#9yQgdFhpfBhZYPIg7l>-b4j2+P#&9bJ!fr*gH z0fjzQ+HpKZY;&Qd7oRkNMi^QAkmiV);}DS7VgNZsYL$JXV%#-$cg~&E>}(ez>`V#F zX*8=Dt?1N~A95hpYVe$vXm6nVAq}1%&?j;5%*)VWH$!JgJkA1T(wUb+7euC~7?*2x zL*WUTMbiY!UQN9v91lFOLF9sz1pp@FZ0uxQGYgey&Jc&i_%FkbC4bKHIwLYaST`(OCbExD@<{D3z84131Z5vvGu9idg4}DlQpF&LH2$<67_B8fy{J@)gFOsPnupX&mvzv@Jb(ikM58xJpIn3f{ z`S<;QOfMbWsftAq--4rs7#8QYMx{fJ!gW1aLz(*#?$05N%qAZMbmD z>kP9OGj$8;dDR_6+>R_i4)fJm(*|a=F28DSfgbO(fS@j-j1M&*I-h|xH~yoiJidGh z+_xNe>W24IzVYD)5AS?9U332iju}!BCxf^QfgVLy|af1~{3XV!b1LTx|2{TbZ zoKuu)go#Bca2>LfB$;-`T>_s5>|{YNal?jW1|*&(E#x_C2V>l@60_m}QT*y_${Ggov-MqEPdj1m0a0R0DBdH#s+UGO&ZB= z26shR+zKMiTGXg#>Qu2b3lq+7BU|6-of{h&A?w}&JaJ*71b(p2sHu<;i7`Uzr6H_u=7xtfqMhD zGHf5ItJs6(xW zFtarfq}oTgAItz|B@VQiicYgLR1+$=TK1GBgXXfF$L>rJt@J96R`V-|>xlM4rRw77-vxhNgW?%0RSgwHu&sLw4tT1ZCn4zAKZ<` zlfELr5SSG_{cA7b!^nP&VD*PsyZ{TVMTF)kO|>ETYzm;5`DOVLn9I|AYuM-^pj&_Mch z{iHY2@wV2vDhiV1v|E=Cgp`l-`=t_< zaRU<1*2CU*(3ZznCAxN@>u*)=TWa%5sm^NH!uph^Rpxm?My7&8q-o$u#f~Jw291H)z(prDd+D zMSd-QhF61X{ALx<$CWl0V~joau?z*G)H0DVnnL6FLkb| zY)eZ|$4 zXQ=WkK)SK&uV%bYqynsgW+UBP&6vAT##H5PZ;2UlE zKOa4MTFn3Q{o{i^|HCdG%<}Yaq$N4SJOR&tbO!Bd0S1)ri@HYfoH~{CIn?Cq&MxuC zAL_ny2J9e*EZ0w%=Yi9cRkRSU(^{j-fo54aylmBy-T@0T;cEQ(C;c;9Q|Q~AA|dNJ zu7D&<*xFq-hsMv;rFLlSWIc&5DBVLdwoGzAv}0s9i6E-Z1BD@+vlplU=u;ELZ|Erz z(5XM}`Wtx3fB*DpZT>fS@cw^x@crYb2an+QgC|d)Jo=0Cv}-`Bo^SmAuUdb+Lx&HpKm z2M0a<-^Ek2{_o!>p}S13qV)ayjW>h+`hWjDTo?WN@8B_6fAm*)pV^JxV-sQwM%bwR z8!h+$lY>(J_h4bfjTE5IKZ{=gz+`1(ad2TqAO8&2+?JAVbi zx1lMgoG;~ir;_Q}4sRIXMkTnX1fHrT3f!aQij`&+U(j1K5c=eXh7f7F?y=kX>ERJ0 zMix=QN0_mb=gxuprEG_Wx3ai%zKjMfTDi*JT5GlO5=d=<23lc1Rqd%YVeMeU=<7fO zN(*Mb48jFkE=%Q`GZSX9gtQya3%~L22u1}IS%cwl8qbCz2~}33{^_E!t*|-`ane6F z@6U2A^Xg$}xBi<mXDN=H_2jqcne%MIOi4HSu}jsB5E~DwoOUj_xK|Xq%P2uG%yF zsA5O||MtGNxosO=bU*K};J6=>&R9~MrrAzrI_IiRntGe09@|as&Y3eYB+(Yv6sbc} zcHDE%f4|QQK!Pt40h*HS1en>`#sWa#0r0%PRCiXXM~}WhwO*Sl+RN!TRHxrmg&y0Q zO7t}C+IsTEu4v5%wI59i&eIg@i_T-K_(0Sbxz4@hl}A?MT7TV(q-o3OSkm-&Jgf1$ zceP)d-AQVXUv}2KMmBXp=Mco;&+70hJApKGcD4#P5Ig{Qbk`=kJSu9}J3oaz9sD zr2FDi*8+#zJu<3}*Q>PM^Lm@H*nLwo*KC^+|Ch|tZk!*tjr`w_FJJud@}OG(^W`&F z|9dCx?)+asxg7Cd>G0|&W}0+HMV1 zp&c9yUzC_Gd8;XTOk;r~!gCtYFOr)U-$%dZ%)bQM`Kh9RlKLne@lL}42rFmEEF)xz zPODhjL>t_!_gc#>7ES7Bcvm{&FZOu?%%guJ546U)Tq~~RGKp8qI&+|4C|9E$8iFiN zDui{APf%qpRJGVVZEOAu`jY=%{q-IEG4HVqSBVJAr(K-h9N3W$<;CGbQmso6U9Lir zlx9%OGbSnm04rF}pueXWh+YD%j--3^FN;cAM#cG`R6^1kh|Uc{=YEpDV5K%`q@*O zpz+HbilF^e1S!^-8lLR>K!nS=(AnJ-aO*;p!31EB#>Yn=e>iRU5kHeV$d)b!ibu@c zrWCg`#RQUhs;DsLvThXOk;*7$dFcDz%^Z-t^5aZ=N(TxjP6=euu@TVXIo*vD|Ki-c z;L2{*7nfijyJm(`L!3>_roN*mxOMJ}s2J6=(y zDvwOO3^GL;(8qLzxj9&?6iil_Ol36lul;b82A2boOy@xcv%|TK0AK@JY(&>~xR`yH z2VXu}%k9>#Bsan^t#C(7gJDiW1=-NP3h*jt-IF(`XGO#i9f1sDDlT9Fcmx{2gp!{@ z&x<5p&@6s5TgDjTN8YnUI=pl>0r?$I^uJfgjKP@!b8!$b6~ z-8R?}vgrUpXb<=%5x+c;Ay*e~D$;o4iD_>iqrFP61v&-^-tG)Nj^RycovRNQ+os2h zt>8qOf3IGqKQUPzMtYG3M&?@vX&?Uk&FLR{?&YU&FCvh^0Dc(!Je2>R2ef}rfqa!g zaFbXGl4*X}g20CPCG@W3caP7Plg6hol{XQ%Jj(hUq-yWCO~+fD)S4rfz{9kHW}s1! zQHhCsJu_jV*8-M*q9-)L_{I7?;kFQ8j*Ij2>J}&2Er?9IwOs{l;>y3p6wT{ax>xo!ET2Y{{6PvVN&`&YK11!rtnFh(&g0hK7~RaR%pNNT!-d6rPb7&8^W zuLxb+#%(u`jdQ>ry*BGz**>l`-(&?A1B}{@Rg&33Ff;ss@#Rwuh%5?xq#^6FiDKOY zq2Eeos41*RP^xXH(>1Lm+bWl>ML8B>;e9A%ZYPd{94ZloaJ$V?u zMn_fJ70ShL2p`)Afelt~-nnuF$SJ^h74Z)+Om5YyuDs_bSg8Y8GbznTOwEM`PUA(9 zX+O;c4eltZu>>eW4n9Enp;FAwEq_K978x*8NMWr zLR`yEw!@m8u6rs{M$1iY*p19mH^bZkQtHjr(~!xQ2b`~YSoya)|6Fd2>YwHLwhXe3 zVnkCb+ctVYcIB)ZHLRCucTDJoYRVXplV~@j>Clnfeq#@bZ<&-MBGplq8A5HIR;Ef* zxN@~zhT0+|ttfv@C9U5gf)uM+fZ7?0AvFe&XUGA|G~yg)KF0)M!9vw(S(ABY4a5Kf zDW&EN!@*S)-$a^Ln`ZQfmjLdQ(=jGve0zB8eUjOkyrUVOPj-v*O0g{8XAWVpTynW1 zrK{0Zq3sQ%{Dl?JI^r-jOeBdhuhKgrFL4(Rp+6pkt~dvCcnxT++o2bBs~O}XjbV39 zelQqewRS6JzI5SEl>VD?QYoe=n~O}*HN{hN)=BK837~cYGq#<|jb}NZHl3H&bB4>VEC0eMS?>x+uM#qFuOr8$0896hh?mH@34$Lyd0MKmavRPcjK;8@yjZj zH9L*#t#nMsZ{Cm4YvAPzc&5`Bl~9LcZ$cBX@C1OBRZ}#(oHSVJg)gLqpS6V-B{QC~ zMtk_bKv}_mJ3Bi*&7%WK`HD#i$4R;q%(kt4hU*~KR)!VO`8Qu6kOvQ1F%S7WLoN0~{xrVYh22SW~eh__{vdySKBlaifHgdHB9FAo?R2FZw zQ_5z}o7?BgmPIMbJ;s51)XDfk89rH4WW5ndOW!c^KSYnhtiCxDoMXLkW3=43dd7{o z(&N%GkmFJB>&*O#tWuocspT)pU-|o9cS(qr!cUeY4+-<(|n7{Agv!)D0vh zHtd`)qhBzZt11l@rUR2lWk`0+7B{FlCbf0foq#sl1};QRt5?oYtkxBpU5|3%!;zsjRD`{HJ|#)I5zNgC)^*I{64#sM@MP$o zoScrgp>U3a*;L`dank6+-PEl4L;Ma!m|_Sax$tl6OIXNfn>$J24a2F~I7V51!`3oW zIYWz4r4un6C*f<@TZ7iaF#~A`B1f^_JPv26@;JzK7hJL_$A{)f16w!i&+q>2(Q9M> z&&CZR12HZpb7ml?KXLk#uSaO6M`Am)f}ZY4 zH=OvFa6j5TYZ~R?6B?``iCjl%AmJ^qt?693cD>--ryPsXDVmlZptH}=> zS9S}i3Fy>yki-!cqECIl2zJGKr$d-#C>G+cIHGbB zL>wy|C(`1^SU9{b5qzimYimx3cS5{F?Awg=ETOQwV~i?JbXdb-4Tm+pDy*?RrJ9{e z4Op~YsnggQz-oCXFn}G+>A%J&AGF**{79;*Di%UTnyy+|ZR2&X$*Z|>=~A-G6Ss6F*`;(L3ORLv0#|4;(&l_~23E1LLmvfK6ia<=$V_xEgztHN}c3wx5`*iiKK& z7hdl*afK5941q}u1C{0)!^&O(x4Tze(4O~HF7|s&a?1%1D<}+@v8S0I3FLbAbN*+) zghao`$yqFzDXZR|no<6}F!y0*&&#MV=`G`#_cZl=Pl(w7{xJw-&+pqT+2(?a)j{Bp z%5DrsD7&WN5aSE2pwZ5^mM+lIg|~o%B^p#a0+fU6r?8J+3zSh_O974fB?~Oyvlwh{ znr{+_i_uz6%|;70<^ih6$v0?pZb~v&J;y#J5Gy5wXVdzQ4VhmVfLHpt?a zn`#o(w(hDWybxC#Ut$R?zd~pI#D!E25{beJ5y2F>z7a>EFGY}S((;$6L_{a^Lw$T;$pSw#g=UCr`8gprTwT@LaevN*!VRK@D8;0vzF`H2)!v1 zz?X4X1aLWBrW@sz5(WTX>-4>qiQsJ@MGhCa>@$q)OEZrE>YHCDT>9GjKr zn!W3>^W*qhl#JM%ABXefctCy}o_ABxQfvc>NwB_?WeB&%ugLFgG4VqF4dA{IyykhB zJ7ke?K_}m+(n>WKbmFMf!>3LUE9k^Fez`)M>_!b^8;VP%VRHM{* zGlMBltxbV`I9n)qb}j|D~ew z@{v43PN1|6Q$55&e9=5yyo0vxL1fe2D%uY^T01_@h0HC(?(Kc?$beZH55Tkcus1LS;wTAT9$s_n{Q_3eV}Vfg@Ab`bGdUAQ%6Zs!D3hER3lwQTR zA&Vi!{`Tc4f$XGZDeH=#gr*$l$-MDw20!*%b=0)*d2O+WV;;z8p39&Qv@+;gQEh{% zEeeWbv%2r*g~e-|d{Qc^;(vj69Eors@5?ZVN7v7WFNOyrq?V)a8ao__a5)zT!^5#! zMqAbyY`ad)g%@sT1;?@7UuLbIijQ~RMEBfUXC-=ptL|8cywln#Ie|_(b*V6 zWtV;iaU!Oh33dr^e^7TT{{lH=D|vu-9aYALLZgnvAd*F;r6P`8B_y(ZVdj)Mh_ z3IScW=sb)LzZIel(6o{Je-r6Ec%%aV$2>iKzp;d3xjZo_1yJYxSbWPJPRP*`;FDph&N67^qfpi9 zYVI~l3yAUoyQWClN0x6c6npr@W z#hxs!1-PE!ML#MH;5p5F!d=eM(EYHZGIRJ8fsYaqqaH`aKq{4uwsn88j1w%K|I0E+=*VkBzbwWI~9e<-C;n+Ih9c&$OPchUE|6sG_Y_aV= zOdMNh;B?}4v3)fhFSd^0vkx7|W)(hOtj|mfYjk&Pb=BmUT)f0+cRA*p!rm2Q+wm7Mw%2q$AQ z<1m;8=IV?4Vk)F(Rt+qh_KTcEW1tR<)4jQlP+39LCD3Ctu}i$lm~Gkj&gns(E2m<4 zxoLD9`3f25!}mEu0Q@q6#pHrGE*kHwu&8H+EAA^)N^S= zzgBGiu+oOw3bnb&W!T>^HpXhz;Y=*8#ou=@$Vl+TXhwwh2>);sCo}jZNHqQ4yVv96 zoq2b3Qh6hQ`jBgzc4{Ee;^(&~%kJC-F~0|jW)fH^5V<8Rr>{$c&B<9#&T^D_t01$M zvlOZ5go*x?gV_*Q;1v2>BVwswlbze+wiq^~AlWyoK;n#5CDZbwE)CzzKET_+-$(;U zpWTMW`>v@_A0IZqzg()lVelPiQhlsg+Y(IM$l7h1F}j?w>&<&lOAbBZ)X@rqUyCr9 zA@;~M`iwsv$Nw<#1~mSjYAChhM$%q0jOu3eI}4ciEQdJ z1m=~-e(Y1S#6iDdP|A4jCfU}^wh?*32@boNM=VsY2cXyzHU!QTTVnP6Hx%^X z(Xim>QWh1Zx5R}nGRh+*fy`&}Cdw!YASHSRTuGZ&N9;$}K@vv`UzYqsi&vav`1)Wb z?>+Ph1~Tv4P%87H^k8r3y~o5_VuH>GQL!96N?_?P$en<+hONir=j$646Se6IjUTaz zwzNUxDIF+CpGkA5vtY3O{xR2j`m+4HQgyO2a*y-xTtCOI(p2e3mq7%7!;2e7MRvEn z5eip({F!uhaS?nWHiRm+;G5A4wiH?8;ymUI6C+jaWdXBuQU;~?q}*u)KQ!zO!iXCF z1=7SP#0#yAO(hAEJ`@0y()A6}I3&tm!#b%ChykQ+&bUDo3IQ|=Kmq}{`m?+~y!Ong zLK)Ru_k00FOrkD%g|%ei@;dw10I`)I_L$M)^fuRM-|>cPQ}%{oI@I2IBIOBr%?q~O z6I@py=Yw@tS6y+nVcP7_{~`dz`@?Cxc=glsXV3QfyIS(8v~NvXIj*1E0O6y2h!P0- z%WizG8mWTQ_XAX7czf3ed&CA^KZgge=F`Ps`lV%2W?a$0yFM`K)CPi{(?PbBcu>J0 z8wsir!*kcgET93;QH;j^Yv-6Wp1igcwd`~m~| zla05h$B-a$nvBP%$=GmoPM6WroG!!ZGTQq&U51e^gQf0PTmtNAz+%s_1^%%BQ|S)< z#*6A}k8Fvx!sv>Nd2eFGwGi1-+hpY@G(-}+tR0RvqZuvjiI#q44abTWPyJ-I z0&7N7+PipJIe2{~4gsB9d+c1hcIDXBaU+&aU0;QBK-ZeCrj9~3zD>sd*ftrCt=$>g zqYp4wRlKsJ{AWC-FW$qTmTDsh1mp1POhp*(R>Q^2QcEXT-CY%2w*n~i7Z3!mCh-?z zHP^pr)H@z!eZ5n3C2`xn8QW%u9jl{`la6iM+Ocgr9ox2Tvt!%dvGLFQe)G-2teH94 zYt_L%se`Ke-PiRzchSXOd^X;i8DXNywWy{@WlXvnD-Xi{VpPOB^?JlpVt-h6@8AIS zCzr84A{oEu!`=|GTsQFC#f5ektd%~@SUkx_F3gMWBdBewJSJlg4W8RLN$4cLWVmKX z;yQ)@ZL_B~f2 z7fKIsDd^)=q77nAZ&?b3Ijc1#0)DHO7tRC^k_ilk5%kTnS%pBQA51~dwm}bS4C?2% za!_QFB9`{!5FEiHX35|K*m-K^cE3DBXONdfvlM8}m7^LXrVlD59>`h z7_$p%NAJY(BN}MDAF#})GCTA;$aDX?>v_q6{+aO8u<3vaNiFhh9uGs6$+q>H#lLb1 z#XEm--ERzAC4=dPb?f;uV>oM%TjL8=_oCMA{Og5C?&}|l*ysD*4-eC=EAKBoq3*fp z9A0a#vnI8&n{`8Kia$8S`R4C~yRS$tL~EU6!%`EpxqPNMN1)5!yT^DpRT#6TKZ2=< ze&>#udwJd8gwBT$Eu(`t0Zd|bUEU}tt-qu~^6E*r<4Xs*-Mrb3cfB>-g}9h3e?maC zX~u)UaGt`~1%NbMHAQ)5T(_%35iP#~k~ih-&x2cxWhV=3ek!tnNim2YtRHsNsNLWJ~ste7EqzjbDo}o8VMr#xWf|Ao6x9rKye>Dt{SOJ#yprVsJ1te# z?ghJsVfBMeO94MEo}MdI-u@f=E9DaGpbe&8IhF{wpJk*_fn1isOnIIF@=VB0ZZ}W- zqbiV)Bm)U0=+RHDwH@|3W3{JPERK`8vQ%?(%&q3q_H|W5qQR6eV;}u`b$a zH#P1q;m1o-ZIr}{bC)gfIZvd#u+Dsx_+UCC@ARpg1_)eA+yY}ufzu^Bo~{^|opT{Rl=>aUEGzXrd0{1@%uYmBzJ;Sotq*VNwdyE7{N9WJ=`{=}w;EOZd9M z|l`P$a z7GAm*`fF{yrmM88z&tiw>JsrVG0d%&_thF`L%#5=isSmuOmz#545A9|o>K3IDlOwy zl#E{&FOe@kuX@Zt_O9=BEWI_J^?Ic6VmekI@ zZjzktC41dDue8dn&gxfm%ZmCv@ef>xP>V;$jy6`0VnBf))?Q@DY^QTxP9!n&scAnH zT<;H(%)fQ#tLjL#w_Fp@V{=Hn9$8~)+dg=Vf&6k?t8MmWCS4!G*=D{JBMEE&p|iz8xxgM4U9)*eOnrj4&keUW?(_^a1r95KGcaa6Y3Gtk%;5L(t7WnJR1 zuQ7cbeaM=rRR+jaO+nOm^y=QfBh*;=k@HV{Z<2`+tr;3`D`<)YCsdyxV3p>(K2$&E z*IWJo^LM_cKKz}pO?XMEn{ww}Y}T*G{Fj9#khcT}ry{D-TiA?X0`8B3S{O{K+}Zu3 zp=V2|-;g8#kpX43nAYBh7dIyLAfEq}d~JRn0!lW$F54`BJqC4cr{+W9csm8c0m|NN z?~h%nZ6>CQS}(uX90%JqIxp*l{1+|du6(1V5au#f(}!=g1fEJT@ZFMk|5Y!$IYg^a ztyBqtm5zxr7N1<#Ah-9I{8W_bH)AVxU}^xR(9^+zQzzns6TxILoOevZQUpQys+euB zYw6%w&7YHuBZ#Z}pL9Y!Q(1)LiE=-_1@!&>XyfcbH?{iCaS+*xIvm>Ff?klBpYVGXOq`lY>*9 zR>~JgXzRy+Xp)`!enmgtrgqRcLHOkioJTvQjKiK@|A+b0fJI`UoOx{sxkGfZf2J0l zW6IFWf)B8gtF;b>;LXXhgxT*C?YI*=zV$vq_W{Zs<_0#5&Sgkz_^{5ZG;$LS0El2%$gBL6>1v$x%vAA&QI2XLR$Q0v2M*M$hyw}TI#-ZIh#|>m1X-mu zR{$<_ZE2qgPdnevfuLI-R0=Ied*>*tfISFr=$#d+&q%Uur6=zU){N?p#d4=P89>Ab zUSr0hIEbK+b@bJ)R=m+0tXAm@ex-e)%jes)@AeC@!kY8yW%d;alze+RT@btrr^^yG zqWtwb0Vo%lT*~bz9niZ&;2Xb)Kyx6qPK^qV2c0WTQYvm{E5Wk2L=9bO!L`s5G;kfk zQOP{14H>{g0(d#A2Ue~di}v7;yY+Jvq}mWR?|6l=A)V(ZQ43)!RhHvZmfFv{x2qzK z%|3sGIRauoAvo{pza`vNGwZ+qI3CQ@?}+7`Wy=4uXN6fJb#PuB)vl}lc?8*bQ4s;x zVnKk(qoHnrK8e1)6IBf6y942kS5wqFwKhtvyBzcg@2E$si_KWP9aa7#ieS>n)TqG} z^`_aPY^X`EQ@-p#3s$35CS+nACV|TL2loAb(1)Ig#W6%RW~K}xp${aY@ou)qOK9A4 z(Ssf37SZOE+`!EYepjzzu&1591cr)x$A>hAzHH04P$r-a`G z6)-Y^KXW-GYs}D`4OIRloH-HTo^qYPG6wXd3!*YSz@&8E+R0#*JhuK^@6vZn`lXdo z5%nU@$s@(2Vej%D_96`QgGXwz_z%+R-3=5AM~>>)180|zCwuxVA6{H}LsWn%G<*wh zD{qCBxb8piqNtYNBT}jRSrZ1rO`zXv?Ec4dOQ~%I_-nqS@_SvsL2nfA)VnzGazoA? z|JzUUS6VRN;bmIr5|EMxBy@iRTVi zXnwwB|1RK%`zw3*Bl;QXmlFpvDaqGcRSuo7@Yl(CKt>MRV|CR(ed#*T!09!lHxEcS z%0?;MqIEB)UHYBx!2neu;Znr%{d{ky+(IiTO*Ry1Hr;D3UfRIPE{Kc;!69?{E{_5h z!s4Jx^GGT}f$v*t*uZyaN1M`byP7o9A6$#>Ykb7KA{7v=s{`ShXuALFcKj(tslSse z`GDf`@9A~!vPo9ip4hWm^$tXoXc?HTT!g4mFB;jW8JebbtaJO5Li&huPIa@hn$8QI zF2nDI!+ZuVV_ipIS{+U>+31dCRsz7nzOR*h>^Xpli0DDwrRv?p+F9;aeaya~A>goy zeGJO=7u~V8U0nQOLE!kp?+0_QjDG}bsU z10UG(ak8E{SEZ9y&K3}_d3BumLvEKGMm4lwKbcupM(hui2IFCCwTk>XWA`rCKcAM) zV=^`D#E>sF#KeNMmpi6mM>31Motcf~)HH~)O6@fQ(OUe`{|to+Ag_*cc-BDx;X7I` z|LDfaVZ3Gu)8yJkeIsn<~ ziDDYJ3*hDv`bZ!I045hS9Y7$mFQ?vh=@1L9xR$JewM>BIV>}K=@SbU;$U1{!C^u-$ zH;oLt^~SVf7!bW-9n{`Kr!<>F(w_*|kJr!NgfnFSzl3v=)q@}cL1LYqCEQ8T+eyw7 z6vdq`9U1%gawBYsM{Qt=60et3IdsL=z~`Y+oo)>#hdgLB$qB>8>S&iYgP@UNVua<$IB zB(FvGm^xO=saVGU3*1bU+`IoY*@|e(N#Rx(kMHc;VO*d{ES)c@$Vy^eqyLs*6TVt; zddAj(h1!&dHrCuRv2G}e)zOd}tg+4RvLXE&E349FQ#Wa{h_NB3Ij(N3TwYoD%u2vh z`xAINW7P-4z0ZTegSP?;`3N=~Z|HTX`d=vE>~M(p&+u^}#$G5-bJ&TGrXD@gdyBUx zO*#~(Ee>%Qh_+8fo&`ZjQxa;PZTT3p(%WN0@grEnFAz2D`d?y<_uow_EU*m6X$au{ zBg}0ou#{o&<9%C?>{pCsPAH&)$n|;#N{xEl*FRLm0HhqXe&TeUMZ{KQe{=>xCc#=O zmiY{SBFv;sf1DJ}$(gwS@SvGveuX#{mMPEo;^FkT78@TvD?t|C4 zkp+Uc6eH^1-f~L_p2a8&)UnloVzgN~e-uGh^flf}OW46HXZGG6Zl~CL*6NoKu2{w; z5AST~f}O>f9-8b}jmxfGK`~NVb6dRG=oCqAu&2wUN5o+xr9!JNq&yorEF(H_nv{dk z-%T8TN{wTcZk_M%hu3%!)spAls>~U-GWG#o@my#^HA1^pLY^j>wT{Co)>`h2e8FHo zU~l}DBfA6gAOF-p%%OE}Jxv>oa+cUndWXWgKb5imE>zM3^{z)MydpvBgyuTf>v;o- z{)O2ZCL};~8IeIxyu(fpB-Y!=Qh2c3qm0k77DX{(%|fmdoXuyA1NM_G01}%GEIYge zG!akCqfw6pm%er{)V`@Rt?X$I#-GY@nD~{D7o)w$=S+rAk8kk&oVf`vbJgx{C7bM2 zUzqT-MQxdE#2l$d9k{iN?dit(eoMcLEydPxm0~f~fH_7kviZ7>GskP}mvufa@ z<;u$T)O`B|E`xjJDOIxFY{Vl)(F0$9w10rHqx{K6m4x|7xADm2VY!}$ zbc6_@+Pa|_;#J|?Ey;|4P3q`}^8Ej(W#g>6VGq<6n34Ol<7KvF2m(uFXL;(pXhA~O z&4@^gmF*nrj+U}-JB`TyLD_sfnfJTVqa}LPN8NW}`=36MN*z>Z1BTk+4Amu07>9IU ze}}xO#Y%f6Xk9LoHqC}Y4c zGyA<9@PM%c12dKAS=AB24@y1!w#HFsY{faNFDz$%2Uz?NL-n<2R=$>(*k=1`2s47 zCnOuCThMlB1f41`=pka8T+JBm-~{v zfYX%uJHDXP%!mz*+{c18@sA0W`Z_y1$eLrL%BZuomaFaJ>P|*KkN$=Ulp)nk7SI<< zsjK)xhaWL6(?}UQ5O6YdU02-mm!atSr(vF2j)R*nL@}*u8PV1Pc?|p2n8ph!K}0is znZ>&XsDh>h%kCuojr5O&nZWK{04Jn>hrP#ZHO54PrfFllzxnh=$g zl&_o+&2$LY&m+zXSf!mM-Tw1rh)%}c7O7oCH-;l+x0cg@I{3hvBJ_*5>L|?$`m(+l zZ$g;4)e;Zvw9W9$a&x#Amxs>AyEgWEvtC475{2wW~@UJMJQFmrzJqI{GoUb5-@@3zGb^FI!?c6ytUwmIv0>gDkT|0+sWitj#R zL6f6)H7lenPN$=pRndzeqc3bk_yw-*ML3m3<){5^Ya^Eh7H|0^(m7L{u!s8jD4ltj+h}@bGnksxghMg z2{qk`i8VhPcz;Y%QmKV(yHKl< z1Rt`8)g~M_U3Fx)gKl~THNUx$WpJF?GB`PW7!u1HLy7r@arBo+kQzDka8+Nw>)JSB zu)$dA2~R!)qL$7p0|u`v2fv4P7O=4yU-)U?upg0H&b7BheS9}M!@aYfqnR6ZDErhW z)`62<3%Xe-|gDXe>?N3T{gX-QaE_VKo}C)Qu;txECiV*GRYrzuY@1r zkjDx@b9T2|U+BUU9KSF=5M{QVRVX?%!|`?=vG(6jkjavy8(eKk6xt@F3X&KcfshET z3+I`W0r2j*t#Tfl;XMeus}XeQ^A%1fU1B1=lmCQ4I562*5yQDd9HUgyhR6P*(E9ty zU%_BAfpvDUVIq1@uu>F48HZpX5gC?VumO@GDui2)2Pw^=CF^8{k^59L;gQ*UV8S|! z-nLB8YLcl{iZzD~m_&kkik;d}3+>HP_w>g@N^eKTa*6ocA<|n2nN~fr4+SpQQ_amI zX|iVdZSk_k_*A3A-PNc0`LiV6M0bZkeDOc*?_cfALqzyw7oiB)iW=I%LD(Oq{h8AiN9b1$F{i1`ryd()q^hE+oPKbT3C3oYFIMB8 z|5is3?Px(3jDJngM+W)+1Bg!CITX_f5ZV2rjuF?qo<1GRBAX9#VtXPQv7Z=B!A&Sd z(RIGs<=AG}b0UUh->`rdTFVNn$pW4Fsz*TcDC_kfnyrjoSC~9C=@X<#${iyb7okSV zRJU#-rc3wKfhf?atXEN#@OQaJF!yTgzjbF%5m?km%)dBe(xuAK#P8~#VIjM z;t(`}R7eqEyeV_IL0hi}X3YoksAXr~I?SJN++s3MhXR!t-%Y?G#~|&sxi(^F(GC4cAR+rUBs(zQplRmKMx%5}gkj(_BwVyg zm2i4+PV?OxIxq@C&uQw9TkJLRZ+qrIy#V2PV3#K^-&Pv71ZC21uu|lY$5#4cM&-DR@3&uNf%aa**<)7lfn6+VQO||umB|TJE8D;3K`8=*7k(61$7!y4Km+^n= zMYe;d!JSa+GxOu)c(K^Vm(*mQD^DbtEpCj-yvq)(Bfa=_j6JF(ukk%%< z!)`714^bfYMI*10r|$g?+(GE!H%$PNYGr2Ovt)l~4r4l6fKy&o*ORs4F#~sDmHwns z_irEiQ@Fr%McY`lE-_Gf&lkvbmQ{bEs;D^x(w5PjU;i^|v!-m$vS$T-|Ikf^tKWVvs871VhQl5BfhA)6xzFEVJH_)Ila_EDWcx0-k(n zh(Of8&5DVp}9Ze%p1 zp8g0WWGb-an5*0QQo|?Dwe0m{<0f7I6*yvXNAD}6PXM9*;UPL#yomZX`#sn)3l@_d zgLY|T|9AI1Fs<`xh7zZD$Zx-uP;yZ(@yEqjn8L3=YQd%Ivf%`%QjEEyS0UQ+d+?eUEP=+G22DTEl-5X+<@h+eTD`s&kCgELILymI})?m%&Uw3kM~4<=P@) zeM;FJMsfZtF4d3xM_jU!FLL^xF8p6{DI_<^^JmIj zXy*}RLhQ|v9fZX++j&%7`<~@M<7(cQ2uQ@!FN*g?xqmsl>*qkrv68uNHVgDN9~NcrIN5Dv`NQ2y9ij_Ve5VhBAD!uP*y*P!$s%d?BH+N+-a;jO7#F?r z1jc_MvZ4t8qWRNCZWfCl5@nyh%_W-Q|1p<($8aFVImCTj2h3zldL?)Om>^PG&yY~} zloGab=Olj)?tjF}0qb^8euNzb;_ldqa9OOR8V*Wog0(eyV|AX&OE}g9si4=vXDIon zR{1o*4W2SfPMtC%IN{Pcx9t5TC7kowhedrfo6JAC2e!NKpR5a`IVPb^c#(YY`h8FQWlCUo) zmC775<6EO*I+dieX*T%)d^=545&;}LTt7d+?J%OZ_2tF-QSo|uA4`jf6s!z% z(ri@nl9&`@V~CN+Td7aI=A1+U^(A3{~Htk8eCZ~YK zO_e@pT0cSGS@P@Pu)#x9L;+aEuYB0XxelN^7%e7_Tt4>o!vd>h#MvKAisU|gi{Ewa z2%YV_1~@gQsTE8u-EMN!bf>V=JehknLQuy{w+(8Hy6@KqZ{1gnG(1&HJi`~27cjjM zrki^xFR6QY2~#SBe^hRl9-!yQpkwr65_TAUO|@KeMZdSMhGm@S0(#qIn%R!oEjnUH z#?N_RTE+;=3Z*!FIy@{go4K1SlF}#XU*$k@c&N2?XlhJ|668yMI@JNQ2I3qakEqP{ z?S32~skm$h2qlSbz6mGsYE07`hcIR9%Iq;F2lR9QSQO!s7s?VAkm?}{7x)p7046yV z-@J4ro2QaNTU7Rnul}6K7IpLOA zFv0#RpE{0fWHm!FCm+I@TtW+43LG2}=W_roZ|p4_4T}YlPEDhs7TwdcqS=Z%pKX!3 z{ubcAHx+8a{nX7Z^;*3(G4iYxUpJINDADvSsw%O3@@7jP4I)51X#ef9dU9tWUA5#+ zW0k5uCHH=z=dPi6`zmA^j9u{fv`AiaqZJa&!vggEd;`SuV~4w2jo~dJ^)r?rGj8vIr#8jTcXrInL&C zB(IC;A^WZ5Aq!p8`6X5(Ne7OL^aN+5I3JIo+W|-FR$235g0pQn^Xucgg#)Ze_<>mH z*NxDndq2bg=`K@<3QgTjlI!i!X2#8FMqlX7+i!fRTE;0WrZ$n9E4#Wp+A%NPhdJbO zd;JH08qL`bANF^PXQM6jt3kumX|ee>@%oUohpl&eZ>7NGB#hgv&`r76kx&Gf1tBV& zadUM6Tj&?PYH;mKThRRRb=N*pza~PSw+_LI%-?Ms5L}{)-RwY{5us$9v; zZ=SXeb890IUa`*7L{i6emI+%W@r@-WHi^TaU?fmTnANiL+3z4i;f#OX^)(#x^8w!7 zv5LSq7XFDoS*PmN7+^NeFnpng_NOUw7P@ps`-;ocq-VMljiQAx|p zgNBfQy`Pns@v{L9uOgW;zEqqlLdKxr_aGRK=OJS2M5oJr-BjFr*62nmn0SWQF#SYh z82AQPe~m1wz23@L@Zs_#3_679>cvcPo4+B5 zUKX?_{Ghxk^miBOd#lz zpk)uS6;tUWc}#Ru-B+R~uDYbp7Z1rT^#Ib@Hof_sc#u2R=);@74816#nb${ScX7_4(`g`zg8e z{H4?AglSAAVdQSb4UE}eE^Sq4XgcO=u3Ynt4-?yRS$b*Gsahsnu!Qq0;@4x zD4d|st6JaL$8p0Ko~mjI8Qww8H9UK5Y&IJt6udInIn8NgMr0 z9r@m-bOD>@V`r{O66}(qoAo`iE4&}`)|85fWLh2*5p8_wzZ4wlYapi=t5+k`E%!Ic-&b=p+iWKHbQ9% znqtXK|DaEya{~cP0>75GD=N?$(o}gGs?Ge0%tfd33qFe?IeZRfg0589Phn6oW0-f1ppw2si_!nzBYk2Q^jxVhfzcmkgQdGWT%^1IKkVC&* zXPw?_ZiB0_KaFF>3V|w*)|3caHB6<`eY#Z2WLb76K?B`ELe3?baVP13>b9f*{qg3$ P`~qK}8u$WXLIC+c2~34q diff --git a/golang-external-secrets/charts/external-secrets-0.9.4.tgz b/golang-external-secrets/charts/external-secrets-0.9.4.tgz new file mode 100644 index 0000000000000000000000000000000000000000..313a13018ce45950fb408eb3c3871bcf63d2409d GIT binary patch literal 82899 zcmV)LK)JskiwFP!000001ML0lcH2mnC=AwbKLrl6YuP>{l9p4p>v&Ffw^DMt)ai?s zq;#HIHMMMzgea6ifDM3>m03A!USl5Qdx7s!=1FE>A_9SX1PRhNV0BkoL_{EBN5sBg zypO3Dhi))TylL#E$@ov*Kab#f_T&lvcmLUwNBV!|^QZlTgJ%bio<4c}=;@yx?H}wv zIrx+FWD9dCJ*y;jW5@Z^!k=DwfmhqE?fn<}c*Xrcx^m-mbmuOD&c1;g{Pf_UcK@F~ zdq(@e|8)P^WBeWdvH$o_&ZEv|(C~cY`|mFOcV3+MQTW`sd3?`ZF0(%$jsDl@;NHwj zrm??F>9-f+xHx=VoP`^@b8q2=sdE{{&d;j}y!KKrxpx&UyywnUnl6*)cEsIRi5J7G6uxFQ(s<$e;n+8}6@D|7O;P2qM zbL|CzA12tsFGf;@ zXf<)7OTA~}xZ%vXif(bHKxHod`3lB?j*EL!H-x1Eg&^Pn}3eX#N!kvw94IYgC#~+{ifcDQ;p9cK@9`M}~*o{%T+)Nf|#{c_| zz6YA0!~c&T9X$KK$N#%{e*b;^KkvQxxI|P!cmo(w#EkgwDWSD{|8or3>i6G=fO#%~ zy>$k~Gc&?Y143$|8RBJ$7T@5A5*K32x4^^3{>*d!<)rZ{oH|dxr+@m3^VQ|0|Irx? z2aXtvaS))ivpfh2C?EwS z3_bqMPZgSmkHb9SBQ$w2+jahq@e$J}@nLB;jw&I-;@)&L@ZLK&b z+$wM==!8vH*7&fxvP{!&E?ApShn#9*r2kmm{r zaWP7+#x#9mh5T?DtY!^M9PuO?8YZxTQ{Y8ji4Tpm(GuV>X=J@j&uqoiocM``-d1sBXcOpvhAgD@~)nxr-oMM&O0w zd!0|wR@92Ew=icp_SWX!c5b>@L*W~nttI2iilU29YdG$78m&;{gZd87=&Vu4L+Lj*2pa^@)@$h= z(5C-;_M~9{e|+%$L9hSY${Jb#y9)-uKjTK+&Naq;vR62?#xZy=gPj_ z$QEcsu!=R{F_NWQ#S75SI%n?)eiFa!IJ7Kd202iS@xPo97KJvV10J-@`Q{;%U5djn zj8Zb^B;3`_e#j=Jxa7(&34DgCMeKsQ9e?U3y!`43h&D$=Q)r;ryYX?O{^}>dyxqM* zRc7-D<@P+1WB_^k3pbr!z0!be3+A#Mk^$i)~P#(i4-g$IsMe2I0w8Mrx9T3yQcl%;g_myelc!5Pjt`>X*()+W9STr z(|9(Fz5iZ$D{trnT;m%z_@RjeP}399GXH5DPn`ZCo`fH4h`Q8S)DK6L{D*4#UNH~+ zN_W#xqgl;DXv&7}Y!?5}aAGtM4zXfD0&!X%@XB+8^y)VruxXpz8C=dJIgO)41bW@Aa|j(Eq+wnS2Q7CoR*IAx(`c8 zAa=vK=lt8Xcem&K+YK%U?Ups5fse%QAzZ9HKm=XClmnF33lPIEzzf`~s#ao(Lh!BY zUG7jsVU<7P7AAQ#Im{6EX%shddq!uHJ`+c2G>wAi&c)Gb;hk_^4o%ZMzyqGf!DRR4@J4jrhK{&%2kGcylz&BY)ZIXVVD& zw$o}nVJ&Hwx0$>W0l@9EDd;1?V@#Kk9KrK}4q$ZgPe3d@cb@^>=*`@{`%hZ`@RWQ_qa z4&=WBKgs^MMf9isS}g&3p89hsUsld_w(t|2Xza~>WO%jRshjhS8~8Jdy%O3q871ZJ za^`ij10=nSkZrG;NABDDq;A*x)Qwm39li*gEkqp39f5NiMe)oJP4=JIrq+2CKu|rG z;c&PCruY$lfK&KIAPku9nRkg}O4v877Fw0jVi#*mR+E1MpCeFL`*?OnH9H1dt}$dfRr04#iqDeB1UCbdeNaK}P z)nK`%y(ov%^VY7CQnt9IZg;Lym3~v4HboH86Et2ao7sB%Fm%=hQw4b12P?v>FjA&x zF5BpembtvXq&wxvL7QT^f4+#vp8gB}bqhA2P5;0DxRC$l@ss^t|FfHCQ~ghMSV2(} zRHW6C>9-@nSDMfv~nvnPH2x1Br%d5D5TkFjY5$rK#6z7 zcuypmuPF~eY}Ce`%}&C9l9*HxdP3a7LNuk=W^6i~y2DG57wcG)KmHi6>)TuagvPzk z;r3#n^@n~N$Vfjm+jG!-iYejLjW3Cjt=d9jtX)Sm&J^+}YX&%LX z=9xDvd|lHRA0C|@(u)iA0<9oFSG;@$zq5oKtYSAPEs5SGS0GovaThHg$2@tfz>SN8 zpmaTrP}88cZg$U+8G)2_gh6_SfJ_!DuE}v=nDFrQ?KzYPlfI=CADH0IS*zYtO_kPG6SQJm)1}3 z9*&#~%&H~kLr|PKv*AewL@VQf4;JUkx0JzS+%qs2^*^jO9)i#YX* z&bb;2&HY~3*!#HqB7}t3kq{unX~Hy|bTT-a92Og68ps6TDRNvQx^p5j^NdK$f{u8? zSHa9-yodQ9r_nt0e^a9+65d4dVV^^h-kE&b_XvY7+&k!q<2tKQ8{vkLqNCDn=HMs^ z1SSeup^xd9aFIV*Az~QMa8t*LKOegB^vX|Rbb|NNAJSw)LZblU@01<#egS-}BgVi32onk@Vjck#agh$-&F$T=dyM>mwLrZYO@M}R+qBcu@91~sKc9U}P5fn5yI z%y(dD)!uCpVs-d{a02u#|JvM#fXxYtP6QgQZd3s0>wF(vaw3R@ zc;%>fMW7K%r?#V-Pqob$ABpu(O1H9h(kGYB0_cvv3@T=c7dL_P9LoHRp0kW*oNpK@ z3ahQ@cR-w+rkH46!`Exicd<#uiI$I0ubjddQ-29ehspMw7dq+tBlAvWFM)|7D7Zng zrOjeSO}H%7J-HPe>0Ut0ThvcYW*tVVHO(!D0>3DIvFjjy{()o8>egbq9()+*HXpyz zvM>)bPg!;^CURNwwgehOrL7u;jF#`2&L5v^ulBk(E&(IW#Jpi*@tw>M&CpfE?SA5X zKs5e=lb~vs2Iw2+N|jle62*XULnM08QZR}hTtm!{4PO9)=%?~Ek~tr?w+$lr7+l5d zA?A7=xagUA3b23);~9-TAmuTg=B-SdwbQn9&!j{#`G#3LC-aam)g}LqoCoY2G8t1g zjt-gk9u@wU`>FWDu>4sbuVc`(B3b$HrR1Gk^IjimL-cpMzuO&7yT9ADOLEPL7@2k1IZTWZM6MmT{cE9<&d$kq+vd0PWYv8v?*jP?O8i|a24;a4Z^Ln-s5I-?)$ zwd>q^K`^`yQJa-%vS~z;PEXFx57FQ5<pd9m+UzZ7O%XyF`6jCiMhT$yxt^ zxW6!I2IpPZ*PXpTbeY!_QD%K zjzY}8c7g3G`U}84cTQK6z@Huk(W*)lkk>)I$d6mV6jCmPk@OiTO@9Sz&ee#Aa`-m+{->zwwO{k`UHp|+RS#y)5nz8ywYG;i~Q$+~m9Qs3( zg;G@#;kb;V3G6A?!Wz*&OyhiF9HW`B(W-4)gJaG|ON5RrERrVb3fDcJ8XAzsXibtY zNnp~fI3t;32q^oFKdUg(ApRn2l4h^MT4ZEV5*8n}OQD`mmK0k8)L^70)6pLO8heap z@td=$8_%+vLAD?P)#SO>{rTcZGf?IHi|Pr7HDZD zIft5U;1@q4v6C=L)}(a))P6DF%G2imCAiRhVz*B7KR?}n_I=U+`{3aFzW(!09?ezi zL4_08!|c~`R=|A4&r*O^?=bewIE+)wHuw-D!g_Kg5>Z2@!&s4 z*zboQ24|QFa`5mUqE*G0;eGSxq%YnCaLlHzc$LQpr3_MDv9p#GnW_(y1E0|u<=%y? zcOmOt$O;|SaUrX`nmI=@Qo48vNwSDeNbOSDttiPhnI`gB_;t;?O4CIJo&>0T&9Wz6+Z2Jnl;V2y?s+--DtFEH1BDphF^BA z=W8s0P-YU0mVnnyQL_9DHb0hi7o;?kW4s*t^_6<{2z|)YThC+X2d${HAj#L>UE;|4 z)~Bq0jOoKQ-)%9P<))Q~$3~NM9uRYd6C3?+`1{`kN1&45(OReQ$sR+;DO7>swixkrPyIem{G6lHJDJ zL*56%14`bPJ#?9#VJuRobCtwe6!oy1wy5rb2$fDhaf(`dIXRxIzWY2bR=~P5OlFQT6J8A+iV+DKNf@+?$~6mW+$;j1{w(y zqc2!_qE_oMET>M2j)G^Cu==06Npc&-v%Ny_tpila#COzsIi?=L4a?Dms$J}XDSpOh zNc&~fu*K_2pQwQ|JzKup(H=e(h+XQK3%tKxG)l7i6*6RtOd}sVbg<^Z*AKfseTBCk z=)c0jDc8q(nB&7fXm37zMpxoZ$wfZD&`*64x;@`U4NWFE%!KF0R*Fe#(N-DwMb>-T z;=TC^t%9cN6Ksqul#J2i8|fnRp)MD5C>mFrQ$6GYGco1_d!Qhq{KWV4s0-G-#Hihj z=b3btIh6zP$dxYCpB62Q89tztTGN&Yp}b~aGWeovwq97xm>IUE>&F687-1@u9PFL; zLUfx&9{cqQraxmXM~YuU3{vqk{!!%=X}frQdYwqa`g)y6cXRD^B8F@2bs}G#PUNzc z+pH!r*iRXQCNBPpnR4LZJ0z5lCkYTDG!&DIh@de)VZWZh^aN(prI|?&xe4OgODYbn zkgdBG;JF25@|5zpjp4AO}O#+SER^pq0A&m+Z=K z%dyc>k($R`S2h%DGe(j|RK*grwMA`jX8qDFKZ#V3W;%9O3X3}Rk}?YyP%0dsWn_bs z!48msv8K^tBI=XzO_vH57?Owr1>K2ZB6**c0HP^3N6xPpT%S}Xmg`VRlU2Q6gRku{sQyHT+&684nnNgMi$9&=a?r#$%p!WEA{w0 ztkcUQVJrvt9)!tY&l#+ef%5FS6h!??X$?>zWlF!P#&4bI zB#HvB;dI*51RXs!rDA17C34!ce>dlyMa7R=?QtP0ZX_KIM*7$Vn%)f!!h}kk48<4# z7ovj#g6m9xgTr*fX%-DGaC2d@T_nV83a69Ldd8b=Bysk_IRME}*`v_W&10kQ8%&kl z(##xIG-OEX=X-Kpd zt}PTOZDGlu`~1yiX>o9xoJi$km`K)6wZqxxUb}|&eOoJ=@vPTuzRryJG0ga`Kh~Bx z)luy7q2(MkocFHXlu_~B^(Jg*gOg9YD_7Oz4ADWpQ+w*;>@ipAW#d~w%obKdKH zH(V1|I^?jaX%89z=Ht9=m=10!?CR7w_9)AniZ8)NB8gk=?9517D*?+b@;VE1;nm^~ zG(+~96bod>$E3AJgyH3pu&|h@0$-NxjRAZ=VPN9fx$>97Yl;g|$+UQE$dg0xIXMY2 zv?ay=y!hy2+Be*TTDioE zau!b7pEjOmF9fiMeOZDwYM3Y_(t(9KQigfd_4Hsf0vXY&LY6ADkg-x1-_LHUD2 zJ4b*;F_CNF<=%-vI(--%0fxdofJPo?&4(KR7>E!qj9#P^d(Q1ulra8O`tAYzHP|bi z?7>Mm*i%`+a%WlLZ8GW&=-pr);<5?7cbt#IYh{5P0yKu=6r|C@pO)HdiDQj819D=h z%@M|Lhb0QiiS;TPm3X58p#7X0)D5et80IH=5au9AmGsj1j>3X$ zpvjMslMOE0aMowk7^i%DbysC{EpB|VP}w%GQ1eGNxG3aR$LX^zZp&#^uc{k!OYS+f z8D6OZlAd}#|1?~i7MRvAxi6seO%>2Ftp6h3QW%Y0@s~ff)PH&M^!rB#j|=r*zJGS` zw6FiNi)WMiFFB{wzWB>GUi?Kv5udQ~i*_dIWvT3o)}XHDi@p-3GF1g%lA3}qTJL+^ zS9?+3Z_?WqWofvsi)D#49lm@~BH&mPqIzJn(ksj{mOgb}lwc~@j~R01xTkrRL6&66 z#pZ2TlO^wtt_cIMHz**eMEpTC&8jowmLadL(hG@*RrZ}0<#ka(2t3&mb1{JU{6R)r zeGpDoeLa`Ho=acPrQ>=o+I?=X;-W`<+v%|c@oSat?icc1v0gHc@!v1b-|9Nb2e9wq z^>f&vIqH8$;=f0Mn>dFkSOEHTEQWqp=!x;!D`WcGqG~d)?%_6CW&4SOF#pXvWQ~JhtnwqgK`;cI$$b=Bi?qaumPI|GlvZ{>0W#18c}bv2n9C!Wo9=;|kqStvrU0DQT$ z;Y--ZTO)5Rs}&?B*Q0X&Z zCQHUiG~mk0UNB)*lA>@w?iHw{^l8Z=DM>}T7v3gkHho+^F3baWGoS*+1Vgt|J3bJ!oGaoe= z#(vuchKzeFU^0&X0Hc0@v+Vi{6<}5b1Ov7PO0a4ayKlW97+#0bZKx@JX+%m;baHln zh#p=qk4~Lmqxd?A+?jKNYF&_Saafzj!(9pr&0@s_*V5r0ggQgnK-qPB5uEtDsF(nV{b=d`1Ceg^bMl&x9 z|7I_BU8pk`Jn`9yEOw!ItwS+{D*90*Wl?ih3j0ap)AQ7=PtQ|-8|d4Lr{)WPVG!(t zPcc<^9*rqo@qLUnAS6ZEp9|+@53LDF?!tf&4_!X6Eks@fzCFDF-Xn|km(U{%qvqDg z;?#}Z*7Obd^cbVZ7zQxvF~--3F$|j;HJiFWuELo`?y}Ptug7SWetH1cZW^I7=IGFw z&>*TB;SEMRqC}NhE7{mWesPGJ;7?&qW=+{;&g>+uq7MGQFRieDy|_3%M;wzz(~Znx90kJ=0Mt^50EDfDn?83Yca4eXjP`u$o;H}{ zD}jEY8R3;?E(1H~+BXM$7bewq%w37GRqPw$T`Z_AZkJjef>11IkK^HJ>qs;oFb*5k zhtXt@@%1x7u#JdS_zaQ%8G4kZ2C zsF56)BUhp|!d z5G#HQ$ETtgKly=oYE;k7HnDybc-xqH!lCD;4l)*F+gJw*`(xN62XTWw0)_@HHK=@A z6NGl573y=YqCnLz%w%;AU{^TH?2_YSZ+ZCHyE{2H^4qIi6cXseY@pr*(d!7dv4mbn zVAR}7s|XiXIMH`=`^5?AF+h(2wy=aA0~j^mlCwviOR(uyKruViU&h`X_P^P)qEbdY zqmJ4|S9-M;88DmL#zk)sb2hw|Yg=1%zBLSq`SsjRWQO(vPl#aXoy*PlNn+Mx$1rXV zb+qK_@8P+0{4$ukEkQ+aaLdrOkcwQE$ zH;bmp2!*fN4GhlV!0{Y9MCV}Kq5Y+(sK1~6*AbB8!qx|pkzfKA%ADv6EB##1_r z0*@Vd>0gIuZ?xPXeCUg68y4W)Bw01Gv_^BSNUzyAT_GJQWe(3(O0FWs)g<%a%Zh*3 zCsUP=Ni|tfa72Klr7<~Zdzh9?Wu>crI?@pGX>4d9;Iy`i@V8a$UEi3g39;?Z@9b2C z*vCOK+FjHmC`LZ+3mPnLj>;EX-HHzE}}7Q?_OMEQ|HDjaij1o=vd;jWK(|m-5`Glwr-i zKGKHh?{NX{Wekhlg`GK+eCQ<`O$<)K`YFH&xABI!F@Zy5 zbuv|x2v#*jkNf;&2F?R9(TA8QQqsLF5`2h*GrK*LR;7U) zQT(2nn=q^1Jj_f=%V_32NIcJR-PsWS82X~+!zL@KnIK>cd~9A=K}&0CM>A+{bWr80 zn|a0DAvw8p7O;6lD@tHSd9e)XJ+yjL)?_U`D&~00Fmp%ZqM4buN^s12YPswxEu)wR zq##dHU6!hZP{sY~g1a}i34g1$U_%?QH70Mj`EDEp)|riW%b{Rj8s*+?7?lx4m-Nj% zdXqaG=QSsix#zc>f|}~wX(_`yP(2s{kl2A=;>IfOxh1hSXP3E$O|)fl8S84pF5TYP6i<8L8gTa69e4T8`tzo=!jdGCb(4JcmZ(>vk!)Z<%s z!ncW=EYJ?~9tMdjH1D1a{qE3kwKO3r#Y)-SL-rLVu@eMwx_|z}Og((^qn&aX+kc`gfooBG++LisT!pMg|Cwup zmwvAb?iqo481;-m&j_?K0!delfRMm#d?SRWNu(}EFu#x`;_)|fL(A$IPUYr^I3#6e zlxjnHmp^Z!o0W1jqPM#@9)w3d#I=P_vkTk$6)&O{vO6n zQxl7HZV#z|6-R?GhNvny-XZFqcy8>)Yg*2GzP9IUD`C|0wOOlO%ROxy~pU|rqR%8OBIKxd1>d3xXl;$kh(={b859A!qX7I9;%55gaT1yi=I^<4_ zj=6N}P%GW46$Wh?*02aFKE~(PtQcx(QTKDn_q~sB2SYa)igPgXqw&q7(ZOhcj8ts= zU1fuPH&|Y|`=g^nIfbSS68P*YRSq7cofJvMXZM+IIw+XB*2LG#b#sx!<2chbilzi7G$6t)u3Pvha+%pz zN9~l8dv;8uvZb}c-T(sIwns;?JFO>_IpDmdrdD=n(WTRzmOJnU)2B`4)4jzqz1!pB z;a8aylVx+-V`1gi7cHP=zBEpjat8z(oY_v@%L;xMvaSJE8*mfc$2eNeF_|oo(bT`m zg-&;w7w-}dg%=~#>=mXzWqqZ;h&G(0nTMw*xGOiPH2@Z&m!o?*di^Ezan$&*G@B}{C5CX`9-SPESQp|Hvyjiu(%#{?R#W{&OvDXEWFgP8=zc4# zwL8v7XX2+!VkZN*r4_h1LYxEcEKUIDm^C{B=2_~DXZB8hp0#Q4$9=K& zGK01}0X)%~SmhlAJGB;hxM`60TQ{{4Y84Q(k%Ry0CRhF!BmlotX^*>!gL0S4vzF2< z<=JzeswwjH7_=nxEtigJ>nyzKl^goWVlP`zOmD-PJh!x2B#Y<#%?pf0m!j{|ZQi*I z211hsFvD5fvdZ^Q;X~q*mSB^yz|O>-USl3jIJpZ%=nM^l>>hgBJa-0S27@+rKR#A0 z0*=~VyrZ-1fSZSiW!bgTv(0tTHcs*QCuKsar{-8#khENPVa=p zKXE2^q$|fW!a)(bL%^P{bLMa^SASpn43eCMmtI9}P})&s*HIRXM(z*#+>4YW2Z9I<4e!_>}SqgSY|&JYy}IdAxIr3*0Y~Qe9~i*9+PytgdUR^ zHSabG;Geg4jM#tOR`OwtA~fbpX>$JS*9#FXkRcHGZT$A+_-N~_R_11ukDpmDrr_<8 zt1mbegB7Bz3NA@{{1n<;Pcw|@rPPLfppnnG$p9$a#q|1qe&J@`$E6?BT>?}dg|iL!G#k!IeWHWNpy0Tl-}df5!n`3X^e8l3v-JP= z!}RHa)rLS1udTSYeAY@ij!$J(afL+aj%mDHs*!S6!+Ql>WeOWxB!Z7MA}Ou%8w^>+ z#}bd=vj#57x|!n>d~CQ&e0Cegnytzb7}gwTX>DIB*SRLh>_S`dF%ECdW@IQP+Xvd( zrXIJRlC|LwIPXo3hR9pn+Y8~t+p!g{WhrDuCa#1;hI%cg5*jwsWbVissL2XSFM})- zUDm{0N0W(9Myi=+$*Q;J_4=9hm(c5HjGBK6Yo76!+w)q=r!K#xiSnm{(_bUAa>uQjI1a1rCY$v?%bWzL}W6Yho`vc}k#YS|d{U$^ZyiyFH67I6MN1Fx@aGyqo_E*)Ef)k3q-2A?{Ne(yqUmclmLrUhw~74aZbn6^AA1$ zu>KNy{=ulZv88y^QidAV>PWd*r+?EC)?Q@`fjF5M=PfvqE~SErSumbuFmsny;^Zsn zU?fOl1Q^0}j6d8)@eJPhv8L~Ob$ocbbuR*I)h+^LQ#r9}Wd;%=e!6?vaHno*YOf_d zBT!HPW=n`f7Y75MUWRFVdKt!c9p4DWw31;Ydbh?Zzle@(Jj(C`{Z@%23YcMMHl!_t zZAhqfMZQTqFs@LSznrowjq1C41j5*KB^0{k|8V{69}QwgH%5rC*@4;U>6r4 z{6x99FfL(RS_$^R05fgC23Q%UlQr6Hvox}$TdR^}O3pdkCXqJNXrmfFX^N8#@bRZj z6>EIyOi<{y)5BU#)(CNSpAy!1jwY#Kca{d$ECsA_`d9PRuN^1eY2^o7Q@t9|48SY5 zZag~yDgf6LaA|Xb$b4eRa$hI)f*=uy2s5B_6Iiu78@iWBaBpKj^`v9DqPN^5fM!S% zSY?mLBk>nYKnbu_@tw31kh&p=qNSiEQ#XVzu$?ZPA3$b=w^6Y!h*CuJ#Y69f?j-PL z0!1P^8mS!Jx3{+=3Lg+9(J=-0ESe@`nf8K2igD~M10h~~=LSJ^`(CUOX7~Qaci&&p z8d~>d5=F4u`lLEW7eB9WdZXbeP8p;I8D)G_!XlQiz+ZZ9N;zr-V0j;2gefKbqNJ*T z-)Nz7TfOkckE3wmiIPxgg^A)6ZxoE=yoNUZP~?Le2_;sT><>oHYfRMRPSCg=X4Ap# z1D5%c%+;hVYuyr0&2O9y)TARbx}0rV!3yo3Fv^qej`~aZGpK%kr!;(?D6uQmdaQsO za1M~0$JkVu%)D^!hwvL7&?wBZKn=zyunhP!$?Edb|43{BRT;p$p%)p*vP(rOo(B`M z6m2*@vr|Hrqj(VIh=3m&Z46bs`hNqC{a3vAjZ8)%`HJ2RfRfNL^^+(ds$0S+i3gV+ zBvsBBKvV^RGV_7M2UvPDRgYJ@!c_f+>fJh?3xr3YC2>@>WW3_odN%;2l}jusqrB;z z(%5czlC>c>#vYb6BJK;mT^#3(J$78qyRG<7oyC+#SuT>OG+I3W%i~9n z2AdxtaTS`EBQ*6lk4+c-PFygtXmw*Nuc1b2V23>)1%796vp*pAC*_k%}hKtWhM&P5Vb^ytxdy9r<2>qL=VD+?^dr_zhdt%lO*U%lN&F-^=*o zVTrWy?gN|WvzTydh${?1kvkKA<%w*%9=7TkV_C^DylyiF8j3TCO_JX;YAy&(+6*gG zH2u#c3Qgi2@@YPxlaf&3R;=QXadtOH-e}_lS7r>nbHT--A_)aoH022fE_Ileciv`QnYo z>(t>haSAc@TQTx^HO(MuW!`S~+Qxh}(aN0iUU2(O>3vHLyh=_3`ruKRxndjMT%e2}K?u5@L?4aRGk>EVuegVml7@RavTGMP`;RjLY~FwD2P@w zDsm+C_Ra$eo45~yj;8o_#K0MaUi$pt;QRXzH$7?gB?bWR_@M|J(RJ0Khh;=CS5whx z&Yfej^0WZ#4tP@hN)-FQd9$CryER?Ut~_lRqRTa9q*_Mp-OQ?<-p#CO?q4FO@X6fF zcA)6Y;od?IwN#@f=ycR2|7Xm`mg&`%PDApX#G|W>!gg@(a>FFRRTwa=Ib0K zopWlPW7DGv5wjUPp5##o^(o$nrcJ`ct)#ZE+@Jfd(y&L>FU<#UUQFqb@mJ}6^XItX&He;-L%J{} z(<^V`X4?j6C_FqpdH4N!;YF!T4)GTnoo@p}21&;gFL!$yK!CBri8u%_H`v(L5wg3Xi6kF|rY*M3D(rWC`0N_fX`w zV~;!^R-rZmHAytD$mhzlaqg$1>%S!E&^V12i&f~ScVo&n3;X{BfyOcPQMU%QyScTMx2$4m!f>N=+@3zsu`S%Y)u0Y; z;nk;>>{2?hWE(0Q2`THTD-h@S9aJ+W3~)SaSN>8cA~~u@((8AqPE|wjxjG5=oHtR5 z|9SDzM|y<&bR2mJ{7S!czIdLF_0qW;9m|mA5sC=Y$atbs#VIgBYapcX53xq&o0^Cr zWR2Z03Wwfench{7AwUCv=#Xg`P7Fn^&-{iNx4=cta<+;YSs`6Xy*Ut$g%{7!0ST~% z3BWB&LX!z5*zP3j9cp$4ra$h*<&nXZLU^(#aKZB|Wdw@3@DmMKy>I8qmAJ`ee>%_l+8T9A>F5hX9|UI4^0m@TYD3O2f`K zGAb01*635ydbf?vRNL8KTkYd$*@Iho==+k1W~wUU$>JFQ{o?$so{0DXTFI_aH$2A! zI1>Lo3fv??L&ZZnF@;X#){Y!#>T?{ANGL7&ajhvCY4Ctv`az0P+lzB&;^Oook=#=7 z9d}g`YK<^oHdsJg)bn~AfrzicY|Y9%Dr{a!rQ`a-{itCgrz$dR;fHNa+Sn}@ZhQbc zVdU|1C>mQi`>0ML19%6{K4Qd|zaB3AU(Q~cd(z0My!@3MetwAqmgj}&bd@<7vsshk zZP7>zjhYabvD1%{5xVUCqrv+sy3OO~io8v7ot*>9LyjFANrjayt)2Mg3&E+*0FSFa~-<+i4$ICE*B7#OB=p~ zeY`cwyZcpS@mCkBPvrkKIF8`cC&9E`$Cv0(vIdy?ME;hE{By{$de}aff1k_Wpi7_2 z-+DdY2vYvCa`{W~!9;hO@$6kGMva;1LiBRZ%~>!hdCL&B6#An6Itv+ z@mhys2u@?NPMnGDr19x_>ei>{slN^MZN*dbg}*S!LW0j{7_EU%k1X1r9$DC~qem9y zCqoven1rmy7_BSrF@_n8dW`WkVvKI1W9^ePj?pUp^uQ!nV`i;nV@pf6#>0YDv^j5M zpLwx9Ah{9nU6@qY%)OY^mxJkRbIK;tJ?lU!h^O+irVvY+4y{W10T2j-lbyk4>ChnvZ@*$r*+stk49Dzh1PV~@) zACOfR9sas>r(uPrt686rG-yfdxzEOyrbnULWvV2;Y>6co z3^r2jbOhYCZkXBB6CeAKSrXKeXY_ik>(=8u`m*YZuGo>a)>hELAB^Ymfz5&hz`}t^ z*h+krJwk2M>d1ZNlqsbmslV!u(jZ_=Q?(tu&M#2E8B>a(!BUf7kwPHeDe&_+KRlv) z*^;I$QKQl{PY9Kn+GA|GIEZ*5jv2erlco)U@((o~;71&x**0J(O+r^Em(%+`+28b) zM#D_!<=c*b!7dSlnkcThNRzZQHHsc-_5r2oPF;wYn9*PNTe3SH>8yvZR|w%v`CYJ2 z&G@EV4TV&lBuZ+~V|~O_XY|#xhodX?Nm5q%@d5x+iGu5Hc1O(;bsB`kEd{s!&ln@^ z3?_Y~@aureUpOpnL>lIJm$oVG7>It^rKdX?|71->LlP1!<{^fumq1u&!8p13$zTg6G>Tc#D4c^Gt|HkXY4uytQA-pfgS!kWg;73%KRr~TG)!B z(OG9Rv7-}g)-VRq+rv>h30Ny#|JwrVHUgo&_ha#r(gtuY(#Cj7c-EUIZr-GN4|u=BQ59QWC3go zf~hGAeLOdJ!p0Z5_yIF#2uM>+C>A&>4aX3uF6+0u%g32-_LVQ2(#FIQt}&U-QC*^- z&|m%^Oqc^DnbR-jD%QOtJ7<(X4U*9M({gfgX+&VVBFw32h>5T|ej824*ub#$5=d#~Dw16}tT znye@Ee98By;#)IOZxn89N?c9VY`9c&o0+q!L@Vl4sU?!3hrBuU1yw6KBu*b9ZqhyCCymIp9(R`?E1eq$DVi z5L@_1Q%W0!lB8kqSWhW)v6LiQ2jgp6WJZkAu;XXI>2kFSz%ZmHC|dK;P?4m9z1)iXaB5J0+L zUEY#j((Mq+=x`hTL;)`wqNe+<;r=*2t$5g#fqkZ|D48XD%@U!uD zo4iHRN;+DYXG=F1<~F5C%Qm+!P0Y$&He_j;aQUGS3pyKMC7h@eRys>Ik~YsnA=;z@4^E=-eV$xx*bzHUKigm5S#Pd69NVERVd@P$DXgV}S8+zMuU>8M%qfTV9WVjIjc*_v7 zjP#5)8-q;rNxPHFBab=#=9hOA&^k_}DPs`-OVqlxbG|(+A#|A+=Y0a1b>~++&7uqt zU-2cUJqf&?F33$rfYxWn{puff2iiPY4Wmy&Y4g;1w>v=`sL z{*fdgev=&}tjKBCd;W4i3V-xVS-3eMdX)J~AqS5S8OLVPB3xawn861JNqQA*hL*T} zy2FMx)QJ;JEd?*(k-2-TF9uDaFBURG-Fnm_DjYDCTuR2fu!*gnp?=1F;%0P@~6S z<2Ph2k!Qf^e_6-rlvLca|9dxF=jr!k@mK+R*OAT1SpMAIl8VE}7-bq{7G;~ucNC72 zZe5W07yWr@DiT5NS?l0~Z^V$^F1@D&qq$4Wf35k%ny8}UKM8mWG}jSg|FdREbjcu} zF?ld<*2@Q{`!@h*)o&EB!g{?3$-!pTOhZasM^-la zX&!9Dh!Ze>*aM8Je9k@x(kL0`gWU{ZMxK$?Z2@p&#kaKss3-R88G!CkLt%;XR8su3 zIqj*)opx?ae$=jDe5g%9d8Is9*L^*r=+OgLbp&OF;ILjPfUq=1r=h;FNcLx9RWCBR znSX4}RS_Pi(s7DY?ByF8qex~z7W*HOoeY1C%0yiJ0+lRgIx7v>16vo**{c znlweu{sKmRyDL{JFb{xbXh~2X42PA^vEjOEtBg7c_4E3;r%(f{oWlJ> zF_4w!26K*1?CfMeBuvcVuxfc+bUJ6Kw5vNpt{5m)aP`>Gq<5%5;b&&XVn@or|S((`Dg z_`>97{GTk}Y_-7?_9@@Q(CINf?KRh2aSwG$KE=R#=eY))gA8b9ad=bsF`N8J3F&Ov|xl|3pI)$>9oi>~h^AiZ~*Al9PQzHC48a;!m_% zJ5(ZA>em~Sg-nGhuO-gJ@GO+oV8$-nL@cWN<{G}|NIisD$NmevWR}=OP}(9nAcH}i zP?t)ml$4BACn1CAgLJHr1c=&^;HNICx#sVutGh$`YFy3 z56yYb1H4rXf%365onl)0x!8-|`xS1*6;N@SE<2T!o(7eZjSil5_9~xAc!{(bf0nqi zOeRP03c=2%saj;=7s1V}qO>3;hX&1lu->?iTZ1JD0BQ$`F{Ft=kks=Kkmvgy_nZ^% z?$FDFXW&uR1KJq_!G%cu*VLrX1gqWE%wQ3<6)QyThf&tscYCzp{W zprf6$z_Xd96RGNO@c&^IW^AX_9?as>J6U@2g)jS$F+W1H@Zo?b#D|yg@V;dxH$2n% zN&2Ei1f~v4tMkw6G8>Ay1x*_BAN_ZA*jbz6&4IZ%!xc9q{McJO*mzNoLq7)wx0KnI zT>}pi+xWS-{wSK2^@mSY-QL%Uli?S?e-0NECU-|p!W6(fzmaf7RBs#{rD`X~hdCiQ zuPe0z0nRngIx2Z@9#W1YabE{hvrd@@*7ie5j-XsfVL&AgKI9fRN$vk&7LgPox62-x z^__U7&bwuz%_`{m)@g6|c8tIL%(-^+sq+Wnjai10CmxQMse} z!Ij-T(LTc1tj*+R2LL{YV)g85&93WYY8AkdnXTxX1FR?F?05O=o~k!z1#5m2NwB8# z>(kOQT4T-)?cXei2+@DD979avN@ZX&ZzqvULKmG&#iJ*+LQj&%wE^D{0XTQ`Do(>> zjkM~jQQ0PIBOJvZABtr-QN~|!ilJU3(F}tm(W-hRYTPA@vaYc3=G>nugY`ckEMfRt zV-8meMJlN)q|^UqSXSCnrwwhklZ^=WV8%`yaBffTg2e%ux`B<8+3lrvBxDbT%P+kd zoPjsVQTS14Bz)RIs_S7|Sm#~0{69w6disR+e%P8C99T+;et{_a0s(1|&;%ALNUAeHgXAJMzKS^|HM{m9 z9f2qkTKi)+#^QKL7I0==hx`_eEy5w>^lt7NNIdb zN{=+RX=RBf>Boky7h6Q|bo?L&P2(!7tiP`5@5uh~SO-3E&z2#v0A!s6bv4^-2(w=! zNoR7>R2*%J38e+~4|St3@>Wb&l%xtA#<^@(3juKTxX3-Fm9#*ZCLF%hUyT2js*USS zqbrBUq9OX_H!%+s7Va>~#MA>S??bFD3*7i}>6}y~RSXvVgu8PbJ0X|aW!{I?vouw? zsvM<<$SeS)?s`Baq~KHs6>*yd!-z}(dN&P05@sq~`iXJSPKlWxCPl#+aswixP#EmU zr@drb{9RM{Gi_YfH9?yTBPA`lpar$3c-b*>zjHdXF*KOw2zKZ4i<*kXrJK%<R>2xx=(>{rNdOa|~12v~wv9v>Li^UV{5G>e>_b7cJ z)YraIoW@0}Re7${{2zgA=Ftx=7|=rGTk@XhuZ!yG7q8bR@~&rd`Iq?4$ypCAqgsS| zspU#FXc>hegw7diQ_AQ!60s5`kOpp0GE89e8pV+~NnAmeKE z8kP*+hym#$@)tGhFMivLl>x^pW9@kE49j8z9V`0&-nq5z^_PaD5SGe@kl%fx(qa~K z+@@_XUKJ?Ws@1z|zx?P3B1MjGYsv9E*z5bhA9B;5&*|hne3X;AYl;dB8@)^KouuPZ zEv5DiQ-I{N<8y)Za6)XY(5>t2K_mc0x&`&ko&nTN5R^}=j>KDB8X z*gllI)t3=^8%CO6?u49T_xOCjTsdUJ>#{~Y+*Ha!n0t54#%Yg{!R!Xl`uy?5Oghkw zB(g!2lR*^AViXTMCsW$qJ(+l9U=IJSkm1&s`D5Wz>ICFQpVOx`)yRKGKXAD7dOSF| z$>djlTvW>4A@fZvpq7~GoWAYxM^8~{#n_rM1I__fHm4W(lYqj#4=Z-x!<( z_chl@>n(1|0`wRNfpEYHYHSX&RfZrgRgkO1%fEJu=}}}6joL>E*VCyaDbfvVx`nFP z%p6D~T`mpBr#)7ZzX%6POCEKSjTd^wP3Dwd1c?X!y#+VI$}xN+5zw0#%&v|!eL$@} z5Iq0$E!K}bl;%giQR_~fSQbH%EMi4dO$VXCqm!WqA3ZTm>u6e_QrXQ+RfsMuM=d+= zQ_<1p{g32@NO#?elNB4bJ|^r>r$cMx2Uy(CqD8y?or^&4Z%z#QUYlk-IUm%HY!*fv zfSnVRFwqO@VwAOLgmQ2m#e@D0iml&p0_LLLGt7bJLc-;?_`l*Ox_Qq z7;0we%oVc3f0zDCijnQby)o0^S=720g-cx5_Na@X$y$dyL;RICcVVT3mvCi^o(c*e zK(FgT6P=Y3mAa427!#Y0^htd#@t`a)9?48=Z&mx_2pHX&ymN69I2WDwghrLN6oJt- zPYCw$D0qj4c@%;7>ZnK`kC!;_&${!cTZKH?pg>@Fh!K({R#@Or$l!x7|fSA~|%Lxk#z? z7zYBv8h|;eOd1nN_y05Ikc}c1I5m}X2Y)KY&mSojn$m2@YZe>-7nod=Hb1x1Q8FM8 z#TsrEn-rJj|02cm${~B5+W$1xCsz?zzE-IYg%)c*snn=k5DTy+0?JiP2#I!!+mhj3 z)6N7h8VgLpU~XT{B`6jLdb*2~^;ffWLTa^#*o7D?jWEMED{DvT^GI-FncfAul!?PZ zOqNsO&0(n~N#|RMw@Q`&2!1*owz4HSaa=veH0a-tbUKLWa_0H0dLM*+Utqan+xJsl z%d7l-!I&`zU3^z`*q~iVq)FD=ZzY2=6MS4}m)Wu0>!iiHDifY(-r~L6=t1!~Tq}98 zNwqcX)FcYYdTO#@bN1B=W{$iSE~eK3WlI+;apLE4BA?g1nEc0+w~n>ZBQ!P1?XVfr zlUWmDv}$2S8i5um#EyYcd=yqOXNC<IFbVp zQq3q!+mA?H?JCh_a79}RhP7P&HlUpQuOv1C=8k zk^_u+?hK)4!D=*bviouz;vA*QOU1Af=-2%pvH!py$dc#w`&z&+>Wl94_{LP%) zS10yurpoiBU*;kA1JYD*dbyhHS(!m7xB&(IO7lrkgQ&u3Q2Fz%n+Yq8Yw>AgA-z(Hu zev4LIP1s@iQKVr|T6YUhDaZNR+8XizendnE3IFb~rFwWoN+P3}3+&(lH#=0`+LJ*v zL)#O3=UZ0au;x=?(sGOQnw6-jIB=-$l`d@Kyw+%#B#j&q+(OJ;WV`ZuIr&AUj<0H&A5oE179y z+VP?U5*!gtjlcbhcQ~e?tRtIZ$@S7_a=E-nRW1d9oKKWbxU%dmstMrtFQ!}JLOQ)3 zpKfJ48+=2>9jLyWa|FC^e2w}EVURKu;lJ=Tss6%bnQAZU&T-(IWcvmrfZvNKH72HI z0U9r}mq}+(NK&s*q9>7031i+YRGHhu7X-*swc8br`fs5Zq7ED&vRuPc3VDJ4)*yKl zL^|GKR4uK*5*QHU!$3cXnjktM1IGp$xCDw#l*J4E5MckLe)$Js6Uzt|ky{?fRpp(upFY;BK1Qu8Vd`5a*I_#_ z?IqCu2wR{4eYCdei)%s{~?J-oX^+Uh~Q1^QIHg`$C&GkPNa z?9^b6fE|#U*JkjdE1B;NhY^Bzu-QXo>#*vTV;>v8>kOLygSnZSPs_mD2NO{`nC@2d z#7i&rJ%Skipy3QzvNnMbwkJ0)A=G_+#$v??BDW4nq)PiVMF}RCk9r~1pO%m)v7=G3 zALeLFC$Vj?51?U&*lgL}m5UDwpgH{m4w_6DAW=tZr%)pba6z{t)F6p(rsDxxOlc2(_@b%#&;%Y{zO=PKW{)spq;0Y^hO>-6d~Y zd46MgX;s*|BY3|%5`!!D3k|2lZoTkJ==dO-J=K(j>$^hy%Tlh9;P0-f;kQ&qn(cDkHf*s5+G;==uVzUsIVeRm!*hTeV2dDc`+!5LzAA~C5dD3Q#C~-nS&RSeWWne@adxT!n$1Vrax>OKj z(I9Fz%1;EFQ#$RglN<&9f?KGAy1y(GWVj&2?U7tUY^P?AVB=}FWuDdCUr|?#v9x>`0 z$EnVdOK%i%-+*@6z@8mC7d(tj*kF-`^FuR$4DDT~iXa=FN_@`O|LR|F7IbqT)-83& zg8#(2XyCF-xF>e$3mOzBG2lIoKSJXyBwD37i%!01cJ$BNZQrpJdlX_fzfEAr%zOQ* z0Y7t2v&o59`FrO_8NVjqZIwgS;L+;vp|&4HxvNw4Aof*#J_~;xWxpQL z_(X_XSMcl(y7>-Xm(N=wGwwo@!g_xAVW!)}FR2I&c5I ztb8%-Z>KY+tzvYmqlXg1s9e^I(lWyHF(1}6m929}JQ?LKO9j^+T z_kFhwwFh#~UxdwG7T=Or;VAaXz6IZRMxA7ARG|29p*~J6&2bTa^_QL~QxfPL$2+rz zFSnqeNOSpvLRm3Xq{H4{3G(JLK2C^e63vxLDX%Savl`Ps_#jKr)5Tj;m@BftIt~C& zyT|8pzP`DehNJh{XKU@hu@;hhs3FXYO9yxmy zE$+Paph8SxJb9bjAz%zkB_d(m`nB8w(jTY9a#UUG*uie7y$g2PHwTIqGUP=sz7VN5 zn>ti|9bx*yWioS*UGPbmmcQlHpw_I<*@r^KYiNs5OxTH|F9cyK52dvU$~NIRDUxn* zS_oMNj7!M`2 z;_=4+F}pgTJAZ14#!P)Ep-}RG(-4Zf8ukW%8UpbI7Aho{3%5DNjs(zr6fT(6_R38)Pwk88gZ|SL)iyq7gBBebQ)d$|4$ty_C?7!zY z6Px|UR0e?oEV@m^`?-P-Qx?0p*i>0CuSmX7J7q=fbkJs3}0?{nMfu$8#doaNVa)A-=;_w=zo zc_zv-`HD4_Ma=Tjz5aUsl)w6_O)q}>I{x4v{Um6SwB5g`&59KNxVj@A+vwuR?~{nkpy=IJ#K&sisWO=A%)i%A?#z|S6)sp+_bmhGv@tT}o=rAQZwO0!HZ)5|mc zXJ(mua*-okv;$yzluN4pe(_FC@PS!wmgAnGxU8Gx+b1K$PPtJ3Yfqe}b4Ab5)^AcQ z!4V(HyBj+Cll}eU3bEW&v+|d@D#+j_h*7tgvBlWz>2H5ABK2DwB6- zLvaUqGhmj0ZUt*L?C#b`X~mT~1zI4|Z}Yq!-Q# z994GWbq2^r6J>?0K0%bpnW%qLkaSXR=axxdoD71H3zXzP?7F)Y*3T;K`^{1@ zuEH7qHMGQ$vW_Zj>zjs9twv_H1*O3mFU7<0C6=;ccv69D7d~GDW-9M%#v!E2hlATK zJ6A3Aqg>8&$R7cmI^6IDWp&9768+YbkRjy>S~y%r=1hpW=Wt0>I>H+WpZ-ZXV|OK( z0{58FLyg6OSTj5F_2Ekf%JDN0{6p%izWb{Mo1=OqN^&5U_=uf8h9wA=hfbwMBoIrA zqrWa@C{5k|YjiatX=!sA?Bguu_Hi`y0o6If9S5QEcMOfGgpdO#nh-f6 z|FrMGY=7ZuGoqv4M^49!9CP3z&b-mjYN{(a-j4_JX)k}v@3Ob8?J0r48zyg}bUly~ZKE(EMmDH=Y;IxijkVSbd?cJnXO=q#?k zaLR-Yr5PspwkL)dUTXV`Xyp$Idc|@&vURUPbKmAeP}_6w`yv|qnFXX&HPCZ#^Ty?M z3Y?thhbLc_7jP`W2ALi8u1}s#^m=#N5cwmyn6V{l8Z?^89Ik>)5dpNyDtPJ2DKfqd z3eLViLIC<_gG0-CDgHF+1Qw{_?@yr-oO@!x2E)f}bD4*6PigVX60%uaox0DDWvr;F zetW1{1f4(0IPIE|mhlhoL_}*DG}iiGuh7S3UZ3$uBXz++2y$UQFfm&A2o`n!sFs$` z_wr)?uZcY$<)f}KF5~Ext8u__`Oi(3rH{wRJX%Hx)(mv7r#5FA z6~&htJ5_}s915>HUmR!sU3LpQzO1hJ%e#(0Og+C=7A+;RFFs89DZW<5pCYOvd!>0| z_6x$0$J}wlFc87Ya3v|SZp`lGS_tpynhs)RNKo@=m+m0KW*{Qtbw7&QgUNndxzLoP zlwD&8;0MVtB9psjMXjXtvxvFlK~EaVRyPxkS_5r7|6{HF7~_U(yfnK4bUZS3N@O3y zm@>9Ia%J{LLb}EvyQR_Fi#+`PJ^p0Ve~Il=MIq0+t>nW|t$1-lW3iZq#$A(=#$8L2lTDAR%rg;@B4ejDvh z?wWI6%aFsu$%`$e>KZ&=MxbD@sK_p1lke7ago~ePKT9qH3O=9LftxXaG7cx%C^`L6 z-2`o^D7+B&SkNe6?mara|A*!I&}8be-Kl=)ox0jSj6WRF_R;Fet@>oAZSZ~^(V6|I zu~D7?^+6q6v~f$HFu#`D+751nW|>s@N2~E!;7V*Nv_`AX55hp3b)hg{&~%w!2&Zh3 zJd~%I8<3+w%!9%&*I()-8zR?#-HDZUSh}i7wzzSa&(3!>*)~Rt&_q_J{Cml zwxCOxeKDM<0O53u&`BuRJpHsI08iJN3_4B>iWo_h9@&5d-gVInG_q%ihnLzj{MCI_isSiBt}xS zBW9)!8d~Yx_(%E*-uAEbHHia}&i|~qTgC~;L#-`Mh23s0-Q0y9$6x#emj((w`Ev|} zHNN(bejL>JKgL&7h06%@sL6KYZJ7>XvjW1EBI9~{2U8ETU89YlW>J^JUXKGIF>%}h z^uOhhw3+>9pyAf0^nn2zsEz78cg}jYEntrHPq~IAd!;u+Rar*j^4gV`7iXGMPG^F| zi7u&p$%zSyRY;|y7~ybmDc(B>ZB^445H6V?R?Q5D{fH~b5jN#YgxzsL%`?lyq2c?P zN^osOUO1dx2DEhr4!tWpk$Z2PHp+y38Clqo{&juvk)jNO5j_&ue?0DFFXqyHuTLiU zJ6?*}zwQsSS&N@mQhi^I{ zV}8rJ@01at4cFK&-t$2$w(EFca#hcqZ(Mf@8gFDAct+~2mc38&WW_%s-{cvJem8qP(`K^ZN|A8R%$S%>PXYj-i-WexS8kVL&zU`V zyVUp2>3Y4~W9a(w9SpEPfDC6`mXVy6lTT-E&6{R}R!uE0GOh6$YG&6~em|O8oiR6g z=S5k++FC=e!H9O8S?DUpAD_G8Q^)gNO;&4Yo68$&_|Tp?9TM^RCeuVjO)->XbxUN# z{7lG^PBu^Nl+7r#+L&Eh!Bi@5scL@g%J|aJ55SctN+-BIa~UMQTjH&ZXnLHI)h_d< zkwxw`q^bs52r*>tbgJ%{$!vpSd;Y)w?PFw9KtgVPnSX6>uYAsZn8w~b4aH{ha(z!P zmeeb=@zC6lx@PbLQ*{OODg0-75mhdj0E8ppd!r@AV%ZWQc-VeNA9^KS(?~v0auR$k zcDPtRZ_aRD5s=^@tLmSuH1h21AUKgrU76Zmw9F9w32}r(_xZi*)fpbXgV3;~vEo@v zNewj{0D9o*FCnJb{8c@2jf&G~Z(5&#B=3ewkW?H^8wxvv+I^rnw=<8I2av*xc-@P= zY5iFP6!t@Q-0m`%#K@3>hCk)TigeS}20!|dAQnlN8D{W%#U%|-_-bU{>5fB#c5VIV zz_h_yZSNaT1gnhe&#eOfXPytzFSvKo5A~0Gn9scIwA``vOBsyDNIdE`?O@hRBuAvqAWe;6CQ}ruHE9V?jeRi(YOKd*XS>qm*I zCDNv*9m-Q2x{x?0R=WPHwFP|+Y-Q*6f!kydBlwf#W{U(#52fD`k!n0|I$TId4=YSz z8rHw07X2cZm8=@3-80!IZgQP36qgyE}>lmb0TiH1?dizfk3lHQe0^iY*bFgmM{ zsGZx{%>!W~_CRcdqnBt7uTAr| zp6E_SU<=I-=>AolUQnJJZShe$W?0j67G4=!6MFj-6nz*w7$HTVtX0riTHoaD64@%J9q5Z~B$R2nZrZ3Z4ZbN@le=N~Q z%b;X2j=(D_pH+pKfPenfS|f*hU6O3j`1B4~>rJiU!k!#TsP4Lx1vg7L!fPn2x=UiW#8KX_%r9Q6dC~(>GG3;7zun-OnFma;}`$)Z@TF zNXCr*SCB(kbb4LWf}}^FfGyH7JP2M}+=yn?`Y;glopy?w>Sc7SD7KoNHu|;T%0tP_ zte-RbRD0nOn+duj-9`>mu#e4b7!g7}@QH?hkkD_8&nU=2!vv8K zjZ&NTk!ffRN;?*eH&8A-5lXX?MqG;5xVc%>0e9j2ujVp=*Uux_%KeDBBfBQ^RnYch z?Zna;h;z;0v52cG?_~~jy}daqDYAxYoECRleaFcMb(45AhwT}P>eSd#4o)x72NoJT zh>Fc;t5mgoq$3*!DB$82K!KRmUZK4`xm|wbMv4XrErfKl7COEe=SuJnz(sde{6{=7 zd{sr@GvO%ln+@LXRt#FqY}we`A=9>ECO2JP{3r&NUrOL)1;O}`LHvFLFNMi zet7Di%8z9XE&lmQfcWVc)Zz22Zx`~Q3xW8>xZ93XD4U6Kr=(<8l}_E_u^?Z~b*d~L zj9^(BgQ}A-N;3QDv2dDS(HOm2LI+lz=K~Bmjb>yll7AjfV#%o_@r-l8uhD$cko#$1 zQ&g>8_Pjmry)46N>%?TdAi<`I;x-U<#w$AhUaf@pXhIM#qzr!^IKT>Zx83g`a9)-d zrGB$Bdw<`qTee>#$8Kc4WC@5nASgezgG$PlW@1^KWKY-41#t=;NE;s;C_!UwVyUzj zIWq&7T{xiC0SP>DQczLXju}+3&~L2NAezrAh(wY&Sc9LnYg`gGD@V;J96BXQC`1c=?)$M$Xe}Qee!-xmf(_%$xs0%urYq5VQS}4 z*?CIgF29i_0n7>UQG{}O0gHskJ^cOHjt~2GVcn3!eg=)B8-W7_+eVDJtr#*__cj6WpUopo?2D||EKEx=rD zx$}Eo@<^L&WbS02eEAupk>bU+W}NP=E{?XyL8O*CfG3t~aO}&#gP2r>MeG&M68Ff8X^Zs9bGQxR`BXeBH@KxLmf)d5ArS3KsQt}i`NEC^!1Ho_db4=WC z%F^#Ol@jUb3muP_Aj{As`M0@d8Hn-M!F(YyAgg!aJ9a9=P)3=XxqSs1?+_M_oJapv z%*V2vUQp8cJOz@s|8C|Fic0O&X>5hdVg4F}U|XHO9OoRpV0KrWb%5XePB=D4L}X_u zGk6cU+T=b>s|Z6`ITtO9KkH9dStKgJf~k;6jj8ta58#@Gb|_fx+}2D7SXQ)G0nO9G zjv9NTWP9iH|I;eIRTMTjG@1j{kFa254V^{v|I&)DsmOAip*4_CRcCfNQU6vEe;h-h zQ#n^Eu@>Kn`dF&QSSY9wMZWfnbcnL&#| z7t?&pNXfEv08)cu5?&Rvi(|zI_6PNC$qZ^`nIx+DW8hY7Ws(@x|B-&*qlx@a={E}1 z&1M8K)I2HJEoFL_*N^too7zE1w#|okr?4pQ(bsHUiP%4F z;0Y~H(0rB3Ah-1ly$@`k^a@@KW@R_$w3W1(#^)Q2FrrE+sQbNZDapaUkP^;~+<+cH zcIL1_YTHXLra=da+L;t~c2=>C_xC93n!uI{53BBTq*(RUxp@_Mq0_(R9vUO zZaRt2>a8NhX2cT<0`=Z-xbFkaIb!y)of;t8Zo9G`hjMBcbj2f!zhH%W7514zF z{g@%r(P`2f1ga^F!^=soue%SJm9w&l+pE$fN`ts~TmWp51*EPXD^yB(tc~ zdF0QEY6My1v4x#Kb6JHIAlqmngc(yIP8PcGrdCs zrzDyC@G+_<YVJD!fSsbyJ>or_W{H}Tn zTEF!OiGn~kbU(ylwRRVXpaudSIm&^o@xl*52lWGA8T!swv`Y0Xc@nI2r$^cKQ_)Iq~jO-nnSBxb$PFa?LQ-j$)gi`h*u4Qi>(w2VD=L=`Dl zr^w=^r6X5nPC{U+$a7-#L?QNkX2%j=&jHw`aL8EnsPjr_i=R%~$CL)(2;ZEazt>0x zo9|B*p@`!RMmzT%7$z6M4t5DhF3S3B$G15x>4xQZuMLg^WVQ`Y3F`}wmwP`kABP3k zb7K8+1tsCBvlqc~watQGGroliWV1|_M$R!L%KtxjxH(^u{@)&M!lVC}hnrKmU6ZP# z7v){b8!pb__X_L(BJCZcGi}(k%h*Z9wr$(CZQH5Xw(W{-S1Pv6ify~TyPo&$p6=q(N&W{bxvp~`d%t5jC*%*uyZB7u6`&Llaroherl_&qcGD_4pvxsA!Lc}FTsbIl zW{F2L#c+m5!5?FS&jflwh3)!)7huaofQp9CI=lWoU5nD$f5>GEs&%KsTi(7N+u6-e zNkeJC{{ItkGl}KOCeDOp8simfYPw(sLwIlnZB}(Ylwr8)9>{ zJS~@`Mj~Uwx)N1g8oPitpQwCvqg!&2r52xx?~^hkI^!~bQ|Nw8aQd1ImC(#e4r%ud z_LOb;>|9o4w5*{pFkoSwy8mA~fl~6pzd8XGG-HBs{jLA0;U<6jf6{Pkn8^&1Y2Vof zXA!=buV`ey?l;LV{bWe?Z6LNRwVim!kLfQa3lBsX)+~)3VU`tu#oJ>!24kUdiK|8` z$K&cNiM%$CDOeowBO>_+MH7q0t?=Q2MIHzrg)ur3_=e&y4ik1naC6o!Gn=X54{bghSyq`a)3CxSVW#o) zyhD%8RD&*zdS3$faIi)BQn%|1V>h&s-L}8q;)JSZ!MRQo>HEn#-m}3-O%g?an(g^O z?!h^2BTwow0{+78|8E}d6rC@HS;gOS8kBtzu7*#}{!zNi z86d50e@shw|Ts8gT{gF$%^_EG#L`Rglg zA{rV2!jRon4I_!U(H0JEe~?p6ULM@NH{5)zs-LqA*TZ`E?S!zek)fe)dXNu`|20E~ zT2(gz^#~?ponWxWvfJj_^#_f@czuw6;d^{p&b_hQBZqAs=nfYuf|=@~5b5P%)uoe* zkmhI0xgdHk^5RS5CgKesmRL5=DsYFMQvmfaCMSM7rSUMPAaPFju)i?5T{&174F=>W z@``9lbq*&F#v^5z3Spze8V1b?Mua+yFFInY9fE;Y%a||8egW*K3y#G{deQ-b@eo^?*Un|Qd^pHDY z7TyvdwRpYy!_+wQ9R&E6|S$1Hd)ykB#)mGU^Jarp-dtwgZA-?t|E_z z^}9b|FyW&Arahc;fU- zU$;eht`3V4QEDA+nhSWsdOL4d)UE|D;Y`qJ9cyc|aCANHDXR`p2S(p@RsOAirknKm zeyLJSTI^+f=<4>qb0FGBGW(`f9)k6KnU{+_)jNQAD7Ik>Fh`#+AZL%8JO+X#_nEDG z;+pPxOTVl5ok@5~-{?(cS>r9*azHA}^Sf72mvgW-s3DRUS7&F?&(2gh335dNDOIt(n9 z6DYq>Y9iIPh3Zvo62r40JbT?#a0=CdnQ*vX>g=5l{e?hocv7F*vpFd}I(TiUrj+ye zo-4A572<_tgnv7qmbXs#XX|$q{^uxg%tatI0K~b4 zuFB^RDnBW`6*M~*?1k*+DcjLpNOG58$n@V)U}4#miv^)MqovC8zR3{rPWj~!kF?By z*~yxaMUsNO4v86qe0Rz`dR?0Ai1jZb!`Y6abxjQwefRC(J6S`xhlYZ24nWDM3#*5H zLc zT+~7yu!~b}3xs3m$LEq6!ZEGyr)rB3C3s@)cxHW;2VVsp`3Y!2${VJ?x+m7vDNQO1r;+AR|y~dhIJr9U)pXJ68kkK+eeV(>=?tW)G!I$8JaZe4FPL`Obut< z8DOc8Vw&>@EoaV91uv=qipRq5{#J5OL~xhcs|z_pV3jXwxCuE=<}auk+h$M!P+(pk zW7r@0>b@~;kqI6uac(#&H0|arQgBDW!Nk44+FgHO#|saz$^3B;kj=-}w1axM zS&y=ru&7Dde^FIO(~u0g${3y~!KU(H>(=ptFFpo0NnaNGMv1Y!Q z{>ZXQtM;!gJwN4eIr&u2C z`bQuvkkoiv$d<7-*-O@z3Rp?E)>!#bVEi;CBa~33JTYcS%@mTB`f-F9-puxj$qRSZ zQhC^jmn`>`b`l~KqCx!6vLj!+$e;R?%fO;a*@#2y-~tj_Hkw}8(<=h&MkRdLj7aVP8;syua^^9w#!pUWruosPO%G}Zp-eyF3#0V+ z0e9i?^n=!|I6vRFMB(_^&geh^pyn1=R4s}X(h1H0-`9d*@vtJ3{~3fJ9z-f0KqNcz zGGK=~vlIJ&Ev$(Dmxa|?TIrMaJPQaoxM6FEq^lF-&8b;ND_~`1yK)<=C`*GSUgTj@ zeHVQ)I$R7VFzb*iSrmZfZZ5Jeyc!mwt#Ob&2)YmB_BxSyzAihz+r7^-A2vtPwRmQq zly@`H|FdhxVHJ9KOUZqJ0ck=zpmc(^jeJexe$7|E+r3$|3PpZ^y5aKgNNTXTb65PE zA~u(Unz@2Na!_F+B^`zF&EKcl+p99b(=6i&nm*kYCSi{A5v+kNPGk54P=@$faVH@S zR)0TJAR;xN{#svu5fK(%Ht>P_q84{h&7HX-| zgPbNiWGj|f_R8JlQqDCX0{?Z@hT-&Suuyv*zUn2@SVW6wDx=tA%On_g3oFAZrQ*Q>VEZXOI1iyK;6#g!%Zj!AdY;%>Fg8{lN4dJ&u+>LP{@H1jG){cfofe% zh2D{1&+Jbs3fyTlUK4yEmSLoUYER#Jk^LLMQh;>OeoN5+SKtHzrc2t)ZXzcj8>juR zz^3BganR0#D8*O}cpvmnYlspd2@I@x)pwTJ`J|bTRMgRe5iSK=9eI zhxEX4ByyK>%?M08@G($_XN+9v*d*++A5`xyaBM#r4o*Rj`GXQFb`h2g~hhF%0-=k~r7=^`89E&}Cum{fMX? zEVmkYqowCYRe4Nlskj5@>?Q?;oXM-CuYAQvL9;)m>Y2qB{>T>1l5P{_AAxdHL8I9Z zQ6pekc(@$IvdcS~4gcc7(8Z-rQgjqk$urzR7G;!D*HnOMJ+V&eAdgxeE!M3SCYmD^ zZOYIuyhn0mA|j$VVSE<)Wz&@op&xUUCeISAKSo+woy1Jg43V`wM|+`y?k9|<6RytU z``p~mTQjiMae>Km;R6m~VuOL;qLHh|c~;Hj2}_a7u>HMT9*-ZT3{j;QjU-=V0j@~> z=+eB-Zc+oKW|Ha}yzO_&cH82wssq-7Z}_1VIA9wqlZ+zsvq0uewZ=!&;NveyAqD4B z4|9@y_Z(-pQQn$VR_$dZUG9lZ{-?c&+8+CK2U;<4L6&UA#1hI5*SrPwk?)KrmQfB8 za5+jRB{Sa;e)j%xTu3%IFb2AbBZ#U;($m26(i8`CVMEJ3+TwH;NcI)6-8}_fq z!z%~puG3;J)OGGmjzL%1#w}yV0T6u=#;aZbjD>nEa6q)$31+@$a8p`BbM)4+2)9wJ z{s7M}AOS1*)2Yys69f^l>~L5wKVFUpza^$l=BR4oXO!c zC28xPOzwdcOjOCSZXIEstaT1#hiOT9r1{>t>}nd4Q<=iVhDYOfB=WT>WnyM1xJJN% zexsC7PN0Dz707A`yYqJo6rWy{U51!ED4|rZeKO*gp(U0x*Ds@IDj|M?LOsI(*ExIX zE&852kj@}Y)90eg$EaYNveni5XC(L!u2CI5&?oNQC zH!lW&>uLf9;T)x`Dqp7HjIzUkT@>taqyRzZ?E}o{9I9UI2v9YV9QzLy4(-XGDx3u; z)SA&`b)WSiSoif`Ysc+g8m<`V0^rJ6d|rQ-9TRHdh8WWXYeEwNu>l~Q2_3T-wXL*x z&}>An3eH&Qg7&Zia;^r?NV*!b!@_%W&f{&KO^Ar3KmBSeL~18ya`!35V(tnvmuG@8>y|ticEtB z8WOz8sF3h<@=UW@WZkWxhVh})^!HF7>~Ftu+bXzY?kLez)gMt3fr<9ye%4Z{1n_0X zB1VGG69QFKVUOT;%`8RZcHW(dfR9Op9;qf!v%d(qO|ipJL=tCM zdX0wZ921*zYK+CzB{h^C5JF6Lhg=?bL8^^F9HVorY|Ne*$5PFc??L3tg=nDf>`}c+ zZHQDqVUUEfxfa$5=0W~_q3w1||Gw@o;VJ`p_3cS{fV8VHo#KUK4CsaMF2P~ugoc!blJ*c*g5L{VTIE>Uwj>{LHDnzN3!+_S3W| zvocDsk#pF;m(#z>g3vGEsKG`RX66P*exPo|J>uorOXNyM#ZwmoD!^FvUld0M!be9-LT(!|u4zU$NS=4X`s{YwY2QL?TD z1(GmKG=A%Mn$*&5T_-_1aS1WVPE_7as_}7(k!2s+X?eQp`Y$30rii|9zvEQ<+CNE# zniJV>vYTtV;kAByA>Nr2si24RVGKqD4zb8rerTufOQB~hFaGfOBrgwZ41n?1R)xLa6t^5F^Axvj`ZF9sDZSOCUx16)T&_8qb33(@CYtFp` zx~%jz^I$ja2ajYWOM5m{-phq3dY5ANav^5WeR?&Dp)rq@k5^vjBf!V)0P3rAyXdpa z05>1MbB`5fwX;AH4G{RC0|Y*aVy|NfLsaGj=S?@uK!%fBUd($@J!-hlX$KId@tS_vN_DRmxj# zSwCmJzzs}l#U3s#W++PC`Ndr~i;&e%>S;bJ-%(?K`$UNs8}5S$J6KnCr7bqwREf0Uo8E8-QMoTBJ1Tq9Y@)U&}llm%y862teeG zH8{m(&KD&J5}1QNLPYBq*T<4QU<0af>fDm4;C00t4Dyokz+Q0mp=1x3zsg_JbVcrT zgXG#kqSgvqS)PvS|4jK|rZVqgxh>Ln&iA*B2COZ%>P<@4+S$#$E;9xkt+?I_7UpZ;qN%+mqVaC7L{I{7V^wp=A%x^ z$Z}b`nx#B~!(Kz;Cnsi+V)bBK{}Fs0%(64Vbb?C8o(ALs`6*So4J%?_$nqRNDz}Db5s$#?Eh@OO3SIja}>w18O8K{wevuYHPVn%Oe z94;7*u}mq^Ks{5aBe(SRV(V)QjG-$Acd@i=+AtWj`mg5mVa$N)GSfW0KX1Q}-}B+s zJnp$9v?4b;Y|Rp8FTYj6iZfYLxPDctWji{*cujg*jPwTRJ}0F>{nyRj{I4YKLMea3 ze0-G_KEBdMJ&E{=Wn1e-S@W}2CcFSe3X~=8fFjBjGs?vQ7Dz=n&+6tCG<&*Bp1RSU zEzTM{9hTMnvG3A9>?y>$Rl9M{wy@H&r`0I0V#;V#Wu-}`T~f~k5?6eZI&xG%==qp5 z2Ey0z6ie={hy~~8Gl-*jAGT_wj%f)*6q$P5t7N)I%UpM+8Kz70z!bu8N2H7jrecVX zek?{rXd9A9qpa;l9Hq){xTGS$N15srj-(Nn6&;MnLQ;Tg)=eQuuxnrC82)rdiWlEL zg(VdELS7%K68v^oK=^fpdUUr|GB;ICdDbnph$l_j)EfExuZC@==RXZw*&aZ{cDt*K zH?m8HGa`8f0V_!~@T|dSV$lEuudLhIt`Kjy@r|(R z6XS~D#RdsG5jqQ{cP8?VB3PRs>I>)OoTy|uU13fjiTceFjxkxt`;Db6&5YFrpEU2d z{mK@~AI^8~&L}x=Nj8cdf}fqCx}c=x;ehFHxRyScC&UK7N&hlV{ij zB~+0PE@L(*Owd*AIph`TFK#EHps`OrNYELVp|M(Z6y>J{*5P}oWO2|l>G@mI2=e{V z^Kg?#lt_6KyQMKpeBWp5KVv3%v)BUbp@5r}1*%>es%E zC+2gs*DX-&t;%c67f|T0-I`ca0AbDaOoW0ENyS{vVKVO%E$Iq~X6J7iP2Br@-1FpF z9m?vbXia(A_E*x^B9k_niX2JiKhA2=n3OT@sv3SDww*axomf~+RW-k>+2?E(u?URQ z9k;*Ce_z-l_hO_GBjx10w`JYj<*dU=`2g-cOyU-NaWNC+QLM!RPQctY*jsPBC?RY3 z>73r8D*Mm}2=AVG8~J~AR8BFc)Pab})in3;zR@QcVxs&gaVTi2e{d1|Sn?py>2Rmx8%mX2|^-i^>#fEHMpsGi+uuP1XLW?w^>x_+le}`>)l+z6I zkcu4t7R=O#8l{Lo`1+oG&TuJ4ty!he;HL{j|wr_{q$ErF&+d&Y_B!R-SL zwBF%nZDO@Hx}!ilZ=5k}}b<)J8@y1e|VR=`?v88kA7qGnX;iYAyE-c}F?}lc>jJAduxlF#UXb=|f<#$Tl+p5$@%CnQnxiq%s zYLq?_O)MITIp!BBU7P-7=KWY#J|m@}9MdTlnYHl%Navpc0@Vv6w0?-o!|5X+u&Ckc zt5W8T&^Aru!Lr+aKQ3>w_r)nWX+eK^ap78`hOithaesM6rKS920gCMD-=g{{&Wbk4 zX_HSPPSh)D^u>qFGT~nsel+!`bFFQ#RZ!XqytV6!ow?mUfxC0O2aPyV5?Hj zllH-rCQbp-1SliUNk!@cuA{;{E+xP11^wqWjM1Jm>n>T41d|a16AXmHE{vRdMS53J z(`GaMJ=&%BlzZLUUwxN3z+rlvW%nt4t&UW~cs(4%Hiu}^nOv4U4QiH;0ww2M&upJ5 zc<-o^C#5>boO__Kh>M8fbr?lGIcFDf<-y6r>710%MQLjA7I@6NL&Yk$G^q5HFD3Iv z$jfSVQUb_v=zm|B-<#HJ=1K-N>e}WV#%H(yA0FRR*F;5G$mteZj`ZC`;^d7cn^pRt z*qv2H$YJ#6?{OaJP+mf;H7vfH>_xQ z0E})v^5YZk#-pw$l4s2|J#awK)F>8Sw0El@7+y}jM)MY#eOA)^1seN|x1Ht(xN$3r zD?}bK0h>9N|LL2P-WM>+G{PEyka#Vf0(0=0x8C8a{<@zL313d!y53f z$NM5sRDKQ5UWL0=*iCcBGw`cAr5$#AFC8eF^M4(|&4Z?2L7BeLtw|~y6f&V5rm@?W zA>X3le~W?@`T#!vy@AuPuUZEF>jo~TNr$8p=GR43EhaA|Llz?Q}$rtOdK~quMLbQ{{2wy>)a8?q3^L1Z;<2CBP$y@M$Qcj_c}e zO6My!HYmB{OH>_>HF$n)r56PwmRvoFo`C`*VaLavJBzu|XKpLMfnH^2+L`eFq(2sS z1i4O9799NgphmOG+5wThGEH;Z6Gko1o_@EdF?k~*G!XcNMi)9~fjpFIC?n4U=?ay^ z)7!j7gsM01@HXeq8N&J207E{JrSG|5BSZ-9v*8Oyp#(P4lDD)KVmGP2r{0**{TUQ_ z(T}$<4BwOy_T!hlQF5Y7b)h~TWJdQek7M8F;Mb|bBpeOknxCmw&$pra6HXti4da+r zgD8vravP8?TEoo!EG_l3TwmlIazQQC)Zyf}1(t^vY-VeGPWUVv>q!I(Tq2=2Hl;WJ zE;pxFI$??OTdx}t`jLA@m)^5;898M0y}yyC4ZAqPxY%V{?oRD!;R@P}1f4;cOz7y( z&zLNfbFGI3QZy>2miKVc#WekjB5@}Q;N|9O$ zI&y{7LQ-JN`4lQ(9B?he5L-_>_yhp96E-uTlMt33XdIYME{<=RojK9;%R=0tS9lHi z>TJUNo?fIXUdDnFKONtUQb`xF(R1e``SVY?cNqJZ)0KcI3$%phh#GC@#Q~J%vUo*R zBu2QtwtvU{i7H#0aEW~_$@MJ;@x*bdz480oU=lBDW9Jw^Oe;$q0tdc~FXXR&HJ6W? zoB_p{m=XLsT6_crrt^mikOlV*ep?L8$D2w~U~pNh-IpnDun8KY9xLzvA5=nlm>U5X z%{CPc$U&Okcbf4$%_^6g%{<$j)Lt_QGgyklmbkJ^7eMB*kK0xHN+sA07#oKqJ@CGP z30p?)1J4B~kZe}8x?xmIB?3q^ty~0*?RN9~)&Vcy^RkP>660A%n?r^-i#57;H09%V z#zjJ2w zAND~hgWSzY0uK|n_@BZuND;w%q&qi>@E02C*xC5oRGw8SD{27v z!fkhTSlKhumEj2lv(3jSpwkmC4q5!kq}*`pkqZB`W$1Z$6(5Iih?+(1wAWO{+jF55 zIYQMdz-)C&c2m>@QUUVy6%n^*i<30g;i${&N9^KG`?&GD z-_+GZk_UrlU2ll~9lQ0m?^_Tx={&r|Emh;%p7s|u8M>NrBR^M`nnEaLk@829cs4aC zrGh^Rd-IIpD8bXfVm1fzl(XKMXkS1uiyP%;`F{R)dpHK*Ehn1qHVG!~*&pfdNlp|v z+PBop=KIDT+I4ti_i&4gQJyGz?d4nPyV?>OV%h9!GdJiH5BBkcyykyIzaJ6=*abiVGyPa>9InYIE6#Xy*Hj*8N)gC?{d9fj zepfRMlSrx5feU_bdo7Ax*N+v>Ue&Xo$X?Zki-3TreDM=R4U6qUDtl(7haBC&SbO~7 zf504!T1aIXW{KytAuuB?S!WE!#P*a)skgMzeOdkSwZ)$9R7zDaHVa2|$J*!FQAZ6N z4pPpFjKd}_o(SztoWP{Nu<(Ihpf~^FIh>3aAAhcT1=1}-??f*D5ZDw##|+cXrtGdI z&5_;u)#Wxt{C!4jU(Qr3Q?pOEU`d4u(YTJj{)+ySeXXqSYok4YA(?8U$6wgs^SR0C zmd)JcXW&_H0Fd3hdH`ZYNwwxsV4UPyS1~sE?+FV#U#&8|I&Mz z=GLf9&oyH6?N|)LP|3UVn|>z4g;O@d15P~uktWQ}UE3EOR4<%5X>d{5#E?+R+2)W3u_m|KT$DbOJ(pI3yOG`Kmmj?SgI*vyj}Kt2R#$w1 zLnEpZbV~6`O(4Q+s(X*nFUL}jDWN|xhf?%9h2{?&@@J}aZ_z>zjegI}7#tcqpw~}* zC-HQ@L~~xBuV*M)tK>?d;n-SP?A`$?RT0Cl%RmH{-^H~rCoIi|32lX5tH|nt7TfeY zlkox6T`hsmb<&Znn^ja-DQz?IdppNc|A2So+`=TQ_qwPsU(u&qB$}=66a53;Fhq|A zl*U<~)AG_fT~kE&*a|WAz*{J@31*Y^cQxN+fN)(TMQe3lTJ2*&fM9@3Ixe@W-hEX> zR~)JV86&W3SN9F(>U)=aO(bQ3vO+a_XSf}7upqKvHjAn4nE>9VlwZm^Sy{EmB=28{ z()1W@U{mEOl*8sOC8cVz?I1Fq6@hA)St6&okFe5)ZZVIJAWxZp_y9 zCqP!!UL$2BDWv7s$DwJH58*ElQl^ znE)0iuC!OOz8+t^Z)ds5UVO#q;&hHQCwP78lrt({XxV+YM5D$n${p}bK{-(%r)}bU zB^H!0=CPO;`qORMyo=w*YjNxCp}b3CtHi>$mKg&|^N1SF7TltOND_}?*(FuXXWk?U zKiRb_1V0&*?V6pV<+}B#d|W4XQw)t@cn%Evo(yi^eh2rHDOaS9<^*Uq(O zkIJ{|v|hVx@Z{Hc5TLRbVOI$}qR!=Cs77m5;I!}-@y1Tc6;k}_8-HeWyxnhe zY_v2E5RTU?GB7q^8*U!h89n$fJBZ|HY6=e=m68+JAEwu8K?te_kC&=mT9FGTMu?F* zeX6Cxx2I^CI(=+U$oacuQt}K^$r!7xto}CY`SegskFZf*G06oNEleqaR>O92vcRcarxH^Y0v^7A0_Ymx>=5Dns zTG0IH%r=Jr&3^}4ehajExNAEJmV5Ex!@~-QX&RU^F{j%U#|Y-}E3kL6H6LjS57|U!7OXIG!3Nnfc!p8LA2WneLTP&&>cJ z5l*r*CUMpONe4qk|8#Kk$SF9b%RNlX$e{5xiSzznx^%q6V<*&lVB|S`{Cw``4Lb2# ztuT#t)bNrgyxns0o8ZTA`XcatfBwKpF)@NEc|cKGjFP}>9rQP=P&3rK9Cm1G2Rj}d z$Y-FGWEoK70d2#cFs`^<&V0x;c((I*HZ-)s?NE1GoypfFC*EU&pJk?Z^6!W*TB?#F zVCort{9=tt4Zn*yQC;8%&`>JPM8G{&NI=D`EdSf61zenaeZyY>`q9p^hq1z@tK z6e#xM6dICV{h@KTU(}$|sU;FxeNf?klvpeL2Tkih+yS7HOnCiw4}rA{kf!Dd2-F6K z7i}?oiEg1$1ZX4pc$J65`Vg-6<5b$%0n6kmM_~ll`}6Uq5Fi?;p-plM!t8m;Z?H&U z3ZVY<;lvxd#vX?u^_IJG=wPlL(i%=gZ1Z0v1!KD~6O@_u?bV6L;in6rRdlutPziin z8EG@oqfxbuM}(=?KFw9TD_90Kn(Smd&MT;WS%AE(yaI-PagRe>HaBbT>D`R9I zTSs)IAr&3t+&(*%EIoK>k@;M?eU`$V@K9Svu|!rCB$Q9$<3^;1A_H>%N_-eAE$1@w zI(qcO%@F7I`))iuL~o==eGst)?8JURh`->{l#23^la#Gpklxis++S6_3Sk~&;Rd4=#SyzCg+DUe2+2S@|k3ZW|{v2 z(z3o8Z01OI`(H>*&-E`P#sVZL-XQE%b;vwxPI%b3Sr#(4bI)?(6WlpBGef*UM1H0D zZQbO+E?`W3!m|8&GpV3l6eqY90va;b0&8-!B$e|Oh8q()>EtYs5s$&_@&XXz%KCY} zbh~@H+=EuF)&-RycQ{y?iX|EGa(ZPpkbC5%=uLNu6&Td*r#&C(_Wywti(-LpsU7vO zMl(z3&FQF}BE_5L`AK4j>DyR`JEsv>p6?D2a<^9W<%$E&nm#E@c3)zy>j7+kQgl6J z+z!XdFgom^L9`S2o?}&B*4rIqhP%!XA16I#d_3wy@?P1kf9f1~hx8f0Up&!+|T=pTp;5@`V>Svqo<8s=J94V@aGF!2+BaMfOW~YXR zPPvHBS+m6ZA_l_MhWKnLDjAT7v>*ia%j3_2L@7c-2Xe+jM98tFsjs=l@N}y}Q6!Y; zshHEZ=xV^c4c*P`enDtTwfz~>TW^jPKej!~i;@-??g|D7@gbF2**D1P-)&o!;PY9@ ziXaA?k}S<}QDpTLE@`d3^^-V+^0NB1#FA?Q` zER10zHH>4ikoB$Qo;4L$L1>&n5fbc9E%iTF;KtS4fYkCEJCj-eDr8W%kSPvpttvV- z!7bPRmJjec2IfVw>|`QPQo0r(%oNxy>$h9Wp+&9cgmYMy^KZeo%hJ5JC}$D*I;p^b zYMv~|T2K(78$Iuagv2(Kb=ZAWY_3T45GI)7yQ{)|t8%vxc}9LM*;rNJ!!jscCA&g$ zglc6RRMF%1ZwPUL{|mHj2;?3UMx_Pa5pLg#tjc3de=Feczq6#rAf+vOJ$v25EF^cl z5~oIZe|mBkZ3r(iz8;O|eA8AhA~rXRN!$u@dYy(3I(h2RB$>SWjJ^JO%y_r#YgNT# z8NzUPcYy=n;#8``HcDv{jB>OUVz32(*i_l&|3YlwlI^$5Pl?!t$J}(P_Ei|$FHWfR zhFwPM7ZH&UxK22T_~I|chsfXxNyPjAeJVg;;bJ%J-yPWH&EDrdhVZ-$SntwtKmDCo(#b({4&7P z8RA@qWlj9xfV_*^)pChaCA8?1&^hn;I|CZ|CXH@C7~%=|ux}eHvCq^B63Aq#XzaqPw}(gT_Rn0 z)`5Vd^a<*BuPy`$X6x1R6-j=sEwX~3^m zLU>{Irr&B2+jYNdVK=LY=o&#qZDzjED3~rh*7y9voRE&&b<57|Q_PXVrXEe__Yk*3f22g!Lg9S!yB@nQ=4Du7Q_&+QMv4f zU3KgCPYLZiENanse`N9KRM>wyaSpPCa`Q(3Z z&hEw}JxGNUR{rAE!sw-OUs>kC=8*NCKC0n*4S2HXZv}0!M4)hx;D{Qi39pbY5lR(% z=HCf@&y*3_g}5*>ekAp{e9rj>*v{(Q+^>1OMPvaq+zMS1E-i9hf-X4Pitw*DgjxCu zMe*p9&ooyJ2qSb|PrB~=yj{DE;Pu|qotLLlaYVXaN%yJAIeAS0IXC{)qvaKigv-WZ zHI4zOt)WJ({HzGZjnNWZR70ZcK57%)sx1?OT3laX^Ewa7@erVBU-&*j!fq6eS0y<- zKA>)=>SBZqlgs%$`dWZ2(n1T;<86f~%L65GGye!$Hy&FA|BZe#-hx+l%iLPc^Jm?? zm0exG2(0~ji*3gGz@{VPnZiN6yk;dHe9w#qfUU!vr!qXx(LVkWYbng?IqY}gDXWQf zu+%K2;&Cef@vCm5zta=X>eIPNM_g)80VL+vEvFB;(qM*HlE7m%G82bAqCWneMrDpg zu)jD@4ZIDii;=TA}> zVSR^bMVJk{3pb8ysk8AtiYXULMe8gz8Jh9I%jdDG=B-MA*%?0a(~(n^+qcaHd78fV z-+cdkCL-RL<8f7y7GJ=O0MGHliX{YH`+C{O-wM+W0sDoHaPT3I_2YCUDA{-Ubh7KV zM+d2=U`jliHv@v75&D9xLM|O8^Xy$TCg=YB8Z#cF+B}cthQAb(od7O0&C0a{2E>-# zQJnG9XziQ8ex+l`RWsvds|6R`YTfqC_ zLSecXe%b5;$mDc5?w&&PH=3=KU03U~!HG(H0;wc;-`tv05afj3+a3s?gc}8ZT0-r@ z$r}SSgm<4>=aYyaoCMH_r4u>*A0}m)BGzYg{j;KruoWtq0op4PzufLa7bqbcat2U# z+2Y>5vEcJZ28s}eqISoE9&`_(FX-t0(O>McB)_BIxrHnAIKeRgadq9~r=+~s^V->R z0oQ@|x-4#<8V@^!YBXGZcxscD6-Lvi?^ zG;uCh@4Rd(kf{R(%0n%Bu5{`_)B%(4K5NyJ%Si~=rfi(*$&f(O7 zs)iJwR;XBL+qP(!*D^~WE9Qu2vH#}Os<`nB)}OFTKDZzV;sKnRAiE5r953mXL1A(} z)TQ~EOF&sU0jXNQw#q#A-et!_FzUCFu6-Aay}b`&rAa~Bw_0lV4yB-qZ}MZ11j%WT z@Y$O}XWInbK2A*hzTSRJ})WxBHl!fqxDoog_a&S&>4N0CVn0 zPryX}jdSuW2=4T(FBhj)xRQ#A6y7BX#sA?|9Y=>IK&+33alkU@73TBc&ip~BCIty34k3ny z-4uUhXq4`$>eIvaTHXLcAi-k=k4l;9!pAi&Unvc=+)6L4l3MSDO-b`u0UD_3VIfr0 zF0YflueH6WDO)9d>MSa~MEVT~NI`~LO&($(OwuSF)Cx%}j+P2m9|G}|xKmYw&;~ZR zpx#X#tDjPypzAJVr~Rhq&U?Y!Vcj;v-C@1j&Kl9d43}#S$qNbf?p~Nl7-hXU6Mt$^ zzDt6oMNVff#;P=O8Fj=QIM>|I$}u&z1U!bJ3K@+tJRaRU?c@+LBu(5!WCIj+?>AxT zfKA+s&DoLi0=j@niu?L%U~G*E-2^>@o|}nXI{)uC)%8WcPu6w5${H^()eY8X{H41l ze2F%3JcPg<*Bo2xUkuG=b9qC?(qlDS({yh@O+rBv+eQdw*@bv&Z4}SBo&j&Qqz`z!<`tV z99tP|)9#)>-7O|d(^Bj|i<{S8k=MbjDii2(2)PHFv(r5pz$F(K^R8s_7T8W?@{aR= z9=u}6o-YF}lrebPZ!S*qu9!N2CXr0Wnfk9(ilkiFv`K}xvN>!gjd!!Uk>A`u1?bQW zq?^gs=Z}h>vgSq%fFtcg6Z75DQW>}nBkK4c)1Ua7bCeZt0;Ys6+L%I&j zB<1&XC7bML49{01`Qn>{QeAesfwf*<+@9J{xAObS4LJnkYa`M}7A&@GT|DIvT+OpL3;%o6nu1iJ@(E#3*b~<9kHm8PdztY5d6}#z?(%^7bVx^1T9W4 z!c%Mk@JD$P^dk4D*gs1l?*=k%osTN|muTdlPyg zO;ycC{_gL4DizBqzi@{>Rb}A;a z(N_WntoyC=T!(sdn8llmf6iB63;n078*esVF2`=?mSgC(0A2JM>CNCJEN>bcF?BfA z*+HA_XE8jUM(PU=C`c*oQe9vSFRHC~f!R@u%>&S|0oPM%OS8aH5_<3m*#U-80d7NS zv)+f6#pQCqKkhH40p*Q~l+p0Jec#APX2Su;<~i*2ewwjfeN9>ZS+&;yS%@X2WWU^1 z2P|*Z*)y&cBkDLlZq*4iw(oN7?63nv+M-jFhP!~lV~*)TPV%{J{rQ@vs=MIC>U{@p zfGtVnVG2rAkCD-Ee=--JJ6sFCXJ}W9bL)TT@5meRqV< zNtX3AYsK(LCw49EPWv|4dbS`<-Zt2qU8GX}HkdVkuo0H;)p9p=5kX+__8@XJZ_jUv(6YzIc4MaM{(EW@^&OY%8nVmClI#c5{{hrME5Gtr zZI+DssG;6zwSL;Y(`s$gT}2HUeJx|SjkPYCfftl=ZNXeY+^iWol5OG30zSf?o8?1heB5Q$yrV zMqVWnUW)R%jsO(v0U|-0VSYR2yC@n2HFgy?34SHP=tRThKX-XC-`|- z7COSF9qRvjtl9-=Y&2~KdcKZ@FC{{>v};K`W;Cl{<}QuJvscjANH9a!0fgHaf4Gg} z8NBghP5<-i`0#XVR&gs8CIVm+xvOd=1`-#3wmVsCXYuK3KP9c9<&&pKOZuTrHy@u~ zKB#(n`Jid;UpXZ+mJcLKw!#ppnURht1&M~CbRE;p>7mXDS!C`bi@t&UoA7gwoe-Rxv zV10MZ)#5BR;cDfQX#w`!Eq?xM;BGA$+p2@oGq*i+YcTDexvg!w8|K!SuT_`)+v9Cp zn@rswsh)J*jPq3}Uc3e@G>uR>oK>Qh&{uV+bpw5_0JZZr3`Bu?Khv~AqhHQmao99& z*sXikvxkHtaoCt5B7KTh^9fbW1@ahqhl7_;x*2`(GGFq_`nli@0sWtFv zPF5J`mN)~R-GGAP2I^o8nr7;StH#uRh;_BfYxRxP=Qab2NxAGs3eZqJ)d2GY#<+Fk z*$LqDF!5o>!`fK8-1hub?&qXl5J>V0J0*sO6}q#bdx?Ar7b((;bZHJk0f&~~OzG|I z?TBKq;G|K-H$UP?Qdu2^n1nG#sS1YSJ2wcT+xKFPFuV6RzWbg-VjK5j5=F4m>Leho z_dPHBz0uGSXPZ<5j4~E85tZ54f9le6Qz}a&fXT3bfpzMZv5%Ajy@+XXax1;?#*d?L zfz{cl8rW*NjN%k;3050_4Q>1(8~_0O-AD+MVX{9EQM~R1-IT&?Iyv-CG@x}GJT<>@ zHc*|M+Is1lB=QRBTDVk^%*-XN1k4#cKfhCwwX94h#c39R2VBA7<}o%EP7)lCE2|M^ zCC>(9l-~yYnPhc&>3<}me(9ytE4%|TpqHxQ@dU7no(g`%`aDGozR%2$&^}UVoN^<> z57kDM85DkYXMY311lt)%Bj21QWKDvN{J&dn^{+Gv(9u3w%DmT?dJF_JS z4cN_NlSRtXo??;eCRScAja0xcdOqqkoWafhfY^qc$8bTlQlUy>rZ+W{Y33rdI^2~n z>z3F8#Aa4uNFr^`E8gUamyHYpoE8AaMGf1gw(J;CR^|KLwNHpGK#oqhJeA+InXG7XDm3S9Dvx%&Zj%iiaz`iRMr=!k|L=agz6vx-G>I!vVzL121FW*nsc zhu$D8VERVx=QN=M7DWu4Bv8{fNqmn&Dy&`_6F zlm{W+Pn$0LgX!SqBFF1iw9dw0rmv=E_P?C}UPY-V3t#g?Jd86Im5A?Y#j&-2e(DPB zpY|zk#TX;~DHYau2Ao24haBzb&RqoSf8!B6PY(|8zx&UgJktM*|J?un`~5%d9~?Z} zf4cwdG5-E||MC98pPWbCEwlasuE>oY=T8ejvv8_v+qJ#_LZ1>&K|KF<$s`4@hgM1) zndM{*2!PA5AFK0mPcO)9eckWbUhlx2c!5AlH2>NUaZCKnznl;zjhy`f4}u-Z&Lx+g z9rr`!Bb;#C%xPHrY8w3wZbjYD0=dY-(}L?xczxoF;>RPBnW$$BXcs~KS04$)-7A0L zr-d%%2HaITf%61`Z=YVh(%{qvDcSv11;n267bIp7UntbzF2Qpd%}Rj8rv%Iffc_=x zrU8|I2y(02WPN|psYca0xKp0Cw9mLSXT1t}=`;QU_<>Wn80SKGRpS)jYy`D@B*mNd zyISs`ZY}NRDve0!ugcM+pWOU7Bh@cI*COOeed4Egz-FW{HGLhq7aPIkDoWBfUV0nF z*SRTczh{jFKbxyWKFpBb=;+FgQ^H*3^O9R#Qn$3m6QpAy#>~TuPbnk3R;*ui%kA#Tdl998`I{rFvPOG|(fUJr86D z)_K0b`};8am94jmLTGP*U&oZ7Qa6uU`P#R#ItyPdtr=`H>;Rg5wI5+8V)rnfClB+J zcHyVzP;(wpYroii$T?2!}-~K_BF6H zm_YuBw$8)?CTq;;bd_ASOk@d7*8jN4LC*qkj{@{nD9t2m0APU@`gw2cZ1QykFlvln zdui-Xt5^dK7_#wjL9K_(wCM9`AI=aY0$o)?L%b`%p|VHq*J3xEd(OX6Aoai9;2_TP zADj|utT{`fM`>~}BODT5;N+@W)!p5E?+s2?=|?o&6!i4NjP9Sp+Z;iItAUO&-byh! zBQpIICgmuni|5Y8(P`lwGZ1`AlEMUYYy$!MMR=1R-V1NK;}J^VRBEYwy9#Yp7oX1G zwb*?Z1*?Vk8kxny1a7o9MfxjeP&<;<%&O*fq&UH1_sWlJ%{8{PURo%9bQUJ8ZOdAy zpITO*bv5IN>$gE$D?>jgU)BRXvG{WC?1jA7b2ZCgO-+ zy8TWX1zuc~jkO=U{@OAMqWRtV5>1D27EmAT=Pp!jlZ|Jhoup;#Mu&Hg=zl@{p7}|{N%f4|@V{&BDW-NjS9pV91C z?fH+q=bBwfR|Zphp}R~Sd|K|hnO=CfoK5b10^P7w(;0wf;fIGet{)(P`1|@Vc?4$E zJ%>L{Cg6VjC3Fi@$o5`;YSY zfB)IDgWmqPlc#`VXwQYS$FP9#=^Z+E03-GrF{z{Lza;3)1vFr7r63azs(_)(`TVm` zfn{$QCv8SnG7V6MW7kn;76?;K?;Lf?;nfaERlEvV02?~6Q!lCb2@g1|7{-rSM_-WJ zhtda=l$gmt{wOqxmG9)(;$^W<-D}hh=6<{J;z>3QAV;bxrczRW+#=B#Y`)IAc@=&i zv(ruOYmf)s!2b+`Ebao1kCut~h*EllrgNZD-B544_on|NL*of%R9# z(efu}wZy2MtYg!8Q66)wd^J2e?lk5ixa7H}oelV1$BBj4m+rN;ar2r%WNdEs3pv5f z?f}R5)6Q>9JsKeOrO2L^O(t_E-~Jppm|)>9ce36M0L!x_QmOtd9K#tf45p{ja8KAD zvueiKRBVPPpYBQs6qeRF`At!9riS*G6mm{OCUq71I3gC8)SAK+lVN^>addW5FRVI0r z3BX5WTapL9Vb?ZWu0{@uUMyvjP+00lo0P05{z#xWgP|I|d~Jhocx`!yV7rT=0dBK{ z%Wjp@tFn9ow{J~qRMD9V!LkKSvsUWTPC%PTYojk&Pf~9dN2gJQaeex}*L{XIs8vR9 zmKnwR);xm}oqq90(H8aP@YlvG&LgEtBq6cQuCP^=^+OCk;Z2tzQP_HoGEl?|4L`pjZMI9{y$HjeqZqadGzGb_r3jh z7f%*%Q{m1mJ%8jY;hwDAoNGLT6!sI?eM8KbIYob>Fwva~4ah(X{(toB;Bf)}0|9u_ zL3Q< zdZN@njbbmt$j-(1W`B_1BC(^no$3H>HXk2%HuE{>Vum%#(Q$2^{^is#LYdA!K z$y3P>8jqzsN!!oIqULs9{!~s;Y;cuiQcj`c;d8D>qxzVt@x~3BfF;{V;>RM*g}?Bk zRoc`s*HHGpibA>kroLLUHaaf5@VWpZ*b9|uvSaB*RcvEE4Gq9=>X}!zPYY_k`1#X>!o)vU+EAS4*Fz(W&+2DJD{M}DPcwR&QA;y= z%X$CM)1d#o;aXU5*rbrKrV6;l{`2JVv!ea~@soqz{5p*VD2fl;Y&F&YEMvy4Kl#pLJ-<4b52+5x*%ME-VWn}P$d z0u#{V*KX*}y}0BoQPl>xnv?LKM26dwOcSwRh@P-spL!0b?huPV)bY}P{4riH)xNf7 z@gvMbIu8%zzKpcZstXFJbQXgJ{k_st>>sq)?3h8*zYlKqCvZ&m#mi`^vSQ&VM`te% zFJ27xoWU=r$KqedFJ8TXfBi#{N$Gb+U(H2Y{6jDu5sCo(3>5TcIQ?>AgF;aQ`(gxZ zYcZq%!i5avCKQu=CPP0O3Xl!8)#9m=SGG{mfnbb)(;WBzo@hdnV)wP zO%(l>zj`W?U+{J?gyUSgez1zYiY)Q+^ygaif@|kbgZw8A6MFUIZRCGMX|E{%J$v%B zm;ZM0G!ZA30XL;t`0s`wnj)3TLT9pL*Nt_0iaC(y{9ZM}Mda^%mdZlKY6o9hQ^>)J&fLHW5I z)f23%ohPTEy;pr~YY=Li&`mAE>hX+f5^B47zKhy~P2~p5Ws+;Bn|vGe?h=G1jP!d52*2cB#Y;R-Z#XS4W+s_ir?m4zc+d2Wzi{l+6z(l>Vow<1YS z<7nccUOe`wm|N9X3ysTPtYLxz_#YQ$GOJ&)YSIY0mVqko4*u9&a!ro@g_d5ktg>cx zglPOVs;wDLVa@NAkx2ez=7DjQrpuRJ0UJBzxOL-GPQBR#fNk%7%e})ASH2q@dx3kW zbo7rO<=QO0*eAM|Y4GJ!OL;y;y}Aq}yQ*7PSBWko)`&in!fp_=aY|caimQ44?(A(C z0A;JF5?g`CTB_(ZD6BTBnpda~R}Ts%eR;5Lsx~qOhz1KYIILaBPL8$ff_Cz|yDDra zzqi{0ciC4zk3x4DxmvT}oxuQI`12a*|2D1z*Ove3;BnFa^We$TKK_3vkG=ixvnmm% z&geEv#dT`ltEn#61YL_Nb9Td2xhWZqWLI_0cBq2=XgB)G?4zIhO78SA!2iNiYm51x z_MaB;|NgV5ef`g!Jni`ZK^UbbP9P79*k6k;B_Fb{z(~}E69;$f(3Z53h1}@=Zx16h2=95EBfaPc}n;_&$Oi_v0sFJrEz@hll``-a~`PU>4@T_fIud9&OCy<7mS?ax6Yfl z7tV|0lZ(;Ad-wjw-!Dk3;4aCN9udz!9>_(&sz%9`?*%hbrcB(~+#6&2q1JrphnG>j za5)#uZ_i)d-wk3{OsL<+=2HD{&OsRy76DhH1^3f?ELHAzlQkm|H}X5 z{zZSmXv(YUUrt2j1>qkbe*fbG{0E0|3YkzXn+xIgU(O_o0&I$!SZsX0^rrr$4=V*; z2^JeWuA{}tXL}xNTSJF|Mx2eRzE&M+RGz8+Hs!kw|H;C!s>aTogw0m2+&2H^U=coS z{P%w}HZHgcTJ9?R^Q$Sv#kbhD^RXsmxs zTnhd}l*-M50i5J2S_QKVy`qN#)hA3g6J-xKFey*N71cj*k~1 z@G1#^E{IqSxL z*uU`uZ|=QFrf$G`uZqiQGTzi(x)VPDstIeD#nJNbkN%P0oWobIYImnNnv5Vhi=y-= z)O+2*C0kTnBBQ}p;bHP73URPahWG_;zJ3HAJu>YMf1`u~mjab|qm*R}4K~2zVfry4 zZ8U(X)|g^SB=MvwmPU+Y@+lR1Tn6Bwe3w*>4j;YtLVy~{ZCSW5UKDJ;tft!4Ry;F7 zr#J&M#MxjGD+BihzIDXwjiDP>lUWpg>6HLg8L~)_M$XBlgFcXn$1<*f6$tnUq&)x# z4FZ2>DydZ>Wq2vHsB{RG6V`)eOxW%EFqi7ydJ4-%cATx92yc%dj)#gA2`u9)G%r>G zNOTp)UWv5iQFpjnOi-k{)H_JU7E2j`%kdrJqN1h({)YRGS_Kz2fbs~cuM2n(GgYy} zuyY9wkkg?h%!2@~NKq&d77C&NbjZw*WXcV92{aqHv4U8C!**DnNIXkgutnAm#<*b} zUQYfw*-oHzM_M&^oi3|a%H~& z>{XaFHh4>UJxTO2KwV++D2TkEQKOosQ@zYAOgJfM#Q@oiKR~m$EUtXOL1y_ua1|lk zP?j*&y)JPU0-7}!0J&{o%Kp(cBJ&R4nksNY18Nw?qw&GSXCuqnb5N zhAO6Zosy{%$y969t}$9iMbx=Ys7VQgbdcUV_+C;y{8gsh@Dp9ns}gTeBftMe59zq2itLt4HJYI3PVUuR}+8aoU*DPS@xHH`q$MY)BZS?ChN4OuX09GX~w3&uWQ`1)?Dj+R;$`C+nS;2LP(56$;>}#swy|mr$Q<09 z#FW|c@W^?AJK*@zn#g`PFwUZCCUtz6#RRx0gW+czIW&Wh zE|FI=U+GTtm!+?5TVu%|+>MTsz9Oa}Iwh9IOFk#`c|a<2bl@rsNLox3j&Y0YCeos9 zY*iC}@p?h32{6;Wk4=SAG5-koHrUHDGok(m9O7JYEeJWIDXD z=5#|<>1Mjob(EpD+Rx6^o!iuJ?p&d{0}bX@RhFN>j=#LrW#8>DmpI=|mg5wv0^uKcC zE?)>KlIPD$B`D(tAfBxUz3qT453fpajZz;or$lE_|}pPGM=7C?zMWVGHY%kX9L}1sa(O4w06DHx)aQ{36`F5Fb)Cq|!Gq z?_O@?ak^NJDRJGPa#Af7x@w3Zyao0Kjua6KF{}ETU789-k``HNi?BZGokq@K;sg;) z+fC?JT)8)1X>8}{(2?_W1VR>Gt0e8Xx!D0#SuJs}Sbpz*f+L7Iph}6D%*kDT0dNB| zUc0!oJhu%i^yPf&U|TAAQb~YV2b|M3iFs-jWfbukHGi#^t2mNGX<9KTaofbYqZcr% z{#rq?O5o42Hr%**YO3r<899RBBRp{bdj*&Uc1AUR;E!OGP+HZsBgTnZOy#dCT!iy8?2!B&%iRXjG z-H^u`(24kob;Y^O7fc?W*KCXS#4*RlJV1Al;5oJfL9et@-qycHw|uh&)uBtx)>OEp zH0POq9{+#;pZ_l!QH%#PP-Db3b!G5GO(>v zPe*=?vKYJ5eqOpU$q%y|mz%n2ahVGB*CT*7eKhV(9;_S}$J3GGFn$+9=~A4LFghZx zP5?(tub6)?jr2gUF!1%nTe`@v2X_yN1IZy~+H+PHslAjy$?fTdI_AzS02in8yb{!K*4*`xu6?O<`(Rs+ogLW; z8RRNP)DmRjckB zTeogCXWQ!3%4LwUYIAE>%||{%t5;S1ibfXOO6C2j^L-Sb`ur~=&Z*JCH(KkzKYmio z|McwpgFgSuE*{MC_HQI`o?)Jl=RY`u_Ou`aO7}-yqj*l8N}@3}`MR^q{QkQ_9nOH& z{7Aii#5^yYp0J{Yi2cwSRSq=E(qYI}E$LmbARF$+AAit4vpt2r%_&l{p5uy{WC@$Q z%jVMfnYz?2jh(C)@dc%OX@-_b?w59m>?RRR)qS8is&jT@7Zdu_NbwtbN(gl7kGuW` z)%bYwq&EK}JVgHw9zA{X_|a3K{|8SFp8d&r()C2Do^PD~SM5L2(4mNLT)*l|_O#{y zd{nIewg2qtL7)G5Cl8ls#`OE{{JDcW-(%lz>8Z&7p1}Wh`VD*h|K$7qr#=4P#Z$BY z@82h(yG*X4^!@vd4}<;wKYR9Mzu*5|JSO{(rr-CO3jRGdA;w^YjoQD_LjMmQKP}e( zeEO)*|GS$<=!7W=h9mbJ)vnSff%!n>5cZ!t|DqBCdYuP%xlFLYC;rX_A2O#KuGpv* ze85L9z`%noM|mTVj81;;oV|GU;_&>%@Xg`t7Y!ZQGif%Q+&y>x3W9G#Q!Y7Cs`Wq) zPT2`>7~uck-n%ZhjjIcy>vx`l<;<=mQ4QOFA@QolI;Yj+Iw{@00ce&-~Dot7r3AVzIjg+xQFSS zm1YfJ(0lVD^vw+nSESXtz+tDy<3k3FDx!oSZ=N zoz=CEKxqrKrB&`z)1B(L)*dd5ehxIDG6t2)pu9sPP_2A>VGXL5kQd2$qx ziVlOpG?@)#Qn!I}<^TEFqOq?uvkY+4KX1ODWbNRB?`{SI~|7t#q$w#F- z&j3yOKR-ujg~o1kUxeBGucF&Qr5g)Sp0Ur^^9th7W;xD%*`9m4|5~LlU&(bhkXv3l zt-7Q9j&ZTB#(meTZdF^oPFK;&9uD<2pqBrYpVfcxJg$j1!WpjqYWiKRxq1iyz$nN9 z5x($4adnFEvopYdPwIPH`bj_zKiuP4r9;IyU0>unyZ>{K%>A|gx_Q58%V_a$`a7Q0 z`1R}BFU@xT8Y+>UHFN%^E@-t!dhoaoud;Q%X{B7ZuWY2(x@m5lbr{vlIvL>Fn`W85 zsb<9gcW3>nwebHx9MtMRKZ6dA|KCM({689{*ALhczyHni_qUs$zbXEGFenmY{(M_F z%eBDac7v>?%r)C)#Q!C;v>WHgZ6p8pkdN<>&b?c((2z>d5vFAf)yYF&cpd=-kMG=pNE zG3^-uSiyQG|9(*e*uX0?BmobCy4FJs^Q{B*&oqvX>12lL0kCI3jKvf!fRYx=#%nTu zRXt%Q@GynH{v$A7wxZ1AyuS3G|NY_rAYav=f8N7jqpQ6~|H2TGR6fh|cZi#HW%yz#`ew@+zC^w$C%g9LAPh91Z8CbZ7ghl_2~7FVp*! zt7xzWMkb60X&?Uk<>?=K^7x0CFbHHYfFH(@4(0#mvB8fhCaN+BZW1d&GR+TL5ZEwO zU++qO_xOCd>O~4uc@u%li)Ef7x%aE4<1J2V%@IrBVUkia&?v~LG-tk^=QPo4fy+P9 z6PjQsSpA-GTZpg6#rb)48Mrm-k-eQ+(w%W_IKxT5y{m2GIj85Cz0AXR~>1oKgDd zg<0>)_Hm{8CM&Q&8~;33DXRy;%2I zc#;HShD+f=&wcMR-R#dQvyN11&}b@z!I`k~;rn?$!wIY>k7(BDsLEhL#Y7C@WBVYm z!RpOBSB?NV1sJb_rvZk^m3q~c_xu1$4FPK=r5TB-xl+n$yeLYAq`9EM9VIoE07b~b z2PoA}in;#f&&a|e17->-Y}WS`TV(O{8sVi!n^#~28{wQGz`iHMwd`a&tl8Ep z+|-8M$b?@r%$p>o-b_6WnYw+z+24nif2;G)<;JM~S)OmpAloQLG_|sAqX%SHPEb(8 zdYN{|gkEW@i~%_%e?!It9m(xC_K^6NNjV}?9aWhj)aGer)+vQ6SIcFnEke?Y^4C-e zj4dKav6=;_ov|2FV*q)E9I#9yKEuqPG1pSCP=%(}B=J}SF@QiySq{T+@Fj{bBRy>| z&gc)%0o*62W6W#!>hRe6C==ayM>9O14AYYOQY_2&nL}7Cmt3z+>1uRUXnO-Ge_;i* zjyOyW6G>vstMrb@OWcJ+=ywO9E6%|jUISX|cIbuOY6iJTW7u7j9}GrVt=)>5FI~73 zrT?b%K8h)#T)L93DV~}W_;GUDTmZEbn6d3tZhV*Xd1E<^DiK&Cl{K44UmL*Y_7nKA(}qI~>*pO^gdOAfv%txBdWL-UF5BUsUhXK3Zqe(!<*KKEsgDY+F(fhF_Ue zBsc`Kz3n&zvl|3+EE2JBSjKwy4$IiX%VC-C3Cmb?H|{zWzpSEJv(vcVO2>5k^34dn z27Y`B&vY8266$d5O=uz(o&d120EK3klLo7DNfNT~v$pW8WX4n0Xb=A{P*(8Y-n~0M z&7%WKsb5J6$N7#EOkAvehO4U9R)!(PzjOMPAM@oZ*HIKKQlUN{>s&K#oVfuQxN4 z(rQbkH36UtaPk{SSJOEsCDj|%ra_RW4HmU|{U@S~L>Q#X*5*sycHjDEpruBtRt zm<~)Hl_A+NTil@LnAFx?cLLhb*Jzc1G$&%x=&Yz(nR(~_b}n6V%wZU>X1#iJ8MqKN ztzJ1pv07JXc0H>714oA1P!aa(`IIE-Mld%oSl2O&b6jth!;_(Va&kJ}hQc`xW>bX+ z$4R3PcT=p{;}~W64O`1h14|zAgQUhAm_ONPieZ-X4cF9M*7HtMX;^Tc5KvAk*VY$Q(+X=MlZI`h7*%wu82Loj_`GMogt^hRwow^8;IHH=~ zsqYuTu2?#92-6J3Li`m+R4xi;9KfOm=a`a$gIy6EgYy-DZKR+N&>cX};XGS}_u2A# zg@4l)ur4Oj7R=cGyll*Uxe?mUN)G@b-;GB1b|b)EmX^B;Hq<7!)u z*SoX+{M#NqU}nZ#?Am%%R7Mq z>}XE^H9mQ(<^JJEQdL#45GvAi)yircuX{~i&5cW!l3lLA;&m%YT&BsJ6rQ7$yGYMz z)bcSiuQBB@mP#-g44@zbA6{5FM`m;DS(C~)xTTYX;k8w%f2|V#%Z0f*75o4G!A_-$ zV>~3`{b$zNZCWba$PiG43ip6+@`hs~(rK_1A&oQgGqqtyIA!6Hk(gZ>^X3|d({Q)c@O1cA7PSPPIy>BVZe+%%=}0o*R!AVKl>#l`Uodyv0$dGdV6X{ z`S-%yhnYRkqr#-OjA!1%)b~9hW&`-gAdo#DwOO*w1sAJ>z#*007>rPMO~WC^7g|B1 zoo_8&prbQy0S8MosCEP>2iH$wAH5bRqr8>^8uLpQSiWa5*xWSVBoG&)wVaxb7HrG| zRFRV}(dgWiWUhLSeM%r!cKq+zes5v7??f5Q?DV|^Q}$|?-%&{AUE~sOF6_{lgHUwT zE`pi|y(nvoln}zda@ziARxZQPlZwR{u*8DAPeQBhu{P@_^0Aw064kb@t0g=WUpBtP z5?FqP&iaW9sT?E{g%u)#DRO-yjzV9GAlanlHKkGO3Sa2W5H;Hgug0RQCAKHIPQzU>ddd59J0{DzE=OH;yCPJyXLsqtv`NR)03FRaY**N;Zc zObQONP)y9l;2?V~CU$Z#5BVUHKukhEy?L3p;v&b(9W10s-ryuB)Fzau)29{6N*e7J z??m~hx^X_m_zgm?OAqa&-0f}0@e#K|%C#}mnPwr`8Nn??9??jAIoY!$zrC5@R+iQ` z%vxQsD^>^(b{Tpvw9BSdYzIP4xhr;Tk!#74VsysA#9S4B^oyM$9{b;dY^j|%54-}n zezhanwNL(I7n=8F;MDGpA?XG$$B;ONq_NpBBx&CaiPS{c`qxMeoh(m*$M7zA92l?qnjfV)TVi!#Q>bYH9eZbSY{MdRfoxr3ZQX&a_`h=us7 zdAN87ZQX;&rn^Ke4GoJTZY}+4`B{UEyMpr4nU|BHN;X)28b;u*@bO!Ql0Po1dP~XB3Reb(pnO*G@&f&xW()%>eLbuPt*kvGlK+kg7wIT%=2yVEt z8*^E#_}{4dHdu8=q0! zBc;oI7whFPiNhq{5+<>gnl!G$a72NrdbclOV#TQucBrRooaUHMPUHP?c=STXe-YL4ilpL}8QEq6E}M@xWDhN(KspplP4Ri~@D+bAs{$_MP4B4r<0 zzPV8B;S-zrz%S=T@L1@Q-Y-o;g$mdeyePP2{91WYPPBBQ<@UDYM9bSFS|VXI?81Vq2uFOZXzkE7n<>cdt+BH|g<_dXp=%Mc_T+Kp@>AWM9|gbNHjsf3JN8$=Jfr z=CKLXb{q+LSOjvCD~!I6Dm5y2KI+emVAp+i*()|jHyqt?bYpwQjq9<&C39|_4bEXL zSz!&q>Ns^h8(hUf4*NLlZ=INTYFSGb>*W68S5%lxtAYCB%Tgq29dimRF)V~DnBS&%CBWN9tH^$aiiQE31_ z)66H_cf<;<3fUST}Q{ZME$TY(X`MH*0 z`Exnjpz!csl7>wNl71#V`)5S=h$(XQkj+!S-Qk)3*JY5jQ#O{&}knuxOpjJYZ`|&{ZHEdWRbSm}zW+ z&~Rl1x7om|3(~>X3Gvi+{GEn`W9xu-uyw>e#ZWu^gUyz+#kTt}acrG|(}~-~_RVm- z*gAsGK6D(LRrq+ZIt%f>TTpwKG7p`W*U3N&l0YBES>*NY?XvLk?b>8JTVz?Gi@PFm zX1z8y7ig}p(cQ7tRg+_Ky%Vx)^w?8#9aJIa8L6H&OMuR5>hw?B+m6#e-8rkN)hf*L z@T#^hAFqj$tAXoe99Z~gsk58aF26ij8^1Mdwm6Td?r|5Upnzi^yg@T)4OxU@DYuYQ z7*~?qR7Gn(;SG)QnIeoJ?cnqzp;lzw#jV+p%f$oSc9@N0h7OGSpU}WX95`5M}sctJu+{4RvWlJ(o80TgB$LD{ZK)P@9Wf zhW!m=W2{yk&cxDM{Cy3Bj09hdW<+?8@DG=9GJ{`&MAPrRelb4YnRiDgl{W&Y54pB! zrv?%&etvth?9N>f^LwCZCV_^3ys*G+|b_ptfR zRrX!mSEaO)^5{`(dCR?FW-1ta_9-Cj#e1_LWIE#vA1pv(*x;--XD3! z;h>Q2p_42-D)U#odv|<__#%s^afkum@Y7Nx0QHQs&jj))kxgBOz`W9!do=d?+Uk*-qT*eow6u%ZX^~v=>7-H`OKNd&J^8Z4H8Uf?K`D`kZt^-37WOG&e)B7z}s+yd?yAV{JO zXsnRLFQ)b4L-8Xq3H_Nwy@=X|dA)+m%gZ5!vcSzn&|x`(Sv*ZgD$NXuL!-oBhEg=T zFTyas{3Q1XtNU~jh);9cgV7mH;uvRjOPu*4 zqdZa)$b2R*ql}UOQle+Tm9%Mf#C~)UByqIxWywFZc*RMEuMcMO-awyVAoIQrr7|x{ z5B7%M8%(SvCg^++70bb+1eX4c+zCi)*m^vEzP?d0QJb#N_z|0EOB*zv(t(2XnKXww z3kKWoA9AgyFU!9xRVOPW_c;H~#S`o*O_hFh9z^grytr{xWOv&ep>VawpGj9|XTeuu zL#Sd4z8Sq>OOZ7$&STCnF;dlD7BD*}Wl)Mw%AH2=L&M%6jHuz?L7Mo8c%hZCsU$(t zhXR07y1qdgheX+HSSR%XF@Utq88?VRA%JE9NFV@Lf0oyW*Pc05D5IL|o-criNz^5; zu$C-bUT6OTAhr_39y3~;Uga9?Yu<2e%HA+chuS+2q&y+7dBL`Og6j(8e6Y^ysw=KG zOq(70Uj?9ee>ja7&;S19@#DSzu9kc%?OT&pj*BNYK=>dZq69+zvKybPMylZS{Q#91 z-rmK*9WmsdR^a<3;tgN4CUTVRXgC zyfHE2T8M0^ZL;zc8X}2Z)(%G-^6+OGN2W15<@7u-Fqh8Zg{_i^X+^(J$M}FHH)#df zQ^|p%vJHiJG{H*dGesnp76n_CkdN+1g-fjLjeOMEDw0xMO0<*dCOWGDrxg(bgaPLP z2)HJ1+|Ya0&SGtDjIEh4bL+CofvNV!mS%89ox2TCmq|iZL?$Bwr$(S6Lf5+WAkLKecrQcf7x}u%=rgq z&ARWpt}#XeXrQBqYbi0Td)1ziSw#s-^;IWyEe$a08x2&TKKZS>G23b%pnR2gQfh64 zgpaLxj+=67x1>2z1COoF!UJ=(3Ja}GxfSi2Cc57frbi=DCxppYQ&B22LHcm>6(u6X9IEBZs30boE ztsT6z@_S!jAhSrzVpxi_7b}rW;4+7mlMm%AMI<-FDMS>^FAr736j@8pQd;n+$s5U8 zrq)4i72&cH>J*CCxavH6Nz^wiDAs;rU5)=2JTq!)5fCNG<&%%~P z6lW)aCw||59SGbDAV`r*4_i$b;?|-sm;K1{2HOsM<{+9iL~kNhZNNzkmF$N5KTM@6 zErw^rz442r`>)~c%J=II!B-qn?8oZ~-@|nGy7xmJk^b4}TrNA_<9gN7i){mH5+N+2 zBCFrNgD+$kg6+1+LCF!C0zs4f{hz3yU)i}=m1%M(U&6_8IP=D={QPe>LzY8v7r%fy zTN=d~x_pz8TEE$b6xQN%BvkhCx_dM4Z+fe{iSRI6kU;`r0FFNH;z9Il`16o4+|bBrF&fP>o{SS!Bf4Y zJ}iCk6V@Zmr?`}uG0ldJ5I4tVI_3SAD5FXFI}ZQDzs?h_=!fQ<+ji~=ar|DrBe#12 z9t|3zLT+q@s9pmm;5RbjEZ>6&;+78lh!ZaG#mN@%k7?%;h4FA$D})D#|>jR3I4ihDH!Qx5xV*`qe$SEZe^`{YCd# zfp!o1DP0H7ov1`~^3gi`Sc5_f#0oN}=@CW?b503UgCG^71m*z?+!Xp1@<;Ko39{$h zxQi(-^zJBVqe zf5I_v)3uMUP;ddjjZ55}`yMdv?(zD34GXoHXED?jaccGBq309(k}teiRM*tkv=e}c zbU_iLx}Eo%XcX8P&c~#zT%sPrli8?Jme33bYP%M5*mipJeIn1;T}`09a#|CDdWX#m zt~ic@`nxz0Qk8h74c#L-UjDpY`9Sx&i!by(v(Mi6>-oXOZ|}YKgny!bN%^l~6MFm< zK==PXI~Sw1P^2syWiIxFpTRYm#|dZu2-~#3YR{RZQUQLWjD`5mrJ8M)XaMYczM$EV zn+t6O_J(1aF%+a@V~yI7G>nz{el7rt?&A=*XCG)N9b|_&&g4Yqcj+sM?fq3h9Q$QO zY@xnr)yx;vW1kqQk%FV)jg*a+hA`K+t zWahw-+Y?eiQf~8|iEQu;51NQ(lmWfGkK`a}7*jHXiPs%k7?JB<=u_wbkEj??g#82| zhjcT6wk+u)^G$5S^bQjCwcf8(t!PR5&g}dE1gXUcv;3q|*1l%n|qBPem#@8X8IGJwn z_b-hykl4t}6@1UwXKBXg4FMFowm4Zdi-%!L%8Hq`PzpwoodHD{IWKlPmc0|uv=F#_ zqymgkt!jq$9JJit%9Ewfoav#9?i3J9uinSLT| z>LQTY&|lO~abH$;T~>BV|0-aYzku#;2Vtn~mo`<0FR;fjaqsGnLbjA~oeBAvZ$n;- z%&047pnQFlE-kl-G0w&Q;*rUs@kocB{_94~(L43p-H;vXOydf-blwPT6FcYb(@O%g zad3|%+07yo7W^+Yl*7;&YLo~w%;2X^-I_4nc)ItrKKk(rFSQ{oZHRdXC3Q`X(!1@l zmE-~Y<+egHabr+;8lozdwe291&C7XEJFRhrH>-a%=oFCb8&ljOI-v45vbERg>4jPI zPtQq(KwIKn3&5`TQKtpddvM!cMiDfczf&ZnW$BaS?f$2_{ODL|^C?XvUzl^f^Md|2 z&Kq_Ovcjc^4EFB~6`H2E&7E0bnbQTUEo;(E^M8Au`HXg8mWC+u;(~?~-+1 zLA=2)U_Ude`*a6)8tf`^CxOl3)Y{-*hcWWorCN+w_GO(oPm!`rZ19o@dnpu)E-iw^ zE@F>vxB8?fL)mmNY%Vb^wLyR@5bK%DUXDUOvMd)7LxuS+7%+DjXWnweydEKSGI!_1!Q#a9HTnX$lql4o> zmP?aV${U%=QJgK|zb`kUo9PPbFN~w9TvsNO3!6_h_rSyJ=bz%;Yr zY0%rRtrodFFl*suR*gscjYS7*iw=9xyF!T@~e zUZOgFtz^jxKCeX=hD|N6t+n%!<#lCaB$Z6pCx-YJ{IKmmKq5F|VSr;h{0ZGfY? zPd~Y4;o)}PYCAU0Y-G+eCA{Xu_OOns&m-yo;IlaDtmHJZ06cg`tB&q`zUu;=OA^{P zLg}|;MNCFx1Qr40@yE-Pk2$|z5&VM_n(TR0-=%zPMIYXKFTq^LKkTf-5^JtItm4}H z=Z|$0Cq(GYZw1iLvPP(e2MJSgGiDU_^uU5=>YdNzDOvuW#L_ zH3DIr-f(qVQK?i)t&%(FV|oSKt*#MLL)F134cezu5kkU7X;%0ygp;s?8>fG`v!BRq z?ua>>#%b69foe4p0wv7!s{z6?-6#NUX9x_r#3=24IXIfOIeNh}dg2lIsASCoc{FK3 z7;J&^8zsV%#CG-UhPU_oZFda?@9+EeJ3#$5Bu@e3wQR@}@z?84-G>Pg@te3l#^y(L zgS)LSgmEMsrpt=k-&Gm~&H`O8nyuraCgB~$-y-FSH3x;h4@RZQ#9EU0)6e=h+E=?mAHKRgB;xxymHRU#vBufwURe!7Hxi%;3p1q$=T5b;h4HIO%{(DM1DpeQQ z!aM5hi0ZzZA#-D5P^9RP?21<7Jr&h7uY5uJN|`DLE>ffp+Da;1_+fwHeg9>Da?j-F z#u?{;|FA!ru={uC7N#uC4ktp_w#DRo47VJxu}C0#(BHbUkbTy}KP!ZNAT-}wedl!L z<77-dC|2w?vlkPde6~AfD77B@^-+e-e@6^4!gO0R{6Iaa0Nar)%$E#Ix_zy%I0>;C z^S!~`0G)-=v2Lrhe7C$+V^`l-^W!j~!z^=9-+|9Pe}*rO(n%yG=;)GmU@n%krUR&N zZ^q}LBPhxR8@3sx=~_evKtOUT1nELdX%$$W6@pS?g}1C`?|~45%Ih*^>>)@`iG)*^ z7t-Z?8F8aGrae+$Rmi21kfxknH8ow~WQ)p^La|M<^OL{%_jND0`8UV8-8eR;H?6vg z8>3W7E(0wCOM4%Hw%f>*R4t?Q?7k9EwK*M&QEr}6%hl295Mb56DjGW|EPi&=GPG(t zvuC~G%ju(QzeCh(mZb?9l`*&g@XUTofczFHGH1G5Bs_4wnz>5}NoDU@9ey%y$;S#s zrJm}}b6`V_G$4wG)zGjr5S!Tj+a5$je}h2%&=k3Q^CbQ3fdE#p5{b)xcgo}6Ff3tH zCEF|DSuar1f>EQ-2F^d@-zlx9+MXYNnp?%!!ZK*0zpnMy zw-M-KK6E0(B<24iEY^RUY6_6p#T#utaMVQaUkbz&%#{Y=Wox(jS=V&w*j{kh=}CWN8@&BvjURV7NB7A1`Khge)kER>y{!aArtQriIzQ6-T9KN z1ksAd+f2$EO{Nfg|8?`ApOa*|$7x`7d7Z8z!_|g?TA@uO3vvPW=@w!xgO55Bz>&G; z(vv{~>W#i2{`0f0xh3H&g}J-Dcl!gweaHU`!!`9DI8m{X2W?Xk&Wb6|^e#XMozx#l z={4;D4`}Nq9Fz4covA*Jf+wHIP6xIB;ozN%Tpd#_)@kXw zqL%V|gNX>@`V-94#k+(sBf~nd@QtfRMih|u@d!#K+1uUI+K%wMNAT!Mr1lO7h*l_4 zOJ{)JfFBI5rc&7z)ZdQTH^I>_*&#Q%weE&X;7C)fgX7&{d24eo3>)Iu*rceOb%2tC zjSmM9xW2J)c_HTTF5MnFn8&D?!lRMGue4|0GUO5ToD8%)5V|#Q)H4&az@KorD#wPN~8gjen+(H%% z5*;xDJT!2ZEFquXDRu^=y$XZhh)f2GGv^(^kO(E6p7q);w~tx-V7BtnX=B?!voOAY z_ZTl@muENHdNs0~EP?g}-v!%)fi1OqM^JgvXS}-4Z?ioxyt(W8L-adf-kWMJu5O<_ zz#TrV#2c+QelZNI&-h*9HM=4ZXI-gTz8z4a2pv)wgZ?qB!h0;pH0*BxKdhDRAFvX= zMmBLXds$kLwZ=)%4bbRLo{(rOh10vcIvXc3PCNm@>>VaZa<-30f9)sDSEWZg>MOPhIPcl!%YBKu22BN1>6OxgeI|SPlpB&eV!ROS zn7ql=G`q-DX(NWqhU;N2R^9>fZIs*G{6*gbAWF0EM3~1^4rt;8dswJ#UjqTL1Kh#i zjp*X2){*-mzpj~qDT)DJ5ou8Aq(EQkH-&n{esqM^DedCrKd~#AhV)AI$Ry2_<6nr! z1|w9TN&7L%zqpY%>h(l7@n--d`trVg1bHlJnU5;~tL1FkGn0vb6n?WznL`XKUG6U< z_olGuSKi6?9Jy)1X*0MRjX&@{of3Mu%JoW5xHPZ@{c`238p< z(^)!Lu5F#y%8s;XUn^NY@o#>LEF1q-778kfnPK-AMh^A>djn7vN2h+qVgGT`_YFKa zf2M<~d}3@@UzgUpFzR$)FI5&#S$5OR(GsDYCWv&`IE*~soAOzNmE@*!$CEF(b0QS+ zmaJ_Hc>v$FBIFLsV{etW;1CP*Ji$qkMGB5sLd8}(N#(NB8$@@O!|Ov}{`BS-$DF3irqLBUyW*lVb8{gs$Vp1yYTx@e1MZ652?^ z^SzB|@nCQANk|FGTztJu`a4%$7gED;=q9_T4;)Hm4!mYCmZ;Ikl#?r%yueq~oFOnR zNo%i_eamNB2MRJ(F(9qwW-JM^pA5Y>9M{7(l5Dz>ULU5GH(gd7H+SqEtuzO!G1UvB zE~xNCxu*I|9Q7xWDsLAfb-PU6k3_)~kMr7)&!rMby|AF^aXD*#9~1j?S1=&IASqBj z2kOH)`(Hy<3cDL4kQy4%J%yiqT(he-V!sb0>U;VflH zny;eKc-uhmpmb=FpoH7~)<+9wU7>X|71|6g0l}W7ZvG{ZPqr}&URKACSaPNTiSQXFKO)i2|Ho%p7S)`{CY1t!H_YIbGvw!rc^=zafU(pgooyM9UGsh+nziq_e0EYKPLxFV99jNjdDgae zpV?L*I$jkcw^29x$6zH*i}-d9E-_mLm&GBxlZ7{+n~pSDO9zAJU(hnOKzYe7vE>V> zrz|P@q&*CSmn`>RJo8K=i_Gw^)szoF1=OdsA-=0WHRJL(&@(y497wxD>kNUld-{~cCsBQ2BAD1f$I6CDjiyT z_AdQ|D2Mp&08Xg`g?rIte@a_NQVQ$=xN!pl%dDb;+fp7zHv~Bsz1XL~saK`wbeL7@02#GXCLK#^u3{6`XYHsJ3?KvE~t4k^b(b&P5L;$keU<6`W! zhikoGwL=-dwq;WVKb$T*Kt& z-dr+^G^jqy2==ZgdXrXUhZW^C4VhsC2%|{Xz0bP5KLuNG?{BZ*I<{?vkz6gC8RICF zPqkZvV?C1XLAo%OS$b#$$77AT=0-5uM)6UdblJaX$qDV+W#{(Nk2KQlQtw(Ht7fX2 zvrx4)gqv4AAxIuJaKYc?)n1ucPH@k{9?|<-kNTtUMDPUD#(C-hoIEQ;QBhnT z?|Qcf^JYa+i=#e`oPT{{&m?yS!z+erTJ4#kId#KbSy4?(g)s7x)`Da1!*?p2Lymz+ zM;-zXk!e^qaH24M?iWEfQK|vY6G>bfyA_>m=jx2)N{XT(df4m}k>$p|>6AoVA`M zTbrKuub^NjC4agVFgtJ2u!qg!ayy^eR1t18nlm~*hw9GYiO{KUQTYo{T-HTtUH_DY zW%iPf5I17tg{lX+DtFGB>Bm*?e|#4f11kI=)-FQwW@66w{`-%=oRCBnYbKc1+oeo_~DK(n0PQtRvJHUX}WVB*M~;+sIqR zX70a|HSBJ5sS(h{9LWdx#6NPfiQV1->$w&TM+=-@5EjrlZsx5RVuc5A${aAtW`fLjlG%@` znO$Pdp(@wP?#RhhIOQp_$?U?qs6pBGs}_eK9cA=sjQ%>lXE+jDhIyIZx8w%XG^9k| zuj4FyxhuNOQVd_8unEq{*Q*K45P&wzYnJ5STgo$-0d+N9{EwbqiVVpfhJ+_p%@&4q z4JFo$FC2LH!pw&ZGh4L1U~{wU4==I61cc-ZRBqMZ1gAkdob2iu>tcTeLz^Pf?&?G) z+de5$m`v#a0fXsKyULXTZhj}|nD@|$;*T}hgr-Ses=rw941osBB7g$CtFkoDN_vGk z%cN$CO(88@4F)3IL88+Sb#t|%!umi5NRP*k$5P_-2`^~GsMbWTM&8Rs$;-#19AU>2 z0MmAy<^B6XOn8@i<$WUAtyQX$VTBbofQojPzIdb*Kbm708AOFsI7vVeQf@*=m+0nGsMvT+($gb~KWf{h+hJ^A)_ zKafrVU=sQY|#$gx*<_)HMmrX2tu+H8%a&jXtI?TSVEpcDWaRisJKR+J8y z3Jj5<7E0PljZ(tpY*@Dv(qMfqgc9S?Hmxd71o^kiH2-1|a&&J)A5b}fE%I}8tchEy z-j!N-V>!!HOTgiCpE;ugym%Ds9YVvqlKY$A>GLdQeBflF{BH1dCij0&fjd9_aBIgH zw{VZ=e7lhl%oyY!$K&ta0(zd^`-dA6*rl}c%|=AovFq~5n2gs2ZgHn{MH}~l$M>|f z>DY@%5%Y1xA)QI@1>l;ox96F){F)WD*QzN*G^lQ|$Q-sRl(F9B#)3qjuv=Q2u}ag& zua-q7{J#ii%pz){7s9@B%?a6%VT_Sq%)b;$lq4fmIu%V-J8Az>p`B(g}*$4Z7ip+1X?Uz~g*e-yJeysN!f;V( z?l(7^bzXBQpqM0F#oE*<1?_-DEa|*Nh|~>RsCPNn;kJ#$Wft_EK>ojm_qqo6^w?@J zJ=dur>pF+bm-guvOIFK!GPE@#JFAT25;vl&xWUg*-ws$5EH7jdte@K#7zYPAo(V2W zo+N+X;;pmXtOytOPueJ^u6XUG{18xQNbBPv)up}qd+FiWDa^UJrWB8B0;jcRm5K{d z$h{aKn}}-rsUF(IhLr}$dD_NonS2WZO?Ik<&G1H){FX}gveqCya9(gMfzFz0Gh?et z0lqLlZ$bP1FKKz`ZI>v8v+`b;eblo-sr3}id( z1_$BUjQxUi7vW?~g>f?3HwkL}&HTTzSsA7gZ3k)gKs0j3tY)~eJ-=q+^R`2vg0OP4 zuLz6G!3ezkMfOsfInl8FPlJzE1G@$PcDaw0g?}`8V2AyNFPN(;GZN7J7aI;>21^t* zz9W(tCk6tl&X_hafhZcp>`l`fVA1iA#7#?VbpG|cF6RJd=Dtd96OabYmGP6-61Voj zDBIUilWyXs6Pc@=){+~GG7j=J+Dus(Xz^JIRlq?$6&uuXu&#&WTkfuY_YuPwFN z=PwRaw;ygid7C{xbS7ClTs;qA0vGoTWU%scYR|8s-Aq?tug$GMt9{VWr{a_Ma0_!F zX6kjL`v;zal=NiQ{~acSY`P`)(0AdRwbU^MZ%Km*nR6YZFWosPc=B@vQoUAGc+m43 zc#?736{jg$8j>J;VF}H-H+_H+%c`sqL$wvuzm7@-HtYJ29Cxae4B=2zxT0~n==iNg zaBb>`N$mzIr{@Pk_>(bl4E5PGMKy3 zcI-|@g?H3I9B_nbH~%9Sk(l2VM4G}>qdtc<5zZa*&Nc0fp`)hOWKOG~ z`W} z_!lML-y}`aVX!TXA|oy`ClS`FM~@F5-y)+zcHh8T6842^RtxcB81ge-3wT#_*w!6^ z!%U=?K2uzg4(?zybZ;HW%l%(&DYJ6f2!lmYIO3us&pk+!0Pl|s7gslQAF|S;>A6y8 z9~SC@VNGO)Ll6UBM||8kSL!N!4qz64r6^gVg^mp>zjC(BznFvJ*k5>HBXQvvT5I`I zTpYfXK$ImHIK_?Db8^Rz&F$h*+Li<{3Z=WyADeCYf8$87MB zT_c1gNFL4^qEB!-jt=F;z6vS@@A57|)g<0=LqxkEfanl8}a+&*YCC zKTH8$Qf2QyQXJYT`hTUkT@xAD0Zs`&_aSpx(|$=_YcwEf?H6!}2MS4h`3oYU!v}nK z`Rj(gQ@e=cAgo;n(NOTe=|;m+S|A-QzUbX&3KN_~!Kx??Fj>k0={0^$(8FiUQZr}F zur64%u5J5?#G?BR;9m_u%4hJOqfzJ9aW-`ggQt;Ep2kM!4Tx(>hiU&r*l_<3XHK#2 zNF?z8>dYvp`$M`imt2wY4tkooU0uQbv3=lG9 zEh>RqT6`|v(^yEa$*WEcU$>C*7C@w7h_@ewEDrkInf*3a-zLE0Fgkm^jd}6a0G*mV z85#v0W?-5&7e2&c+&P8OoRm%@Jkm0--}8vh`S)%I4Wo-81K3 zh>}LT?3DzW?%?glvh+doR)cQMhl|~UKhVpe(B8R{C*8Y7Ix$^A)K4NC_H}w2_Xt*( z{(QP%@=DvGe6id+ue_0Bsv~3?aTLIi^ioxlf0HThngG9~K!G&BXfH+_AGTZkN6ERk zriE+qSVDWXRt~z{ZblLqUX^`lykPPgQ}s#?+TtHbeUNe%K>-H$?y4(IiveTm5?Z$czo{yacR6%;iT7XUkPU;d*q%rkKUZP(h#MV_FoRnr z6rf@-g%3sOw9dJKX`lw?_A$6@!{t=Vw3UGU)hbKXh(xeC$A)QZ6mr2Sfqmz6fZWjG zU>Y;0!3s<>=r=gHbnaq!A{d%{xOlo_wK&#AEnK|~&r*d(q$DClR;=$9HbP57NecY| zD;znISj#JJV(RfUa7-~~(3)1@yjT1S=7hN2zfA29uj!ZY|Ig@)8O~D7>3kmFf}4l$ zSyQkL?Zcp?&igU?{^HXvwrZgK$LO>CG5UPW$!kDlFyOmVJC?@fs9ql#YvoXQI$ zF6`S@19c;WrvbC{e!+!4vD(nAhr?fIXDQ#5EJWkP@ELG$O9TqCi~t?V42535{!=^? z1*HD2{Ri{z+_fIcz!oy@n#yL93^nq1{(-+PR|dQ}0PApaNL^d$Xq$B_C)T=qbY%WV z5w2-W9~{f798sT0oEA`r`2+OIXo>6qVLgNlxoz+V=|g~k7b1A=<(^gwvb`cN=4ykn zPjBeKXq}UYdLE*Q@YOS!E|ug%Jtr*R&)NUYG+$&aFOLq}n@%A#vlKL1fErTp&k z#Q1+ZX|&n6*)4Fk|Ps~+th?Ofn-xuMy(JWyExBZc&edZ+I2 zp4DETQ1`0GbCj3=HpEgv?NO&bnuQfl&Y0!X${hW{q4a0^BwNaMCXr`L33jGlpKxwv zulLuxHqGF7al{;TgN9b|9>=VnYT00~hw%XvfNJbjz-44Q>pNCe$cP_8*+w_Egm&FdqXrBz;`}?`h`z+@Qjf{8P!a zLwsH0tH{K#g7^su23g;j2<)43Wv*~mjucBKaFLLJ-#Go8-Kgrk=34n?I8IBMFF-+gMPpFB;hl&G+1!J!Xzfy>_~2mWw0Re)b`b~^7Cmd0>d)YT=-mrbMP zS9S|2@@c8Do|(wg2MUHATCv8G)7mU?rMJxT(0ZlJ16uI%IG;h-zHka`w$hMt5l9w# zp3g}QZ|!i>T@g99W~LW5)l4uOgT3r{=~h{5D{QpZ_n%AcW2s%5`z-fIdI#{T0yP8$ zZ-V!QQ^J_PSqef z?^f>ft@T|5S?=l!=7JktDAjx1Uv(5Kaau6n&xdP5Dc?LEZ^O6#JeFi%bHR^YJk_}e z|LjuRn#_-JpO4$ydfVG@3|8&1)7&jDPW_psz4JddwNAeW1`a6D-o9UU#l^qf7o{Eu z;Co8X0{UVOw+IoswopDb2aTV|K!tcs2D&#wD_mw?JEb&&ayFa`=pZ{Lfh5Uie zlB`b$4PBC{T`H_2d!bg?W(U@AzF+r9x~fj!Gz=;oZ01(>F3^f`8EiqRUG=(m>1)Fg zad5r28+KYWl-J0DW?@X0y2i`x)@Wn6ByVa)D-3nhlKs5t3uDnNJ@069zwu~YUBNpI z@_&u;huFu6_8^DUn1V<-Oqp~uJOpkscaaKd1Gx2pa`+&LZ+y?vN>Q<*Gfue)pcbD^ z%FBH;?Rg^CrNR-A#KC0HIAIRA0t$CDhN{Jv2HYCiFf1L}plFYkHo%sZK?2B*<*#|Lbr^9oOF~&uCb;8%kK#bUZk?JnMH0N~o!Qaz4P3-Bi?LmQ>X_ zz}S9wr8KTQsdPJ6yk#pISPb~G5|ku`SU0L^k5TK^)Y9`mRn~vpc4bl$6{^_3UoDq& z-ko$`bFwH6gNfpd{3^C6YA6Nm8O>T~eVC(G#RY$;8Bl?(kKEToS)S!{7e}9~-osq6 zqJKGQS&xE%^{JAt3_AZ+`zVul$2cRFCPmjf;tSmN0;EK6s- z`>{$xW_n+~7QNHoL_3z8(!(m3cpuX&On_ebqDN%hIi$ry;`uuTMjFe5mYwQJ!8eP% ztx2@69SL{&pu=JSK>jUIFu{&fk9+=LC^AnX1h>qN^hp_xpZX=SDz0iwFJQ(xCO5P3 zW**|ICJoqow4BP8Nfw<=3Od78q>sZta?|k2yB?;`KrEFoI82Mttw2j4jO&RhN0%Qh zFR#ffL?^?eg;#z!x|s|L4AE%r1T|)89&=hj^^@`?(jA`|tyvu+qE6H1+4lkU2N zmkH}5cxu14G3YJFe|92g?U30~J@dPkYze% zUffmBy6(?&|zvwUTRbEhWwuPK=Py_X|3O)V?^;X%dFm$W6y^ zKP!U$OKy!L6nxxeKCJD7>z?5`sw>gHwpY@lKy7oOaEyRUp>(4ozcV?923N6A>FMkw zFpu5Lp<>dk=STs((0<00Jh(vXnP(;zqkWR%jz;$Y4#VbI+{h&wo!pY5X5OhizHFSZ zz5;T;Gkx1v%9waspnsNU@)YDLJ%~wpT7@{To@j1p)Qle6p? zz>TS{MND+?V0cZ!P-PXuesLDubDzP-P6&gcgs!)v-?R7>e4>eTCisTH&DcV1)936MO zSjXx89FgH&^?^qz&K2<{xp{@muA1v2z$(x=DRru zu$hmt$XTLWEs=8%tyu1a0MRs!8<87(>TB5$H%;#l6q?L*m@rO?swhNfG8CpQhsf~c z)1nuGB*IKbH5o}FxY!3h{78Cn={1*sH3@au{IG)kRC_xFif#7xF7)G4pUe9V z4kkC&SO=K8HC~)-p+00!da!wcmV&sY^$T7l))%kYG(I+Dw_Sg?*=?(d{XK=3K1&d*9Zoaw}n(4`B>W%cTfaRv4=G zoGgJV%c56r?+y7l8Pt!beH}{T80hf^zfLKF;j}f#Je@fj(MG{^DR{{n8_Elfd+s1- z6WHT$3`VAAY6FrKj#t%JT~i%G`Sg*HZocEBFlpR@Kn-WmS{sv}N;+8!RD8-HzUL_s zZW&YMXRG<4>Gk8_K9YfVs@-?=;C(h^7O4VR+cc&og0!{e+N$FnWogMPJBhfv6`PeR zh{l=WC3Q6#CCtM1QLF?cFyzE2C532Z#9r~Y1qOkCVay6T5E=Ale8_oq-y*ZY@GqN; zj#mYDS<&-ahW=RI{{2{^c$pD^>8)gK>jy`vzt5R9-kqYZ?#s>?0a-%donw}!FUmh+ zCcUmy0rCts&Wl$>Q}cnB2mQ$r3LKrH;KTKOTof$cSqP;|O(}m^igf<9DNejY9<2Dd z;2*k4(pjhIV6EOB=tI>vm672A*&iE+Y0;p>xrvemYVq~G;=}z~6@S$~eFkr^|2`7@ zO0l<1c)H}rAw#cp{)`RrdYS6F0Pae7&(`n@AI^JK<` z)L+4rf!?rsZdJ**QwFS?fQa+JXbE(N14jNnZe3z32rS>%%-!CpBv>6NW&GlxP4dqhA{DTD>`jkw|Y+J(=a zSxJ%6l}m8;Ow`9t-+WmvhvKLxVoXY9^1qE|Cc5W$iBtb0I3RTeS!w*A=EjKWsA3Gw z-^dl-i%QLk?NwbeI8bg*g%!!_5v+vlW5b@L$XM@pqRm19~LHc`n>3Dflr2QD!es%sBa=N-?9^mK7;fu{#5de@<%x&$Y2U?B* z7mh%iHCUx|G*R7nQ&=adg!* z7${c@tJ7{%MJAU~UdJ!ti0`?zP#H3c#IfkiG4&+SNp-u%$#~H*?ac7NoCy)B zlBD4bDb?b@6#n3E*dP{y*Aa2(cf9ktW_wDDBZ@S*YXVMZKFDV-qMNIS3 zou=>m?{E9&UfQ5T>b%lu+zeRscFWH9neFF4;DbHk(}3<7He_EpcUD6z=km=dD;ZaQ z0>Xb4`sV8C?aoIq!c(sH=vUOg0dg+yhIhoLyYZhK9ji>(6K= z$p^$#O%}bB#UNA3C?`tZo7SXA^V!FkRKjg$*hrn(WEKO&?o`thvP@OxXfBexl^<%g zH6$YzuU9MI5M*5eA6vHN_L*p$$JE)K$YVM&fE$G?HVPSa7IKRY9um0D%iw3jeHx?K zzLPo)YAoxFZwrv_l>%7Sg}d@8^Oc!P3eTI(8+92|(~l$o>T%zB;z|b4QGX(m!_Z&X z(Edf%PO5s&2r(oI`NkPsj~Uuk=57@T(;%UG(@f*UxyeM|y7X1*kJ_D8G9p*!hz_8XaXdXH*ypNC8G+A0E%1DR>E6l6Bb7W|ef z5LHh6$ eGovXft}|?_I?n(99&T%24BVpj>p(BCK>rW4ZUJ)u literal 0 HcmV?d00001 diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index 0030eda3..dc26b4c1 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -11,10 +11,10 @@ clusterGroup: external-secrets: image: - tag: v0.8.5-ubi + tag: v0.9.4-ubi webhook: image: - tag: v0.8.5-ubi + tag: v0.9.4-ubi certController: image: - tag: v0.8.5-ubi + tag: v0.9.4-ubi diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index 1452df28..867d9ef3 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -208,7 +208,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -224,15 +224,12 @@ spec: scope: Cluster versions: - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name + - jsonPath: .spec.externalSecretSpec.secretStoreRef.name name: Store type: string - - jsonPath: .spec.refreshInterval + - jsonPath: .spec.refreshTime name: Refresh Interval type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -252,6 +249,18 @@ spec: spec: description: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret. properties: + externalSecretMetadata: + description: The metadata of the external secrets to be created + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object externalSecretName: description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret type: string @@ -665,7 +674,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -2155,6 +2164,24 @@ spec: - SecretsManager - ParameterStore type: string + sessionTags: + description: AWS STS assume role session tags + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + transitiveTagKeys: + description: AWS STS assume role transitive session tags. Required when multiple rules are used with SecretStore + items: + type: string + type: array required: - region - service @@ -2238,6 +2265,114 @@ spec: required: - vaultUrl type: object + conjur: + description: Conjur configures this store to sync secrets using conjur provider + properties: + auth: + properties: + apikey: + properties: + account: + type: string + apiKeyRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + userRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - account + - apiKeyRef + - userRef + type: object + required: + - apikey + type: object + caBundle: + type: string + url: + type: string + required: + - auth + - url + type: object + delinea: + description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + properties: + clientId: + description: ClientID is the non-secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + clientSecret: + description: ClientSecret is the secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + tenant: + description: Tenant is the chosen hostname / site name. + type: string + tld: + description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + type: string + urlTemplate: + description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + type: string + required: + - clientId + - clientSecret + - tenant + type: object doppler: description: Doppler configures this store to sync secrets using the Doppler provider properties: @@ -2285,6 +2420,7 @@ spec: - lower-snake - tf-var - dotnet-env + - lower-kebab type: string project: description: Doppler project (required if not using a Service Token) @@ -3103,6 +3239,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -3387,7 +3550,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3515,7 +3678,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -4152,7 +4315,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4213,7 +4376,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4321,7 +4484,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4399,7 +4562,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4619,7 +4782,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -6109,6 +6272,24 @@ spec: - SecretsManager - ParameterStore type: string + sessionTags: + description: AWS STS assume role session tags + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + transitiveTagKeys: + description: AWS STS assume role transitive session tags. Required when multiple rules are used with SecretStore + items: + type: string + type: array required: - region - service @@ -6192,6 +6373,114 @@ spec: required: - vaultUrl type: object + conjur: + description: Conjur configures this store to sync secrets using conjur provider + properties: + auth: + properties: + apikey: + properties: + account: + type: string + apiKeyRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + userRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - account + - apiKeyRef + - userRef + type: object + required: + - apikey + type: object + caBundle: + type: string + url: + type: string + required: + - auth + - url + type: object + delinea: + description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + properties: + clientId: + description: ClientID is the non-secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + clientSecret: + description: ClientSecret is the secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + tenant: + description: Tenant is the chosen hostname / site name. + type: string + tld: + description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + type: string + urlTemplate: + description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + type: string + required: + - clientId + - clientSecret + - tenant + type: object doppler: description: Doppler configures this store to sync secrets using the Doppler provider properties: @@ -6239,6 +6528,7 @@ spec: - lower-snake - tf-var - dotnet-env + - lower-kebab type: string project: description: Doppler project (required if not using a Service Token) @@ -7057,6 +7347,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -7341,7 +7658,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -7688,6 +8005,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -7771,10 +8115,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +8182,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +8291,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +8331,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8375,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8396,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8416,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8452,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8491,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8512,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8536,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8222,7 +8566,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8231,6 +8575,8 @@ spec: - --service-namespace=default - --secret-name=golang-external-secrets-webhook - --secret-namespace=default + - --metrics-addr=:8080 + - --healthz-addr=:8081 ports: - containerPort: 8080 @@ -8250,10 +8596,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8280,7 +8626,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8296,10 +8642,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8326,7 +8672,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index f0314907..5f6831b7 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -208,7 +208,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -224,15 +224,12 @@ spec: scope: Cluster versions: - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name + - jsonPath: .spec.externalSecretSpec.secretStoreRef.name name: Store type: string - - jsonPath: .spec.refreshInterval + - jsonPath: .spec.refreshTime name: Refresh Interval type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -252,6 +249,18 @@ spec: spec: description: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret. properties: + externalSecretMetadata: + description: The metadata of the external secrets to be created + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object externalSecretName: description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret type: string @@ -665,7 +674,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -2155,6 +2164,24 @@ spec: - SecretsManager - ParameterStore type: string + sessionTags: + description: AWS STS assume role session tags + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + transitiveTagKeys: + description: AWS STS assume role transitive session tags. Required when multiple rules are used with SecretStore + items: + type: string + type: array required: - region - service @@ -2238,6 +2265,114 @@ spec: required: - vaultUrl type: object + conjur: + description: Conjur configures this store to sync secrets using conjur provider + properties: + auth: + properties: + apikey: + properties: + account: + type: string + apiKeyRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + userRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - account + - apiKeyRef + - userRef + type: object + required: + - apikey + type: object + caBundle: + type: string + url: + type: string + required: + - auth + - url + type: object + delinea: + description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + properties: + clientId: + description: ClientID is the non-secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + clientSecret: + description: ClientSecret is the secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + tenant: + description: Tenant is the chosen hostname / site name. + type: string + tld: + description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + type: string + urlTemplate: + description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + type: string + required: + - clientId + - clientSecret + - tenant + type: object doppler: description: Doppler configures this store to sync secrets using the Doppler provider properties: @@ -2285,6 +2420,7 @@ spec: - lower-snake - tf-var - dotnet-env + - lower-kebab type: string project: description: Doppler project (required if not using a Service Token) @@ -3103,6 +3239,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -3387,7 +3550,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3515,7 +3678,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -4152,7 +4315,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4213,7 +4376,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4321,7 +4484,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4399,7 +4562,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4619,7 +4782,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -6109,6 +6272,24 @@ spec: - SecretsManager - ParameterStore type: string + sessionTags: + description: AWS STS assume role session tags + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + transitiveTagKeys: + description: AWS STS assume role transitive session tags. Required when multiple rules are used with SecretStore + items: + type: string + type: array required: - region - service @@ -6192,6 +6373,114 @@ spec: required: - vaultUrl type: object + conjur: + description: Conjur configures this store to sync secrets using conjur provider + properties: + auth: + properties: + apikey: + properties: + account: + type: string + apiKeyRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + userRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - account + - apiKeyRef + - userRef + type: object + required: + - apikey + type: object + caBundle: + type: string + url: + type: string + required: + - auth + - url + type: object + delinea: + description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + properties: + clientId: + description: ClientID is the non-secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + clientSecret: + description: ClientSecret is the secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + tenant: + description: Tenant is the chosen hostname / site name. + type: string + tld: + description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + type: string + urlTemplate: + description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + type: string + required: + - clientId + - clientSecret + - tenant + type: object doppler: description: Doppler configures this store to sync secrets using the Doppler provider properties: @@ -6239,6 +6528,7 @@ spec: - lower-snake - tf-var - dotnet-env + - lower-kebab type: string project: description: Doppler project (required if not using a Service Token) @@ -7057,6 +7347,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -7341,7 +7658,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -7688,6 +8005,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -7771,10 +8115,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +8182,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +8291,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +8331,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8375,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8396,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8416,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8452,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8491,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8512,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8536,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8222,7 +8566,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8231,6 +8575,8 @@ spec: - --service-namespace=default - --secret-name=golang-external-secrets-webhook - --secret-namespace=default + - --metrics-addr=:8080 + - --healthz-addr=:8081 ports: - containerPort: 8080 @@ -8250,10 +8596,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8280,7 +8626,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8296,10 +8642,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8326,7 +8672,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index f0314907..5f6831b7 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -208,7 +208,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -224,15 +224,12 @@ spec: scope: Cluster versions: - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name + - jsonPath: .spec.externalSecretSpec.secretStoreRef.name name: Store type: string - - jsonPath: .spec.refreshInterval + - jsonPath: .spec.refreshTime name: Refresh Interval type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -252,6 +249,18 @@ spec: spec: description: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret. properties: + externalSecretMetadata: + description: The metadata of the external secrets to be created + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object externalSecretName: description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret type: string @@ -665,7 +674,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -2155,6 +2164,24 @@ spec: - SecretsManager - ParameterStore type: string + sessionTags: + description: AWS STS assume role session tags + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + transitiveTagKeys: + description: AWS STS assume role transitive session tags. Required when multiple rules are used with SecretStore + items: + type: string + type: array required: - region - service @@ -2238,6 +2265,114 @@ spec: required: - vaultUrl type: object + conjur: + description: Conjur configures this store to sync secrets using conjur provider + properties: + auth: + properties: + apikey: + properties: + account: + type: string + apiKeyRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + userRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - account + - apiKeyRef + - userRef + type: object + required: + - apikey + type: object + caBundle: + type: string + url: + type: string + required: + - auth + - url + type: object + delinea: + description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + properties: + clientId: + description: ClientID is the non-secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + clientSecret: + description: ClientSecret is the secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + tenant: + description: Tenant is the chosen hostname / site name. + type: string + tld: + description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + type: string + urlTemplate: + description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + type: string + required: + - clientId + - clientSecret + - tenant + type: object doppler: description: Doppler configures this store to sync secrets using the Doppler provider properties: @@ -2285,6 +2420,7 @@ spec: - lower-snake - tf-var - dotnet-env + - lower-kebab type: string project: description: Doppler project (required if not using a Service Token) @@ -3103,6 +3239,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -3387,7 +3550,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3515,7 +3678,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -4152,7 +4315,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4213,7 +4376,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4321,7 +4484,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4399,7 +4562,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4619,7 +4782,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -6109,6 +6272,24 @@ spec: - SecretsManager - ParameterStore type: string + sessionTags: + description: AWS STS assume role session tags + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + transitiveTagKeys: + description: AWS STS assume role transitive session tags. Required when multiple rules are used with SecretStore + items: + type: string + type: array required: - region - service @@ -6192,6 +6373,114 @@ spec: required: - vaultUrl type: object + conjur: + description: Conjur configures this store to sync secrets using conjur provider + properties: + auth: + properties: + apikey: + properties: + account: + type: string + apiKeyRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + userRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - account + - apiKeyRef + - userRef + type: object + required: + - apikey + type: object + caBundle: + type: string + url: + type: string + required: + - auth + - url + type: object + delinea: + description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + properties: + clientId: + description: ClientID is the non-secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + clientSecret: + description: ClientSecret is the secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + tenant: + description: Tenant is the chosen hostname / site name. + type: string + tld: + description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + type: string + urlTemplate: + description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + type: string + required: + - clientId + - clientSecret + - tenant + type: object doppler: description: Doppler configures this store to sync secrets using the Doppler provider properties: @@ -6239,6 +6528,7 @@ spec: - lower-snake - tf-var - dotnet-env + - lower-kebab type: string project: description: Doppler project (required if not using a Service Token) @@ -7057,6 +7347,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -7341,7 +7658,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -7688,6 +8005,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -7771,10 +8115,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +8182,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +8291,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +8331,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8375,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8396,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8416,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8452,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8491,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8512,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8536,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8222,7 +8566,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8231,6 +8575,8 @@ spec: - --service-namespace=default - --secret-name=golang-external-secrets-webhook - --secret-namespace=default + - --metrics-addr=:8080 + - --healthz-addr=:8081 ports: - containerPort: 8080 @@ -8250,10 +8596,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8280,7 +8626,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8296,10 +8642,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8326,7 +8672,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 063464e7..127be81d 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -208,7 +208,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -224,15 +224,12 @@ spec: scope: Cluster versions: - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name + - jsonPath: .spec.externalSecretSpec.secretStoreRef.name name: Store type: string - - jsonPath: .spec.refreshInterval + - jsonPath: .spec.refreshTime name: Refresh Interval type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -252,6 +249,18 @@ spec: spec: description: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret. properties: + externalSecretMetadata: + description: The metadata of the external secrets to be created + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object externalSecretName: description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret type: string @@ -665,7 +674,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -2155,6 +2164,24 @@ spec: - SecretsManager - ParameterStore type: string + sessionTags: + description: AWS STS assume role session tags + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + transitiveTagKeys: + description: AWS STS assume role transitive session tags. Required when multiple rules are used with SecretStore + items: + type: string + type: array required: - region - service @@ -2238,6 +2265,114 @@ spec: required: - vaultUrl type: object + conjur: + description: Conjur configures this store to sync secrets using conjur provider + properties: + auth: + properties: + apikey: + properties: + account: + type: string + apiKeyRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + userRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - account + - apiKeyRef + - userRef + type: object + required: + - apikey + type: object + caBundle: + type: string + url: + type: string + required: + - auth + - url + type: object + delinea: + description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + properties: + clientId: + description: ClientID is the non-secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + clientSecret: + description: ClientSecret is the secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + tenant: + description: Tenant is the chosen hostname / site name. + type: string + tld: + description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + type: string + urlTemplate: + description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + type: string + required: + - clientId + - clientSecret + - tenant + type: object doppler: description: Doppler configures this store to sync secrets using the Doppler provider properties: @@ -2285,6 +2420,7 @@ spec: - lower-snake - tf-var - dotnet-env + - lower-kebab type: string project: description: Doppler project (required if not using a Service Token) @@ -3103,6 +3239,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -3387,7 +3550,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3515,7 +3678,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -4152,7 +4315,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4213,7 +4376,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4321,7 +4484,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4399,7 +4562,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4619,7 +4782,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -6109,6 +6272,24 @@ spec: - SecretsManager - ParameterStore type: string + sessionTags: + description: AWS STS assume role session tags + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + transitiveTagKeys: + description: AWS STS assume role transitive session tags. Required when multiple rules are used with SecretStore + items: + type: string + type: array required: - region - service @@ -6192,6 +6373,114 @@ spec: required: - vaultUrl type: object + conjur: + description: Conjur configures this store to sync secrets using conjur provider + properties: + auth: + properties: + apikey: + properties: + account: + type: string + apiKeyRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + userRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - account + - apiKeyRef + - userRef + type: object + required: + - apikey + type: object + caBundle: + type: string + url: + type: string + required: + - auth + - url + type: object + delinea: + description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + properties: + clientId: + description: ClientID is the non-secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + clientSecret: + description: ClientSecret is the secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + tenant: + description: Tenant is the chosen hostname / site name. + type: string + tld: + description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + type: string + urlTemplate: + description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + type: string + required: + - clientId + - clientSecret + - tenant + type: object doppler: description: Doppler configures this store to sync secrets using the Doppler provider properties: @@ -6239,6 +6528,7 @@ spec: - lower-snake - tf-var - dotnet-env + - lower-kebab type: string project: description: Doppler project (required if not using a Service Token) @@ -7057,6 +7347,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -7341,7 +7658,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -7688,6 +8005,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -7771,10 +8115,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +8182,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +8291,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +8331,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8375,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8396,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8416,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8452,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8491,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8512,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8536,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8222,7 +8566,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8231,6 +8575,8 @@ spec: - --service-namespace=default - --secret-name=golang-external-secrets-webhook - --secret-namespace=default + - --metrics-addr=:8080 + - --healthz-addr=:8081 ports: - containerPort: 8080 @@ -8250,10 +8596,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8280,7 +8626,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8296,10 +8642,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8326,7 +8672,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index f0314907..5f6831b7 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -208,7 +208,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -224,15 +224,12 @@ spec: scope: Cluster versions: - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name + - jsonPath: .spec.externalSecretSpec.secretStoreRef.name name: Store type: string - - jsonPath: .spec.refreshInterval + - jsonPath: .spec.refreshTime name: Refresh Interval type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -252,6 +249,18 @@ spec: spec: description: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret. properties: + externalSecretMetadata: + description: The metadata of the external secrets to be created + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object externalSecretName: description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret type: string @@ -665,7 +674,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -2155,6 +2164,24 @@ spec: - SecretsManager - ParameterStore type: string + sessionTags: + description: AWS STS assume role session tags + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + transitiveTagKeys: + description: AWS STS assume role transitive session tags. Required when multiple rules are used with SecretStore + items: + type: string + type: array required: - region - service @@ -2238,6 +2265,114 @@ spec: required: - vaultUrl type: object + conjur: + description: Conjur configures this store to sync secrets using conjur provider + properties: + auth: + properties: + apikey: + properties: + account: + type: string + apiKeyRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + userRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - account + - apiKeyRef + - userRef + type: object + required: + - apikey + type: object + caBundle: + type: string + url: + type: string + required: + - auth + - url + type: object + delinea: + description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + properties: + clientId: + description: ClientID is the non-secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + clientSecret: + description: ClientSecret is the secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + tenant: + description: Tenant is the chosen hostname / site name. + type: string + tld: + description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + type: string + urlTemplate: + description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + type: string + required: + - clientId + - clientSecret + - tenant + type: object doppler: description: Doppler configures this store to sync secrets using the Doppler provider properties: @@ -2285,6 +2420,7 @@ spec: - lower-snake - tf-var - dotnet-env + - lower-kebab type: string project: description: Doppler project (required if not using a Service Token) @@ -3103,6 +3239,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -3387,7 +3550,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3515,7 +3678,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -4152,7 +4315,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4213,7 +4376,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4321,7 +4484,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4399,7 +4562,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4619,7 +4782,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -6109,6 +6272,24 @@ spec: - SecretsManager - ParameterStore type: string + sessionTags: + description: AWS STS assume role session tags + items: + properties: + key: + type: string + value: + type: string + required: + - key + - value + type: object + type: array + transitiveTagKeys: + description: AWS STS assume role transitive session tags. Required when multiple rules are used with SecretStore + items: + type: string + type: array required: - region - service @@ -6192,6 +6373,114 @@ spec: required: - vaultUrl type: object + conjur: + description: Conjur configures this store to sync secrets using conjur provider + properties: + auth: + properties: + apikey: + properties: + account: + type: string + apiKeyRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + userRef: + description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + required: + - account + - apiKeyRef + - userRef + type: object + required: + - apikey + type: object + caBundle: + type: string + url: + type: string + required: + - auth + - url + type: object + delinea: + description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + properties: + clientId: + description: ClientID is the non-secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + clientSecret: + description: ClientSecret is the secret part of the credential. + properties: + secretRef: + description: SecretRef references a key in a secret that will be used as value. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + value: + description: Value can be specified directly to set a value without using a secret. + type: string + type: object + tenant: + description: Tenant is the chosen hostname / site name. + type: string + tld: + description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + type: string + urlTemplate: + description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + type: string + required: + - clientId + - clientSecret + - tenant + type: object doppler: description: Doppler configures this store to sync secrets using the Doppler provider properties: @@ -6239,6 +6528,7 @@ spec: - lower-snake - tf-var - dotnet-env + - lower-kebab type: string project: description: Doppler project (required if not using a Service Token) @@ -7057,6 +7347,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -7341,7 +7658,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.1 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -7688,6 +8005,33 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + userPass: + description: UserPass authenticates with Vault by passing username/password pair + properties: + path: + default: user + description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + type: string + secretRef: + description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + username: + description: Username is a user name used to authenticate using the UserPass Vault authentication method + type: string + required: + - path + - username + type: object type: object caBundle: description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. @@ -7771,10 +8115,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +8182,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +8291,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +8331,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8375,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8396,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8416,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8452,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8491,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8512,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8536,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8222,7 +8566,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8231,6 +8575,8 @@ spec: - --service-namespace=default - --secret-name=golang-external-secrets-webhook - --secret-namespace=default + - --metrics-addr=:8080 + - --healthz-addr=:8081 ports: - containerPort: 8080 @@ -8250,10 +8596,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8280,7 +8626,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8296,10 +8642,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.5 + helm.sh/chart: external-secrets-0.9.4 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.5" + app.kubernetes.io/version: "v0.9.4" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8326,7 +8672,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.4-ubi" imagePullPolicy: IfNotPresent args: - webhook From 71f5639671f848d67abfb035a9f9d6f8f07cb810 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 31 Aug 2023 23:10:33 +0200 Subject: [PATCH 0983/1263] Release 0.0.2 golang-external-secrets --- golang-external-secrets/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index c618b677..8b58c7e5 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -3,7 +3,7 @@ description: A Helm chart to configure the golang-based external-secrets. keywords: - pattern name: golang-external-secrets -version: 0.0.1 +version: 0.0.2 dependencies: - name: external-secrets version: "0.9.4" From a968124172c424cba44d9460850c9e7ebd67c13c Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Sat, 2 Sep 2023 08:57:31 -0600 Subject: [PATCH 0984/1263] Adding label validatedpatterns.io/pattern to all applications. --- clustergroup/templates/plumbing/applications.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 536ff0d3..c09e3c8c 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -121,6 +121,8 @@ kind: Application metadata: name: {{ .name }} namespace: {{ $namespace }} + labels: + validatedpatterns.io/pattern: {{ $.Values.global.pattern }} finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: From 7a0a5c0fb72ba93b9be979e5df6d3d2cf27a04c4 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Sat, 2 Sep 2023 09:05:47 -0600 Subject: [PATCH 0985/1263] CI test updates --- ...roup-industrial-edge-factory.expected.yaml | 4 +++ ...tergroup-industrial-edge-hub.expected.yaml | 16 ++++++++++++ ...rgroup-medical-diagnosis-hub.expected.yaml | 26 +++++++++++++++++++ tests/clustergroup-normal.expected.yaml | 4 +++ 4 files changed, 50 insertions(+) diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 3c6d6286..4986e2b5 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -368,6 +368,8 @@ kind: Application metadata: name: stormshift namespace: mypattern-factory + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -393,6 +395,8 @@ kind: Application metadata: name: odh namespace: mypattern-factory + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 8a5220b2..e48f2ed8 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -668,6 +668,8 @@ kind: Application metadata: name: acm namespace: mypattern-datacenter + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -723,6 +725,8 @@ kind: Application metadata: name: odh namespace: mypattern-datacenter + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -769,6 +773,8 @@ kind: Application metadata: name: pipelines namespace: mypattern-datacenter + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -815,6 +821,8 @@ kind: Application metadata: name: production-data-lake namespace: mypattern-datacenter + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -891,6 +899,8 @@ kind: Application metadata: name: external-secrets namespace: mypattern-datacenter + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -937,6 +947,8 @@ kind: Application metadata: name: golang-external-secrets namespace: mypattern-datacenter + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -983,6 +995,8 @@ kind: Application metadata: name: manuela-test namespace: mypattern-datacenter + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -1008,6 +1022,8 @@ kind: Application metadata: name: vault namespace: mypattern-datacenter + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index dbecaa84..0ff5754f 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -613,6 +613,8 @@ kind: Application metadata: name: golang-external-secrets namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -659,6 +661,8 @@ kind: Application metadata: name: kafdrop namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -705,6 +709,8 @@ kind: Application metadata: name: kafka namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -751,6 +757,8 @@ kind: Application metadata: name: odh namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -797,6 +805,8 @@ kind: Application metadata: name: odf namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -843,6 +853,8 @@ kind: Application metadata: name: serverless namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -889,6 +901,8 @@ kind: Application metadata: name: xraylab-service-account namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -935,6 +949,8 @@ kind: Application metadata: name: vault namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -999,6 +1015,8 @@ kind: Application metadata: name: xraylab-database namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -1045,6 +1063,8 @@ kind: Application metadata: name: xraylab-grafana-dashboards namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -1091,6 +1111,8 @@ kind: Application metadata: name: xraylab-image-generator namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -1146,6 +1168,8 @@ kind: Application metadata: name: xraylab-image-server namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -1201,6 +1225,8 @@ kind: Application metadata: name: xraylab-init namespace: mypattern-hub + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 901a7d62..8c12d1b3 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -523,6 +523,8 @@ kind: Application metadata: name: acm namespace: mypattern-example + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: @@ -578,6 +580,8 @@ kind: Application metadata: name: pipelines namespace: mypattern-example + labels: + validatedpatterns.io/pattern: mypattern finalizers: - resources-finalizer.argocd.argoproj.io/foreground spec: From dcd9b8184eb27cf5401a19c1bef171da6de0d989 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 4 Sep 2023 09:05:14 +0200 Subject: [PATCH 0986/1263] Simplify the passing of KUBECONFIG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to `man podman run` if we simply pass `-e KUBECONFIG` to the podman invocation: "If an environment variable is spec‐ ified without a value, Podman checks the host environment for a value and set the variable only if it is set on the host" So let's just do that and drop the current more complex fragile logic. Tested with: * No KUBECONFIG set unset KUBECONFIG; ./pattern.sh make install make -f common/Makefile operator-deploy make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Checking prerequisites: Check for 'git helm oc ansible': OK Check for python-kubernetes: OK Check for kubernetes.core collection: OK Checking repository: https://github.com/mbaldessari/multicloud-gitops.git - branch script-fix: Running inside a container: Skipping git ssh checks Running helm: Error: Kubernetes cluster unreachable: Get "https://localhos:6443/version" * With KUBECONFIG set export KUBECONFIG=~/sno1-kubeconfig ./pattern.sh make install make -f common/Makefile operator-deploy make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Checking prerequisites: Check for 'git helm oc ansible': OK Check for python-kubernetes: OK Check for kubernetes.core collection: OK Checking repository: https://github.com/mbaldessari/multicloud-gitops.git - branch script-fix: Running inside a container: Skipping git ssh checks Running helm: WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/michele/sno1-kubeconfig Release "multicloud-gitops" does not exist. Installing it now. NAME: multicloud-gitops LAST DEPLOYED: Mon Sep 4 07:04:16 2023 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' make load-secrets make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' make -f common/Makefile load-secrets ... --- scripts/pattern-util.sh | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index baa0e9de..e02776df 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -25,19 +25,12 @@ fi # $HOME is mounted as itself for any files that are referenced with absolute paths # $HOME is mounted to /root because the UID in the container is 0 and that's where SSH looks for credentials -# We must pass -e KUBECONFIG *only* if it is set, otherwise we end up passing -# KUBECONFIG="" which then will confuse ansible -KUBECONF_ENV="" -if [ -n "$KUBECONFIG" ]; then - KUBECONF_ENV="-e KUBECONFIG=${KUBECONFIG}" -fi - # Do not quote the ${KUBECONF_ENV} below, otherwise we will pass '' to podman # which will be confused podman run -it --rm \ --security-opt label=disable \ -e EXTRA_HELM_OPTS \ - ${KUBECONF_ENV} \ + -e KUBECONFIG \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ -v "${HOME}":/root \ From 7207fbd29cba586e547fa49895cf3d5f33c489e9 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 4 Sep 2023 14:59:42 +0200 Subject: [PATCH 0987/1263] Update CRD for the operator --- .../crds/gitops.hybrid-cloud-patterns.io_patterns.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml index d0464ef6..330e0222 100644 --- a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml +++ b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml @@ -45,6 +45,10 @@ spec: spec: description: PatternSpec defines the desired state of Pattern properties: + analyticsUUID: + description: Analytics UUID. Leave empty to autogenerate a random + one. Not PII information + type: string clusterGroupName: type: string extraParameters: From 4d0fafd6048a54434236c19915385475f5a707a1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 4 Sep 2023 15:49:01 +0200 Subject: [PATCH 0988/1263] Expose UUID Tested as: $ helm template . > /tmp/a; helm template . --set 'main.analyticsUUID=foo' > /tmp/b; diff -u /tmp/a /tmp/b --- /tmp/a 2023-09-04 15:49:51.160607725 +0200 +++ /tmp/b 2023-09-04 15:49:51.177607813 +0200 @@ -15,6 +15,7 @@ operatorSource: redhat-operators multiSourceConfig: enabled: false + analyticsUUID: foo --- clustergroup/values.schema.json | 4 ++++ operator-install/templates/pattern.yaml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index a33432b2..07f8e717 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -102,6 +102,10 @@ "description": "Enable the experimental support for multi source" } } + }, + "analyticsUUID": { + "type": "string", + "description": "UUID used to generate analytics" } } }, diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index a340598b..d0227e58 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -13,6 +13,9 @@ spec: operatorSource: {{ default "redhat-operators" .Values.main.gitops.operatorSource }} multiSourceConfig: enabled: {{ .Values.main.multiSourceConfig.enabled }} +{{- if .Values.main.analyticsUUID }} + analyticsUUID: {{ .Values.main.analyticsUUID }} +{{- end }} {{/* if .Values.main.analyticsUUID */}} {{- if .Values.main.extraParameters }} extraParameters: {{- range .Values.main.extraParameters }} From 39833a3dfb72efee9b27cc53ee4f8432a20ecf90 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 4 Sep 2023 16:07:10 +0200 Subject: [PATCH 0989/1263] Move to newly released checkout action version --- .github/workflows/ansible-lint.yml | 2 +- .github/workflows/ansible-unittest.yml | 2 +- .github/workflows/chart-branches.yml | 2 +- .github/workflows/chart-split.yml | 2 +- .github/workflows/jsonschema.yaml | 2 +- .github/workflows/linter.yml | 2 +- .github/workflows/superlinter.yml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ansible-lint.yml b/.github/workflows/ansible-lint.yml index 1bf2f7dd..c2b2981b 100644 --- a/.github/workflows/ansible-lint.yml +++ b/.github/workflows/ansible-lint.yml @@ -8,7 +8,7 @@ jobs: steps: # Important: This sets up your GITHUB_WORKSPACE environment variable - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Lint Ansible Playbook uses: ansible/ansible-lint-action@v6 diff --git a/.github/workflows/ansible-unittest.yml b/.github/workflows/ansible-unittest.yml index af326b66..90d4507f 100644 --- a/.github/workflows/ansible-unittest.yml +++ b/.github/workflows/ansible-unittest.yml @@ -32,7 +32,7 @@ jobs: # Checkout the code base # ########################## - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 diff --git a/.github/workflows/chart-branches.yml b/.github/workflows/chart-branches.yml index a1b36cf6..d93b1dbb 100644 --- a/.github/workflows/chart-branches.yml +++ b/.github/workflows/chart-branches.yml @@ -30,7 +30,7 @@ jobs: clustergroup: ${{ steps.filter.outputs.clustergroup }} steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: dorny/paths-filter@v2 id: filter diff --git a/.github/workflows/chart-split.yml b/.github/workflows/chart-split.yml index 84f027f6..2792d6ad 100644 --- a/.github/workflows/chart-split.yml +++ b/.github/workflows/chart-split.yml @@ -19,7 +19,7 @@ jobs: contents: write steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.CHARTS_REPOS_TOKEN }} diff --git a/.github/workflows/jsonschema.yaml b/.github/workflows/jsonschema.yaml index a0c60c11..ad83173a 100644 --- a/.github/workflows/jsonschema.yaml +++ b/.github/workflows/jsonschema.yaml @@ -32,7 +32,7 @@ jobs: # Checkout the code base # ########################## - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 59e64541..947cc127 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -29,7 +29,7 @@ jobs: # Checkout the code base # ########################## - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index 30cb00a8..7430db09 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 From c328c1ec3d39faac747424ff022fb0458d24928b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Sep 2023 08:09:12 +0200 Subject: [PATCH 0990/1263] Update URLs to new github org --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bb60d248..568a2396 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,17 @@ This repository is never used as standalone. It is usually imported in each pattern as a subtree. In order to import the common/ the very first time you can use -`https://github.com/hybrid-cloud-patterns/multicloud-gitops/blob/main/common/scripts/make_common_subtree.sh` +`https://github.com/validatedpatterns/multicloud-gitops/blob/main/common/scripts/make_common_subtree.sh` In order to update your common subtree inside your pattern repository you can either use -`https://github.com/hybrid-cloud-patterns/utilities/blob/main/scripts/update-common-everywhere.sh` or +`https://github.com/validatedpatterns/utilities/blob/main/scripts/update-common-everywhere.sh` or do it manually by doing the following: ```sh -git remote add -f upstream-common https://github.com/hybrid-cloud-patterns/common.git +git remote add -f upstream-common https://github.com/validatedpatterns/common.git git merge -s subtree -Xtheirs -Xsubtree=common upstream-common/ha-vault ``` ## Secrets -There are two different secret formats parsed by the ansible bits. Both are documented [here](https://github.com/hybrid-cloud-patterns/common/tree/main/ansible/roles/vault_utils/README.md) +There are two different secret formats parsed by the ansible bits. Both are documented [here](https://github.com/validatedpatterns/common/tree/main/ansible/roles/vault_utils/README.md) From 5585af0508e2a9680ac996efbe9db24b50c2e010 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Sep 2023 08:10:30 +0200 Subject: [PATCH 0991/1263] Add ~/.config/validated-patterns in the secret search path --- ansible/roles/vault_utils/README.md | 14 ++++++++++---- ansible/roles/vault_utils/tasks/push_secrets.yaml | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index 55babb03..7198752c 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -42,10 +42,16 @@ This relies on [kubernetes.core](https://docs.ansible.com/ansible/latest/collect ## Values secret file format -Currently this role supports two formats: version 1.0 (which is the assumed default when not specified) and version 2.0. -The latter is more fatureful and supports generating secrets directly into the vault and also prompting the user for a secret. -By default, the first file that will looked up is `~/.config/hybrid-cloud-patterns/values-secret-.yaml`, then -`~/values-secret-.yaml` and should that not exist it will look for `~/values-secret.yaml`. +Currently this role supports two formats: version 1.0 (which is the assumed +default when not specified) and version 2.0. The latter is more fatureful and +supports generating secrets directly into the vault and also prompting the user +for a secret. + +By default, the first file that will looked up is +`~/.config/hybrid-cloud-patterns/values-secret-.yaml`, then +`~/.config/validated-patterns/values-secret-.yaml`, +`~/values-secret-.yaml` and should that not exist it will look for +`~/values-secret.yaml`. The paths can be overridden by setting the environment variable `VALUES_SECRET` to the path of the secret file. diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index a820f40a..9255a0ca 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -66,6 +66,7 @@ vars: findme: - "~/.config/hybrid-cloud-patterns/values-secret-{{ pattern_name }}.yaml" + - "~/.config/validated-patterns/values-secret-{{ pattern_name }}.yaml" - "~/values-secret-{{ pattern_name }}.yaml" - "~/values-secret.yaml" - "{{ pattern_dir }}/values-secret.yaml.template" From 92e83bf0b184eaffad82a99e58038423b480d85f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Sep 2023 08:18:14 +0200 Subject: [PATCH 0992/1263] Add support for ~/.config/validated-patterns/pattern-uuid * Pristine environment: $ make show helm template common/operator-install/ --name-template common -f values-global.yaml --set main.git.repoURL="https://github.com/hybrid-cloud-patterns/common.git" --set main.git.revision=vp-paths --- apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern metadata: name: common namespace: openshift-operators spec: clusterGroupName: example gitSpec: targetRepo: https://github.com/hybrid-cloud-patterns/common.git targetRevision: vp-paths gitOpsSpec: operatorChannel: gitops-1.8 operatorSource: redhat-operators multiSourceConfig: enabled: false ... * Add UUID to the environment $ echo "vp-team-bandini" >> ~/.config/validated-patterns/pattern-uuid $ make show helm template common/operator-install/ --name-template common -f values-global.yaml --set main.git.repoURL="https://github.com/hybrid-cloud-patterns/common.git" --set main.git.revision=vp-paths --set main.analyticsUUID=vp-team-bandini --- apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern metadata: name: common namespace: openshift-operators spec: clusterGroupName: example gitSpec: targetRepo: https://github.com/hybrid-cloud-patterns/common.git targetRevision: vp-paths gitOpsSpec: operatorChannel: gitops-1.8 operatorSource: redhat-operators multiSourceConfig: enabled: false analyticsUUID: vp-team-bandini ... --- Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d8182da6..23d43f7c 100644 --- a/Makefile +++ b/Makefile @@ -19,8 +19,16 @@ TARGET_REPO=$(shell git ls-remote --get-url --symref $(TARGET_ORIGIN) | sed -e ' # git branch --show-current is also available as of git 2.22, but we will use this for compatibility TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) +UUID_FILE ?= ~/.config/validated-patterns/pattern-uuid + # --set values always take precedence over the contents of -f -HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) $(EXTRA_HELM_OPTS) +ifneq ("$(wildcard $(UUID_FILE))","") + UUID := $(shell cat $(UUID_FILE)) + HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) --set main.analyticsUUID=$(UUID) $(EXTRA_HELM_OPTS) +else + HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) $(EXTRA_HELM_OPTS) +endif + ##@ Pattern Common Tasks From 34a22f01f496517d6e00a5b73b67c5ac6f2f5589 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Sep 2023 09:07:52 +0200 Subject: [PATCH 0993/1263] Simplify the code around UUID variable definition --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 23d43f7c..682e53ba 100644 --- a/Makefile +++ b/Makefile @@ -20,15 +20,16 @@ TARGET_REPO=$(shell git ls-remote --get-url --symref $(TARGET_ORIGIN) | sed -e ' TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) UUID_FILE ?= ~/.config/validated-patterns/pattern-uuid +UUID_HELM_OPTS ?= # --set values always take precedence over the contents of -f ifneq ("$(wildcard $(UUID_FILE))","") UUID := $(shell cat $(UUID_FILE)) - HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) --set main.analyticsUUID=$(UUID) $(EXTRA_HELM_OPTS) -else - HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) $(EXTRA_HELM_OPTS) + UUID_HELM_OPTS := --set main.analyticsUUID=$(UUID) endif +HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) $(UUID_HELM_OPTS) $(EXTRA_HELM_OPTS) + ##@ Pattern Common Tasks From f25f4399e51cd3804746fd323a04862a18522beb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 9 Sep 2023 10:10:07 +0200 Subject: [PATCH 0994/1263] Rework installation target Drop the old logic and just install the CRD via OC and use helm template for the rest. The rationale is that helm install is very picky whenever it encounters things that already exist. We have three potential scenarios at work here: A) User installs operator+pattern via CLI and updates the pattern via CLI (this worked before this change as well) B) User installs operator+pattern via UI but runs updates (changing branch for example) via CLI (this worked before this change as well) C) User installs only the operator via UI. Installs and updated the pattern via CLI. This was broken before this change. The error you'd get was: ``` ./pattern.sh make install ... https://github.com/mbaldessari/multicloud-gitops.git - branch main: Running inside a container: Skipping git ssh checks + oc get crds patterns.gitops.hybrid-cloud-patterns.io + echo 'Reapplying helm chart:' Reapplying helm chart: + helm template --name-template multicloud-gitops common/operator-install/ -f values-global.yaml --set main.git.repoURL=https://github.com/mbaldessari/multicloud-gitops.git --set main.git.revision=main + oc apply set-last-applied --create-annotation -f- WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/michele/sno1-kubeconfig Error from server (NotFound): patterns.gitops.hybrid-cloud-patterns.io "multicloud-gitops" not found ``` With this change we simplify the process and we forcefully apply/install the CRD for patterns via the oc command. And then we simply template out the operator-install chart and oc apply it. We retry it a few times, because the CRD might not yet be fully registered in the cluster. Tested with on the A), B) and C) scenarios successfully. --- Makefile | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 682e53ba..b7f4ec1b 100644 --- a/Makefile +++ b/Makefile @@ -44,21 +44,15 @@ help: ## This help message show: ## show the starting template without installing it helm template common/operator-install/ --name-template $(NAME) $(HELM_OPTS) -# Only call helm install if the CRD is missing. If it already exists just -# push the templated files. -# The reason we have two helm template calls in the else branch is to avoid -# warnings when the chart gets applied the first time, but the resources were -# created first via the VP operator's UI .PHONY: operator-deploy operator-deploy operator-upgrade: validate-prereq validate-origin ## runs helm install - @set -e; if ! oc get crds patterns.gitops.hybrid-cloud-patterns.io >/dev/null 2>&1; then \ - echo "Running helm:"; \ - helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS); \ - else \ - echo "Reapplying helm chart:"; \ - helm template --name-template $(NAME) common/operator-install/ $(HELM_OPTS) | oc apply set-last-applied --create-annotation -f-; \ - helm template --name-template $(NAME) common/operator-install/ $(HELM_OPTS) | oc apply -f-; \ - fi + @set -e -o pipefail + # We reapply the CRD and if it already exists we do not error out + oc apply common/operator-install/crds/*.yaml 2>/dev/null || /bin/true + # Retry five times because the CRD might not be fully installed yet + for i in {1..5}; do \ + helm template --name-template $(NAME) common/operator-install/ $(HELM_OPTS) | oc apply -f- && break || sleep 10; \ + done .PHONY: uninstall uninstall: ## runs helm uninstall From ae57e3895dffbe5e86a40781927f318e9db3d85c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 10 Sep 2023 10:46:35 +0200 Subject: [PATCH 0995/1263] Simplify the loop Install the CRD inside the loop to simplify the code a bit --- Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Makefile b/Makefile index b7f4ec1b..2b3400fd 100644 --- a/Makefile +++ b/Makefile @@ -47,11 +47,9 @@ show: ## show the starting template without installing it .PHONY: operator-deploy operator-deploy operator-upgrade: validate-prereq validate-origin ## runs helm install @set -e -o pipefail - # We reapply the CRD and if it already exists we do not error out - oc apply common/operator-install/crds/*.yaml 2>/dev/null || /bin/true # Retry five times because the CRD might not be fully installed yet for i in {1..5}; do \ - helm template --name-template $(NAME) common/operator-install/ $(HELM_OPTS) | oc apply -f- && break || sleep 10; \ + helm template --include-crds --name-template $(NAME) common/operator-install/ $(HELM_OPTS) | oc apply -f- && break || sleep 10; \ done .PHONY: uninstall From 62fb3370feeee5acc4ac7bd7434c3b03f12357c2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 12 Sep 2023 12:18:06 +0200 Subject: [PATCH 0996/1263] Introduce a validate-cluster target in the install target The validate-cluster target will be in charge of doing some sanity check on the cluster. Initially we just check the connection to the cluster and that at least one storageclass is available to the cluster. Tested as follows: 1) Cluster with a storageclass (LVM in my case) $ make validate-cluster Checking cluster: cluster-info: OK storageclass: OK 2) Cluster without a storageclass: $ make validate-cluster Checking cluster: cluster-info: OK storageclass: None Found make: *** [Makefile:99: validate-cluster] Error 1 --- Makefile | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2b3400fd..6fc5fbe6 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ show: ## show the starting template without installing it helm template common/operator-install/ --name-template $(NAME) $(HELM_OPTS) .PHONY: operator-deploy -operator-deploy operator-upgrade: validate-prereq validate-origin ## runs helm install +operator-deploy operator-upgrade: validate-prereq validate-origin validate-cluster ## runs helm install @set -e -o pipefail # Retry five times because the CRD might not be fully installed yet for i in {1..5}; do \ @@ -90,6 +90,19 @@ validate-origin: ## verify the git origin is available echo "Running inside a container: Skipping git ssh checks";\ fi +.PHONY: validate-cluster +validate-cluster: ## Do some cluster validations before installing + @echo "Checking cluster:" + @echo -n " cluster-info: " + @oc cluster-info >/dev/null && echo "OK" || (echo "Error"; exit 1) + @echo -n " storageclass: " + @if [ `oc get storageclass -o go-template='{{printf "%d\n" (len .items)}}'` -eq 0 ]; then\ + echo "None Found"; exit 1;\ + else\ + echo "OK";\ + fi + + .PHONY: validate-schema validate-schema: ## validates values files against schema in common/clustergroup $(eval VAL_PARAMS := $(shell for i in ./values-*.yaml; do echo -n "$${i} "; done)) From 01718b090634a8b67457db36e8fd5d8321c434cd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 19 Sep 2023 10:34:48 +0200 Subject: [PATCH 0997/1263] Increase the wait for the internal registry --- ansible/roles/iib_ci/tasks/setup-internal-registry.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml index 4e31928f..e45def74 100644 --- a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml +++ b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml @@ -34,7 +34,7 @@ oc registry info --public=true register: registry_route_raw retries: 20 - delay: 10 + delay: 20 until: - registry_route_raw is not failed - registry_route_raw.stdout | length > 0 From a7873c660592b6aca52e23c6630841564cd08529 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 19 Sep 2023 10:35:00 +0200 Subject: [PATCH 0998/1263] Add a note about SNOs and internal registries --- ansible/roles/iib_ci/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 1d8b447e..9f421e8f 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -26,6 +26,13 @@ make load-iib # This will install the pattern using the gitops operator from the IIB ``` +***NOTE:*** When using an SNO without shared storage in a non-production environment, the enablement of the internal registry will fail. You need to run the following to enable it: + +```sh +oc patch configs.imageregistry.operator.openshift.io cluster --type merge --patch '{"spec":{"managementState":"Managed"}}' +oc patch configs.imageregistry.operator.openshift.io cluster --type merge --patch '{"spec":{"storage":{"emptyDir":{}}}}' +``` + Then in case of the `openshift-gitops-operator` we would install with: ```sh From 5bdd554e7be4759f7e8afa4281c327168d41c599 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 19 Sep 2023 11:46:25 +0200 Subject: [PATCH 0999/1263] Move from resourceCustomization to resourceHealthcheck Our resourceCustomization is currently giving the following warning: Warning DeprecationNotice 27m ResourceCustomizations is deprecated, please use the new formats `ResourceHealthChecks`, `ResourceIgnoreDifferences`, and `Resource Actions` instead. This actually becomes a problem with gitops-1.10 because it dropped support for v1alpha versions of argoCD (it upgrades them automatically to v1beta). So the cluster-wide argo app which is in charge of creating the namespaced argoCD instance will always be OutOfSync as it will never be able to set the `resourceCustomization` field. Move to resourceHealthcheck which is the new supported way. This is also backwards compatible with gitops-1.8. Tested as follows: 1. Deployed 4.13 with gitops-1.10 and observed the multicloud-gitops-hub being OutOfSync 2. Applied this patch and observed it going to green and sync correctly 3. Tested this on gitops-1.8.5 on 4.13 and deployed MCG correctly with all apps becoming green everywhere. Fixes: https://github.com/validatedpatterns/common/issues/367 --- clustergroup/templates/plumbing/argocd.yaml | 37 +++++++++++---------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 84643644..de83b53c 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -14,26 +14,27 @@ metadata: spec: # Adding health checks to argocd to prevent pvc resources # that aren't bound state from blocking deployments - resourceCustomizations: | - PersistentVolumeClaim: - health.lua: | - hs = {} - if obj.status ~= nil then - if obj.status.phase ~= nil then - if obj.status.phase == "Pending" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - elseif obj.status.phase == "Bound" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - end + resourceHealthChecks: + - kind: PersistentVolumeClaim + check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs end end - hs.status = "Progressing" - hs.message = "Waiting for PVC" - return hs + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs + applicationInstanceLabelKey: argocd.argoproj.io/instance # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize From fe84601ebb358a8bb0b5ff59574121eea58eb28e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 19 Sep 2023 13:28:39 +0200 Subject: [PATCH 1000/1263] Fix up common/ tests --- ...roup-industrial-edge-factory.expected.yaml | 37 ++++++++++--------- ...tergroup-industrial-edge-hub.expected.yaml | 37 ++++++++++--------- ...rgroup-medical-diagnosis-hub.expected.yaml | 37 ++++++++++--------- tests/clustergroup-naked.expected.yaml | 37 ++++++++++--------- tests/clustergroup-normal.expected.yaml | 37 ++++++++++--------- 5 files changed, 95 insertions(+), 90 deletions(-) diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 4986e2b5..6ff3a848 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -452,26 +452,27 @@ metadata: spec: # Adding health checks to argocd to prevent pvc resources # that aren't bound state from blocking deployments - resourceCustomizations: | - PersistentVolumeClaim: - health.lua: | - hs = {} - if obj.status ~= nil then - if obj.status.phase ~= nil then - if obj.status.phase == "Pending" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - elseif obj.status.phase == "Bound" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - end + resourceHealthChecks: + - kind: PersistentVolumeClaim + check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs end end - hs.status = "Progressing" - hs.message = "Waiting for PVC" - return hs + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs + applicationInstanceLabelKey: argocd.argoproj.io/instance # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index e48f2ed8..3f5207ab 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -1097,26 +1097,27 @@ metadata: spec: # Adding health checks to argocd to prevent pvc resources # that aren't bound state from blocking deployments - resourceCustomizations: | - PersistentVolumeClaim: - health.lua: | - hs = {} - if obj.status ~= nil then - if obj.status.phase ~= nil then - if obj.status.phase == "Pending" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - elseif obj.status.phase == "Bound" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - end + resourceHealthChecks: + - kind: PersistentVolumeClaim + check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs end end - hs.status = "Progressing" - hs.message = "Waiting for PVC" - return hs + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs + applicationInstanceLabelKey: argocd.argoproj.io/instance # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 0ff5754f..4ffbd77d 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -1282,26 +1282,27 @@ metadata: spec: # Adding health checks to argocd to prevent pvc resources # that aren't bound state from blocking deployments - resourceCustomizations: | - PersistentVolumeClaim: - health.lua: | - hs = {} - if obj.status ~= nil then - if obj.status.phase ~= nil then - if obj.status.phase == "Pending" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - elseif obj.status.phase == "Bound" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - end + resourceHealthChecks: + - kind: PersistentVolumeClaim + check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs end end - hs.status = "Progressing" - hs.message = "Waiting for PVC" - return hs + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs + applicationInstanceLabelKey: argocd.argoproj.io/instance # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 9499eb5d..7f167c74 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -270,26 +270,27 @@ metadata: spec: # Adding health checks to argocd to prevent pvc resources # that aren't bound state from blocking deployments - resourceCustomizations: | - PersistentVolumeClaim: - health.lua: | - hs = {} - if obj.status ~= nil then - if obj.status.phase ~= nil then - if obj.status.phase == "Pending" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - elseif obj.status.phase == "Bound" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - end + resourceHealthChecks: + - kind: PersistentVolumeClaim + check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs end end - hs.status = "Progressing" - hs.message = "Waiting for PVC" - return hs + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs + applicationInstanceLabelKey: argocd.argoproj.io/instance # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 8c12d1b3..4767db6c 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -877,26 +877,27 @@ metadata: spec: # Adding health checks to argocd to prevent pvc resources # that aren't bound state from blocking deployments - resourceCustomizations: | - PersistentVolumeClaim: - health.lua: | - hs = {} - if obj.status ~= nil then - if obj.status.phase ~= nil then - if obj.status.phase == "Pending" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - elseif obj.status.phase == "Bound" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - end + resourceHealthChecks: + - kind: PersistentVolumeClaim + check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs end end - hs.status = "Progressing" - hs.message = "Waiting for PVC" - return hs + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs + applicationInstanceLabelKey: argocd.argoproj.io/instance # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize From 314f9503f7d18389377b964bebb4644e9b7da0a8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 25 Sep 2023 10:01:40 +0200 Subject: [PATCH 1001/1263] Upgrade to ESO 0.9.5 --- Changes.md | 4 + golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.9.4.tgz | Bin 82899 -> 0 bytes .../charts/external-secrets-0.9.5.tgz | Bin 0 -> 83210 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 114 ++++++++++-------- ...-secrets-industrial-edge-hub.expected.yaml | 114 ++++++++++-------- ...ecrets-medical-diagnosis-hub.expected.yaml | 114 ++++++++++-------- ...olang-external-secrets-naked.expected.yaml | 114 ++++++++++-------- ...lang-external-secrets-normal.expected.yaml | 114 ++++++++++-------- 10 files changed, 338 insertions(+), 244 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.4.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.9.5.tgz diff --git a/Changes.md b/Changes.md index ab6027d0..ed7d4bf6 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## Sep 25, 2023 + +* Upgraded ESO to v0.9.5 + ## Aug 17, 2023 * Introduced support for multisource applications via .chart + .chartVersion diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 8b58c7e5..c8ef35ae 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.2 dependencies: - name: external-secrets - version: "0.9.4" + version: "0.9.5" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.9.4.tgz b/golang-external-secrets/charts/external-secrets-0.9.4.tgz deleted file mode 100644 index 313a13018ce45950fb408eb3c3871bcf63d2409d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82899 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 diff --git a/golang-external-secrets/charts/external-secrets-0.9.5.tgz b/golang-external-secrets/charts/external-secrets-0.9.5.tgz new file mode 100644 index 0000000000000000000000000000000000000000..f40bed88db4266e6bf9882d70ba5991c949f7414 GIT binary patch literal 83210 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

HS(Vpq_lUB z-&?;QWVV~%MV?x511fxa%7sQZntK~fXhj0i1&&%MQOvKU;ao=Y9L^H+Rwy}r{K{_o zF6ENG{0J0vZ4Q7}_r9N@zsAqM;c|IzKKfiAoZr7?zxC$7r+!|fnZA$O(nr1}KgpKQ zhtt4ZJ((WGp}aTo%B?luoeovKTG6#rE(C17Y90JtlSGHd_A8RPDM%Z_bl5jzcfbjI z;M3dvMCb2@RJ3`wAPdGVr%|R*2#$gta)e1CYxTjF8r$C(Ae=$`X7J`4A<{dJ$D&~A z8M`3~Tt-xIQOx!OE(t^6B5OsOQoySk+-_*JRWsBl+6;ZnA-<-I;RLaZfajV4!Geis zU(w*B$D|R+RmxnFD#wVK?)AGRmKoKDs4P3FpECPX3;i@UvuJ zXCxRfgGFS13J83`q>ot&9i+oFONq=n-szh(?#}DY2Wllw2&iB!gVvZsiwc*gn8AIt zs46$%tf$9;lZp%a$wgC96lzO|e&|CwYqAl~1QB+pMe}^B!05y7CR|6bu%DpJ1UZOo z8Bhm=H76@$6S6D#X!@MD7f!Qd3#XqV*l(ITl957^I}dYd-SDT4OU=%isW%&=OXc4h zqfB3xJ}~k0^?GAa-G!VpjNpDBmjlB(`tVS!x$+*i%QyS1s} z1z<$S^PZRmPgtFb;yHcYn&Lg=l%bA97MXa@qw+6B;3e^f`|M9?kJL3^G_XNTg#i|t ziW!aQNp$@%S#*%e?`OwRKzuF+LVqf=qa9>{jJ-#0eZn4jLo^anBdme0% zd7a~@73BNcY$}c}l|`~H%N4Od^&E;g6y2mLV#S{>Q!8Q?R#+_xD!+6kK7nxg(~?pU zVjShLHKq!r2Y1Q87qUqXGvA{!GI~vw?*}qG>vJ4;&WtBj$j+@L_E4B={~$9w_9?oo zU-}3;s<70^l2)K+e;kS~1e06{#w3EjEuL)D$6bo20S%K|q%-Nott5a9C6>H^TEP*Rd@S4LS3vU_}E!}!^y z3B}QAxED|iEp4iuRXo^qtlUHrwK=D#uGcAj-Kv@~c9rT|`fdt_9^W{lCm$GEzZ|OA znArwJEBM~#m8=tc>YiUy^6tW*<}p7{_$H*T134NElMnSpSTkWI!1bWhQSP6}W-H;E zinub}LEV4(RoW*nQdh)ACId(#n!Cv%_V*&<*M?~CenSFd8hF|kf9p^<9_>NFQ9JyE zXSu^sBER?;++kpSLj21#J6+N8awtUw(^T(Cfs(fA$nzl_vzct$aH2=S`FcMeHeQwW z1ft@qk{bT3@j%GiZX8Gdef3OtNO{1N6TSpRR$63|(4CVD<`p20#2p_UuLrrv0dG#Q zEp*=^T@78X+?eNiA$zV^LzghuL~|L$?mhpg3OT)i6};a*o?%P|r5F1!wmfd*KlbzO zpY)3cN5~)SYo0)fDH}af^8SuZdpl4>k#yErBFS`dIfTTkVGmL>V6=qg`5{iE(rpUl z^kie$=0YK>nD3S1!DMUF2e*Z1}#u{Okj~P>a*vZ&2f& ze;pJ%Hd8}?^~o);WGQR_L7)6LllhS%G2#w!ziaSK0>j)wHP2q-V7if=id-eQ-}4_x zwqiHTr$5S#Thlr6%0ZA@&u2PzK8$O!3G&`o56X{ayyO)LMiY;Qjc4=}I+nl&nN&74 zbWX!8)#@VfjYJ!4rvuM$4dfPI@*+J2k~1CUijL_b+G+tDWJOAubfqgQ!*+IxFx3<{ z)dj96{9%X-z-SR6-AN3t;zD)m*-8S27;LwbHDz6*=R;XWg_f||Ll%~lHyRbYl^AWa zIHY7jDXrutZ!}OS$`%O~4M8ekqmdP$o3Oh|fVbZKA67g*51Sr$R;S+7Vly(lIW|3D z-5xt1=Ze>M94o#fv{j!y!8;v1s$`SSSuv!WB~RaqO7CS`TAuJT8){ghYr%!A>jkY= z5sYhO=$b{EEn*uMT+=p+Tg{RhX`zg*(l%qPCKh5$X_hn8%BVn91u2!FTq>6eAEc|4 zOkODj-PKkjJ1|u%qpnm;t9_fUCIDZdU?eU^&H?eKr-H}!#nn2%vCjbx2?r!==THu@ z-|3zT_$;I~ed}zgXNZ9&JNt7A2xE-Mg-(07*soouQtp`_-y?Iup{pF>6 zyy;!K)IEO9(OV%gPaMvgY}BbT=^1~zGltlYk(ZOVq|iNNBh=G=3IU6I3yA>EsKdmCu7x8go@6mFI7pMJmtR0W%9zAlahl~Z`*b?VuMR_HW-^~g zZM1k@4#}ct%*6>GT8BrIHB&hw&N~Uf!@+NW;SFP@HL)bLjFnN9^TUIHT z)7fYIH zv>0Y(sHs|hc)tdzv%?4e>LyL%;886>z;h{kvXH#e{y*jAm_A?CYV+#c1&&{fK9ZYi z9pIVvq=z?2o32N*>$^L8scE9|CHTuBf^Kr8{RyV=`^6O6q>CeOL^+#ri~7{v4<4=Q z{V5pfDsKDu;C7<)cqTgYXIat{GAeM;`bJLc_YBU_wEmwrDGu>*B}G*lf%lT=Et)$9 zMI`7X*mOE$I#S%dr0LN!b+76vH1TpM2a~2Rbo%weTw?!Gi?fzfqUHYwcWZcx6i$p1 zR}kYe9dGbf-~XVv-$^tiStbUVo%4lh2$*UOyo#=3PqtlWQ=SpvMZE`pRX-%jVh8$a z4v*HVe2>?W2ut1qP5Qaw^TwzUHa5`@KC|ZXDtgnKSKeR2)X;1PUg0b`ia4`VjDtKl z=%RjIj~%bT%7DmuyAno~#6F7|hCfT@*DzUlbLMvbBLTMq_R+du{utC-eQzt1^P!{(Sh?gS7(W?*#AxqEng{J(VH+_VQQT2Gm# z*I-h>p_O_$9R$1bKAkxBEy|3otdr__^P?&z|F`Qqy^WgaN7vBtC!F^mR;Br1V`f6K)#Q|@zL0Z!;{)qqL~tV~ z#xGY$27x+z`Ae>@MbHaC^H{;iGEJ?HI|f#jbNnoR&)Cl6X|R)zkLhNeMi&j`INv#E z>dWj2bW?4hyoABRhIBbJrW)sLv(xJ!%n74l2xwDHAR;&x+wb^#iDa3YlVlMlo&|mJ z3$%yst@=C_ao*zSH8FM1Mo4VN%(bCaXk@3Qj$~_XZRai~_%IJ&Lkeb*pZ*l0vA0tP zhp(hee`<}fI6}C!9Piz(b(pizvXi9$-=uE=_Ww@$?)~3O--;3VzVqbYF)mHPwY+@V zOJy0fV0RpI344a71xvLp%HzimZAT&6ErRk*jW<*)SixzrchZnj~(S4bRX^T>zIQGFE zSz-P~7ZA+e{_X)-`k$S2rx4C6XC`$D(2NEfRTK_#T!j-n949Efm*v@KTsjVDfjf^z z1k7y>JO*n`t^C&Heagzrs@Uj6=8$Ps=_<>6gOgr~XW2Vm3N}KkhmW`{fm#qL5o5<0 z*IE+;8`Zw0|4#bO<=q6{ZC=%d#R0s5R5d#XgqZC%7#VffHXHEYTi;!?n1Z{%)Xa+` zH&E6g0ZiRgbHsG=AhIjM7oo^cFCls38nIt7968+uAob&oPRDFJndLBdG4!8M}Us;mex;kJvYb`eIGsf5pD< zb?X0*u!u zu>d_TCtAwD|1<1cT1>g_W*-5EWgJEL;ISOxVrzx`t#a)bf=$m-YRBqfy91JjHTxD_ z$gfSdvCefyYLamJlMIhL@=N>pMS$dCR`&r_Nq`i)sXJU56bT3l&z1E)E<>2LlWW?~ z6;W;Sl-erxvv6JYt)ht=-{9aETjty^By;|&7nj!=z(k|m1Gwy7QUMzwchr(HL zq2~VnFkjANp3>>ydBK(__3gN@X?O<9+NRdhQ5o+wPxiW&nrh=cPNB<2#RgG3FUT^Z zF&)SlvRey#>UBDrl+?T7`%5pwHD*|{$&Qs@TD^5SoLwL&8+zJte&VgRy1Tl*wwK`b z7uT<^SM62RZdw1b#Qlio;IizHBPH#5lunGX$`nA_vm-XpjzZW#_%%b@U1HkcaTNH8 zK&iJs&$Z*Wu&3?T*Bq8HF^hOh02xNjdYOgxelcU;$Vg1Rx?o=yau#uVFL@ex1rq+> zaNpO<{}=Zy`Tr^RO?~?h_x%s|{SWv35BL2K_x%s|{SWv35BL2K_x%s|{SWv35BL2K z_x%s|{r^|)yF|R~xNEo*y%m>+D-$Sv!{^?%_Ue(h^^g>EiC=`#p7({x$Vg0%=my~d zrSE9idPOG;wuGcISi65o-x73x%tB=3ItS1ck@(Fd(V^kPrT!&-n*gP63-Fz=f0Mq? z=xJ#8{!RLZ1xnv7jLe8+qTxX48*H7V`@2a4Ns11^zf0e4Ky3)B8r z>3d`rOE>D@rElebN#C4Am(&_zWPhb^?0-q$4u7R@jK9)1-@l~qaaEx7Jw^DR(s%0& zYE_dCH;R?;P4>R221x5)>Dzk~%3hZV8!k*D0%p-pEcXiQBK*Hd-~4~2Z#giJl~k#J zm%gFZ@aEK7@~O%HC4HwkO_<`~-{%}^g};>UJ%iNbURGh#lQAjvqbRLajEe_GG04~|eBKP~+Epxv<`Kr`;@YL0Y%LiJa9 zhEs(jmlI2Otis-GMgqBNh$2BDV64pZ8#9q;$LJE<0h)n=8HuQoKP$B0n9APB0X_C8 zZDl_N<*@mZp`vTN#&-c70XKED3oonD$eYl?^_fLUNlgV^f6o5P93Q4bkl}jvTMS|F z(S8roPM;7@6}B$|>czcTx8$<_+Sf|P*k{WNa|G+3q%2`h7aMmvZ$(9$_CO%RLd=i2 zS2zw(0k^e81y^5d#Sap3B8W{KHi_V0F1+G4lB0MK ztRuID%-*uXiII@jg%Y%y5kMaI>UVbbA)I7F^2tNPx(O>H><0X;R`aK^ta;~)XTMlR zkMs8wGfc^#Om#Y-b}QB2Hb%mV3Et4#~OflL}Nm!M_sg5m)WjlI%F&kA41%F9-vKG%~#rMvU`NtnbZzqKqSR)XjU>j zy+?;yFl$h8&XMa4h*DJJ=c_$5c5t;aY`uTJsg7ATzWvGO9=Sbl+T8$LwB3J`5-dx$dTRs;Yo4a-=yi6E*4@%vOfke|1{)y^=ADSRfKAIC)yJMZV~GX~ z2A>eAVss?ic5@QrLmo5sU?k5Ng5)1+J0gra!mw{TQksRVO^syseJsBkCXR(3Ehx4h zOT#S_ffp&SyE2iqHZ_VJ;r4^1KVrImV`;#2-(`baS%}h#%TO)Zt-$|h zo~m4cCx<9aW&b-u{lhNkm69qSfQ2GU$7$rcfHa-8-#E<{6#EZ4Zf<@CIYuvvGdPx6 z&*9l4V^6w`2v+Uagj0q7ol`#wj9!t2Yg6zXRP>aKF^ zDPeLnmS(^q(W|F{tUMu5yY$+qUaW6GbQlCw*-M{$U=kzz(^VvH7w!=9y-DF$Alm6w}&a<=+q z2ac^R4rewqW7aGXGtUh_12iINbNfMnr7k8AO@&e3_K*AS=}TX3mBq;;t})n+DOHky zKwDo8UB<}oJh`{(QEP$o`O6|%Zmv%>BQ6Y5VcT;276D#8xdJ_aS84>>QuNb1sGAc; z)%kft>U9_ARIAA%@kx^+Tikh2XcuP1N^r_>M_ou^K3)fq!PsGGC*7T)xvu$m_^rnI zwI;TJR~BrAu!Zx2(3EgDkxrhFgCZyNaGt#rSoDOs(A!q&`|J^Gereq)D$tay)bc}- zNyL)5XV!n!Ymvq1UoO-NoRVXROE^2t4#@QOl{xJ(Q!wY~e^H1;?~yXjn294hDOrh% z($ydrhW&65y_GJWY?Xe66sxP8F&2qaR&+Ey=o4*Mru&R(K=6;(rjOClk<4yR4WwOAolucm7XAf-SMj$ga` z+kv8YPQv8d^@;Rd62ER(7Y~t-hTo9J*>+0&pX4Q|i=qwNh zW922x+NmRmi@wD69Yjqua4OU9M)r;$>R^%fxatE@FnHg_f0Up-)s*pz%bHQlI}wD2 z@eed?=i!NBAb^V85jlVd>x^XWN<1)2UJ)ZsnJ$LiS)Ys|4_=n`!ySIlf-F@Y|6j_u z^Y!Ucv;SGf&Ekcej6y6sgf;hSn@T46jjNoKcirn5_bxw0CS8|_^-{ufG)iq$3*TSJ zD2l8ea|Bs_nMk&|kN2q1>^K+NIUi^7X^Fg>At6C=s@=icuX_BtgMgSi1qvb5WRx{6 z&c76PYb<+l$&KZnMJ3O!9WA-4FpniKB8!7iLL(ncB7lc*GyxZUN`aCYyjHf?%lh`Q z*qb21o%0CLXV43#;m9N3E#6DRWW2siv1K&}eXl(MWnb6-p_!^I^6K1&fY7-3!?W+8 zvkG{=sQCVldJz;238oA+qQ&GZpA6NapV|eelUi|ygv^>Ki~1OIr_q}7_@wTscc=$~ z)2aAeWa4Hcueg>duDx zOWM8=+gh7x=R$3U4#@buX>iz|pZ?a#LYINfL7!Dy^hPJCeZ2b}&Bgo+X z)toD-%`m_iMYqrggUvudSkmC9BbQ1gGiYS{cJgBoNPc^+0blA$LAls(5GPA>azbPS z09GFq1WS1jcCEq)A9o_^}NZP$ZUw;bP3Z6Mp0veH{m3w_tE$|4HISl#usGbz1uK4xwFg;Eb7kjX-le2dEUjV7p&_* zNOb-OP)gOUuhc);q|QS>+|;Nc$1#$1t8=N>VTPfM>L0$kVkP07Jf)NYM%n<4{9q-=N$XM;3)XoCz9~Rc8qrRY z!ZKo=-l#FuOX(bZpKt6N6$fDTP0@yLc8w`C>a27U}5!}E&sTaB2$LEPq* zy@8Vh@ar^PaBt-f7?4Q1%;3PAxs!rkC&K-kjF@P#BGIo}5TsOSDQ5K8LzZq>Q+1Sh zXaDHUowy^b?>kbCnef#yD zANcog@ERTWM?=rQg4a3Z70ID=V%_IRD*0w`sCn^RdExL>l`{xtBC%_{oPK%X?`B## zW-zldUhK@M$esJ>xT~X~9pgN?bCKQ9;EdTuzVxCCD5#7Hs3}ImxCC}XC>h?fG53)@ z5|+QYU+pMBT~W5zWR5Lb6mQwd1eMoIq-quFMK?;Xt>^+$mOjDHVSi=KlN~)dyL;uy z8r9xzPq=BG-JA)vDZtt(&{I|M;QdVN5bVD+EcM%Zt(ZnY^yX7n!Wz*57O44i-Hr3l zSYcBl=236o+8M;4h^03#!WN*wzOfSEFAv6_>NtAJoP2fvz3>Bf=h~E)K08} zpvFwPX*c8KhZ^NOP08MR)GO4*F zgS^uTwBOhU)EuVRTJrw;Yu)p>^)kc+q=g&yB9;S-{Yy}rPAL775O?bZU2V1+n=t=R zPf}%1BSDD^&i5K=6f1-XwG+{lX=i+UEWNxw!_*xp1APEm@Ed^b2}x;q--|T_#;5p-QH^VJ%#%!B0ufs#DxBD7apgbJhdV|lDcAHCwvIqt)&lM9KY%b z#~~=lO5-uEem8UOn_$IppSIG&fL5mU!GpUM;sns=rKNl2@NLDPhVW4({(19hhmLwhp=MS$Fzj->fvIz%o zy6m=T^glP?Tuuad` zF=1U3Id*`PG=Aj{IXy=EXhVsYwmO&Qtx87TDha?1k1HWX{+Y3r*ZOR}wqmQl{NsJ5mqm%Sv;?+Lei z4cyWphop#eDD?JusjbZYP9PdNx?bY(dIC2VE6Ia5cr;=bVf}24rL7Vs^=e|F#)h63 zDt#@3Vp|5E!~5R2GQ%H0{8@uZdeM+La7#5P3Q#qth8vk>}6uIMz^RRZWb^u=e z`hIsqLL`soe0a5y4LiNjp4GWzvUyIC+C;{2U&Pub!ZLRo^;$!zXE2XGY4cCgLx%^2 zKnZ%2_j^8h3On9euyU?bop?1?2t@3uGntafz}iuJY=a@i7Qj@^u@^jtQ<9iTG|Fgd7**-C{(&t& z^eS0F7Hq#okevmcfbI}(iP5?wTwfeXvB24j4z)D$lbA0L!HlSZ_Cga{+r2rI{nV zAcBJr(Mq7~s3OISNxR%)tcTu$n{`LkR3leo#I(3qfe&06hpVU$S|)vUUi7ubwC6~x z+kB+}{@;y-sc+1Fh+Faj2R%b>0|6bUH5P8V|0gu@r&}V0P{1R&6gwmOT2Y{OQ5p*a zvX{Ul3q{eToc%+yecgdQGXwFPcm}jr!%ZMgh!EaOx_;^taeYNDmIZV>pyzFmyO<$A zNQ+tFaoWWvY)LJihe3B>K)01&DU`Dvkpl~i9bX?@I68jk(F?;HL-wUR#J~NzOhvFx ztPgEAKmQnCjKPCD9LTvv=iMHa7axl^sZil9HYnb?pb{$^J~u+hairF+WYXu4frWBy zl&@)H>o;C{>BVs6nAn@5hz59IatX$+v1$|9@nUpm1@sMXtY<-@WsO+RPBD zxM$Z<-JBF6W6vxo+GMsykX>}Tccsu%<~8? zpgHkq1+n$Qo(bOsM$+7S=V5Wuq<7pc$?31;!p$7av_@b|WeXzh4Qsz+(m%c5ww)no zmd``V9P4BPEq!i5obs(}OBgbk(^u16GM0B@etauno@~Jcm?vX9AS}n>tYG>~Ztc$+ zq^)$4ye*g&Q#`y`49Y)YFruvpQDkhn*Wt+j!}xG#fsz%FZt9HpJWXWH_@ksbLw{^WPAwT}9trz#gX6{8_J->Py%oke6B-o~3^$SP^ z(WvFEW4!!8?K?zjO;IW2wP zxbeMvbEQ54vm@C>>cU=+2mRKl9TX#L3-WNPh>xSdGKdWgkDi)YDPxOkU3J?y>8ZmD z{kIyJ^Rou2a7ql@$xM41nRrSJ!sdXvTi9HEdEJ*_HafbB^QyUdHX*YrrnneGFi~&u zU@$)$tgc$khN&SKQn^&0xT)O>n6mE;mRkebHRkM)NQv|TEEtW5S%2h5dRFvOr^t-H z?>s9giyUxbl94(wI#!R2`ads|$^#PHs zWI`-+l&=KSCpB=-9fbGUZ*uk0P|%^aSfV(nDnrL07`x5woCrcPNb?g(s8@1OG&gMx&joBTNde!|jeF?v~^!W%Fi zmJ~xvgub|ZZA1A)E1yzl5clcaewFD)f$OS)!=0DoA@Pg8kn)lEz^Ljy|63f0)36N2 zul6KXtkNh@@Mc&7jEMOV#FAS4xl_dQ2GCFSE=iEy{R8IRAR37HXWTupz39tbj8*d6 zbQ1JJD+#bVieg!`$m)o+?ia|G;g0}IGcjlkh%^6C?`EDM@DX=k5>#s7o)e4mdSL;{jC!^O( zQCgO>2^I97ot~j~n7a?j@X7T?`>+Ma4=^TSExuHE?3NHFVB&X5!Kq#aanl|NF{wkQ zxb%ff$pG3pWkD%AvR{*w`(qa~t+HPZe{W+#ejjD5L;a6^qTs}<~ih}$4avLQ9R z-UWZ|kkhaW0>&`0Ug^H6o9O25N<7y>Nm#XprhsNNS~9h>boX-O~BeJKv)}mMh7TvZBjHOsmx*9pG1$Jjy&)D9*hCm5xVS8ySBRJdWd` z7t71nu-13@3*=-z+Vc5ZOvN#^HKJ13kOT`t_o2O!x#z`S@>`R>9QF31;$+p9{rwxx zhu5JE@;w~hN70h~{?2)T_fG#h%$+XHM2ZB|wsZk{+c#E07>bM_sIw8?%2Cp>RcsI1 zwu;);JtyiS>}Q^38GGG*?W4O6;BTN}^ZdhXlAFn=@lpeWLfyFnepE7LnBrjvjr2&7 zi|GRUlEK3?a`N6`NONRW-g>enstxwS)q@;X(#;drE+kl=#BsGfH#T1>@Di0=!P$W&gXau_csaW}=3E2wN&cXhVR z66Ce0Kc^(xEC>tTvN?Mec;I!yBvl{ztb**sRZ8@9_yODisNYe{(z(&T$4?=^+dRB3`0wpCd>R-H$}K~VVQ??WSo!ZlIDf;)$y z1du9e6zf7Qrwlkdm~gJ7II-Wl#<%Q0PS8CFt#TB-Z)hJwPLe~oVBHO6+0L)9y{*oh zhpc!$+Hb=xqe-FXns$*vn)!9sY6s|B9`e>^-ZBj-G4A3yYzIa-Cz=&+H<^DAc{K~e z<6qh={+RI5f<%qD5iw`b182t#DRCCycNAaHKA#E)Qgm+OZZ$LYw}^mde_xsk;?gD@ zoG_!oyqU>^gH?!ce@O8{KKw$ymWTgbkyp|GCR}&r-|?Mr5h6?;1TKUt0VYJ2HBn{D zWmmgJ&4~iGUslOP&$YP*<;;IIsS(uLfNb+aqFHK_9VfG%i3c^wC(;c{v5fX>fyIwD zxsJ2?Rq;iyxx`Q$q-mwRG)GxR7rw^&l5-EStuLBZu-1GZ&@pai+goi<%k?g84x)cuV19egD@0lpn^C0nF zj2fF*tt{H9)A@(|%kHn{2^pAXSX@25iuhn*&rAE~tpZIL;rh})xS%XQlBI>m|8|u~p(v;Bq47wnpK>%!d z1qQL|^XcPBlrRC>{lojaT3fNdpXHXU@LUNKc+=4+BG)qyjM(dFSV{XPu}aFiWm(r{3E3RbPym!X!nZ`W?Jpfst-lAbUtg z%{XOZP&Y3ksOh0WI<45Q@!wTK=YO$7i`?%BX4fM`1pm={E(kp=L};sAaf3{=aWI0;rl5V{mP>| zI$)9H2Q{9dJ8!!P6f6APSZ62kST5g3+P@uNBi|j98)AtGXSnryDyIZQ!1TNcqr*qC z&D~^S6PR}e1}FCkC;cS$DPhl>gD!Kw|3m~^{@L-^NGnzK7Q2PuhceG}zdJ)=q}xMT z3ztG*0;{JJPy!2Py6f);SrbSvY~<8H1D`;#g|c*R6b$O0JfL_FHpOTp;`3t@UC?qg z)c=6e&a0eN2sg{bW+V#uSHGaRnHh^^$2c<{~AAN&y&Jn`HJ zG2%#ockTD3jZff!YKeaI%$F*HH2Rm!76YT%)1hQNk*I|p#CC#H2f)^8j>OsMDYA|U zS5_5F4hI{m)uGzU)M-ws&}O`wa$OQ(XgFk4E<>!|ujug)3d<3MN<2z8?iz&oAP&Vy*Y&I~!&Rt3Y ze8`|AIVFiy#KD<^P!fA|B(&0uHH!Jwi3)gAnZNwdrHL1P+|t%NE5ceDaTVhiu|wIn zBn6ZCd;6dMOvy=j{eQ?=0&+P~msMgJyxsumaKZ@-{bOiypBN$jQfS8PQdrJPDAXI_ zrfBwn&8aw!HO}57Z178AdxsK9;a+r?v0wfX$h`xWD4bL)c`!T(oJf^%60KU0!g*m~ zW)7Y~K5#vQUjhL+{R`lr)hBv51LUc=b(0hJ5D`Vp>~NN1P;z?~7_O{vp8@feBKgH# zKeZf}hjz}qKqL7;yI=)CWY%k1zxL4cf&i~R1>Jwr)Vynhj;vhRSs`rIsM|n(9nW<9^IOa|IJza4QX3 zeUpNFmkXLSKi|^bnv^!Lgzk1nqw&P0(Q!-cSNeVu-`xylOqjEBKJ*`ax%H)q zBfIR+31I5Nx4%LF0;x$Z3OEkdz$LD7AF-| z4*DAmDncKgJ@RM8pwypKISXZ96x|0ZI6Ml&p3#SK9 zn(_*?DJzkY*&ZE-=;ySF!!G}M}6euI%*yR{M@34e# zzM0#KIO%<0VcQOHIk)yhI2$`d<8uZjOr9hn#FgTZXIlR?iLv+n01HpDypzRVZvu0a zuC4pb3AJgZHr}MdYo#u#j6qf;7ZsD9i!jlsBv>rRjRE18CDabI9@Dhj_y%SA)i%JJ z`>0>VrbsR6$RSC=+6kRN%YL(}uwRAXFg-xg``fE(pVEQFv3D1(U;aR>Jg7G20yl|G zAW7aR>cw!@U|^w;o=XO+865kKSG5&f`Jqe2{rH3}@=z_u4HMY#!46C9x1oiEw^EA2 z&{o%gtDD$vr4wH3H7+7Qy9Vf^UuMouc3 zCFiTTC|(=pTib6oaFIK6CdDIov0e!nVK!uxGUydd$}Ulv9H$6<4S2J<0WI>=?}-L0 z9ltTPih3949&0tHv3u@b#RT3G%lrHxkE&w)y#o?WUz%^LO`%t!F&GG8w;Y4LG+}zb z_E&wpDSsZh{SkO}flq2*MB)g6>Xx-NyxB_Sz}~29wY!h^o<9=+oJdB3id=E4i~a!?SQ$TT|*fO|D%ufrn99QJaH{Z#7_~IJq-Za zt*CU7zuQo2s?V81)WU1EMp(}e$)ZO5OW+f)D#FUqPxATfF9nu*=+$ud29q5%P&H-c zYk%clD3mPwnBr-@}IUSNwz9;g;V% z<+4?@l19*x8#j>rT5rL7Dd9z-RU?UXZcJV!>vEnIp1&5R#GmWWNvi z+L}tz!gRt_H6hoaX?#%UIhPu?pB7|D)o?;?pHZ%+&tvKRIJ9=9_Q^&=YB|p9EqK9Q zJtLWR|Bc3;*~I@#W0R8;?rDx5%#KzBu@T52=gV&fl{bNv%2GRj?CvVj;0GFDD)N+B z0>E%VA?%3+>=Xthom|puXyRJrybvAD|H{~;5Q)a%Z1g1x+C=>VxqU~&_KfewqB84{ zY{IM;Bu^9mmGg;4AHXDlgupJUsxp|2Zfx)nDVz zM)SVH<~r>83hxc{lG#-R8?7JAIq4|gbBx2-*Am%wt+kC`G|)Q0 zSl}g6t7E5Ph{(zwP;{)U{T5fgkC78sQn+V}xdyX28@jL`=6P$f{GRT^t<@T(Pcl85 zkT_K@b(^esjJOTHF<8aEeOL?=oo}%JI}_XSi!!IZ+I`k*gD!&$=TJSf*Ak^ySEI>) zvFoQcjsK-Wh7W>;jiy>M$)D~Yqzmk$+z;*N55aojoi{Lj&-p%ee@Dt!N)4(p-$p5? zI68gM8DKOtFHjYhr|k)izHfj@-SgrO8IZ!}O`{m}*!}W4A8aOk=0Xz!}iSmXL1> z)cX?jR`)$}CeYQQNQ5P6{$fVfbY7g*INdUjX|PsPusBU0N5|CI5_E5TOcY{bCx0+& z(DNBIw{Ui2<^d$goo2wN|T(;=7UAtvN#HVrNyPfaCNY-+*W73a2mK!WpC6M zh;j|F8qY#pT$=0@X?)X+$=A+D3=y>FmS-vJuEj|1FYbpmRR}>pJ=`*c*fS$2ms3v$ z{R6~(u0_hd5XcQz4gQwxgtf{5D;bb;5B}ZR1f5F>7bU-&24SUeA5OwDNd#p71RaNa zGE2)xm8S0=;3WsJh&$9#S;k&Je7G79m+8j-@RG5tuP1AOnNuOw<`ilOwd7#9)q{>L zDdF2p;w`qGN#dR6*B!m(k^Qp-GJnM2VY{&~!K-d=2Odu{5pCkTTqc5cX3gOc(ZuGq z7CY9#=pw&eJPF#Y7R)f7YQT^jHfPCA{1sC)k|OkOq%$wa@lZd37sWBFu#&2p$_u8i z@>a0z&eWOGhLUgwnUi{7@5wW1*&l2)VQ~hvpdFjfNGaGt0f~SR0 z3+XSVW!L(3-#Gk=eh_@fl_3;vcp$BS1wL?$;1hOp^s@ZP-~D;=U6mu7#dOY#Vf+I$ z(q#$M#?Zs$R}c%MCsiaPThtWJU{wY;gRyrW=v5auyLopAD8?Oj7C|S0qum|xlC$@J zomM3|J^iiICCshh7Xe41vcMKEqH^VC;+K~EjHkp&53HaBDc6Nm6o7rNj#1{kIDV4C9<)Mx;B2`=^blJL;gl?IK~9*6mL=80T^vS0+uOF27SN zd*pHI;Z)+=r+U^<=Ga(8TF$(iN0@CSau;`u1DgjKFF^kV_T9-UDd=}F@Lh>+HglE= zV5sv{+a;)&TG7^|sWZs|kn7!cP5)VmQ{C|iu#YV;4Tou&kvZA=j1%WSlH)hxl+CF6 zrG^noyQ}0U$@cc^Eg!r{#en5N>v~3=3bBO=c#MnUpy7l_ zovVFr+%r#Zj3oRS<_9k-D56bCBd&wMZ*BxtbK4Q+Wa6l1UnhF`XJ!mwZp-K(6kPd=7{auL3l!>Z}G z5g@fw6OOaymF*D~lgB`!%44WNSM{rPQcO=%Tf*{Qr42Xf8 ziSWTTbOm@>g-(o)l!=PTf6WE4p{wo*7S%9g8D;3U)s^&+IS-ssi22|XBTlX1NCs6T z?p`6)AqPxcauJ>YPn{Cz@5U-!GTM`&sBAu#pm$;Snj~%)n{LdaljG}N9(w|RiMQ#k zE>Mb2DrKt$`D-bash;FG@7JrZ2@?s)k0Q65I#*3+&Zj@#mx4l!v&KeBz>=OMUtTrXXc)@w`tp z7wcm&KRH~S@O3Opp1&mHRb75G@&G(6zLIgEIuDh@y}&Kgho@H_7NKa9sX0l`t$zdO zw(&IH_h*NVaRSq8*r}BIB&T-JE-+3f42;wDTasD9c&mnJam<**e&?|R*GDUPRFb1% zeYt-_|C%vG_xk5wld9!^n^eh{X<@7Ih;G1%zQ7T}$r=DhRWPC;aKT&&9{v+h^wmZv zzx&wIcoebB;^T+VEHpJ>9(vA-*8I@zwp{5 zkXcYIgUv50ml}mB5^<=Tq)jPtYYOCc8RWYJ?wQZ4YO@-Wfh=zK3huWs3w=GLeYT|fRAqd-|G6JK3 zK_o7QoIGeYR4o(_fy&UDY17e>kpP8*w|>0(3KbJ;MXcP*8SFEPUV$XdTp<_qGcP~| z%g$ll*sklURs+YrV@^-jeE#ov3Obag!ywYsSvN9;6h87$O-M^Py*i5~oTLkDYF=|KrK5iHCHdAf}`D= zq}Y0hOqq-o)1qD#{uQMY1xv+yV5pJb{B{$ol!M7cCvRJbThT!Yf2ffeRpw8J8Nn!h zm2A*>y*xAYrymrg<~L#>5%vHSAM)V1IPj^fV16{mEe|wmeY@-#Vcx%Ne_=>Qogywp za=EWC+{8?8PPzvWaFpUEU6-76QouP0u}x6DUGuj|m+&QKIB5}jKHeNSp|bnW300oY z0r?*G7AW?N%;MG4mzwY_Sf7CxUJtmg;WWZr?}8c51My(OvPfzc%C`w}U#Q*#zO6sf z4@BzpB9~x(p(mW44#%j(bOsSCnZ`jaObV0=i6Ed%*McLUPePc)_nd3@`-_WIPN@-! zvtS1nWS_B${lRbUZ5qvXqoJ0-uB2Ca0|{=HhRsML6S#zyS%{kb{O(GTEX~k`u_FH< z*=b}BZ=IuILy7-u7$~eOQ`g7y*O-ULpMMCGR6S;=oMD8)n>g{8AJRhADkcX5Z_TeEQhl4z+O)*Ho=M(uuBBngJSqbb6U-FCI|_Ob%DMIU+J zBN0%>nu!}=)u2<2UMn#=Szm@Hvz=zT(<6mSeIhrPzLN5*a7Q+~?#0)lfD}wFz2-a@ zZb(NnF&xc3$n;JaRT(*)S0!0s1VDrfA zUt(MT^S=<=pB})LR<9dWh{6M5e|Hvs&t(jvUnKAuySaJd6sqOh-)C$m!Fu~L>UFlx z?tBH%VxlbH_H9y#>JT+)ytD&%XlTC_LjlF3y)3=h)t9#4^_^S%xUF4+(yX6jU$2v& z(tLS9WpTJ(MfjmzNqJIqJUusJ5@YNq){Er(*VGfPF70W`f-y$KFtRXo+$NSOx+r~k z$~cT@YXD1ShNVGm$B zvbDfn=>#T#5nWS()I;V76l}8Q+z`G^oGx*}2pliu{rJGc(oCP@o$u4RKivzOc6Zta zpV}7IfzDNpO7N{mZm2^>vrE6Us10bVY={^qB{l4(0xldsyRv^r_P6%l>qs#e!-`#x(F*!1Ev@q# zt7P}9lBdB5?nOheSygTF_drR?NIp^WnH`TEm%gGaDk`;5RRTH;C5*S*X}u2`cav$x zhbwk)RsJpEPFi`KW26}XR2}%TOP65BU;b9x2HkXK3nV+bRIpH2_A zwE;2k$9VCmKL2s!@`ceVT)$mvKWbb9909A&!B@WW74SZDG|vklmi*F29Te=I5+Gt3 zGgd7|o7y(z`yJ%ox56Ij=s89nluZplHk;4Lm5PiA!(i`dT3L2s+w=%YQGP{&^TYAw z*+=?udfO&Ar*}~>1k0^76#$y%<(S}@SU>1=bn`^}7|nw8mN?J~W3~bbb{AoggujKL zc76>VWJm3y(xe-fTo$EZeyKIE74!l85)e0I;_y#;AXo4`fTxzIOV?cOh(@PKz0`Yg zsB9fdR+E7Xijn`7YP~v#aatU_SgbqAxqE6evow~Q9Y<5{PoV9^<@E5&ephd#$81sd z(}iTK`-;!iZ-|J zngxX=vIp@fo8-@WVWk~a#8EZZvxQbVUbYu~q<#uLJi1X%HZA$0i}dhq&-crZ-uwUx zuJ_BmfB^H%4>wGOkF4MX)W#!$Yrf?(={DxnIk?TD)9rer<;lCg_dyfqOP1r{!nP?8 zRwo8dEv9J+kn77o-6qIsijVLFY-u2`ch)_Q$|G+}ykYUg%=0TvW?b(pM_us0x0M4v z^O14wES~oi0^To2jeDN4-+ZsmR`&uBKQDJjcd*u+cFty}iM>2Srto`W-{mJx|91B> zTm2v_D)IIQ0;mm+Z}>koOZw)T@hEJ>)s*DEhZ`>|S{*_wi(SIIjBPJsNUAp6wGu74 z85HX&U8C|Fbemu??6F28k41@}u=?SZ z7Ntr{y*mVm7B@e0mmem*dXHqsd{CbIbW@5RNhO7wi8Et>NGLw z*>rKz_vRN^@_vSld`ZDS``($x%>eR)X70dPLy(}YWn;o`G>B!NRCA1|$_cTshh!{kJ+<i(kk;<@VXZ~) zImfAf`|{aWlB{(XZ@Ew^TStxR7cAx6PS62T zWYP0=4f6L@qbYvCIej=}%OoPbTXo`A(jaP!x3yAb_>>BCGMzC zyk{pTH=;=7pZUkmQhPuOCvDOF9u$S{x_fJxOMl*#KkGNOzLV79 zHc4GG{uh>~4ah(P{@;Ig_$Y_}fdD+}@c%BJd@Nx$TDQUzD39PJ`)-NnYuNbDfayy^ zbu-YQu!d};Ry{T>!H0F@w}^a=(M9w9pN9d42XF2E9~=MzDBS;NPrCcRi$~LLK5z!d zY{5pJ?_nO>!O_{NOaL_aD<%>ZCjkY_aTvKF1{2PQm?G?9{z$(Thei_aCJb_SSwthw zMzR|}?EkR;xj2Zt_M&<_vs)v!GP6T1z^$C{$aBLc6OU(WOz~|ozOJd$znuyes?{@A zKAAu}aVmX6?QxVRY5SR6)Z8wLpUSA14X%((=P9&9{G99YtR5zLTrs~6Sh9^In_L*W z^yY53OzJvj8p__YFp$fy>#H?u;s9h9zAk_W_ChtB>{xhF79CmKh=OF%G=Zr2>1d;m zB38&#IZ6R?S=;6H}%{t+ouH;U;OfELT=(;Ds9M5^Xs7z#%J{lqZKwMzM~l(&8Vaq zoz4BLdTR8)D_#o=j*MjW*Hi&F*ngfpdX~5UKYH@Gv;XYmv6B7Ki9W`EM()(3jPhkZ zPw&*Z5vk^n_4+_1yrSj_-xr6@&9NILE=EJ(TNYt}wU`_|VSE8OLOWo3@+jQSY?E^U zR$v0E`kDo7>P7`;iLy4po16x}5ixB}GEF3VE_%XxJ?hyXvpyDosN#tK{ByWos(o$E z;zyW=bRO=>eHmz*RTUIa>MRBe`bVj!*gt8p=`n+*|LCm_MsQ3H#LIA@vSZ;W$LBAO zE?@NaoZhczC*ofxFJ8TXfBjPsJLz{uU(G~X{8O+FAu{s(=_%+X1Z#^PgHq4Txt%u~ z0C^I`dG+vG1Z%CHgDS3O3qua#)fk2avbc035m;B654&>zR9A!gRF#8F1ar*$$-nps zuZg8L|3Ksh?#)l`l`4p#{m}Rs4%_w5Q%CT(`WVpvjq3n5`2X%dJ1F@7KYiTgf8NPs zr~lnb{l71RK3Af{-PZ+_UtXg=VA^#%bpnl?($=dNC`bOH>IT-Hocj8K+In_TM^Jul zNA(1&YUjymXzx`Y+Zu%0VQZ%rVflDQH3_xdyx2u;!n$&U#q!9N(~ZBBVuP9ESzUB6 z+pN9fgV}MbiV)`O`SnXcjpe7%edWiJ(TnAuur~}s=DP3a|xD|1H z7KI}h_2H3A#oWrinrmGAVg>W#!~Zat$)tM4s!1*A8U`x8JNRRL$u&9p9WA|PS!K=Y z2+{a!R9iEg!kXVJB9ZLL%mQPUB#W1B4jVhgcy;4bPQAJ6AKTvko_U8Qt~}Sine;e0elc?1Ayzu5FIY|wb-%aqS>zzFfHFK!TkJ>PS>aIC; zO4V+q8v7dAU~@rWtC?26%*UMmub&6DN&mBdn5X}b51)1Z-#dBi^*_5z5o_WB@>zL@ z8ZLjksbi~LS-udlqI>ShQ^5ZNz;bhM8iY}6xUNb6^K}1N7XKeSebnXu-Ocm8bH)-p z0x@6(hwr;{GjjuHwDf!|CgT7Sy=GIuIrqMIE@xis#LES-=1vR{@tvt3j<{kLTJKS& znZ#SUHe{V4QJsQ&g${%LaA^wm2GhPM5bf6Ke zZ@F-;Jl~D){m(&svw;5{v1|C>WRCw1VVrya^ZyM!-m%CFm$7qt@&djK+$u)Y-Wzxm zmkoJy`03tY6_3M-JG_VAI6raDUmTsheleI&?xoDtIGV)6^u=e}|HA!Ob87D!_4ohq z;K|ed-2H!Y_^7-8J9)l$UWgMQ)U@%vd;fdkFdy#9k3HYLN_zkKzrFd?iKFq4J@ieA zpAUx-yTSW5TaK2%fS~6TMu%V`xx#rW{4i%;Fl2E|FXOQ9hg0|;6d(tSVA=z8FiB=V z_73*@*`^Rwj&*#qf&I5Hk9Z{SV5Xk7(nTBxp{DE`w)_L>x(s^-m zdO3J_@819T$0cbM*n&Li5%K)z9u7Z;he~2Nf&*Qh zUzQ_y?E*>xYyn-9u82AqJttCX<4m%aQXOqhD=liB~>?7r-3> zL|+)7akFtIG_O-W1d@|v<~robd+#z-L*tJ$4NeX4&xkYf#NRs~^CN$Jo?YeUa|B!s zxFDQ6=h98=-cPsE`FJlgL-ApGOwAAf#?|KKoAE}yAob0OUR+ZlzSk4;e%i;XW9?%2EXV5Pt-!D1ta zIa-{2y62I$HFOwg#L1xSYvqwf#hL1F6MnbhKWR8t+1RPGu-VF$`{utLEW(G4|MAb- z#yNLEt6pXur1{|O%8er6xu98`U?x-S*6USu(?U_SoxH-_7TD!{8Sq1#=>>c!{feD| zX%)STgBJn{GIKh;dJ`sRksHIVRL-vu5|J5VIvZ15d?Oxf7@x;dn82$LL1eNwb=HEC z6RdKy%Ag=8)1i5MsW~s%&=>RywOY5~(g6kq#t!L@rnzt7!i0$B07IWC?Ks{dZgZin z7oRkljWDu!0j&}9|Kdbmivi>ksa5)ol5y8K-5Ix1v%8&(uroz4XW^t`w7geOcF3OC ztKM_kqP?E(h}3(2K%d0HQ!hh@yBRut;&J9ElTO?Ox*#xJ#kgE63WYah5{_dmdpdR( za6RzC`XK`;3nmy3)3M`W#VS;~U_%(@6%7W_Jk`;5Sl?VK?-Brd3wXp~r!O6~-Z zmdG7*%0v9i$QATnkn#P&v%&rz-Ii=JS>kkDR=)L>=EmGj*v{vv{;may#`?#^rQknA zsoXRuz=>z!(w{UD>52%X;wB!6H+X_oC$w;lO0Rxd7>5qW1EN)#MjWBw_;?X~x0LW_ zf{4|C2UW*2a{sk-m#zv}%B<_jyb=wiU^Kf1r2)$6^!Bqmi0GK{5Y6z)nNc5a4B()( zT_BOV{z2{Fwd*dN2lj{}W!eB=C~j)_{TTSzpub%7xj*Z}vi0O*DF?trhc{=DxAJ^< z>b{7_%;$QqlFMl_-dL~9HHpH-KlcBb-JGLWuPS#ZKbnjrIS<3+XViP$!X=xRTq2{v zmcdc{CJb<}O@{atZoYm5_V-P@!`~>Oz@GWzw}Cgst8#mNCW5e%0VB<*yS>=fE5V%2&6p#2n_;%rz)vsEM<5p zw5W6lr4!bJW=z=a>Ts9x-g*klT6UaUI}zS4K^zYiDH6Dhuh6_$1t8I76uAY`l11IY zay~+l>Pqh*6i|m_wk*=5=5*SG zTT~8a#eT40ekjEuMI94y2x5S|ws%nRA^w&?vCda?P!>LMwv1=?3&38vNn?Yzl-H9) z9|P1C*Ym0b9MlLf1Vj1UySj1-vGbv>e6|8o z0mny*`g^b)*Vr&dh@miq)O0!W2F@8*^&`vv(o6oj9HrX-5RW4w05wDYC(I`nUy0xx zrmA|HsrC`x4^{xH5*OM`!=oh?fzKU>S88GLksyqft+ccQ;6 zd~MqrOa8&#=qTwcVj7}TY-zmY=Y&2FNM(u+T)6>Bi;2Q9ZgJH_8nlg#YQj6O7o?g1 z6W#mRR47F*Dnu8!NQ%M}RO^ruCkj^{vglgOJ{(m)SDxuy#!_rn4O4p2IFuZuL28lI=r#wbVF6?X1dXJ zl%clT&(_qP+thFFT%oxG4dzx=mS4V(vbnHovm&u%oEkD;+$&h0SPRRQrd70rc+)ZB z=P}j|(z$<~-><3bSu&GX07y;iftyL%Bjgs`$p?pfFVpnG;0kaceY$$lUa3Lt@je&P zmANgIENZ4+tS_3EU%AvGi+<7+y(vGHiv{3n{0xJw20)K^UUwMAjj1A&^AFKG{Xpu{ zn2kRrf|>p_B-iF-IMz!x<^KAfPyU0L+6OVUhh^&wYKUn1?uJwlC>P!^ej%hto;@$6 zpp0sOc)lL=wga{-yh_29t#X?I*`p`?oXMBK!^`tEX2Jc0bGPt=yW&g-@_zvd|4w>x z;ggMa3QNO8AxT*UTUecpw9G&)(8ySDh_np6so0U^7vb)O_>ih06~2M__c9|7llfvu z3G8~MlWM5YRY3&dEwDFmq=;CESyk8U(o`stw8&Chg!NJHG;oe$#}8rJET&sAV=K2X zwsU;s$ay*fAq%fnl6F{Tc0g5@3mhzx-@6~<2x1PXQX(dEbemlO+`yFA&Mz&?ZNmyZ zIiD)n7D}F!5+JSv&S;y&Jhh54ig*m_zgEdr9801!E*X@#ZDQTg3z)0^8bPs);m@Hq z+^~LXs_aJ*IfCFLJaGTD1k3_EqZ&W(2C!z|yK?bXJd|vO1{#B@0_{Up@7Nu(h1aK_ zzAx7C8EAvzjjUgeUJ6B5T1~O`Tkm-hV-;`P6d2fE(=VSHgMEuNk z#hJ|)Odg)sbc^=HF~`Pyfc7B4=hzGcz0yi?TmKr~@S82D4qa+)O@&KJbDrqu@&EV# z`TwFZb>T(Xdnwsn`kF!wTi^aS7=)Y5SfbaD(OB;(?MlQ|xHUPHfo&bTI`ZQvi(_}% z&np&@{4iND-jpTzWh&HPjR2bT(YQBRu=2P#J{>6z<99KXF2NZIqa)(#7;waR#`*Wc zNDl-H17DBb1w($_zkNs?NCq*}o^y4P+Di$P%${DVWA02sAd4s~NH!_+W#pv+(eE{% zf-Kx@q%S$c8k1{%@r^Z2uf@dK4ctb(NwO|ku37VjmYLdzS$yy_yy}(1H!a(qG6x0u zmg|FZ9FXGeCz+9WpX*b7xc zXp8$~Vn3`Xpkr%CtjX5t+yQ%HvFuJWxD4%!nfy|ks`k;7eR;q6cIR0Wyq_+KY?)y; zHCNUol-gbhKvPnwZTbGI-2OW;s5R6ZvG?|`tJbiVn}0fBy~V9q6QSXB-+2pMb)rpE zR48rV*}*omQdJFPX|1Xn#?ES0If{Yxs+SV+C6i6FewW_|cb?a7rwyjpJ zTm~ttHn(hp z;=wF$|3L!hIpzs@{-e`tP7Bhbbbr(}jHcA7AR1GXuRFWUAAcy+;q}F3PaYpU>hS+Ao{Ihd@F5P^BA$iG zhYuSc2K)Vg_U!OUxBt6%O!gm5zaLT+{0D48jKKgK{r}s0*XFiyZ9#N@&aYrOb1KP{ zCB=Dk@A0j!8MWh1dv_i#JDtp_UHc}GL`&>YBri$Xai2L=_ixPKn_n_(Ej$Q5Bmy)g z+i9TcoZYbi5Lf`#`_cZ57Wxk-|EyB~;fJSA|FfGWbi$Mb!;@!@YFFu#!1+Mr5DpH# z|3xJP^g0hLNir<(iGSw>9}1@%Ua?Ut_<&6>z`!R@5XPB zHw@sONwdM^`q29o1m6u^dC7@dtp{>&DsK3O2`*KE3rgUtdZNHR%obc}*6;6FS|Qtd(X<(g1#kfQ{}2GAJ8UMt14d@r>d3hSvs%$jsG3Dxn`HV z!(cE?X9JOAZXhEIetNlV>?^E30|e?%o1aU${QLT8=<)niemnL4IOom3s%A0ysPHZt zpohUvhv+-e*lppnFkk#xbUUbYVBsr2 zWY^~y&N~Bi{H(sW#7_hAKjJ;A=TNbg*B80Y4dKuu?}J)@-P@pP%jkg6^mja~@tZfb zUz*(xYN$kX*1QWgbwMWrBu`K4@G3hCG_91E5iI)*Xx%ig(mIUlRi+GZ`E7G>H)}@x ze|OfOS_}XG{CTbZ>q`(A9sj?J=J&E%AFKM`=P!SF;rPE@w43vP z0VSiwe`Uk3f9C{H=Lfl)mGrWLH&3#095r%>wQ{xC3i)F&_ah>4D+o6^^Yu$PU&QZ@&Rzqei(}>S^y<2lg4W@eqB9b zPElbBfBi>bzI;WQ$NBoQfByH!|ATy0fBI<;gN?5Dp8N|#NHXy(tDilm2^zo5UH7%0 zig>RY7r`gHJYZq6U^;)90d8HgGME7D(fIV_M(FY8F*RGA0}6^Fj>T`d5~OFvGP{scC5a*6mw!W zb8s5sY~t?p9X-OW^S+3RQG-dol;YZ9VgLdQkB|tmV%vOwG1hb!Q5!?bgoHsR0^198 zK#2zDQShglO{GfVDT#RQAA}oG0jl8(qyjS(QZ&da(q@ne@!#@*#K;sEumC&)4PZjqEigd^CiB5r{AiZM z7%fHKvqU<)Y&8M-J(Jm6@Jxo@5m81;=a|fR?9NF9e_i_F5&G6{8|(<#Y=9uN2Yi!= zUmnOISC=O$;&0=LX>Xs>jOecgItD4;?i@Xi;Z10rs}C33rpJq|;6$2#uU@9#GhRi5 zYhXmeco47QzmLz~>&fFEW5OU1dH{YHM>-V$t73znP)t;15ZolL1j#i&Y(`+iP<_2C z`Q795dM`hPzF`(FX#(_^%g>K4qG*#(Q zd90cm^pO~H36KrnG+Dd8{@0(zN$~sGo6Wt~?dA0+4~Uibdm3`Q#;GU|+vEFyeZk3H z_#k7zsbi<6$EUZ}%ci!l%0;SbpviXYhmra@EaIzjN^6`NG2oQ4;nUladv064=>cFX zbceX2_WqS^Xu%m97K~9$37`^5y~_GS5tK@oP~{v(j4_j;&?;S;!SZZ2kBw)PK6z!< zyRv;;soG=(7HCvo#4=^|Aeb3`!1(gX21FJGK2k|_*+jAK0n=|KXXSVdYoxY0cn#-! z#jdxto6Ph%HSUF1ttkrZ5SJ{Kv3UKMeoU2n_>WH?cqYM%^82G?Mx_Ucp}$3Vub_B> z1Y(9u=0VSW?+e}RFEX=^P-@U%%j!cs%Pnh9w}U}|0|Nl3l9d>SHk`+#SEA6EV?&tJ)nQT?-gzAb}nqZrZD%Ep8qkX<=JK@IEW z+8twhrL8gspX?lDKk}B%!tlK`Y8%lO-^= zh#=W&7NB;0wdQG6BYX?t-- ze>ewlpPi2}uifjTQ}2^VbmN`O@O*MiOX>@;Ozl%am?a6X*Cur}x+=82ft0_r0$N8L zCWo;!HRe@%N8}~$!XfnggU}`CU=FVVt#v!}!frJKCDIsn*W?F-5msxrV&+R1?o8^x zDZP(miYS+^plgz+<_Y|Ga#|&T+6m0qb}Bc1P<-B4Oe0GK)<|W|CeqgiusJ^8@%fG{ zY>&_9Na6zy>w+f6g&B~eL8V)N1TXKAO!UvHcWEE3Gg;|j?R#Hf$QN!~k`IQzGN(vz z2xfcRaR_EN2y-Vxie}AT+IlM;)9LZs5qb^$ z@B*IcG)5)V(b${NL|k|Rz={GCnq5v9tjZ-x$imOHg%>3=p0q}L_1eU8{da1J8f~9 zXgq`unxll$P!`9)bK{yHM4zT?Gb+%C{mHqFT&)0yV?_j+5Lxb&5<~ON?ejXhMPb`L zjsy3olktNxe6ps=hPfN!@jz_((H#d~h zYD=Xx0iX+T@;8vJrVDi5l-FY)74Cc7H~TG1_Dpu*M=L|7ZXhVJVQ00Be!*z2sx(xX z4on`EA=xoo+@R)|)Ye^h0@~2mXqA98Cv4K_tSDQVdFTD@T)M=VqcC30diCfsa3N}1 zy>y1+YF(z;^{Dm_92sguS=g)RQ<`QQ!Q8xHUB}GkxZYgBlSB9H?0mcpg>xLtrV8OSIQ8dnKiWNO8s*?K z8mu7+rK2>EbBkrRo~*GhVb#K}57d5dtU9=|2nG9eZl^MU{sQNS0_C@=H+V!E13lIf zV^2{>P9ZBk-e(3BrRo)?BzD+NpjB_Xg5A%)%3?bhD67d2Jg)2-P!rIp%OH&-s_C8i zei7`7r6Y$h&5$kGpK(OxqF}}WENU>vloUMJ6~Qr>uK;W#34MU>0D2A=`7(USEw9)3 zH*EpyVj^w9jO~xB#$=Nlq1~+X01)!K(deGM6{)KqxA~gAdyS*6^B;HqTT_H#wgBBl8vg1n;jo2B7eqW(I8KGdjbq{PxaVRiA>Ik`4zX`D(zArZZjLdkIMHDZhcz75_^zP?lw@R#w}1-D~n{Zd|&M?34nFuUksuB2C_;@EoPwMS4!7 zmXDcvjY*HOOn}K?00kNN@WRSDGMih^npA#+TRI6C-dL6T=PLETUYe^@vHx!$?Nq8b z#zPX`e`c-SrlrD-3;|Wha1ZDvZa5|)odyXDX`GRtsSP{AQx={WiP?40i{LM(kQ6~a z!i29pT$8|}-DTT$FW4;Ds_%rQvL>cBtkiDUs7_n3VxPVX*2xk~RxA^L#Qa2WfD4Ya zX&gRq_`uyW#^jiOm;#Kdf;z_9knJ6_IQ|F;^7}wE!=?-b>;N1^yWVlNbgv z%{7LV9RjzzS6$Ga_gF0U2`0Ja2@fkM44ARUxgRm)diE9nvtL4@PjGTB7R;noZ%@uB z{$7~-Ftg`8DolDwJo6rBzV9(M8^AvXf#~_9&5~^{xL6$o4yo+MV1%@58V)hO&=MN$ zd~4|fom_ZJI9Q@VwIe_|xPA)z=(Ru@<+T*hm|wEM@;!^e=BD{3fw&m0<#L45C*~jC5?8A zccT1L-8i3O{01S{rH6J>?)J9h_=r0p<=U9(OtX;ejNld`4{Id8JlV4#zrC5@R+iQ` z%vxQsD^>^(b{TrFw9BSdYzIP4xhr;Tk!#74VsysA#9S4B^0S>H9{b;cY^j|%54-}n zez_yjwNL(I7n=7ZaB6qQkaUBWV@MoB(%5VmlB{orL}(&x{cD7VPL!v>V>pgxNgTkj z;@`+MvZ&WPm4_!{{gl~Qs$eA!xPJt{C}V6)_qE#ZHsrrlG+sQCd&mitwqdG=Sh8=L zhl_X6);)-9x?4s2K}T!H#|0C)Ww?9$5zIlUW%xgl17I>m4e=Me-1Js#Z}R*7*pFP1 zF30q2gqLG_{z^=bS70=UU#xfU@QXdX9DeyL;TOvsg1U{>nr9 zXPpm_^8spY&IhQrD~Hv$3$nZA17y`bH}x{sj2jnu&{dcA(r_!E?|FBU|pV6oED!vU_3@P@vFGmSvCoM}^ zSNtS2SU3>(Wf;Vx%csK^!-El0%hAKe4hJku7VKbnG?vR~%Q}N?*QvSi!tJc! zIJVo%tkqNT@y?s*o?GifiC*BUI~F4EwD#N}HPzZ>)B3hllpLFRVRCF%#mV^qn*!z7 zEWv9Vo?t8_85JzYX2lQ~xB+5QB)hPU56!4IAH4fa4=zh-5VnyQ^a3u8pCTl?DWZQV#!DX}ZAwYE z$EPQFT9>Fr0hYz-5}Yn!d)slkgl+l;h-chYXgTG>g}<~!*6^ATMVtro2w_aWM&9Z1 zTOnQUyI3!WNgO8mo-m26)TD70h9e40)w_KO6Dv-QutPmv<5XcjF^%{B=;W1%|02${ z2)*t-?I+G}z3kt`qF5v%c=?K}ZsEfEevA1yP5tUgwC2D!=!r1vPj7BrJ4w_{5_g7A zAZ`oK>ur8r?6P%Qt@5K>3fD%$KOTu|BC8+B+rk&UCfC4@g9VKW0bRD}Jd6&%6`~E$ zw2}LNW!WOQrvm>+m7c!eSVER0X9lIf>O0}%nwHC#%0W(oZIXDZuebE43l;_0a=+MI zO20-kdDrO>Ne@wqp{OuC+Yt2=)_$S|JcRZSV2C^D!s(JX1tJpI!zhJXwUvPG_;!m}0;+FIb%rnX7x9yj*%x8X1>E%}m zr=e>=BjB+$1A&zes}6#)|H)S_zA1+jaxt$WOcflyN%QWqI|$zQ-tgz z$~PB^J$zy_ANXZe1dj_{!uzF3sE`4>j4ui<8Go&OQBJgUqUH9s<3!85BU&P1H0;Xh zgkfiMam2PrS(ord90Z*g6E_DoDuB0&n|n#=IDl_8;)*l&$w|jHn?QYt+T-j z)`At*5Uh?<*R#P@9OST%!#)oCd@tC?s{0>T_6*=Zt{VA2j#@OPPid_F@#%wzgeedY z{B899q5Ez}Wh(d-fsXeO*#NczK23p}Dv)W07xGIj z!Sa`Kwn66My(A5r2qgVNdiF1f?h#Wc^^na|zuV!N{CFL2h@)8#=Uhzbn zz%*Q0!EH9M>VkBzbwWI~9sfl`!m)M0JJ>qno?@sS{=sI;*<#y$m^il1!0E*8V*745 zUTht~XCFF_%_@AnSe=D<-z})UOPPmG%j;yI1xcU}<1F&}_I6qL_;ziwoh`Clp^LjB zab~?XR|zyX*XZup>Z-{xO7Dd18a?*RTnAN%c}A+I%@UxqnmYZ{_O|2nPxsDhYPAZp zJiM~4%g1Y?ti>VNv zSv9b1+Anevje$BaPWR?IL1hI|mq3rr#4hnF=WNTq_k|wh7wJ?iE;o&iBVQt8HGH3E z2!LNEuoxwX@NgnnAmV>v#>@hRKej^k6dYo+g_jIM_1dQ!5k(>>})ZojdIK(?F% zK`dHQxwm7}<~ex+dW9%gNfK%+kTPTGQ;0HrwpHxt(uTUUp`J?{`n_WFyOlQ7R;X1X zmtlXy*chu-hclK~i@$GRkdfev(ToW15&q#SPG|5-kZSt9H?PL0JM-@7r1C}p^&!_b z?bJY`#m{d~mfg7vqPho?W)fH^5V<8Rr>{$c&B<9#&T^D_t01$Mvm~kLgo%C^gSjED zz$x^%M#NIVCOfysZ82;}Lb7jGfy5cBN~YyUT^hcbeSo)tzmW!zKD!N#_f1ow-al-9 zd%0A5!{9s4q8 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 MquU1Z0uS Date: Mon, 25 Sep 2023 10:35:09 +0200 Subject: [PATCH 1002/1263] Release 0.0.3 golang-external-secrets --- golang-external-secrets/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index c8ef35ae..9b2c3b6d 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -3,7 +3,7 @@ description: A Helm chart to configure the golang-based external-secrets. keywords: - pattern name: golang-external-secrets -version: 0.0.2 +version: 0.0.3 dependencies: - name: external-secrets version: "0.9.5" From fc06ec7368ff7493821f4de0f6d34dfde45d24d4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 27 Sep 2023 07:59:57 +0200 Subject: [PATCH 1003/1263] Release 0.0.3 clustergroup --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 031b6ff0..98e44d9d 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.0.2 +version: 0.0.3 From edab5060c1605227e6be8e0d274c31063166d825 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 29 Sep 2023 08:17:28 +0000 Subject: [PATCH 1004/1263] Allow custom templating in .extraValueFiles --- clustergroup/templates/plumbing/applications.yaml | 6 +++--- clustergroup/values.yaml | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index c09e3c8c..227efab3 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -59,8 +59,8 @@ spec: ignoreMissingValueFiles: true valueFiles: {{- include "clustergroup.app.globalvalues.valuefiles" $ | nindent 12 }} - {{- range .extraValueFiles }} - - {{ . | quote }} + {{- range $valueFile := .extraValueFiles }} + - {{ tpl $valueFile $.Values.global | quote }} {{- end }} {{- if .useGeneratorValues }} values: |- @@ -212,7 +212,7 @@ spec: valueFiles: {{- include "clustergroup.app.globalvalues.valuefiles" $ | nindent 6 }} {{- range $valueFile := .extraValueFiles }} - - {{ $valueFile | quote }} + - {{ tpl $valueFile $.Values.global | quote }} {{- end }} parameters: {{- include "clustergroup.app.globalvalues.helmparameters" $ | nindent 8 }} diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index b63e8cc2..71ad088d 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -8,6 +8,11 @@ global: installPlanApproval: Automatic applicationRetryLimit: 20 + # This is deeply unpleasant but makes values files easier to template + Template: + BasePath: "global-vars.yml" + Name: "global-vars" + enabled: "all" # Note that sometimes changing helm values might require a hard refresh (https://github.com/helm/helm/issues/3486) From ba686e2a6b58920b658d81b16a92d340eff9555b Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 29 Sep 2023 09:05:10 +0000 Subject: [PATCH 1005/1263] Support pattern-wide templated value files --- clustergroup/templates/plumbing/applications.yaml | 15 ++++++++++++--- clustergroup/values.schema.json | 4 ++++ clustergroup/values.yaml | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 227efab3..a54ed99f 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -59,8 +59,11 @@ spec: ignoreMissingValueFiles: true valueFiles: {{- include "clustergroup.app.globalvalues.valuefiles" $ | nindent 12 }} + {{- range $valueFile := $.Values.clusterGroup.sharedValueFiles }} + - {{ tpl $valueFile $ | quote }} + {{- end }} {{- range $valueFile := .extraValueFiles }} - - {{ tpl $valueFile $.Values.global | quote }} + - {{ tpl $valueFile $ | quote }} {{- end }} {{- if .useGeneratorValues }} values: |- @@ -147,8 +150,11 @@ spec: ignoreMissingValueFiles: true valueFiles: {{- include "clustergroup.app.globalvalues.prefixedvaluefiles" $ | nindent 8 }} + {{- range $valueFile := $.Values.clusterGroup.sharedValueFiles }} + - {{ tpl $valueFile $ | quote }} + {{- end }} {{- range $valueFile := .extraValueFiles }} - - {{ $valueFile | quote }} + - {{ tpl $valueFile $ | quote }} {{- end }} parameters: {{- include "clustergroup.app.globalvalues.helmparameters" $ | nindent 8 }} @@ -211,8 +217,11 @@ spec: ignoreMissingValueFiles: true valueFiles: {{- include "clustergroup.app.globalvalues.valuefiles" $ | nindent 6 }} + {{- range $valueFile := $.Values.clusterGroup.sharedValueFiles }} + - {{ tpl $valueFile $ | quote }} + {{- end }} {{- range $valueFile := .extraValueFiles }} - - {{ tpl $valueFile $.Values.global | quote }} + - {{ tpl $valueFile $ | quote }} {{- end }} parameters: {{- include "clustergroup.app.globalvalues.helmparameters" $ | nindent 8 }} diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 07f8e717..4b94bf26 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -234,6 +234,10 @@ "type": "boolean", "description": "If set to true the values is used to identify whether this is the hub cluster or an edge/spoke cluster configuration." }, + "sharedValueFiles": { + "type": "array", + "description": "Templated value file paths." + }, "namespaces": { "type": "array", "description": "This is the array of namespaces that the VP framework will create. In addition, operator groups will also be created for each namespace.", diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 71ad088d..36e02636 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -20,6 +20,7 @@ clusterGroup: name: example isHubCluster: true targetCluster: in-cluster + sharedValueFiles: [] imperative: jobs: [] From 79453fd6cc09c6be22ff0b682095cd3d804aa979 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 29 Sep 2023 08:25:07 +0000 Subject: [PATCH 1006/1263] Update tests --- tests/clustergroup-industrial-edge-factory.expected.yaml | 3 +++ tests/clustergroup-industrial-edge-hub.expected.yaml | 3 +++ tests/clustergroup-medical-diagnosis-hub.expected.yaml | 3 +++ tests/clustergroup-naked.expected.yaml | 3 +++ tests/clustergroup-normal.expected.yaml | 3 +++ 5 files changed, 15 insertions(+) diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 6ff3a848..dfed17f2 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -135,6 +135,9 @@ data: targetCluster: in-cluster enabled: all global: + Template: + BasePath: global-vars.yml + Name: global-vars clusterDomain: region.example.com extraValueFiles: [] git: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 3f5207ab..75683c47 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -296,6 +296,9 @@ data: targetCluster: in-cluster enabled: all global: + Template: + BasePath: global-vars.yml + Name: global-vars clusterDomain: region.example.com extraValueFiles: [] git: diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 4ffbd77d..561feb8c 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -283,6 +283,9 @@ data: targetCluster: in-cluster enabled: all global: + Template: + BasePath: global-vars.yml + Name: global-vars clusterDomain: region.example.com extraValueFiles: [] git: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 7f167c74..95a8a410 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -65,6 +65,9 @@ data: targetCluster: in-cluster enabled: all global: + Template: + BasePath: global-vars.yml + Name: global-vars extraValueFiles: [] options: applicationRetryLimit: 20 diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 4767db6c..5780ef8b 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -192,6 +192,9 @@ data: targetCluster: in-cluster enabled: all global: + Template: + BasePath: global-vars.yml + Name: global-vars clusterDomain: region.example.com extraValueFiles: [] git: From 2c5974c61f8691d7b676525194674426be30e878 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 29 Sep 2023 08:35:39 +0000 Subject: [PATCH 1007/1263] Pass in platform and ocp version as charts would expect --- Makefile | 16 ++- tests/acm-industrial-edge-hub.expected.yaml | 2 +- tests/acm-medical-diagnosis-hub.expected.yaml | 2 +- tests/acm-normal.expected.yaml | 4 +- ...roup-industrial-edge-factory.expected.yaml | 12 +- ...tergroup-industrial-edge-hub.expected.yaml | 72 +++++++--- ...rgroup-medical-diagnosis-hub.expected.yaml | 132 ++++++++++++------ tests/clustergroup-normal.expected.yaml | 22 ++- 8 files changed, 185 insertions(+), 77 deletions(-) diff --git a/Makefile b/Makefile index ad61cafe..f0593552 100644 --- a/Makefile +++ b/Makefile @@ -150,10 +150,18 @@ argo-healthcheck: ## Checks if all argo applications are synced CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | grep -v examples | sed -e 's/.\///') # Section related to tests and linting -TEST_OPTS= -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" \ - --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" \ - --set global.namespace="pattern-namespace" --set global.hubClusterDomain=apps.hub.example.com --set global.localClusterDomain=apps.region.example.com --set global.clusterDomain=region.example.com\ - --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" +TEST_OPTS= -f values-global.yaml \ + --set global.repoURL="https://github.com/pattern-clone/mypattern" \ + --set main.git.repoURL="https://github.com/pattern-clone/mypattern" \ + --set main.git.revision=main --set global.pattern="mypattern" \ + --set global.namespace="pattern-namespace" \ + --set global.hubClusterDomain=apps.hub.example.com \ + --set global.localClusterDomain=apps.region.example.com \ + --set global.clusterDomain=region.example.com \ + --set global.clusterVersion="4.12" \ + --set global.clusterPlatform=aws \ + --set "clusterGroup.imperative.jobs[0].name"="test" \ + --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" PATTERN_OPTS=-f common/examples/values-example.yaml EXECUTABLES=git helm oc ansible diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 444b833c..a474b4e3 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -234,7 +234,7 @@ spec: - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' - name: global.clusterPlatform - value: + value: aws - name: clusterGroup.name value: factory - name: clusterGroup.isHubCluster diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index f79e013b..f54648fe 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -225,7 +225,7 @@ spec: - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' - name: global.clusterPlatform - value: + value: aws - name: clusterGroup.name value: region-one - name: clusterGroup.isHubCluster diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 900cc291..0429824d 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -628,7 +628,7 @@ spec: - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' - name: global.clusterPlatform - value: + value: aws - name: clusterGroup.name value: acm-edge - name: clusterGroup.isHubCluster @@ -722,7 +722,7 @@ spec: - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' - name: global.clusterPlatform - value: + value: aws - name: clusterGroup.name value: acm-provision-edge - name: clusterGroup.isHubCluster diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index dfed17f2..42d0c975 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -139,6 +139,8 @@ data: BasePath: global-vars.yml Name: global-vars clusterDomain: region.example.com + clusterPlatform: aws + clusterVersion: "4.12" extraValueFiles: [] git: account: hybrid-cloud-patterns @@ -415,7 +417,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-factory.yaml" + - "/values-factory.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-factory.yaml" + - "/values-4.12-factory.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -428,9 +434,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 75683c47..5a112b6e 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -300,6 +300,8 @@ data: BasePath: global-vars.yml Name: global-vars clusterDomain: region.example.com + clusterPlatform: aws + clusterVersion: "4.12" extraValueFiles: [] git: account: hybrid-cloud-patterns @@ -688,7 +690,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-datacenter.yaml" + - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -701,9 +707,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -745,7 +751,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-datacenter.yaml" + - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -758,9 +768,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -793,7 +803,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-datacenter.yaml" + - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -806,9 +820,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -841,7 +855,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-datacenter.yaml" + - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -854,9 +872,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -919,7 +937,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-datacenter.yaml" + - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -932,9 +954,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -967,7 +989,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-datacenter.yaml" + - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -980,9 +1006,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -1042,7 +1068,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-datacenter.yaml" + - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1055,9 +1085,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 561feb8c..0f754109 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -287,6 +287,8 @@ data: BasePath: global-vars.yml Name: global-vars clusterDomain: region.example.com + clusterPlatform: aws + clusterVersion: "4.12" extraValueFiles: [] git: account: hybrid-cloud-patterns @@ -633,7 +635,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -646,9 +652,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -681,7 +687,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -694,9 +704,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -729,7 +739,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -742,9 +756,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -777,7 +791,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -790,9 +808,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -825,7 +843,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -838,9 +860,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -873,7 +895,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -886,9 +912,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -921,7 +947,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -934,9 +964,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -969,7 +999,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -982,9 +1016,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -1035,7 +1069,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1048,9 +1086,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -1083,7 +1121,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1096,9 +1138,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -1131,7 +1173,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1144,9 +1190,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -1188,7 +1234,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1201,9 +1251,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -1245,7 +1295,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-hub.yaml" + - "/values-4.12-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1258,9 +1312,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 5780ef8b..551af959 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -196,6 +196,8 @@ data: BasePath: global-vars.yml Name: global-vars clusterDomain: region.example.com + clusterPlatform: aws + clusterVersion: "4.12" extraValueFiles: [] git: account: hybrid-cloud-patterns @@ -543,7 +545,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-example.yaml" + - "/values-example.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-example.yaml" + - "/values-4.12-example.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -556,9 +562,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain @@ -600,7 +606,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-example.yaml" + - "/values-example.yaml" + - "/values-aws.yaml" + - "/values-aws-4.12.yaml" + - "/values-aws-example.yaml" + - "/values-4.12-example.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -613,9 +623,9 @@ spec: - name: global.clusterDomain value: region.example.com - name: global.clusterVersion - value: "" + value: "4.12" - name: global.clusterPlatform - value: "" + value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com - name: global.localClusterDomain From 491d5218221a2deada15299bbe0f839c40d4a211 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 29 Sep 2023 09:04:43 +0000 Subject: [PATCH 1008/1263] Add test for value file template expansion --- examples/values-example.yaml | 5 +++++ .../clustergroup-industrial-edge-factory.expected.yaml | 1 + tests/clustergroup-industrial-edge-hub.expected.yaml | 1 + tests/clustergroup-medical-diagnosis-hub.expected.yaml | 1 + tests/clustergroup-naked.expected.yaml | 1 + tests/clustergroup-normal.expected.yaml | 10 ++++++++++ 6 files changed, 19 insertions(+) diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 4035c431..2a224b62 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -11,6 +11,9 @@ clusterGroup: name: example #insecureUnsealVaultInsideCluster: false isHubCluster: true + sharedValueFiles: + - /values/{{ .Values.global.clusterPlatform }}.yaml + - /values/{{ .Values.global.clusterVersion }}.yaml namespaces: - open-cluster-management: @@ -63,6 +66,8 @@ clusterGroup: namespace: application-ci project: datacenter path: charts/datacenter/pipelines + extraValueFiles: + - /values/{{ .Values.global.clusterVersion }}/{{ .Values.global.clusterPlatform }}.yaml imperative: namespace: imperative diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 42d0c975..8c37450e 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -115,6 +115,7 @@ data: - manuela-factory-ml-workspace projects: - factory + sharedValueFiles: [] subscriptions: - channel: stable name: opendatahub-operator diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 5a112b6e..24f37053 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -257,6 +257,7 @@ data: - production-datalake - golang-external-secrets - vault + sharedValueFiles: [] subscriptions: acm: channel: release-2.6 diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 0f754109..eb367bce 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -260,6 +260,7 @@ data: projects: - hub - medical-diagnosis + sharedValueFiles: [] subscriptions: amq-streams: channel: stable diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 95a8a410..1ec01860 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -61,6 +61,7 @@ data: name: example namespaces: [] projects: [] + sharedValueFiles: [] subscriptions: {} targetCluster: in-cluster enabled: all diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 551af959..f96708a9 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -81,6 +81,8 @@ data: path: common/acm project: datacenter pipe: + extraValueFiles: + - /values/4.12/aws.yaml name: pipelines namespace: application-ci path: charts/datacenter/pipelines @@ -175,6 +177,9 @@ data: - excludes-ci projects: - datacenter + sharedValueFiles: + - /values/aws.yaml + - /values/4.12.yaml subscriptions: acm: channel: release-2.4 @@ -550,6 +555,8 @@ spec: - "/values-aws-4.12.yaml" - "/values-aws-example.yaml" - "/values-4.12-example.yaml" + - "/values/aws.yaml" + - "/values/4.12.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -611,6 +618,9 @@ spec: - "/values-aws-4.12.yaml" - "/values-aws-example.yaml" - "/values-4.12-example.yaml" + - "/values/aws.yaml" + - "/values/4.12.yaml" + - "/values/4.12/aws.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL From 7cda9c48f9861563dada519de40d89134e0eb683 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 29 Sep 2023 09:29:38 +0000 Subject: [PATCH 1009/1263] Drop the Template.{Name,BasePath} hack due to problems with the imperative configmap --- clustergroup/values.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 36e02636..e9720d20 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -8,10 +8,6 @@ global: installPlanApproval: Automatic applicationRetryLimit: 20 - # This is deeply unpleasant but makes values files easier to template - Template: - BasePath: "global-vars.yml" - Name: "global-vars" enabled: "all" From 8d84b0abaa14a11d9aa5df87d0ecdbee2f513925 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 2 Oct 2023 11:30:55 +0200 Subject: [PATCH 1010/1263] Fix up tests after last PR --- tests/clustergroup-industrial-edge-factory.expected.yaml | 3 --- tests/clustergroup-industrial-edge-hub.expected.yaml | 3 --- tests/clustergroup-medical-diagnosis-hub.expected.yaml | 3 --- tests/clustergroup-naked.expected.yaml | 3 --- tests/clustergroup-normal.expected.yaml | 3 --- 5 files changed, 15 deletions(-) diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 8c37450e..3eed3296 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -136,9 +136,6 @@ data: targetCluster: in-cluster enabled: all global: - Template: - BasePath: global-vars.yml - Name: global-vars clusterDomain: region.example.com clusterPlatform: aws clusterVersion: "4.12" diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 24f37053..12e1ee28 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -297,9 +297,6 @@ data: targetCluster: in-cluster enabled: all global: - Template: - BasePath: global-vars.yml - Name: global-vars clusterDomain: region.example.com clusterPlatform: aws clusterVersion: "4.12" diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index eb367bce..9efc2431 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -284,9 +284,6 @@ data: targetCluster: in-cluster enabled: all global: - Template: - BasePath: global-vars.yml - Name: global-vars clusterDomain: region.example.com clusterPlatform: aws clusterVersion: "4.12" diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 1ec01860..75359902 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -66,9 +66,6 @@ data: targetCluster: in-cluster enabled: all global: - Template: - BasePath: global-vars.yml - Name: global-vars extraValueFiles: [] options: applicationRetryLimit: 20 diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index f96708a9..d6886bed 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -197,9 +197,6 @@ data: targetCluster: in-cluster enabled: all global: - Template: - BasePath: global-vars.yml - Name: global-vars clusterDomain: region.example.com clusterPlatform: aws clusterVersion: "4.12" From 61dd6e7af45581131e4bfe6c928158639bb6ea09 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 2 Oct 2023 14:10:19 +0200 Subject: [PATCH 1011/1263] Release clustergroup v0.0.4 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 98e44d9d..1256786b 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.0.3 +version: 0.0.4 From 2c34456e40e1e1f423d3ceced05554ff98452b05 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 2 Feb 2023 11:06:30 +0100 Subject: [PATCH 1012/1263] Add --pull=newer when running the container From https://docs.podman.io/en/latest/markdown/podman-run.1.html#pull-policy Pull image policy. The default is missing. always: Always pull the image and throw an error if the pull fails. missing: Pull the image only if it could not be found in the local containers storage. Throw an error if no image could be found and the pull fails. never: Never pull the image but use the one from the local containers storage. Throw an error if no image could be found. newer: Pull if the image on the registry is newer than the one in the local containers storage. An image is considered to be newer when the digests are different. Comparing the time stamps is prone to errors. Pull errors are suppressed if a local image was found. Switching to pull=newer will allow us to keep this image uptodate for users without erroring out if podman cannot check or pull a new image (i.e. we'd keep running the local one) --- scripts/pattern-util.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index e02776df..bc833866 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -27,7 +27,7 @@ fi # Do not quote the ${KUBECONF_ENV} below, otherwise we will pass '' to podman # which will be confused -podman run -it --rm \ +podman run -it --rm --pull=newer \ --security-opt label=disable \ -e EXTRA_HELM_OPTS \ -e KUBECONFIG \ From c641fded5f60481271559b5cfb6bf29abac507f1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 2 Oct 2023 19:46:17 +0200 Subject: [PATCH 1013/1263] Allow imperative to be nil Tested by commenting out the whole `imperative` section in values-hub and deploying MCG. --- clustergroup/templates/imperative/job.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index b9437c3f..cb092649 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -1,6 +1,6 @@ {{- if not (eq .Values.enabled "plumbing") }} {{/* Define this if needed (jobs defined */}} -{{- if (gt (len $.Values.clusterGroup.imperative.jobs) 0) -}} +{{- if (and $.Values.clusterGroup.imperative (gt (len $.Values.clusterGroup.imperative.jobs) 0)) -}} --- apiVersion: batch/v1 kind: CronJob @@ -66,4 +66,4 @@ spec: name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} restartPolicy: Never {{- end }} -{{- end }} \ No newline at end of file +{{- end }} From 445128d7de6a872666e039b9d4bf5ebf978ef967 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 13 Oct 2023 10:41:37 -0600 Subject: [PATCH 1014/1263] Adding key to exclude target namespace in operatorgroup --- clustergroup/templates/core/operatorgroup.yaml | 3 ++- clustergroup/values.schema.json | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index 66774fa6..d25dfae4 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -15,7 +15,6 @@ spec: targetNamespaces: - {{ $k }} {{- end }}{{- /* range $k, $v := $ns */}} - {{- else if kindIs "string" $ns }} apiVersion: operators.coreos.com/v1 kind: OperatorGroup @@ -23,8 +22,10 @@ metadata: name: {{ . }}-operator-group namespace: {{ . }} spec: + {{- if not ( has . $.Values.clusterGroup.operatorgroupExcludeTargetNS ) }} targetNamespaces: - {{ . }} + {{- end }} {{- end }} {{- /* if kindIs "string" $ns */}} --- {{- end }} {{- /* if or (empty $.Values.clusterGroup.operatorgroupExcludes) (not (has . $.Values.clusterGroup.operatorgroupExcludes)) */}} diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 4b94bf26..e8d93561 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -266,6 +266,13 @@ "type": "string" } }, + "operatorgroupExcludeTargetNS": { + "type": "array", + "description": "List of namespaces to exclude the target namespace.", + "items": { + "type": "string" + } + }, "hostedSite": { "type": "object", "items": { From 0791109b73b766474a122db2422bc232d06be31e Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 13 Oct 2023 11:03:51 -0600 Subject: [PATCH 1015/1263] Added target namespace logic to namespace map case --- clustergroup/templates/core/operatorgroup.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index d25dfae4..29c542c4 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -12,8 +12,10 @@ metadata: name: {{ $k }}-operator-group namespace: {{ $k }} spec: + {{- if not ( has $k $.Values.clusterGroup.operatorgroupExcludeTargetNS ) }} targetNamespaces: - {{ $k }} + {{- end }} {{- end }}{{- /* range $k, $v := $ns */}} {{- else if kindIs "string" $ns }} apiVersion: operators.coreos.com/v1 From 709e3216bbdd99781894610e791b221929371d87 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Mon, 16 Oct 2023 09:53:51 -0600 Subject: [PATCH 1016/1263] Changed description in schema --- clustergroup/values.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index e8d93561..863b8489 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -268,7 +268,7 @@ }, "operatorgroupExcludeTargetNS": { "type": "array", - "description": "List of namespaces to exclude the target namespace.", + "description": "Specify the list of namespaces where the target namespace field in the corresponding operatorgroup object should be excluded.", "items": { "type": "string" } From c0d3e9d27f2bbc5613512f34b333d8e167cf3b76 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Mon, 16 Oct 2023 10:03:58 -0600 Subject: [PATCH 1017/1263] Added example to operatorgroupExcludeTargetNS --- examples/values-example.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 2a224b62..fac8d0ee 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -29,6 +29,8 @@ clusterGroup: operatorgroupExcludes: - excludes-ci + operatorgroupExcludeTargetNS: + - application-ci subscriptions: acm: From abb0b2d634706e782ac47a8cca7ec998481ff6ee Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Mon, 16 Oct 2023 10:11:19 -0600 Subject: [PATCH 1018/1263] Fixing CI tests --- tests/clustergroup-normal.expected.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index d6886bed..780f5412 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -173,6 +173,8 @@ data: openshift.io/node-selector: "" - application-ci - excludes-ci + operatorgroupExcludeTargetNS: + - application-ci operatorgroupExcludes: - excludes-ci projects: @@ -1051,8 +1053,6 @@ metadata: name: application-ci-operator-group namespace: application-ci spec: - targetNamespaces: - - application-ci --- # Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 From 3740cb53f2f7648dfe5e6b0018767322e68e6654 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 16 Oct 2023 23:16:24 +0200 Subject: [PATCH 1019/1263] Push localClusterName to remote clusters too Use the same approach e use in the operator to obtain the short-name of the cluster https://github.com/validatedpatterns/patterns-operator/blob/main/controllers/argo.go#L77 Tested on MCG and observed that values.localClusterName was `sno1` on the hub and `sno2` on the regional cluster. --- acm/templates/policies/application-policies.yaml | 2 ++ tests/acm-industrial-edge-hub.expected.yaml | 2 ++ tests/acm-medical-diagnosis-hub.expected.yaml | 2 ++ tests/acm-normal.expected.yaml | 4 ++++ 4 files changed, 10 insertions(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index d854ae72..e2e717ca 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -69,6 +69,8 @@ spec: # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}' + - name: global.localClusterName + value: '{{ `{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}` }}' - name: global.clusterPlatform value: {{ $.Values.global.clusterPlatform }} - name: clusterGroup.name diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index a474b4e3..6bb30bac 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -233,6 +233,8 @@ spec: # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + - name: global.localClusterName + value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform value: aws - name: clusterGroup.name diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index f54648fe..2361be7a 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -224,6 +224,8 @@ spec: # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + - name: global.localClusterName + value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform value: aws - name: clusterGroup.name diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 0429824d..a83284bb 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -627,6 +627,8 @@ spec: # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + - name: global.localClusterName + value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform value: aws - name: clusterGroup.name @@ -721,6 +723,8 @@ spec: # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + - name: global.localClusterName + value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform value: aws - name: clusterGroup.name From 9ebdf711ecbb24fdb5860d629600f238cdac3c27 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Wed, 18 Oct 2023 03:33:21 +0000 Subject: [PATCH 1020/1263] Preview a chart based on the current k8s cluster --- Makefile | 3 +++ scripts/preview.sh | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100755 scripts/preview.sh diff --git a/Makefile b/Makefile index f0593552..73cb99ed 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,9 @@ help: ## This help message show: ## show the starting template without installing it helm template common/operator-install/ --name-template $(NAME) $(HELM_OPTS) +preview-%: + common/scripts/preview.sh hub $* $(TARGET_REPO) $(TARGET_BRANCH) + .PHONY: operator-deploy operator-deploy operator-upgrade: validate-prereq validate-origin validate-cluster ## runs helm install @set -e -o pipefail diff --git a/scripts/preview.sh b/scripts/preview.sh new file mode 100755 index 00000000..610ee406 --- /dev/null +++ b/scripts/preview.sh @@ -0,0 +1,31 @@ +#!/bin/bash -x + +SITE=$1; shift +APP=$1; shift +GIT_REPO=$1; shift +GIT_BRANCH=$1; shift + + +export APP=config-demo; +chart=$(yq ".clusterGroup.applications.$APP.path" values-$SITE.yaml) +namespace=$(yq ".clusterGroup.applications.$APP.namespace" values-$SITE.yaml) +pattern=$(yq ".global.pattern" values-global.yaml) + +platform=$(oc get Infrastructure.config.openshift.io/cluster -o jsonpath='{.spec.platformSpec.type}') +ocpversion=$(oc get clusterversion/version -o jsonpath='{.status.desired.version}' | awk -F. '{print $1"."$2}') +domain=$(oc get Ingress.config.openshift.io/cluster -o jsonpath='{.spec.domain}' | sed 's/^apps.//') + +CLUSTER_OPTS="" +CLUSTER_OPTS="$CLUSTER_OPTS --set global.pattern=$pattern" +CLUSTER_OPTS="$CLUSTER_OPTS --set global.repoURL=$GIT_REPO" +CLUSTER_OPTS="$CLUSTER_OPTS --set main.git.repoURL=$GIT_REPO" +CLUSTER_OPTS="$CLUSTER_OPTS --set main.git.revision=$GIT_BRANCH" +CLUSTER_OPTS="$CLUSTER_OPTS --set global.namespace=$namespace" +CLUSTER_OPTS="$CLUSTER_OPTS --set global.hubClusterDomain=apps.$domain" +CLUSTER_OPTS="$CLUSTER_OPTS --set global.localClusterDomain=apps.$domain" +CLUSTER_OPTS="$CLUSTER_OPTS --set global.clusterDomain=$domain" +CLUSTER_OPTS="$CLUSTER_OPTS --set global.clusterVersion=$ocpversion" +CLUSTER_OPTS="$CLUSTER_OPTS --set global.clusterPlatform=$platform" + + +helm template $chart --name-template ${APP} -n ${namespace} ${CLUSTER_OPTS} From 27ffad5f258489eec9cc8a9b9577a10d9a7c02fa Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Thu, 19 Oct 2023 01:22:20 +0000 Subject: [PATCH 1021/1263] Handle explcit value files --- scripts/preview.sh | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/scripts/preview.sh b/scripts/preview.sh index 610ee406..5cdcd551 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -15,6 +15,16 @@ platform=$(oc get Infrastructure.config.openshift.io/cluster -o jsonpath='{.spe ocpversion=$(oc get clusterversion/version -o jsonpath='{.status.desired.version}' | awk -F. '{print $1"."$2}') domain=$(oc get Ingress.config.openshift.io/cluster -o jsonpath='{.spec.domain}' | sed 's/^apps.//') +function replaceGlobals() { + output=$( echo $1 | sed -e 's/ //g' -e 's/\$//g' -e s@^-@@g -e s@\'@@g ) + + output=$(echo $output | sed "s@{{.Values.global.clusterPlatform}}@${platform}@g") + output=$(echo $output | sed "s@{{.Values.global.clusterVersion}}@${ocpversion}@g") + output=$(echo $output | sed "s@{{.Values.global.clusterDomain}}@${domain}@g") + + echo $output +} + CLUSTER_OPTS="" CLUSTER_OPTS="$CLUSTER_OPTS --set global.pattern=$pattern" CLUSTER_OPTS="$CLUSTER_OPTS --set global.repoURL=$GIT_REPO" @@ -28,4 +38,26 @@ CLUSTER_OPTS="$CLUSTER_OPTS --set global.clusterVersion=$ocpversion" CLUSTER_OPTS="$CLUSTER_OPTS --set global.clusterPlatform=$platform" -helm template $chart --name-template ${APP} -n ${namespace} ${CLUSTER_OPTS} +sharedValueFiles=$(yq ".clusterGroup.sharedValueFiles" values-$SITE.yaml) +appValueFiles=$(yq ".clusterGroup.applications.$APP.extraValueFiles" values-$SITE.yaml) + +VALUE_FILES="" +IFS=$'\n' +for line in $sharedValueFiles; do + if [ $line != "null" ]; then + file=$(replaceGlobals $line) + VALUE_FILES="$VALUE_FILES -f $PWD$file" + fi +done + +for line in $appValueFiles; do + if [ $line != "null" ]; then + file=$(replaceGlobals $line) + VALUE_FILES="$VALUE_FILES -f $PWD$file" + fi +done + +cmd="helm template $chart --name-template ${APP} -n ${namespace} ${VALUE_FILES} ${CLUSTER_OPTS}" +eval $cmd + + From a8417e7cb51234610f55f17d9d3931499f1bf59d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 19 Oct 2023 12:16:21 +0200 Subject: [PATCH 1022/1263] Update CRD for the operator --- ...ops.hybrid-cloud-patterns.io_patterns.yaml | 51 +++++++++---------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml index 330e0222..ce5b2c07 100644 --- a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml +++ b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml @@ -1,9 +1,9 @@ +--- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.11.4 - creationTimestamp: null name: patterns.gitops.hybrid-cloud-patterns.io spec: group: gitops.hybrid-cloud-patterns.io @@ -72,30 +72,10 @@ spec: type: array gitOpsSpec: properties: - manualApproval: - description: 'Require manual confirmation before installing and - upgrading operators. Default: False' - type: boolean manualSync: description: 'Require manual intervention before Argo will sync new content. Default: False' type: boolean - operatorCSV: - description: Specific version of openshift-gitops to deploy. Requires - UseCSV=True - type: string - operatorChannel: - description: 'Channel to deploy openshift-gitops from. Default: - gitops-1.8' - type: string - operatorSource: - description: 'Source to deploy openshift-gitops from. Default: - redhat-operators' - type: string - useCSV: - description: 'Dangerous. Force a specific version to be installed. - Default: False' - type: boolean type: object gitSpec: properties: @@ -163,8 +143,31 @@ spec: status: description: PatternStatus defines the observed state of Pattern properties: + analyticsSent: + type: boolean + analyticsUUID: + type: string appClusterDomain: type: string + applications: + items: + description: PatternApplicationInfo defines the Applications Status + for the Pattern. This structure is part of the PatternStatus as + an array The Application Status will be included as part of the + Observed state of Pattern + properties: + healthMessage: + type: string + healthStatus: + type: string + name: + type: string + namespace: + type: string + syncStatus: + type: string + type: object + type: array clusterDomain: type: string clusterID: @@ -218,9 +221,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: null - storedVersions: null From 02710dca2aa676330a65c4010e130f95ab938efd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 19 Oct 2023 12:17:41 +0200 Subject: [PATCH 1023/1263] Add a README containing the CRD update instructions --- operator-install/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 operator-install/README.md diff --git a/operator-install/README.md b/operator-install/README.md new file mode 100644 index 00000000..a333860e --- /dev/null +++ b/operator-install/README.md @@ -0,0 +1,4 @@ +# Update CRD + +In order to update the CRD, copy the following file from the last released patterns operator version: +`cp -v patterns-operator/config/crd/bases/gitops.hybrid-cloud-patterns.io_patterns.yaml ./crds/` From e2a836c837e7690241f6e5f9b0ae45d4f480384e Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 19 Oct 2023 11:10:44 -0500 Subject: [PATCH 1024/1263] Add ability to read overrides --- scripts/preview.sh | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/scripts/preview.sh b/scripts/preview.sh index 5cdcd551..199facf6 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -5,8 +5,6 @@ APP=$1; shift GIT_REPO=$1; shift GIT_BRANCH=$1; shift - -export APP=config-demo; chart=$(yq ".clusterGroup.applications.$APP.path" values-$SITE.yaml) namespace=$(yq ".clusterGroup.applications.$APP.namespace" values-$SITE.yaml) pattern=$(yq ".global.pattern" values-global.yaml) @@ -25,6 +23,17 @@ function replaceGlobals() { echo $output } +function getOverrides() { + overrides='' + overrides=$( yq ".clusterGroup.applications.$APP.overrides[]" "values-$SITE.yaml" ) + overrides=$( echo "$overrides" | tr -d '\n' ) + overrides=$( echo "$overrides" | sed -e 's/name:/ --set/g; s/value: /=/g' ) + if [ -n "$overrides" ]; then + echo "$overrides" + fi +} + + CLUSTER_OPTS="" CLUSTER_OPTS="$CLUSTER_OPTS --set global.pattern=$pattern" CLUSTER_OPTS="$CLUSTER_OPTS --set global.repoURL=$GIT_REPO" @@ -32,14 +41,15 @@ CLUSTER_OPTS="$CLUSTER_OPTS --set main.git.repoURL=$GIT_REPO" CLUSTER_OPTS="$CLUSTER_OPTS --set main.git.revision=$GIT_BRANCH" CLUSTER_OPTS="$CLUSTER_OPTS --set global.namespace=$namespace" CLUSTER_OPTS="$CLUSTER_OPTS --set global.hubClusterDomain=apps.$domain" -CLUSTER_OPTS="$CLUSTER_OPTS --set global.localClusterDomain=apps.$domain" +CLUSTER_OPTS="$CLUSTER_OPTS --set global.localClusterDomain=apps.$domain" CLUSTER_OPTS="$CLUSTER_OPTS --set global.clusterDomain=$domain" -CLUSTER_OPTS="$CLUSTER_OPTS --set global.clusterVersion=$ocpversion" +CLUSTER_OPTS="$CLUSTER_OPTS --set global.clusterVersion=$ocpversion" CLUSTER_OPTS="$CLUSTER_OPTS --set global.clusterPlatform=$platform" sharedValueFiles=$(yq ".clusterGroup.sharedValueFiles" values-$SITE.yaml) appValueFiles=$(yq ".clusterGroup.applications.$APP.extraValueFiles" values-$SITE.yaml) +OVERRIDES=$( getOverrides ) VALUE_FILES="" IFS=$'\n' @@ -57,7 +67,5 @@ for line in $appValueFiles; do fi done -cmd="helm template $chart --name-template ${APP} -n ${namespace} ${VALUE_FILES} ${CLUSTER_OPTS}" -eval $cmd - - +cmd="helm template $chart --name-template ${APP} -n ${namespace} ${VALUE_FILES} ${OVERRIDES} ${CLUSTER_OPTS}" +eval "$cmd" From 9fcf9ca1355fd32b52bb67cbe1c771523a58286a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Fri, 20 Oct 2023 09:21:15 +1100 Subject: [PATCH 1025/1263] Clean up tests after 7cda9c4 --- tests/clustergroup-industrial-edge-factory.expected.yaml | 3 --- tests/clustergroup-industrial-edge-hub.expected.yaml | 3 --- tests/clustergroup-medical-diagnosis-hub.expected.yaml | 3 --- tests/clustergroup-naked.expected.yaml | 3 --- tests/clustergroup-normal.expected.yaml | 3 --- 5 files changed, 15 deletions(-) diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 8c37450e..3eed3296 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -136,9 +136,6 @@ data: targetCluster: in-cluster enabled: all global: - Template: - BasePath: global-vars.yml - Name: global-vars clusterDomain: region.example.com clusterPlatform: aws clusterVersion: "4.12" diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 24f37053..12e1ee28 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -297,9 +297,6 @@ data: targetCluster: in-cluster enabled: all global: - Template: - BasePath: global-vars.yml - Name: global-vars clusterDomain: region.example.com clusterPlatform: aws clusterVersion: "4.12" diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index eb367bce..9efc2431 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -284,9 +284,6 @@ data: targetCluster: in-cluster enabled: all global: - Template: - BasePath: global-vars.yml - Name: global-vars clusterDomain: region.example.com clusterPlatform: aws clusterVersion: "4.12" diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 1ec01860..75359902 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -66,9 +66,6 @@ data: targetCluster: in-cluster enabled: all global: - Template: - BasePath: global-vars.yml - Name: global-vars extraValueFiles: [] options: applicationRetryLimit: 20 diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index f96708a9..d6886bed 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -197,9 +197,6 @@ data: targetCluster: in-cluster enabled: all global: - Template: - BasePath: global-vars.yml - Name: global-vars clusterDomain: region.example.com clusterPlatform: aws clusterVersion: "4.12" From 3e611d3c6090a97a38bcb6a846431b23939a0015 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 20 Oct 2023 15:15:36 -0500 Subject: [PATCH 1026/1263] Add preview-all and remove some spurious stdout output --- Makefile | 6 +++++- scripts/preview-all.sh | 15 +++++++++++++++ scripts/preview.sh | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100755 scripts/preview-all.sh diff --git a/Makefile b/Makefile index 73cb99ed..8a32ee4c 100644 --- a/Makefile +++ b/Makefile @@ -44,8 +44,12 @@ help: ## This help message show: ## show the starting template without installing it helm template common/operator-install/ --name-template $(NAME) $(HELM_OPTS) +preview-all: + common/scripts/preview-all.sh $(TARGET_REPO) $(TARGET_BRANCH) + preview-%: - common/scripts/preview.sh hub $* $(TARGET_REPO) $(TARGET_BRANCH) + CLUSTERGROUP?=$(shell yq ".main.clusterGroupName" values-global.yaml) + common/scripts/preview.sh $(CLUSTERGROUP) $* $(TARGET_REPO) $(TARGET_BRANCH) .PHONY: operator-deploy operator-deploy operator-upgrade: validate-prereq validate-origin validate-cluster ## runs helm install diff --git a/scripts/preview-all.sh b/scripts/preview-all.sh new file mode 100755 index 00000000..27649fa4 --- /dev/null +++ b/scripts/preview-all.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +REPO=$1; shift; +TARGET_BRANCH=$1; shift + +HUB=$( yq ".main.clusterGroupName" values-global.yaml ) +MANAGED_CLUSTERS=$( yq ".clusterGroup.managedClusterGroups.[].name" values-$HUB.yaml ) +ALL_CLUSTERS=( $HUB $MANAGED_CLUSTERS ) + +for cluster in ${ALL_CLUSTERS[@]}; do + APPS=$( yq ".clusterGroup.applications.[].name" values-$cluster.yaml ) + for app in $APPS; do + ./common/scripts/preview.sh $cluster $app $REPO $TARGET_BRANCH + done +done diff --git a/scripts/preview.sh b/scripts/preview.sh index 199facf6..8bafb43f 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -1,4 +1,4 @@ -#!/bin/bash -x +#!/bin/bash SITE=$1; shift APP=$1; shift From 16b99dd159c92def62adf19ff5bff5bc9db194e7 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 20 Oct 2023 15:45:23 -0500 Subject: [PATCH 1027/1263] All prototype preview-all and silence some output --- Makefile | 4 ++-- scripts/preview-all.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 8a32ee4c..af2c82f1 100644 --- a/Makefile +++ b/Makefile @@ -45,11 +45,11 @@ show: ## show the starting template without installing it helm template common/operator-install/ --name-template $(NAME) $(HELM_OPTS) preview-all: - common/scripts/preview-all.sh $(TARGET_REPO) $(TARGET_BRANCH) + @common/scripts/preview-all.sh $(TARGET_REPO) $(TARGET_BRANCH) preview-%: CLUSTERGROUP?=$(shell yq ".main.clusterGroupName" values-global.yaml) - common/scripts/preview.sh $(CLUSTERGROUP) $* $(TARGET_REPO) $(TARGET_BRANCH) + @common/scripts/preview.sh $(CLUSTERGROUP) $* $(TARGET_REPO) $(TARGET_BRANCH) .PHONY: operator-deploy operator-deploy operator-upgrade: validate-prereq validate-origin validate-cluster ## runs helm install diff --git a/scripts/preview-all.sh b/scripts/preview-all.sh index 27649fa4..cc7775bf 100755 --- a/scripts/preview-all.sh +++ b/scripts/preview-all.sh @@ -10,6 +10,6 @@ ALL_CLUSTERS=( $HUB $MANAGED_CLUSTERS ) for cluster in ${ALL_CLUSTERS[@]}; do APPS=$( yq ".clusterGroup.applications.[].name" values-$cluster.yaml ) for app in $APPS; do - ./common/scripts/preview.sh $cluster $app $REPO $TARGET_BRANCH + common/scripts/preview.sh $cluster $app $REPO $TARGET_BRANCH done done From 9621a32073f207e502cc351c6e1d6695934cab6d Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Mon, 23 Oct 2023 15:53:34 -0600 Subject: [PATCH 1028/1263] - Removed new key operatorgroupExludeTargetNS - Added key to namespace map entry excludeOperatorGroupTargetNS. --- clustergroup/templates/core/operatorgroup.yaml | 4 +--- examples/values-example.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index 29c542c4..956362c4 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -12,7 +12,7 @@ metadata: name: {{ $k }}-operator-group namespace: {{ $k }} spec: - {{- if not ( has $k $.Values.clusterGroup.operatorgroupExcludeTargetNS ) }} + {{- if not $v.excludeOperatorGroupTargetNS }} targetNamespaces: - {{ $k }} {{- end }} @@ -24,10 +24,8 @@ metadata: name: {{ . }}-operator-group namespace: {{ . }} spec: - {{- if not ( has . $.Values.clusterGroup.operatorgroupExcludeTargetNS ) }} targetNamespaces: - {{ . }} - {{- end }} {{- end }} {{- /* if kindIs "string" $ns */}} --- {{- end }} {{- /* if or (empty $.Values.clusterGroup.operatorgroupExcludes) (not (has . $.Values.clusterGroup.operatorgroupExcludes)) */}} diff --git a/examples/values-example.yaml b/examples/values-example.yaml index fac8d0ee..8418c7d0 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -23,15 +23,15 @@ clusterGroup: annotations: openshift.io/cluster-monitoring: "true" owner: "namespace owner" - - application-ci + excludeOperatorGroupTargetNS: true + - application-ci: + excludeOperatorGroupTargetNS: true + - include-ci - excludes-ci operatorgroupExcludes: - excludes-ci - operatorgroupExcludeTargetNS: - - application-ci - subscriptions: acm: name: advanced-cluster-management From 7d031dee034dea1d132fccdfb992192d7f3133d9 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Mon, 23 Oct 2023 15:56:11 -0600 Subject: [PATCH 1029/1263] Updates to CI --- tests/clustergroup-normal.expected.yaml | 30 ++++++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 780f5412..e908d586 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -17,9 +17,18 @@ spec: apiVersion: v1 kind: Namespace metadata: + name: application-ci labels: argocd.argoproj.io/managed-by: mypattern-example - name: application-ci +spec: +--- +# Source: clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + labels: + argocd.argoproj.io/managed-by: mypattern-example + name: include-ci spec: --- # Source: clustergroup/templates/core/namespaces.yaml @@ -168,13 +177,14 @@ data: annotations: openshift.io/cluster-monitoring: "true" owner: namespace owner + excludeOperatorGroupTargetNS: true labels: kubernetes.io/os: linux openshift.io/node-selector: "" - - application-ci + - application-ci: + excludeOperatorGroupTargetNS: true + - include-ci - excludes-ci - operatorgroupExcludeTargetNS: - - application-ci operatorgroupExcludes: - excludes-ci projects: @@ -1043,8 +1053,6 @@ metadata: name: open-cluster-management-operator-group namespace: open-cluster-management spec: - targetNamespaces: - - open-cluster-management --- # Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 @@ -1054,6 +1062,16 @@ metadata: namespace: application-ci spec: --- +# Source: clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: include-ci-operator-group + namespace: include-ci +spec: + targetNamespaces: + - include-ci +--- # Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription From 098f16afa9bafd38d68013ca9a965f66c4c7ac95 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 26 Oct 2023 17:08:56 +0200 Subject: [PATCH 1030/1263] Update CRD from the operator --- .../crds/gitops.hybrid-cloud-patterns.io_patterns.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml index ce5b2c07..1806661d 100644 --- a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml +++ b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml @@ -144,7 +144,8 @@ spec: description: PatternStatus defines the observed state of Pattern properties: analyticsSent: - type: boolean + default: 0 + type: integer analyticsUUID: type: string appClusterDomain: From d1af1f1f8001c3a12b8f949db753f6d9cec764c8 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 26 Oct 2023 11:16:23 -0500 Subject: [PATCH 1031/1263] Make .plugin handling consistent --- clustergroup/templates/plumbing/applications.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index a54ed99f..3706d839 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -52,7 +52,7 @@ spec: path: {{ .path }} {{- end }} {{- if .plugin }} - plugin: {{ .plugin }} + plugin: {{ .plugin | toPrettyJson }} {{- end }} {{- if not .kustomize }} helm: From 0639916ac9db996f1405f999ab2e114ee93be274 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 27 Oct 2023 08:28:59 +0200 Subject: [PATCH 1032/1263] Preseed the patterns-operator-config configmap This way we keep the main.gitops keys working which is needed for IIB installs. Tested with: export EXTRA_HELM_OPTS="--set main.gitops.channel=gitops-1.10" ./pattern.sh make install and correctly got gitops from 1.10 installed. --- .../templates/pattern-operator-configmap.yaml | 13 +++++++++++++ ...-install-industrial-edge-factory.expected.yaml | 15 +++++++++++++++ ...ator-install-industrial-edge-hub.expected.yaml | 15 +++++++++++++++ ...or-install-medical-diagnosis-hub.expected.yaml | 15 +++++++++++++++ tests/operator-install-naked.expected.yaml | 15 +++++++++++++++ tests/operator-install-normal.expected.yaml | 15 +++++++++++++++ 6 files changed, 88 insertions(+) create mode 100644 operator-install/templates/pattern-operator-configmap.yaml diff --git a/operator-install/templates/pattern-operator-configmap.yaml b/operator-install/templates/pattern-operator-configmap.yaml new file mode 100644 index 00000000..17b7a026 --- /dev/null +++ b/operator-install/templates/pattern-operator-configmap.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: patterns-operator-config + namespace: openshift-operators +data: + gitops.catalogSource: {{ .Values.main.gitops.operatorSource }} + gitops.channel: {{ .Values.main.gitops.channel }} + + # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace + # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan + # gitops.ManualSync: GitOpsDefaultManualSync + # gitops.name: GitOpsDefaultPackageName diff --git a/tests/operator-install-industrial-edge-factory.expected.yaml b/tests/operator-install-industrial-edge-factory.expected.yaml index 8e9adf88..1c57fe6b 100644 --- a/tests/operator-install-industrial-edge-factory.expected.yaml +++ b/tests/operator-install-industrial-edge-factory.expected.yaml @@ -1,4 +1,19 @@ --- +# Source: pattern-install/templates/pattern-operator-configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: patterns-operator-config + namespace: openshift-operators +data: + gitops.catalogSource: redhat-operators + gitops.channel: gitops-1.8 + + # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace + # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan + # gitops.ManualSync: GitOpsDefaultManualSync + # gitops.name: GitOpsDefaultPackageName +--- # Source: pattern-install/templates/pattern.yaml apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern diff --git a/tests/operator-install-industrial-edge-hub.expected.yaml b/tests/operator-install-industrial-edge-hub.expected.yaml index 8e9adf88..1c57fe6b 100644 --- a/tests/operator-install-industrial-edge-hub.expected.yaml +++ b/tests/operator-install-industrial-edge-hub.expected.yaml @@ -1,4 +1,19 @@ --- +# Source: pattern-install/templates/pattern-operator-configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: patterns-operator-config + namespace: openshift-operators +data: + gitops.catalogSource: redhat-operators + gitops.channel: gitops-1.8 + + # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace + # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan + # gitops.ManualSync: GitOpsDefaultManualSync + # gitops.name: GitOpsDefaultPackageName +--- # Source: pattern-install/templates/pattern.yaml apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern diff --git a/tests/operator-install-medical-diagnosis-hub.expected.yaml b/tests/operator-install-medical-diagnosis-hub.expected.yaml index 8e9adf88..1c57fe6b 100644 --- a/tests/operator-install-medical-diagnosis-hub.expected.yaml +++ b/tests/operator-install-medical-diagnosis-hub.expected.yaml @@ -1,4 +1,19 @@ --- +# Source: pattern-install/templates/pattern-operator-configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: patterns-operator-config + namespace: openshift-operators +data: + gitops.catalogSource: redhat-operators + gitops.channel: gitops-1.8 + + # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace + # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan + # gitops.ManualSync: GitOpsDefaultManualSync + # gitops.name: GitOpsDefaultPackageName +--- # Source: pattern-install/templates/pattern.yaml apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index b32c2577..90fca51e 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -1,4 +1,19 @@ --- +# Source: pattern-install/templates/pattern-operator-configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: patterns-operator-config + namespace: openshift-operators +data: + gitops.catalogSource: redhat-operators + gitops.channel: gitops-1.8 + + # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace + # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan + # gitops.ManualSync: GitOpsDefaultManualSync + # gitops.name: GitOpsDefaultPackageName +--- # Source: pattern-install/templates/pattern.yaml apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index 8e9adf88..1c57fe6b 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -1,4 +1,19 @@ --- +# Source: pattern-install/templates/pattern-operator-configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: patterns-operator-config + namespace: openshift-operators +data: + gitops.catalogSource: redhat-operators + gitops.channel: gitops-1.8 + + # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace + # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan + # gitops.ManualSync: GitOpsDefaultManualSync + # gitops.name: GitOpsDefaultPackageName +--- # Source: pattern-install/templates/pattern.yaml apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern From 0b00146c804eedf8c532b1664fa6ac42071b09e1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 27 Oct 2023 09:14:47 +0200 Subject: [PATCH 1033/1263] Small IIB cleanups --- ansible/playbooks/iib-ci/lookup.yml | 4 ++-- ansible/roles/iib_ci/README.md | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ansible/playbooks/iib-ci/lookup.yml b/ansible/playbooks/iib-ci/lookup.yml index f7e42ca4..f39b8ea3 100644 --- a/ansible/playbooks/iib-ci/lookup.yml +++ b/ansible/playbooks/iib-ci/lookup.yml @@ -4,13 +4,13 @@ connection: local gather_facts: false vars: - rh_url: "https://datagrepper.engineering.redhat.com/raw?topic=/topic/VirtualTopic.eng.ci.redhat-container-image.index.built&contains=%s&rows_per_page=20" + rh_url: "https://datagrepper.engineering.redhat.com/raw?topic=/topic/VirtualTopic.eng.ci.redhat-container-image.index.built&delta=15780000&contains=%s" operator: "openshift-gitops-1-gitops-operator-bundle" ocp_versions: {} tasks: - name: Set url fact ansible.builtin.set_fact: - url: "{{ rh_url | format(operator + ':v') }}" + url: "{{ rh_url | format(operator) }}" - name: Fetch URI ansible.builtin.uri: diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 9f421e8f..05ae49fd 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -3,7 +3,9 @@ A set of ansible plays to fetch an IIB (Image Index Bundle, aka a container created by the operator sdk that contains a bunch of references to operators that can be installed in an OpenShift cluster) -Run `make lookup` to see which IIBs are available. +Run `ansible-playbook common/ansible/playbooks/iib-ci/lookup.yml` to see which IIBs are available (defaults to +openshift-gitops). If you want to look up IIBs for a different operator run: +`ansible-playbook -e operator=acm-operator common/ansible/playbooks/iib-ci/lookup.yml` Typically IIB are prerelease stuff that lives on some internal boxes. What these scripts do is fetch the IIB internally, mirror it to the registry inside the cluster, parse all the needed images and mirror From 122680712874d6cc074ba7d3ae173dc83f1b05a2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 27 Oct 2023 12:19:03 +0200 Subject: [PATCH 1034/1263] Add small curl example for IIB --- ansible/roles/iib_ci/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 05ae49fd..828daa05 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -7,6 +7,9 @@ Run `ansible-playbook common/ansible/playbooks/iib-ci/lookup.yml` to see which I openshift-gitops). If you want to look up IIBs for a different operator run: `ansible-playbook -e operator=acm-operator common/ansible/playbooks/iib-ci/lookup.yml` +You can also try running curl manually via: +`curl -sSL "https://datagrepper.engineering.redhat.com/raw?topic=/topic/VirtualTopic.eng.ci.redhat-container-image.index.built&delta=15780000&contains=acm-operator" | jq ".raw_messages[].msg"` + Typically IIB are prerelease stuff that lives on some internal boxes. What these scripts do is fetch the IIB internally, mirror it to the registry inside the cluster, parse all the needed images and mirror those to the internal cluster registry and then set up the registries.conf files on all nodes so From 613f84f62aaa31a43c990714f860dd8114a0314f Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 27 Oct 2023 07:25:49 -0600 Subject: [PATCH 1035/1263] Adding option to include/exclude targetNamespaces in OperatorGroup --- clustergroup/templates/core/operatorgroup.yaml | 8 ++++++-- examples/values-example.yaml | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index 956362c4..5d7e33bd 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -12,10 +12,14 @@ metadata: name: {{ $k }}-operator-group namespace: {{ $k }} spec: - {{- if not $v.excludeOperatorGroupTargetNS }} targetNamespaces: + {{- if $v.operatorgroup }} + {{- range $v.operatorgroup.targetNamespaces }}{{- /* We loop through the list of tergetnamespaces */}} + - {{ . }} + {{- end }}{{- /* End range targetNamespaces */}} + {{- else }} - {{ $k }} - {{- end }} + {{- end }}{{- /* End of if operatorGroup */}} {{- end }}{{- /* range $k, $v := $ns */}} {{- else if kindIs "string" $ns }} apiVersion: operators.coreos.com/v1 diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 8418c7d0..85b01702 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -23,9 +23,14 @@ clusterGroup: annotations: openshift.io/cluster-monitoring: "true" owner: "namespace owner" - excludeOperatorGroupTargetNS: true - application-ci: - excludeOperatorGroupTargetNS: true + operatorgroup: + targetNamespaces: + - application-ci + - other-namespace + - exclude-targetns: + operatorgroup: + targetNamespaces: - include-ci - excludes-ci From 6debf791a67ada2c336b43f108481777b15df529 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 27 Oct 2023 07:26:23 -0600 Subject: [PATCH 1036/1263] Updated CI tests --- tests/clustergroup-normal.expected.yaml | 32 +++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index e908d586..66a9e56d 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -25,6 +25,15 @@ spec: # Source: clustergroup/templates/core/namespaces.yaml apiVersion: v1 kind: Namespace +metadata: + name: exclude-targetns + labels: + argocd.argoproj.io/managed-by: mypattern-example +spec: +--- +# Source: clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace metadata: labels: argocd.argoproj.io/managed-by: mypattern-example @@ -177,12 +186,17 @@ data: annotations: openshift.io/cluster-monitoring: "true" owner: namespace owner - excludeOperatorGroupTargetNS: true labels: kubernetes.io/os: linux openshift.io/node-selector: "" - application-ci: - excludeOperatorGroupTargetNS: true + operatorgroup: + targetNamespaces: + - application-ci + - other-namespace + - exclude-targetns: + operatorgroup: + targetNamespaces: null - include-ci - excludes-ci operatorgroupExcludes: @@ -1053,6 +1067,8 @@ metadata: name: open-cluster-management-operator-group namespace: open-cluster-management spec: + targetNamespaces: + - open-cluster-management --- # Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 @@ -1061,6 +1077,18 @@ metadata: name: application-ci-operator-group namespace: application-ci spec: + targetNamespaces: + - application-ci + - other-namespace +--- +# Source: clustergroup/templates/core/operatorgroup.yaml +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: exclude-targetns-operator-group + namespace: exclude-targetns +spec: + targetNamespaces: --- # Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 From 8da6333214a7d9793944de4838f85ba9de5bc384 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 31 Oct 2023 20:58:05 -0600 Subject: [PATCH 1037/1263] - Fix: bug in task TASK [iib_ci : Mirror all the needed images] Updated the Jinja2 template *ansible/roles/iib_ci/templates/mirror.map.j2* to add a '-' to the image tag if there's a source image that has the same name in the mirror.map file. Example mirror.map entry: registry.redhat.io/openshift4/ose-kube-rbac-proxy@sha256:da5d5061dbc2ec5082cf14b6c600fb5400b83cf91d7ccebfa80680a238d275db=default-route-openshift-image-registry.apps.lc-claudiol-devsec-hub-535d-2.blueprints.rhecoeng.com/openshift-marketplace/ose-kube-rbac-proxy:610968 registry.redhat.io/openshift4/ose-kube-rbac-proxy@sha256:d4de33150cb8c8faf0133b39553e6c972be5ace7ae787c98f3b77fca37748036=default-route-openshift-image-registry.apps.lc-claudiol-devsec-hub-535d-2.blueprints.rhecoeng.com/openshift-marketplace/ose-kube-rbac-proxy:610968 The above will generate an error in TASK [iib_ci : Mirror all the needed images] 2023-10-29 20:56:08,534 INFO fatal: [localhost]: FAILED! => {"attempts": 5, "changed": true, "cmd": "set -o pipefail\noc image mirror -a \"/tmp/ansible.eunmaew5/.dockerconfigjson\" -f mirror.map --insecure --keep-manifest-list 2>&1 | tee -a image-mirror.log\n", "delta": "0:00:00.068476", "end": "2023-10-29 20:56:07.485885", "msg": "non-zero return code", "rc": 1, "start": "2023-10-29 20:56:07.417409", "stderr": "", "stderr_lines": [], "stdout": "error: file mirror.map, line 11: each destination tag may only be specified once: default-route-openshift-image-registry.apps.qe-mg-hub-aws-14022.aws.validatedpatterns.io/openshift-marketplace/ose-kube-rbac-proxy:610968", "stdout_lines": ["error: file mirror.map, line 11: each destination tag may only be specified once: default-route-openshift-image-registry.apps.qe-mg-hub-aws-14022.aws.validatedpatterns.io/openshift-marketplace/ose-kube-rbac-proxy:610968"]} --- ansible/roles/iib_ci/templates/mirror.map.j2 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/templates/mirror.map.j2 b/ansible/roles/iib_ci/templates/mirror.map.j2 index ecef721c..aa1e57b8 100644 --- a/ansible/roles/iib_ci/templates/mirror.map.j2 +++ b/ansible/roles/iib_ci/templates/mirror.map.j2 @@ -1,3 +1,11 @@ +{% set previous_item = [] %} +{% set tag_count = 1 %} {% for item in image_urls.values() %} -{{ item.source }}={{ item.mirrordest_nosha }}:{{ item.mirrordest_tag }} +{% if item.source_nosha in previous_item %} + {{ item.source }}={{ item.mirrordest_nosha }}:{{ item.mirrordest_tag ~ '-' ~ tag_count }} + {% set tag_count = tag_count + 1 %} +{% else %} + {{ item.source }}={{ item.mirrordest_nosha }}:{{ item.mirrordest_tag }} +{% endif %} +{% set previous_item = previous_item.append( item.source_nosha ) %} {% endfor %} From 2eb19f36ac8da5292f24cc8da6c335f30f2052e3 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 1 Nov 2023 05:19:28 -0600 Subject: [PATCH 1038/1263] - Updated the mirrordest_tag to use the sha256 of the image instead of the IIB number. --- ansible/roles/iib_ci/tasks/mirror-related-images.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml index 821e4be0..32a36c07 100644 --- a/ansible/roles/iib_ci/tasks/mirror-related-images.yml +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -89,7 +89,7 @@ image_urls: "{{ image_urls | default({}) | combine({item: {'mirrordest': mirror_dest + item | basename, 'mirrordest_nosha': (mirror_dest + item | basename) | regex_replace('@.*$', ''), - 'mirrordest_tag': iib}}, recursive=true) }}" + 'mirrordest_tag': 'tag-' + item | basename | regex_replace('^.*@sha256:', '')}}, recursive=true) }}" loop: "{{ all_images }}" when: use_internal_registry From dd96c93d76fc1b2a1898760aa85e6802744654be Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 1 Nov 2023 08:20:25 -0600 Subject: [PATCH 1039/1263] Restored mirror template to original implementation --- ansible/roles/iib_ci/templates/mirror.map.j2 | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ansible/roles/iib_ci/templates/mirror.map.j2 b/ansible/roles/iib_ci/templates/mirror.map.j2 index aa1e57b8..ecef721c 100644 --- a/ansible/roles/iib_ci/templates/mirror.map.j2 +++ b/ansible/roles/iib_ci/templates/mirror.map.j2 @@ -1,11 +1,3 @@ -{% set previous_item = [] %} -{% set tag_count = 1 %} {% for item in image_urls.values() %} -{% if item.source_nosha in previous_item %} - {{ item.source }}={{ item.mirrordest_nosha }}:{{ item.mirrordest_tag ~ '-' ~ tag_count }} - {% set tag_count = tag_count + 1 %} -{% else %} - {{ item.source }}={{ item.mirrordest_nosha }}:{{ item.mirrordest_tag }} -{% endif %} -{% set previous_item = previous_item.append( item.source_nosha ) %} +{{ item.source }}={{ item.mirrordest_nosha }}:{{ item.mirrordest_tag }} {% endfor %} From 0b4c7f4640cfa5e5ba243fa94da67d9ee3e8a59f Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 1 Nov 2023 10:09:57 -0600 Subject: [PATCH 1040/1263] - Updated structure for supporting OperatorGroup's per suggestion of decoupling operatorGroup and targetNamespaces. Example: - exclude-targetns: operatorGroup: true targetNamespaces: - Continues to support operatorgroupExcludes - Updated CI tests --- .../templates/core/operatorgroup.yaml | 6 ++- examples/values-example.yaml | 18 ++++---- tests/clustergroup-normal.expected.yaml | 43 ++++++++++--------- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index 5d7e33bd..b12cb13e 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -6,6 +6,7 @@ {{- if kindIs "map" $ns }} {{- range $k, $v := $ns }}{{- /* We loop here even though the map has always just one key */}} +{{- if $v.operatorGroup }}{{- /* Checks if the user sets operatorGroup: false */}} apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -13,14 +14,15 @@ metadata: namespace: {{ $k }} spec: targetNamespaces: - {{- if $v.operatorgroup }} - {{- range $v.operatorgroup.targetNamespaces }}{{- /* We loop through the list of tergetnamespaces */}} + {{- if and $v.operatorGroup (empty $v.targetNamespaces) }} + {{- range $v.targetNamespaces }}{{- /* We loop through the list of tergetnamespaces */}} - {{ . }} {{- end }}{{- /* End range targetNamespaces */}} {{- else }} - {{ $k }} {{- end }}{{- /* End of if operatorGroup */}} {{- end }}{{- /* range $k, $v := $ns */}} +{{- end }}{{- /* End of if operatorGroup */}} {{- else if kindIs "string" $ns }} apiVersion: operators.coreos.com/v1 kind: OperatorGroup diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 85b01702..bb5a5aaa 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -24,18 +24,20 @@ clusterGroup: openshift.io/cluster-monitoring: "true" owner: "namespace owner" - application-ci: - operatorgroup: - targetNamespaces: - - application-ci - - other-namespace + operatorGroup: true + targetNamespaces: + - application-ci + - other-namespace - exclude-targetns: - operatorgroup: - targetNamespaces: + operatorGroup: true + targetNamespaces: - include-ci - - excludes-ci + - exclude-og + - totally-exclude-og: + operatorGroup: false operatorgroupExcludes: - - excludes-ci + - exclude-og subscriptions: acm: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 66a9e56d..e52c1c5e 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -46,7 +46,16 @@ kind: Namespace metadata: labels: argocd.argoproj.io/managed-by: mypattern-example - name: excludes-ci + name: exclude-og +spec: +--- +# Source: clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: totally-exclude-og + labels: + argocd.argoproj.io/managed-by: mypattern-example spec: --- # Source: clustergroup/templates/imperative/namespace.yaml @@ -190,17 +199,19 @@ data: kubernetes.io/os: linux openshift.io/node-selector: "" - application-ci: - operatorgroup: - targetNamespaces: - - application-ci - - other-namespace + operatorGroup: true + targetNamespaces: + - application-ci + - other-namespace - exclude-targetns: - operatorgroup: - targetNamespaces: null + operatorGroup: true + targetNamespaces: null - include-ci - - excludes-ci + - exclude-og + - totally-exclude-og: + operatorGroup: false operatorgroupExcludes: - - excludes-ci + - exclude-og projects: - datacenter sharedValueFiles: @@ -505,6 +516,9 @@ spec: name: helm-values-configmap-example restartPolicy: Never --- +# Source: clustergroup/templates/core/operatorgroup.yaml +--- +--- # Source: clustergroup/templates/core/subscriptions.yaml --- --- @@ -1063,23 +1077,12 @@ spec: # Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup -metadata: - name: open-cluster-management-operator-group - namespace: open-cluster-management -spec: - targetNamespaces: - - open-cluster-management ---- -# Source: clustergroup/templates/core/operatorgroup.yaml -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup metadata: name: application-ci-operator-group namespace: application-ci spec: targetNamespaces: - application-ci - - other-namespace --- # Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 From 4d2da25428cd0b557a56f4276b6618b20cd2d006 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 1 Nov 2023 11:27:46 -0600 Subject: [PATCH 1041/1263] Update logic to fix multiple targetNamespaces --- clustergroup/templates/core/operatorgroup.yaml | 7 +++---- examples/values-example.yaml | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index b12cb13e..c08e91b7 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -5,8 +5,7 @@ {{- if kindIs "map" $ns }} {{- range $k, $v := $ns }}{{- /* We loop here even though the map has always just one key */}} - -{{- if $v.operatorGroup }}{{- /* Checks if the user sets operatorGroup: false */}} + {{- if $v.operatorGroup }}{{- /* Checks if the user sets operatorGroup: false */}} apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -14,7 +13,7 @@ metadata: namespace: {{ $k }} spec: targetNamespaces: - {{- if and $v.operatorGroup (empty $v.targetNamespaces) }} + {{- if or (and $v.operatorGroup (hasKey $v "targetNamespaces")) (and $v.operatorGroup (not (empty $v.targetNamespaces))) }} {{- range $v.targetNamespaces }}{{- /* We loop through the list of tergetnamespaces */}} - {{ . }} {{- end }}{{- /* End range targetNamespaces */}} @@ -22,7 +21,7 @@ spec: - {{ $k }} {{- end }}{{- /* End of if operatorGroup */}} {{- end }}{{- /* range $k, $v := $ns */}} -{{- end }}{{- /* End of if operatorGroup */}} + {{- end }}{{- /* End of if operatorGroup */}} {{- else if kindIs "string" $ns }} apiVersion: operators.coreos.com/v1 kind: OperatorGroup diff --git a/examples/values-example.yaml b/examples/values-example.yaml index bb5a5aaa..6c006b00 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -35,6 +35,8 @@ clusterGroup: - exclude-og - totally-exclude-og: operatorGroup: false + - include-default-og: + operatorGroup: true operatorgroupExcludes: - exclude-og From f13ecf06a5aedacf5be55e5f7593993449d9a345 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 1 Nov 2023 11:28:14 -0600 Subject: [PATCH 1042/1263] Fix ci issues --- tests/clustergroup-normal.expected.yaml | 26 ++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index e52c1c5e..1e7d8e8f 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -58,6 +58,15 @@ metadata: argocd.argoproj.io/managed-by: mypattern-example spec: --- +# Source: clustergroup/templates/core/namespaces.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: include-default-og + labels: + argocd.argoproj.io/managed-by: mypattern-example +spec: +--- # Source: clustergroup/templates/imperative/namespace.yaml apiVersion: v1 kind: Namespace @@ -210,6 +219,8 @@ data: - exclude-og - totally-exclude-og: operatorGroup: false + - include-default-og: + operatorGroup: true operatorgroupExcludes: - exclude-og projects: @@ -516,9 +527,6 @@ spec: name: helm-values-configmap-example restartPolicy: Never --- -# Source: clustergroup/templates/core/operatorgroup.yaml ---- ---- # Source: clustergroup/templates/core/subscriptions.yaml --- --- @@ -1083,6 +1091,7 @@ metadata: spec: targetNamespaces: - application-ci + - other-namespace --- # Source: clustergroup/templates/core/operatorgroup.yaml apiVersion: operators.coreos.com/v1 @@ -1103,6 +1112,17 @@ spec: targetNamespaces: - include-ci --- +# Source: clustergroup/templates/core/operatorgroup.yaml +--- +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: include-default-og-operator-group + namespace: include-default-og +spec: + targetNamespaces: + - include-default-og +--- # Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription From e78cb82746c113fe2ace49b52a52a1deaa573093 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 7 Nov 2023 09:48:28 +0100 Subject: [PATCH 1043/1263] Upgraded ESO to v0.9.8 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.9.5.tgz | Bin 83210 -> 0 bytes .../charts/external-secrets-0.9.8.tgz | Bin 0 -> 84651 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 372 +++++++++++++++--- ...-secrets-industrial-edge-hub.expected.yaml | 372 +++++++++++++++--- ...ecrets-medical-diagnosis-hub.expected.yaml | 372 +++++++++++++++--- ...olang-external-secrets-naked.expected.yaml | 372 +++++++++++++++--- ...lang-external-secrets-normal.expected.yaml | 372 +++++++++++++++--- 9 files changed, 1609 insertions(+), 259 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.5.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.9.8.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 9b2c3b6d..54675dd3 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.3 dependencies: - name: external-secrets - version: "0.9.5" + version: "0.9.8" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.9.5.tgz b/golang-external-secrets/charts/external-secrets-0.9.5.tgz deleted file mode 100644 index f40bed88db4266e6bf9882d70ba5991c949f7414..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83210 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

HS(Vpq_lUB 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%0A5!{9s4q8 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 MquU1Z0uSFsZ4t))ngTPwb3 zNQ!fM^w%RUk`Rdq1h@cDvXahutMdZq$-X&fT~N4JksxK+g|Sx0BB}~iv#RF(;(yBg zBx2zp^(Tp+rK7*}|2%=`yKle6{~mnz?GydK^7+fb^KYL%efHh+7f-(X%aeoW2j9N< zi}&po=2Cf9X~q)I`^zGj%>B@BY}fVvo<4qg|BvP@$%eOV5%%^C+~60_pEvIRi|@Xp z{fA$l9lU_w4_+KRdHNUcNpCY~dA{=fXUpJ&pQJ$?z4Wf1K48mb{^yh7{~rGJ!PHMD zNwCc5w^!o0c>K6{3l_1Nzwo2XyNna>r_~r<`H6Bjl z#i(-P%0D~^Cip4YHDNbH-hGw&3B1bSYi1*j7A%NHEKTWU8V|yF2LHp7nLk`cvjC;x@56o4N7`}f|#@Zf(BpA4Qne1PA|;Q!$*j_1SQ;Y;tz z55pi@OxSgN`=2xX4X6IV^CE`u;V-Tp@V_b_&Tg0RGi*v2Oc?IKgKPP5zBwE3e+6Wj zzgUJ0z|bEhu*d%|4!(Wv@c%BJmi>SC{^Hg7F#D8k`i&0!fAHk_K?(nV_w9=p4*&1s zDeu2GXQ?;#{m7g8%P_w6r(U|6O#C#xT!rE7|9J4=B=P`f`iYmt&~O$+-YN~EnIaA6 ze4u^rHePu*K^S_G52HY9K*Qc@=`oKtInUz6e*jeDD1=S$6YW;e!WnaeyF#jtjz>9{gW6_q}OwdFdxest~QF zUVN$dOg$D&y?K0tGX*Mh8O&BN4s={Rn6Lc7jcZxCS3a|MSngKA`T|>eGV%e+7KE1a@PXEjN<|+VTIvlV?v} z6!HJlC(mCzbNGK3&mVt`{?~(7pO%P92yXx*ikK1qJt4IA;D3z(TmA9J05H!bu(#e` z`OFNl(;gu;(G2mjM2qimM2QQr<$K^^lVIw5|MIeA6-~Ss&*-1Q;(T>^8GQ2g278_u zi{l_fsW8nWMEyP>e)$C=N#1|2SQuOeh!2-bTEOtZFFqfe+GqF$TntjS2?H_0OA%M+ z(h6TTj1f+O4glH@gGG>Ws+G+F={^F=gCIG6caCidb1wY}keicBiDvmAD4>88kaFnp z--1k`Y4|wI6Fx$d7qjh#Fg7ki87Pbds(h&cE1EREHvxQ@s&W1&E#l?(<~y6uQGtUG zGCUI`iTcWU7Jjsx+%f^N>(bl%w{-At>0W6p{!#ODR{zh!>C?6q?~IVga7$@x#W~@v zLN@k8lkKJE`QDb-q=TFPTgafWo&?x528`0)|7XwJ`*qL$QpF1({(P#b!BlxeK3719 zi(xt+(e#NG3ZhB4nzk%)$S2X#Fog}A059@Jd}yehCb9ZMrKP}kaO;P;^gayYkrI+~ z=1lAIFTfaa=9i#R9fj*Qri1YJcmGV&c$rZNltnGgWBnH6>94do{eAI z>l)7i zK!^VC;K|cxCH>!X_^;Fd?c!-i8abBG{RDt!r=#nG2UkHfed!&mWAOkuNH%2|d#UW( zt!#lt1nXD>K1RA^b-V!mtaJ9B;3x6ho=3|vW{^Y082`(QU{Pozdd>%}a=t|f<(J|x zisFpSIVtbT@*na^DK5G8O9G#vY7x7jZYP+qlrO)20;0_k(G(ge@vj5isJ{g%FmJbS zP?gy}LbW{~Niu+Z`U{p#=5I70+k&|&hvYzb!M`Z6kNBc?p5*7+He(r2E4zSCWhYv8 zX0=rpUTg%xd7NhN z{Ol%9uG$u)HOu=7H9h$*$tes34zJ>6(*v6}ExUE;Ez3W6T0Qy&7^q|U(%WkW0kuHP z8K-FVzJ>lTyIN;#1`{b+Kni-Pz49FJYMjP^op0OrgAcz{ZHtRx$t=}DGobCL-Hm}a z7)+AsAo2fu<*)of0B}vNS@?Y$>ZhhBqGj>ZIG#BD13U@e+Yoc9t7smMDERmFbiHOC z_?7;qp+>Wsh0u@<*mRnF-*RF!&keC+Kmtit9dPcmFq{9*2W;CWHigTXrl(0f_J`OS z{%Ak7r9Rc~b1xMjjQI%3G!-mgjZ4m;GJ8C11jFy;HppG5Y)cR~_7zRY2dAT>yzYYv z5=dAy^S%Fh<=^gm|MMD`gZ9b>(7;C$b_f@%2oOQfFXaH0^#a833-AK>s;-roq7ZnS zdRIG?QCRJdxP@sEO%8IzeHtgNT%OUHq|YRA7Ej{vrFU_3T6!m(R{1s1-W3m9agON6 z``RzzcfOi-uC(L)(jY9Gzw}1f_V)L}yC4d(fQ83?$Zp{j$I%qH=%-H#ZI*r#5N+mf zzqO!jbc9|-*TpIM(RF`htZNF=MXq#;)zAITq*+EJP<)8P)xv*^j8SPm*V>zx59%jm zI4GIUjvB}00Yd)h(Ku*LThb&E{`Ga??ylO}MjpNj7 zQ}0?O<(?fr7W!Y1W2ZrytSA&{yqeB@Lq%|x|NrxEpO*B$FP=U>aQfd}JOT%yD_gq` zxLLVztPaHw{7^_Q_mbLpN#bp;I6kd*?N@_5SWd^EgBDq8+dA-l4I~#qba>5z5aG$E zr~lH5^^Ha=kfb%H$3FcWvC;$!zgbVQX2ilIcde4nKYrIYbvus*{!hkivQ}Vl2mXKd z^y%{w{(tu5TZjL5@@$I#=}?F!gA&50cWjl-f&cuS82RDVU%9`Yqya+DbOx+`@7BXD zVDe-Ya`|xJ!9rgr@oFhPF!1)^7aTdl#iwGWTqj(T$)W88mK`tT@8tRskfg&qT>Hs5 zZw!cWPyRa$()^DbM1Sh9)e@lRt3Q|WW$j$2iy*~`CjKlyVMg1Xra51;Fql&8ozSYv zC^@#vnK#W2kn}P}k*sbWg>UPVx>@T}*M7ry_(j-kA>vT(2%M8RPNqR*vj4<3b^~W2GCc_=% zaZ^KJXz)cm_Vi!GFIcbvUHbonr=|QqPrp5I`k&oAo9ch+ z!%E7UpfatNOurq;wvkHiTN?AuZNUslAxSX>C@u3uu$wI}oc^(WC9iE-Vic@bI-$Xp&8fLRpu{oiC?S~>;!S z4YTE7V&*vrJ$X zNfwkHuQe1}_`S5T4{`TJC=p*rN`Mfj3DdCB$>7-pV6h3N0Zjp(BF8DBJ1-_P&yd6{ z=!hqL6;3^l_b?ymB%VdV?`pJE!kZ{Q>~kd2dsA$~K4H)WyM>N8uD6P`5w1B>^i;al z0vsiQz(g@C^dBA(E(*pgL=2-TZt5rvW&@T?=0OId6TFwffF>Ie8U+x4qwJ6mOW@;5 zJ~-lN3e;Z^vS@`hVhlVmOmydK$w7)gAym#@onPdsXbJ2b!g6Fg+5#W#T2r0z2K}~6K2M9iM zV3$L5iyb($3a|9g(}tT+Kyl~LUfl?ho>x#9o?}B`8;<7u;cO^dp@kJ!$CE%}s@ys7 za9)xlM#O|eFZJsa)f=IJZ{ll|s{NZX#G3E{;RNVg{k6Rh0h=eNIuU5JzEKIBZ;O3! z$*CX~;+3b~m4QYmo!Sl?KGij2d?eOCsocuONuOML3!po}GOU>;UfdMUb0qU~`rb00 z@_f@sQMlTgeh0+K(^M19Yx#QP`7Sr9InnYF>XlO%V-hTZ=`h)z^HL{$e`MaV{3S3^ z1O*nRTiPtns0o*arl+uiBi###^A^ohlUb*cYE25up#VTiU(Gzk&)@Ud!=|;Et_L5E zbK8$!Wm%Yqn@?GGFDLR@@@)wWGo3#^SAOGlZ(RaL9Ey3v#FAT?ADly1 z5x0k__Yu+fN1g;%yEH)GFjub3%9Sbxgf~Q@2Q3Ao=)*O{{OIrnAc#RGUn7|dV0*hD zf{(#f%pYQ|*MW#}p0ASH~M9k=~Usgusm$#>i4w~J;z8&>YT+>@j=o?E{*g&``H zgpNV#m2O};1zZUSm)2DVO&^>cTTKCnSAyL;mU$?8snu~+%SRaPBU~01EC}J(Cv8M2 z^Q1nbBkeWwZu~GDTt%qO$~DwC+gENm++7j$E6n9l(~TPs^pA zP%1f_9}xE!1{BEp7PBN_x2-Kruy+UM)(_(b7~FVq#+dtFV{JolbmLfjxO#gtY~InN z^|c>e2T2@Z{=ExqSJPhr?xlCS8i&E;D2!Kint-AX>Q!;v5~h%HA&jKYKxz6?pH^Y~^_~~dK zL`?kqwVy0lM1PbuXMA5*i~5r{=-5YWQTcX5yL>_o4YXM{p3H`uB-M=dM^Za$RGKVC zAQ#Xdnk6I%=OZRUM-~=I6Lp2_KA##I zkVa@tk}pYM(yTZ`nQICt`*kp_G14IZB5RUnufke%WKj|pA8waIJ)bNow*;ucNNuL0 zef%}?Ihw_9PA4px<~M_EK?17jORf9M#qB`*3)i?$Yiwf9jO1d8rP&$LIvgu z^RiacZZ|j@kVQWKoexEkm;US)1P9HCFAVzfYk8+LA1w_Z3I=8ljcaLG=6VsXlhvln zH%bSueS}0cm!56l7k@<3E@6~xNa=moelcIm)8+pqxX@yPzh3iyfA{3si?aXs^XE^U z|MyNF%~k4AjT6}8{MTxB$YRCUN`RJg82f4*#%i&Ik+axZ9%7~M(C>x7>Eg+x_qnH7 zZJUb2SpAYX)voay1MYyoQ_f%S* zj&heMzp$8d@{-DPx1RZz!;+lN_l=ieF0)A`7OnaZggU+@Y-<@ zr2qJjNB=p*e&2t;cZOL$_a6UKw5s_syl?-U^u-$kj@i@|ul5+BltIcX?yMz6ruxI= z!DneEI5*p(#20nl0|evYM0t>Wl6TpG*QIDZyVNCnJy~u zBoLHAR}AGzT*0ljod2p*Ms=A?YHMJWnrMWl+y2sr%T&3b#@)`RUDI6H+jkAtjaG|B z`<~Wn_+{5-zQzIwRVKk`33%NUB`eNg^JCd`K`Jvj#>=5!U#VA*(T6;{@qO<6pcVBN zB>Bp}O+8uP{FLh-6Z&w&cRP$`g=rPxvC$;GN5ov=#D@O|{{HvJ^|Nj^@R*rR6MqqB zeurJSw9GTvr(~AkV~GPz)0L~|&bH#kBf*1=#0+mo!{K8bM2^LC4sxhG97t({A~ zTBqexS%;4KKMXo(VEI{iJBr$UOq+ry=g>J~v#q1}42tRt4WB_Xj+Up zpk!+lUE|WzBhytoHC@+B(rYhCqWjtvZEG23&KY4T`y8HOT!64$Abh;05_a-v7fL?zG8mZ$@+~JV>G5hgLW=x!9Hn1kay79 z*qmijvAUg$uK?h+)>Y#QSw5!6v)tzDk09#&gpJ>Hn`W$O0fKV0$0(0V(YjrO>c@ic z!Yy}{P_vWRAqS0wiP0CV{EzFkSC&&J^;XHBP5648vNXMklj(jbg9 zwq!vfAcb+oLbb(x5?_gK)7aCW7+PU6K_6dmO>Nb1jFNMctgl3olGOG=|O4Vvw0=>38jbJE%g=kqL&9~8uC zLGlZxVVPH3q^ex7B~F4*sTGjUQ={&2o*H$J^VD#j8qQP0d_B%n<1@Jkci^efgYSfh zTb7~8%`Oyz_8c_XkV>&Jg;AyHkS!>09d2Tg12lQ>y(rz=_x4ulp7#h2``-Wi|D=15;fDqNO#idM|C#T3Z+WuT)F+L_ z?|u?%z94X^MB%;wCLB#lR=k782%XLoRt!Qn@>$tB&N9JNUHIwqz)D6JybA;Mg?9`l zW0JSE1+#QwfOd_Fyw;5$r#k>(5Y~6^{Y}*9r-Z|WzTWx_^L#b2%`Gv57hG|p`^Z;K!&33SrL$wRzD<~wZ5GB7z*76YO zDJv*Z1Z?!1^N>E?X7B8_Sq(Z@Ky4BbJKCkBf%g_Y;5yoL7^aNwc{9D-{m5LKrib6Gh z=S0VG9QrM%)1D^i=&3EgI!9EZPz?{?;mPDt@uOCITncp`O2?<6K6Z(w_d|os6rH7P zi9(Ypfxx^maBx^7Aj?y{2;5wnY#&*!+hXM9vz}RT8 ziDPS{EWyeQP5^0*MzOBH$$ zXhGqPKI?}Vq;@GZU(*A@TK8dPjcZQ@^SRI8T-Bjrj;=`?Chxb?yzz{`Z<(QEkgXNb zc-Ct+UuQ=AF|4wqKh`FF)luy7q2(MkO#7kR(o}s+9rB9LV7-rFM|8tEjHYY#UlY1^ zM%sL;UH-pylQYC!#ZKL|G4jV;J)Dnk0Wn)x4fu65Zf&Fn#>WzzAxtA_R?yasZ48yv zsAd>{9QJeAucf2Ie$`G6`&A!HaN0Nq=HV61`Rt01%`F+24@+MDxgcWP=MC}w{Uds7 z=DqviW(^gF)|lg#%Oa`54CfT36(@Dmt|WRCk$-t`*Z5XNE};yo5{{=Om3%oVWKip% z*j?Q^o6KM3PRK^q!g42`lNXbHqZW}?BvwOiB!m!n)gs}Mng2p2)x&qk&7~}D5=%OjLm{&$`S3{4nLf#IrLGmcr4I7lf7Rsk!Z4 z4#CgKNrdsP8UE+hrvTG0;~pHxehR4^z}FiRxT&V~&XIGc?Zn|r|!%6zli%*$1r%_qPyp>VGK z0}&n_ls|d0jUixBET|Crg?GXkTptEUfT3^?ppnn3SHTSc3`7VQMlVx}eeY%-ryPGO z6V)F4wYOh6*?T9^-oDDlTR6)KZ<8H zU24>vNuQT7D>XOBrBD3|M&WqO3##>)PCqXLQny(xC#0LzfelHq(%U9DtsZz*L$pg2 zoda~yoPk#|5c@n=__#D2xp>`D5g#h|4yd^%*tBDc{U*>-<*A8(%Kq zyv_UH{*euCIXNctvO*TO<+QF>-Ho{+568x9O_gEwl3c}S8_YuktZIICg++Z=SoFR; zhV?(iTZ*`}EB^VHj{2Y9e)s&_gL3`PXWu<{^*?v=Y*PQT5WnDxe}3h~KQ$C_hm}9I zGf6KiWk0nBO*KFDm2hfWSMW1!DEO)Me$aolPvs~r12a&U0oQf0tdQWt*RLv!%2dx* zutw=k&KXxebzT-TYuKzQN*}nVMb=1`cVG(yER@SaOdCp1Lb3IS(K6h8~ z)RTkQDH{dx>y%<2mU5tSy>OD?zh9ld*LB2?P!WFh5_V{Yc88((?@`Fo6bZ^>QZtu2 zQH~T?4{QS|xVTb1Pk*8{CH)v)$;%)_+5fqWN;`N=SxgEa8ntGoZdzg zm1A#(JVD;+13Kua#W#G<#5ynM{Q8%}W$^Ra8{+^bHI>)DV9jao=OirGBr299rMPN? zxaCZ61|(%U=c<&$$%OI-WR$6e4w%Y5j9rA68g0&Q2|?wEh7Q zgn2v_gAW3e3@TQxUO?)8wr5Sx>YP2f^Wt!pQ+?LHTot)UvZY(NP1`C7F{t8qvL?A>2m-^>g1qyX&KJ^p)Ot%so;MU!QUDR51s{mv3qaX;81*BZ zWfm+{uxl9*9Iy>gf>oo~edCAW;3|r5B2Dp2BT_EAle6ivt&v{w+H;<0cg*tP=6Q7;PVi$_n zIut{w@_%Jg7PlubvY#|Qj;HQ?98djqpl>UlnqT+}gCH|}%Joi)XiVvfA7YFPAt@?h zQ93XCXiZ297Y2lQ=<<=&gU09C1U21g*Lb0HI9*0R=N22*2$6QE|L? zBop85^_8!)BhV;ss6O2fxXe^?2Fk#e06=|0t>nNYr+e9PrLC#%EYi@fb5?-R#%$b} z-?PXF^R{-bJ)+N17_X+ic=mL-nAFW!I>>MpCDZIli0t9tWE^4jk;XQY!zW2nyL73C zx{j0zT%?(T8;9cA+4*6g3ez~4P9%;xOpNMGS@ByqKGje9F$n!rqoTC7HCW=%-^R>S zp7OxdLB?Wi8|y%6e++vRAa2k{z|f$j29>)tL1-6Rp+0XOhpGs3F01nl0t#oDU2=Tv zEe}8Wwudb6MqK#-|ksaD30Ba&9LwLwkWIL@@Nu<>rSZG3&8o7&j;DwMRalOP1VLJbaY&3QGub^)W!oOQ%=d z&yWZCY5X{kuek+u0yN}0NaBbx>!iM)2kK4ZNjijKhHSxpkE7uvUW{PG0ZeKz!)SRP zsE1$}%vJ!Zk%TxvbO0@f^K21*BWkAOZ`uUfYHOJi)8B47vk)5y{Qx$825Q|xdzD-^ zcT29=MH?I?oqw+L&#if!f9}`CTiv~X?zVMPznoj>rI0DmXy7nicmfaU|*~Vnkn0|eU`;~ z+QzKPy=PM_Kx53F@Jo4aRH?M#ULR>gxV!D{c8}9`ce}A^Kk@`##;!PERrs^aUD%mJ z$%kID(Zt}Cte*mma2v0Q8xuH0R>f1buwYdK^tdlhX3u*hCi)l?MT&ZEMKlBq(WA_d z7;+)|iXYi8Ai>8tIA^z~(yFv4M-;#3<|fRlH;ZzU(lVZUk5b?F7@H2@k3k?>K5ny; znh65Nz{lpL6?C+gb~JjcNVrEiXx}$c=Oi>< z64&PJGWW2FwoEQ#U2WJUyK1~mrAl#EzP_7GD8YzyVD|E0f?U>!N4iq|(+xCUbqUz@ z#{g}b3wcH&o&^Rb5cEIdTedXt2(T)ByVhi^>=f7al_EBnfT_<)SG8hGnWv4Qnp{*o z(d%B)*<_hE^74gS3OvjCTPt<->o~>&#XJ`DG-Mh2l-{+k>xQnb0EHfSS7VPOI~Px9 zx4)0dF?^ie-gX`LjrCp=Oxy5xyeUcJeIg+NzBi(LRPX$O7=H@^+iGm!ZV-fK`9&?G z&7BkeSD<*2k8{Fz)Z=S+!ncW=EYS}09tMdjH1D1q{qE3kwKO3r<%;0EhwLj#f2S&D zT;#~wOcT^CAqH8<#%3a7kiB7JI~kF8`M{C@i^QgPBM+8bnfMfC!Pz=zCMG8?#e_aA z8paZveH;hnim~T5^1dwspi5=yB*68T;Apr}^FAd&Q}d2V27;{db=L+@BSKw2eQAgUDCz5`26OJ;=5{R_rs>fw_g?3BaU{*JN) zu0`>2dvVTk6|x%rXRZxi22K_17=dOOIYz)S0-cON+7}}rBybzw2%%{bsml?}uVjgM z{Eghu@;ZhSxj7;ZNtqd?+ECu*&ztDxr5ufDuU5y_h6EQ1;EKb3PpENW>LxO{LG5(a zw7Z774K(nWpgHSL~vK%Bj`^lBKV(3g_%q>LG+!M)9lrbBfq=3jlvaBam(^w5TdR7GSh-0hRXwjwDoR&NA z2Ggfa<KA(;TWZ4Ie8C4JS>`y0IxvTgo3=Kgp;n8 z%P`O~<047|EF}Z`R6ZlJ8B=#P@=hJrN}e3DQUca5U@4xABwo!hJuMLCEVwQNWA~Y9 z?-GFPCkO+373`0BU+ICO4JWDa;pqv&_!{*Pz;rmt+)3u^FTqLXM$HYa)8bmLBSM_= zuEOh8VA=$3310W}U=|@v>22hlzIrQUx_uWZiOK@1hsCn;UsT#F6Xq{u5-Yg}U z(rnUs%d1J!CJJkbA-wlTC&washPc8!h_tsvdN{7tRDTu|VUdVcC> zQ!L%E2qIr-oOIb`2%m&ajfK1;34*9-mZug4EW?GsitMs+ESF{N5m9$U+!)?j+!dbR z*X**GX9zT&*$4S~uE~Ty9*RpMvzN=e!YA4gC%%I~v(5q=*Dbo1&g0(-wHk=o=pg>a z(s^)?ln)TPVNFp zABP4(b|1Z*UV3|C276tm7JRIjDIB%Ca7txqxC}ir>r_epmZr$c~AhA9M#;1GvhR%5ETvqHy`8 zGgb77i*&-%q)vR1ft-v_3Jw#$g?v&@Sarhc`b%)as!{Xag;gYC2F*BrH)v=k0NE7q zu?>aev2Rrt&f?K8&LXjrmfVod5deLPToh2n2o`y+1iV z+B&P1yLc62m)48tkbXN}&0wVnw~{-U9=nE$c2V3V_C#qKz|$N?^ipcWKGMi%yvav2 zH-CN+ehMae5S>7xsPB;RbW*jUE!STx;{=N=w16syNnH@23a**r!TVTA?E0~ob&Yv= z{z&^If)YQHF8L#(YP|hQjbeIzKfh2v|I;!^=q>>&kE7{^dzugDWdYGaWKeM2&u@DV zo?y<70_O@L2rI$gLi7snuuA|AtCnMENyJY38c}_pG{t}#i#;EySSo4g(+?`WYK25nr zO_V=1oF2zvxj&EGpK0Y#i_J6jZNkR}XAxCAwI$X$+%~1z^`JB)-T5>=6AFGqv2j(=Ex366g-YHn;P-n5LQhP8T9E;i}kbcD5E+d?2t&Ltp5EU1>d zF=PFhJR!D1dLm$2&Opj#g-;??2Hji9JR8-un6 zF$>1C45w^qB~HG94n~3`#>yc~NBF}{oJ`?OkZAg@H^+ykTlXTcR_!7{HkA|WR%Rd} z;-~wU4R`96ruJIWGXe#Ls&<4(^l>ooaWYKT<761yb$lfh(@KVs=-nEt{46?h<57+u z=(k!VQNj#6vmtFEY(qk=FY-;|fpK-f;^i!KZ71n)-tir2!|0QJYIQeB4SBz?@9uL; z_JVPDlv(mVVp$8&^qFDUB=c=C57Mi*o|fcx%v0b>Y8=VIo|y&p2VTZAhBc#Kse($LISaE$Q~|3U>d29>Vm9f1PhlLEJ*d^ z^Mt7BE$rd~grB$&7sks6S5|^OFu+V3umx6zseFxg+bo4|<<_bMrIK^bw@GCAFxse= zPnzOn1AKh9X?l$>oe2uvc4}U$$r>Tf{?qXq&(S0e@6J;2nx)@0PQ7cMcDLsgMVLTH90fvfECc%;E%2`B-!D!G+b0#Y}Gal90?WWplo0^7;p`~Wf|yp4)=L6ms5 zB0Thd#KxgN6(|zX(NN__zqz>?Quu%o1E@svSokYUb8rCh`k7Whk_Wt5{v0G98= zt0<$?Xh>hs$Bh;$x7ClXgCvd?z9`3qR+u=+@J7K%-dkuB3`FLvp-^H)>A~L6dy9#B z*cgr5Q9d2qzA>h&LUT1~%R0BjSMwX?1GVYMj4o%FRyQBUR{v1?4eNY-c zUz9hNYCW!i+v7PvuAgF4VKVch87AMN0~$wp7O1@u3M_m4GwJH`GWbMn0abXwyP+3& z$g@kSTwNHuRk7g&oShP~9L0kuM+E%PaATZ)Gw{$yfAl0F;!DX^_St zQQZngNqxBVAgS_<0Yp_0DANEqe1N4tRke_{D@>J#sMfIOGax(yEs3M5r7af6*1rZQ ztr)SS9OX@JmBx0%ldKK7G4`;m&+$<3?czA+?6Jpq&U3|o>MW)_%5sskz@h(%0m}A= zlX&s+uTP&m+1vaGiL21Q9HFVdermet58{G}MXMWIdkr;G13T;oDDZoG*9UvVo?JhL zi!8^}MM-+oGNEp6W#C=6m8zx&ZAC}6F_x7a!`m)nprJUE+9dfs zqvnFpWbLprMbp2fabyzjP)zdyos@(Mw_=q<9B21)|PS9Z%fPmL^FAcmH%`Kf~ z2EB?&Oj#vmK4=XzougUUr!4%0YPJroX8P{IZ{%Uh!tP1hx$GD_?0f+RV`~=lQ;4bGijmK&X$Da%^LD$}Hs-5|SLT%Wg16t4-gnd=tmQPI z4<3aH3&ZVeW;jFA(g0?&u43kv0cHFILeSMX{$!+{`8)M^!+TgMX?RahcI_i)|1x+b zwC=`R$Y(J;3ggw3%0WrJz4wU1CLY3|!wJ3}a^MUjKYRK7`LlV=yF5Bt&UNhn_1oC z+|1hMejho7J99JJfugg3hqt5c#NpP%REK#gJCTP_+GrmFUU-;0DU@!&zIPINY2n)M zi~0{_lP^ZzJ8xQt=9;JN&|G8FKG58DOK-QnCSRv>UNoonYL^2i=)Ngdn|uyv5R52> zy}M*V=-25zyAa5NeLCaAG(ka2xcRKf2;H4vlNNk}ju3$Un?BWX3R)oAyp@9{DWh>T zbQf7wz)_?5I!8(8oLU#y^mt6fY|0%^iYSEoly5}aCSl@MQrp*VP>Y(fn{Xb1J9czB(hy10NCCb2nvOjJt;G-~AKd#1-0%^b&!1w<6^8sIqZk|F+ zk{re1YM~$dL2ecCh6_Jq$c_xrCsdwa7<8fA_lWj;hp%5%R)XiaeqcVaVSexa@gI-= zbBMjZ|9%fW)^7J6|8vNXU*%Khi*At&|HdA#CDUaqm4N8(T7Swxx?KAK|2$VQYA zMJ8C0C2XJELy_N3eDZu)McN3|B-OkkUn-;?Mohhkz7A|C&KBa&dxI)xLIoS_ck1l1mc}$CUU- zEf^i4Rb7h7JdRUe_q&H50d7>9`<1|((%>F`y|DazN2Zrz-vn!&lq=G4q16_;OH zfb#7O+1L-|V2xv$X-j~Qoy=c-!V8}^9hrvtmAi7JgUONb@X{pn1XhB%TX3v$G4~6< zkZa@NyW=j6hfzAElIE&;A~G&SyeXD9pL;C|uRgV8M(M=TZK!M{q^zf|KwRK=P|cWffa6)42TP%dvb2t|ZxWIR==;uM&m zH4swxhghTXO-;oRvc_%{M+1Mc%x-JP5TJoSbjY*}Cx#-|Xa0sWZXrX?a=J=5vO>C& z`7Nki8x~!VkZjlzy zuTl>_C60#|Y>B7+4@9?g;Qr*!YM5(Rls(el3B9~N432;?kFaOZh=Ma?yh(uV2;IZz zWtxSe5fWKMk6b|y1MI}cEZTx=TJ1Za`sW3L%JbXVHsW#k<;?$7_b|6FU7$bX7`Lk4o^rF1+~Z80rN7( zr@#Hc7Qa2pxo1mu`$);Da&R;s>ex7&YUkg;xl;LIcpyCJ+#H`)Pzp4+s|?SkPdN16 z!jW}MqR<_v}t_o;L!(Y#N~!~* z_j}t3a)}F|h|^{+lFDhV^^ngv33w1BpaLv)1UT$ zce(0;9!Fg16}`E1CoMjwts6!T<JC2NUz7)yYLps=h#Zgx%|B9oA%SsFgHXd97tbcO6CIu}w&Hv*)BWr* zLhuh6$y^~O-h>nMs2bfS1nwtS9fV3gc0avfvtG{#VzLc(Ul?%eq8?gIHN2YvB9~C$ z8dS6R(jdqvZ0?a=Hch{6YZA(Y6I>%?qq6VsYB&3ZpZgqW&m#%aFM20e1ASBHsC!Dg zmx|>StVHPq`pPTM67K~1)7ie*^#;1jdTN{Q1Wkk6sEi4V?av&%x?-NbQtLGsOi#HK z-n>z0yb+O=K29Lf@BE+P8Tlf93F)aL7o$gH08Mbb>1Zrr=hhJqaa zYENm2AicK=meCrv#~S(*e+ghoi(r;de=nTka}S*2dhO5wB}*1&3)0`t8>w)7r3v8{ z+DYJ%~hD2Ib&`Tg*Ifuol2e9=UeR5tXUOu zo0zhyrKyP`nKR1e-$FVM_SX(Fkgbn{S~U3QE__MTC1n8OqJ|OV_j9f-gjo$AKtXYDR$B4L}jj(>~zTbwK&F1$;LE#58OG z-jnEcvfQ~Mx{h#d)6ubY(73f_W3?WW4M1n8x{O{ooq~GZpV+Ynr;Y+lVJv50C-J5F zpC~SQO^)JLUl}+mmHNMG6=k%*IKMS(WWz!TFz(P!JGmH2iYKzN-YS zeEikmtmqcItO}x}F$QfgVMMD37pt0g3GEr_Nvm&bQi8~3R*cZQY$U=C7Z;bx&i)Z5 z?E!t1tzEAd963b2C0dXbjzLx{{m|i8cc}kK@V?Q*QflHSQ}i75aN(R^`@9ts+`*k$ zUw~Pm57C@?VHF4N^uFyjj@P>q_+$6;!|TP%y6pd~4Ow~kP5njJR5@OpD?W;tI z+ufaPn7f4=5#Gu@gn!Z0Q2>YV&5JUFA7>^8KpJ>=oL9^0s{d78vuR9&itJshHQfHD zvzoJ`60IH*p`2SV;IC76XTSc`cn(>`zbpG(Bv(_cSABaSn}_)dU@il zy$*HrMXT+hmo&Zl>qg(5G)^(Sdffnu3B5z=zT_ZEGU3PQN@`cf7x4o5oXYw z4_v1fJm}6nJ%fDO2htoQp>;W^PGHefxrEwr*oBA%l?KGNPtiEC!FzlHbIrv`Y@6Ue z83v(a!Ep!O#B!zjaRhc%xlvJoW~P|67_~FIUldz&UP_q0n7^bv&OejjO6d!OPwh(I zsRM)09@iM<6MQJuj_=AG8#{NA}{ck;vzK9ThGOCpPhj)yu3JGM@-C9qLfuhTCXgo}WHx zaHEeL>6J)EQ7rV<>4R=4 zuyvil#sh>lG=ByQJ4qxH1>Nw%x54++=tktLo96E^&9SJ|Bd{U$@@QEIfbV&nr_ZO= zyWH1*j=D1cMMIQqjeCE8y!fLDZTJ#9S~7LF32qO$S3$vqeKH+mfhuZSPg&})G(UX| zOT|Flhrz?PgqVRE>z}9T=Yi|N61d*pQ@zHpI76efKR3cFA*k;EHgQ@M)xk3I!e@24 znG}FFU1A-SFdQ4FIM$6j_l%Vlm*hJGg$M*%MoGw4C^n-VHTVL22`rbcCR)w?>|fgi z_Rm)iVAenglSKbA2&X8SRMs-JHOeV&6NfzAna_*06sycLOW*4$N~L(;nfH}s&FTs7 zBtOcXCdF0w;L99uDw=R>gULV*7e4zT(H$a&4oKdwI=Qq2mjj<^)Zrgr@v^fo<5%kT zcm|u}!tH4sJS$;m<4urKVPR0I%;4N)=W)n+tOk?&SV_>K*wdYc{0dSXniz}=CU&8s zRnv4^^B4LXh2p)$u`*dh9)^Dg(9^W$i=D139G?=;16Jvjsi?_Bpfni?nN!&!k`BN*dsZjZHXTHde{2m5`r@GV!bhgx}U>j z^Q_BBT-;2BMGN#Dg1AwVE?(F#@pM;<7-)>+81}EVHVwaM^-SIux?F6 zo72+Y_p{#$Ab3W3+f|t~v)3Aon9R;M9H_;9AwxkYgC{N)UB=SM)b`(?;uoly6s|xme7a=?F>v zh{{G{=D}{G{NywJe(Is1BqLW@|) z^a;o*1_Jnaw*Qa>OLI?Ay`kHH6k})Hk8fW9rF3KZowfp`Z-t1joBuB{b-=PI+<1b6 ztTY#>3v?Vud+Pxqg5FdDxGXSRf|za+Is>OKLMy$hd0QGJIUTjwyRUEk*57iQEH3>6vgYMOE_cT3qO zybfjy`U$lvVS4k`c7XT1Rn#`W<^lnuwS|^p1bFuQm_E22B|v^vTo?GJZ~7)SB!XJ^ zJrV!0LBtBmmhoE@3*_p4(&*_<$Faj$i6ssHXcrw#hie5x1UU=GN&l~%L!PT{ITnK_ zBPFE6ubYV%xC9mI=w6da3Y&!wv9g=Yt2yy(SbB%}6IE(=EM{a2jSQv7ejI-ACp^vQK{Ws)Z* z!4h{kIc%kIDBM@P9c1-xdS~Q~6t+0k>-8TIpM!yp*YWjuc0>dgXP3Q`7CJt78h z==`B8M2O}u#ruwocd)i~&>Y;U#F&IOs}=hlv&(e7`tjNF+W*ON2ryT^kE-9vc(~{$ zv{x*jYf-h3VVfS+tjVwSi@R4Hm>*|uDoWJiWyNb-)bj8d8Xhv*@vw*(riJ$y>JyV+ zFvc+3&_XW~2VlKZtFyAl9Bkt}tH2YFba@YLX2>@T>JFxTPaB1T1nG?Xrk114oFaP+ zf2FGWFC&dO_zq4v4QGopi`90TO#-(J73cvm_EQKyWQPp| zk1Lrf#vP;s{n8#?<#I`#DJLGc5!2vl6+ zZ>7c-7dLh}?wi>?k541!xA{FHk+AuhGquOw-O7|4-3%mJTG#i+U#~>SUZ!hPh!|*L-t9LSKzQ62fu8|+-rJ3$lS4+Z+N}=agokK3tA(s znp=~5hRTGKQafe+O_&u4BT7+vjP8oZ0mc`0B4Dq6W8A}o%OB5ZgqxI^fo9o20^?Z(^b;evwJtX-6aKDMYQPZS3UFl3A!8T0+)JwKD1p+;gbL zHqu|!12WQ&$;|4MfV|>>OLIeULz$025)iBzu=2^3s-H$osSH%|`M9K+=d$Bk=76Je z>I(@+g~5EIUAs`)#eadb@mM5Xe6Ec=VaK>FYCyvf^!#;tsFqq8ra0L!`h+GBe4ruy zWXgbu7H(whm3*Wpy(hY|!1bP3svO)T{+;Ro=Nr#^yE3yHL1 zq^d)QupNn3od5MzNsKErW(}Azfjz((*&&c$y=Tm@qKq+f8Ti2^6V=Xxk|MOL?5z4C zgpDM8O`r=Gs*y6Ur=7*mp`YhFFlmrABYYrT=#v)Eul2Z+pn?EdNhds$%%75Ydj6}0 zcGsuArsMh0&N-eJ+tkc3`5vFEFe=T2uP( z#wl(a?8$|J-&wl(O-n|YljsajHJ=C+LK<43_|X6vGSvWtWNW9`JduEr#eq8qqdRWO zlujE&O;M+NeMw^ou*q4FfczE|MWIbZ6T_8Nj1G9Jz+3Y5hhBLZ2S57W`~Hn$HvM(ZjnGQG>--ufSsq;kW<6I z`}lknBNY8Yz4vn2Y6@ycTBh4SE9X9~qTgy+>^!6_Q55FUVRr_}?8n@c^tNKOJtI`bsrzwM? zni@96-XSx`tcdkegIJgc$~mt^!+}Hvm<OyEe6Sd8`=zwFzNh?1>Py+6w+C$FbdS5Ob(s)^%Ghv8}dJwQTeFSItbHPHVC7Z+%yAZKmP=ymJNXQ= zjE152wQ)e~gzI89wD(2jl#BNZ+v3(M+v1g+>ydg+1$B^=N}B|zW}mtRgu+Ppl$J4O z4x7|Iy?BqLUFu+|dyjf2@N(Kzg&DI!BnMBFD6-jP3Vl?=Ea%5jX0;zYqZ!sDc@maO z-%j0h8D0Rb?*2L66bmDSQ0GYd_(?1*nPF4OC{q?HQYFN{FL;Ea>30{@W-!0gl zE_a!#pVeda(-o5jVD`NINLw4dn?oRXAxu^EArS+@g4Z?+oTeR6UX==iD>eHU5~y_e zk%@Qrb)>i+tPLpl_jP$0FPF4MUiQk#y|u+fNlja-9_^%~lgy;|3{t3NGox|Oo*mHiJn#UbPN zv=U;+dY8KK3Bb@bV#yIdxBN=>7nney9)?r4R0>fvd>u`aGF%VYh>AKY6%}8jS1Nq7 zy6~;DNk~QC}K96LA8P0L2wA$0&OpoLPO?P-dLv zm-||gbfDZzMb-mFqg?js(NIgNZ7Hcdr=Hj(5P)Nj$N-m7MxcH%wb-l_`vCP0XmzY7 zB+Lc3csPGD{Cd!L?my(Te-Cf3$c3h~So*!#YgGFd@j(}KoA`Z(@_d8Y;SUkT|H10h z?f$;7T^`qF4A^LbuCXM6{c^(FE+n(+YoMyh1M_|Hg%x9<2atCKAUEw5#j=ZoMs_NL z8QC#H48w{}9|Rm=ABLCOHxh@4PnIop3_`{L{JPXG;@_daTdM&`76k8rV=%f+x75W@ zCA&>F%pss|(6u+_IY%ok%?Iq&}ce&E3aT6y2nsr*>pBFde@m zbrcHUh2~8M>^ethV%Zjl3iHP7fUzPE?t1xQ+)4`IjSW+vVqR)=X@k0XT9OBB3!ceF zL^WA0j*j#r+E1S-MY~hZRxQA&)EOj-jGW#<>V-CYP~b8effxaaJtzdF{tI!FI;e+D zQLvPhK|G_#JxoUGGf{FdMp>y)YeQ2z;wx)-B&WjIn~gy|SRXM^!R7 zcYX_-gqi0*f{yx34yUWHmP;$WUmXx^(i)ubo0eu5mIR(%51QY+J@C~4gWTfVjo3G9 zKNBAvd8pST4A?c_ojH;_%#ggS&O}Id?VJwuqg7p_q$m>Ma06mu*U;~ySb$Ag0tqDd zyxX*Z?vNxql;tfxdSI;2uc%}fGseQ7V*1eN51)R#l$x1-q)s^+?DVwsXwI0*jR?7! z?xe~0@gLY(#QMY4>0L|b<;LmX{SzqNp8{xwHwv%@fsyYSHQf5)rj;1w@p143x3wN`Zocxg7nc30lJHm$i2en+7q5loG zNlOL9z)lmDyIDw9I<{I%G+gkTi?u(Pdy%4i)F#4KQ^y}u8szI9YO(wqYg-beppo2L zL|Gcjzs-_^d(Lht05_n^-=@kn(P# zN8*OZ-mlCR=M^cg{JIkitQ&K{C=no1G4ui9O`MW_FkzpvG?7q}%nWCj3HEaDs0S!CHNq2Ya|7cD!mIHh7UmRGFm>w*<{q_e z|G>+(TvzE;tqc2EQIaiJP4Wp0%0AO!Uo)))u3L)FK%g8xEyT%G#svDuBMSN>y`-CTh)x4O71L;QyglxO^$<(rzrx;K4?WMxY}wB2RQxLPy756x)C z`v9ez>3Spnu9{Qm&#TL%E4-xLWQ+X67URyaSBn75pXX)^7Jc5z{4(L!yam;tW`?wZ zQae7bhv9jZ%Sn20ABwApYwc{q16zQKZ()d0zfn&cDZ3NBwx4PR(x)h*q5*{~+k|g$LyO2{&Kl}UX z?~BwkIk}T8Zgy9-`ukk@Z)I`=N1jf8!KX8yUSo~5nuK|n)5r`3j9vj&>a=VjwIFUk zWTd9yY_#UP5er?qhXJ{G_f>fYuF7<2@JpoEqKsdoUYNSYt0ddRhlH#B2(r#+0YgaB z%vQ<{RMy{4*#m4kP~(AaWMO)on3?!|-a$umTC`$nQV&ZHL-s-G{cYIgY?m1s8H7W4 z5fNPj1jlnL^+>1;RK!u2Xi*ca)G!AbxtWZ*Yl>BNzurDNAI^#zc!81K*XYF9kc$!-PLY2dY@_{l3{L>V~lx1 zTL=6TKLu>$A^a(L)RPtJYhY-v6n=GrG72=4JV}1B@G0=hWm(%eb#O7qwP*zIU;5(& zw5Z#k6L?+%FX0IA-4hikRtDYrleSgtYVi&g_alakG-cCZStJH_~tYHRC& z<%ET|J~oVTU{PD z_C7&xzpEq{D*1@rfbof+rGlB`Ak|gtB`$_a!ZU)>Rr4!>`ZL1v@d2v|pcB@)ucL;H zA=^S;{AC#M zCYg>$0BTDjG?y?H+!K$8%>4zwk%1Gh4B;YEe7)Ze$F`3}#k7L|9zjU&J}E8;`y!xA zQEP0iu3MJIVXf-(@^SxZarLehy&QW|Y#nvr(njX9)gWD*s)Fgf2(13W@3y7KRD1}q zu$&l)SijuxMpUJD=q}gkI;)iRRgR}VMr8p)4d^*X@b@!dbC}}thVL4c{V1j0!Oc3` z2HSA0{Q!a&z^z(2IcLEet(I^tK-dN_tHm(kBIBgw_~X$g)=mN_t#L3B3v*XUrgsmN zfSbz!|Kam;Bnzutd1V7#EW)T@_~qGi^B=WrN5-HJkNM1H%bf9?H|I#>JW`ozg?| z^Ti7BwZHF~`5ao*s-l)&HGqaxr^ac9WgF+2qK^sS98z>qplQ z-#gD@bq!n#4e!THYODYG4P^DL0>Ie(o}n=qO{6Xrd8L6luwht7Z>`OBQxDyD(eYqhLAxyhItmBU2g*hj=-nQR9=`@-qGJdW3MlvSm~ z>V?qdR!ZxQD4nwlSnqu{m4292!EoN}a2@tjCE*X8`U1gGcH{CG5KTS}39y z{Parn!y;-_p$FR1SGcE_I<2pt<7!TKmNsXDt-&qkfMmA+*r0@J!Ye9E1X4y`3-&_8 zJEnxp!>*1Dp8rGVdE)0yh}!hgne5mjGQ&+V%WmYg4HtXow!zYrhIqMQ$W{;EkwG87 z!`{@vjnH&(&~`Xyf7-19t^R4a_P`lq2#O?T*JT^C&uluV#7%cJyM`8Z+&HS#)CIQE zQ#~d-&4YfawE;U?6>GPTSVOaF#t5Yx+2zx?%F27T_`!bWK93+-^&@dB#D~X2t2Rpi z_A{Yy*k30+@Y95xtATht&l9A&ApF@SffneQj?RaMr(G@i%_Fs7VxjDw&HUQvqPmv{ z+H$4ABXyu}!;UdU<}mwDlp6NoQnNU6 zv(W(oOnP&zxsIqtPyc1IeLC0y0x#cGaXxzEnpqrp z0zCk!zo)px!cn-?lQ)sb^!ovP)Cjby;?Bmy59!B(4;Ped?$!n+Xw`fItN#{Qj_)(A zyvH~2n|H9cTYsAIbK?C*8D{kl8XZ2=_K!PX9kUO9p!(}W@%u#g`b05gPpr)&`c^M$ zSGBB`ka|MVYO7w@>?@k=w$tqk=iBpkt3~$ld!84*+w(3Z@MMQ4*q4XX4X}sT`|IP( z)%Pyj%69D)Sc*oH>xM2;k1_OMcD4HAliOQj)Gw&@UpC-7xdvkb+8arwrhWbJQ%d+B zB+wuiQ|q9D0I1Lj#4ZZm6zC3zxQLYUS#Qr2=18P-+#SlyANuy*FFJJ3ZZdVOG+LMs6Q-!FCz^r<9(xQCZOOf5~RIvyR+y~G(21DBh(vD@9X+cDs9 zBTG7OjzTdqFe>TLBzDj{;KkV8W`&`n6dh4Z|EO1XKJOXo<=1Ea&tIoBy!JJJrs1BR z(a*V$fxgSyrm)i}y)dhh^bW9-C(dv`(z_AtHsL1K!Os!>HK zukfHUt!RcDMS>=E{{?D-C_E$9-tAMgg$pCBGN0}l?i&82P>qk?ElvONzPN|;1}esG!Z9rPwU7%RUITycd79=+ zvzN>gE9-D)1Jt+sf9uxn0G8;PKMB5(@p%ggF=6XlKRXUG>9b{%rk!FY(KX2y}|AqlaUNF$-W@?>2n|sRh zr@o*tHvQWeJUvkM(Ei*&kN^TD+Xp2!cNJ!*Pp?BLf+f8t7FxiR3aiGL7O2G9rvyq2 zu>3D=Fg|VZ4!KS{j>RUq6@rIFFf$QZ9Hl?LH6BAQAEl#aRu@OlETtOZs_<>9Mf!2C z2p=F0`6dBE-kVkh)nrED6QK)saFB3pkw-O*#8W^7uhIpr{B&pB0 z*d+_vQm@j&TH){HO1BPQc2TRelDe#sJ*A-zip@dA1Olj~fQe4~s8X+hYu10UT!V$T zd5bmMKrA9NsUf5@LO~Rclc;Df<049?hagF^!=tO(ZHjLeyUYJa(Gdp%#srp!pfJY|5V04RKTq+P8nbl>$xMA+OLvK{SRY@aXIxE#6-a}(BZ(NV^A zK2lQJ>Jd8H3DDmAGI(iQM?ICst0E_d^bS3mT>r>^>CajSa!j|ILCco;hQG`1X?FTR zkgfP0=}S1F%jTkZ_2u$(u-pCMb-82vhClh1fBGiZ@*uX2dD7yF*qO`X9K@!X%0FI` zcT;yhDaCABjv=Puv_|2;@~}KTgB|noMhUGDjt|W07)XHIC&8CHZ9A0rt?` zvzEA2bUsxu;x}IXjmRLk(7Ovg2oLLse&+izQt*vq&~wwXGf;Wn;u-VJewf|Q3{C02 zyzFHgVPY*2sj)l#_7p~uiI!PhKBLqbr~;XX}MWRqhickty6u3XSr1G|4w(9(MfEY3yY#-L`pk zQ!}?=?yyr-2;WX%;2YTY`~Bc;ZMj0BJ;^V;2LQ8HIc>eJ@i%*bhM~Rzm%htvvh$Xd za+;(6i2}W?GJe?`{KyABZnP!Q_KODq;8D)s!xl}^-(w=&^y%o$@9a>smi0|pdBv5A zYnpz6wLdV#0TnLGQ$x#5UIS@6bc^7iozMQSRVf7<6{rukT`!t_O*qn~Kj&%=0zX7C zZ0gtj9ghTVzKOgwPQeog%yV0OsxGuXJ=bmr%x3z{bcq+)7S zU2wXnb!3y^0Nd0XB8f0^doXNL9L8^A!#mQ-nxZ}7pVcd&`C$gKv|XUfs~RDj1ju0; z80B^g-8R{~zu=qteHbTdoOF)DwRAj4d^6_4Y~J()v7^oee3|kh@lt04Y&i7n`ZVvp zS?qfHGTZIHaQ8d|+g$fwara;FU}Fr>5PhAXZJ9|M9J zJ>raH?&*Z;ki`R?RexqpreE0v2ME1i#*@Aw<)(C@bt~&zn{OwpLYqa zXiiPvazMXkyki-vd^R#y3$0QBL#b;e804?5=D+8L=OxfvR3-7a9a!MIj>M^Ty zLjlG(-tz}lgOds5#R-@84Q28#6I`G;F}wWg@sFo4b)dHPZ$W>n#nKq1&`6RP*q<;< znoY`B+(;87v^Q}tF?<_`Ept;;u&RdPXWO_EOZRWM-tOx=_UCR!Le2+MJKZyHTj9E&&fZlYew3)Pb}3uQ?xM61y18LbNS_rh*XyHvCDj za@`*zyu9!61ydRppXS6823@v8^yh^W_OC-z&?^y&)z$oT`j*Cu?vjcZ2{E$Y6f5+7 zbF1(-Q(n>OXQ4gT@nG^!(0|1>Ygc^W(fEzFSGlAQg;a8wEQttAJDvAMD&=PgjOy;dRQV$5y`|~-I2zhT^(tRP% z9oo=aFbpvUwIZoL0)$3A8T^4}m}cc-TcHCnB0J!njT0j}OC1&46gY4mmHO03oD|Yt zCa7_TR$766kR@a?3`u&v?-@XFPY$BczB$%M{G0vHyZ4msJ*RO&7rBTwt>A!nJd19X z_d@v1XO+H3-tX$eq^EMVkDKeF!bJ#%7b%)BSuw!EK@-Hj;ZsLbaA9+ zFRkL4_jY~=g$Dh(2Vl-V9UvuShL*TdXo`%NpoQ9{$*OGUJV`t01bzol@~!Mi)@Ft4 zy~a)j0dSjw+s+sJ*%Q~_-zSfknb}Y1(`Re!+^#y3Z5|0)Vyb0JxdIqQan@Y=G|obAe<+8VuH` z%lnd!uk!@2myuCCkZgfIj-OAYp)|aOuiEJh=BL`C9oO&z0roQ%JEBz+`N3E?{kM)h z45wd_Sw>aI;OSnKXjBQo`V0e2;09Kkt%oB@*8#JPv7w=Ts+z&4U`lx%gSkUUlaqnblCz(W&5)Lgjt72!3mfE1lICXZ zQkz?2PG+ug#vVpKC~NG)ZU(2ge49NxSDXZN4Ly!S+jAb)*G7fGeN^WBw_Gts&H71c zuDOMo%Z$Q=TfDsK-w{m%lBvZ&jOge2N)5LAiWABs+NBM1$e2*19Hz@*QDp;Kx{pmi z3fMo37d9K49n&+$513eG9RGDV#xS#{RDHN|1CH?AQ_6j|Qiv?>Ro#hw$Z@wWr!8TDO z3a#O!hw2~!qYdZ?Q86YPF)FxFB84W93E^`_w>A-nSV!~G8A7Num*c%>P&+G<0A|-@ z{4#^fwdNzvPeGyZCGvk|1YYNU_wQvW@&Mq00&f(> z2J~C!M=xCTre}clAHDFe4y6f}JN8U(`fzyj0*-tvKJzXI!U1Bu{S2pzL7D^2bY!m8 zX&q8aJlTE2>Wo3;myUmB+2=ts9a>IH~s`<)gLpne*?z$2qF3lj{`zsI)kVs*%Fk)Bqvv< z1Jq|G2Q=0y8hmrNy$0)I@jOp_#&j#LS^tGz1yM8+KJu|a`%pEN6J9WJFRr5d>y16a z-GD5Uz`4bbSQve_nkF=yghWoE$>apXqepLOsnMnXm22Vv95hZLb~ivAK{DGBUOS|y z`=@G@u&ZbRlHbtCDapw*J%3Go^{>NH=BA;cv12iombp+8_I0T!@M447)u;(H`dMcS2n*1fLk}Ehu?_-(&R1oMPCbikH2$ zJtqFz_mn;+tC3=znz3qzIFxD4EV&_^^o{x zZpWcKq*UHyzJI@tKA)kG*}h&&mLmgPJL5*BByS&xfnxv8^laWKf(?sFnDgoBhl-|L zj^l6F3%B;MX7@hqzdC;0^ttZv%za1Bw9Mr2zW-~lX@AWOoKEkJY)jmy={3&ZraJAFch9#(8)pPSw*K4GOSYkfvbJ?1$DmM>{W)eLLb^@S`F&Lf{z7k~&3KOAD zM~$`6A*T6A^ypecYBKzBO>5dHCZxEGy<{8HR~C0JJ~-+yVUd z$Kcgc9TI4dU*K$4tto+^KFTE5t`(X)60f*RtVS**!`GDwkjlhfwkjQ!t&uw&8#gKB zxu{9Lb}7UXK)yfEKf1F2JR(}zIXxjY8-(!Z{S@tpW0@5O9b+f}$`-=|iu5qU6lQrt zOY6|Uigvb#tW4mlHrXj(P%1W^$5`dP;Wsd^0~<0!PMt zWxgs#eTO7_EX8YIoTW*`-k#6yQtP}%Dm3q``~jt|-{)!f@0De7A#AT1u-{_Jj* zjg*|seYAR!_bV3m&}jnp08iopW$SWd%!^0K7&svv93vTKOopP@D6Vq`i3D3V+^P?D zRX0UOZctWVn@@#llj{D7)Fc{_#u0 z=^d(KWXo5hh-PP~x zKLAHI8vq|=CLCJTzSCF_?TDwt;9N~*Gf>u>LmOxM+qM8n5FK^sNy17xsS?A<0CK}@JQ)2>FF=zVJi;- zi^F53cG90MBL~uxbY~%dR4h7OaYq(RDi?l<$YtW#a^=ct0W|ju0kD)_*Pdz)6;32! zl>f$n!~gkwU1I8mcYa$a$-S(y?52{JIY#dp_=2hy z7poaIWUF|!Hdl4_tFEl8E0Z&=3%0A|S!OFKV8)vpTld-oc4y=Mzh( zZ(el%2a(P`haCd;Ab7Mr`)4V6(m)G@IZ=Kmz?dGsF{*{{D+`#RK1>tG@7qSx^vX1i z=u5}$OfVCz%}cUb{JoeUnf$q?=#1_YN!XZrCO+uHUzytAYKCFZ)e}fOrR{i48d$Zf z4{1G*>Xy^oH}SzuB+^g`A~5!;@M0@azfj^kZ%w)Ad052{)r!{S%PA(7nfyMhl_yYe zTT0c%B~h!;40H5GMB^kp!lg=}vIQ3vhwOOnMhK|P5VC#VZ7JD8XN}x{W32?W8mlDq z{()a;2bH~$ctKJ5D0ZRk@6T0Hle$&sw0PGlcsV($AH$tH?#xuQmcojvUjd~sX3*S& zM{d1Xr>Nukb#`Td6Dn!}5NKCv5IWzN*W*Wes8~J~zvPv9sRZi)So~nabK-^Ly`3`@ zXS-B%$X0kv^X)YSZBLt3JY=r1*$`c#_}w6J^0D-WmFx03vw$uOuao%sGw^nP%gN$O zFF%xzMmn|sq8f2e9DYE719b&-yKEHFtw|Qk10z0~^}x#ez;0I%%j&7u5bG+X43Qu< zPse{6mU}J$FN!tTBZpIsB&zzNi7rsg_jm8tNt?ue_|*%mNllgbcKcKT#ru1SiW?Q| z_+yNI2KGMY+wCz&@bc;N+0#@F)BNU+HsA`)}vMX>~J35P>DaR~>dvznxH z;cdDSUfkc68ddzGD(giNX+k&VByKkVZE+P5rg3h2qof25NZ$e_;h)8+8s~dxCU42| zG$^)cy)NVK1ql?YS%(cI{;HF$Yl?%D-;{Ug1dwZ}#vBDaYAzx4@G_MEgWVQ1W2=)k zJ0-)6#10T;Hlxr;Ovg}EJSw1wh}67khecMIwmiJbZe-TfLv_beQ_vJ;s7=jGRbRpv zry#OhM>M)XfoD#=eIQ_g9}bPp3ec8T#sGQOPN%|Jgg?l;#;{|uu~q&aguTNk75r`; zQM^Byb>7qTOd3S)HaHjxrYjxDF*d4QMNubr`4v-kC0e|tIZt|Ul)aFJ|1g{iBD)Hl z8(hb*kt(VBFhtjK_ZC5nn(HS@k)93tg2JJq(He^EqO|{38n7gqM9E_#%r;@8)B-@$ ztd@Z1+0~r|8S%%ij=XF4h;Ie7p6ztwng&l%-G|i05@Wf!$~~$w$%G#6rnZd9t7Y+}fmk>!FGMoXqe9h=%WB-BPENclp+QZHl2ldF>WEI@&RyuYKw3&P5}k%h{G z&zSK1txFEl#9bz0W0MjE;}Inamd3Ayu#8CBv^vxvdYvQY6&f@7HY#*(x%d$pifM)X zYS0uKx3lvP^ztm%m+Mw4zTwe_j>Fa+E<{4AF;=c+g=4Ti2ZGTE4E{l3kwl>sqE~ok z880OIvlsnLL>8p`0!%W~#N7@0I#>NQRW6jsiQZP95C7xIvs^XC8nA4vmy83$qNDkV4e}#syUvA`1Nfp`x1% zA!d8=IgB(ykOk%EsJ=ofY-@F<7c;8O=B3i7YuQ*-#`r7d3!6QRU+ctDZPS9cxQ^PS z#a$C{u7E-B{}d0ik4-HLl)gptXJt5-Z(<1cNN~R-vf-gMEqwDiNrGR?vQYs-%=it+ zFF;S`e96>?z0tD)!32APMSDe9^=E*r{+Z=wd?QJ3g4a^XbhxadY`OZ61mLQIt0kB- zuM{jPcC*0M^CF?mXZ&v8FeN{o_ljR83r8{yJ>jTJJbVF5Z9#j%#v7B=T7L*DAf0=x z+dZcGE8Qcrz^gL3h_To)>IJx?2wVnFP;aH(6ZD>M3kGav~N$>fbo60ZzX=3ur7=rF1zV~?m zs<{q#GU~~;IT+Kgr+{C23`3jX@U>fxJ(K)mE!NOmPpi;v%=t|q^MLnQ2U9Fs{DSUp zv~hcoUfReDZN#2hh)Sy;06M~MwsSV^x9{EWyl7`&0FBS@p3m#o2HTvJ&+i;=*RO9* zcdak*lQrGD%N5$szgq9cCFP#JYYD{9H!IspI3G{qj*OZKAA~fV-A>G)0s&~<26 zIegVfZw3Pu z@Cc*foB?lrcM9vcpb{9zJAt~b?A#C7+8W^-e{l39dhJdR3&MS!vkbd5(>7GWkBM*A zjbH_V2TcG~Hf&@CETOIqjj*B~{N6%HXXx?^9WL48B-FyWEpRGW_6X#koE=Hhk4One zWa@MC5jgH;MA}St@@;KJ>wxsY-nXF|Vh%vhE;h>nJ!BpJJ-nQIbfkZWkkG2r_~2zG zhak>DUX?JM)%AJf91eRLAZhjdbY0^6>LfBinJ6Dk32X{zAL>|LRld zUCsWhPoZ%Q^@}Q4qXul9=YPj3=FVfX1+JQU6RTf|#mZ3P{R5`QPfQ4_pe^?LeUT7# z`5m=GJE1=Xpm9ay(2@c-ysZQMh7C;WhI*=a33YLvZmt94qos2TJ zp7`ed7L5d?-~cu=&A8FrFOG zXvg_<{Q`!Q7yT^bBNJ3v+C*RW3J)9T-zGe}l}W`WM5vnqx0VFD9=^WHFJaVMQ2=f}buK~MDL7B9Dyh;$X!NiyTpA0-H zwsO!p?69iIL^Q39tczJ( zuxnNWNFayl!7x@63F+jYvKI>e3rw*eY;v>fPsEA;E!b$O4qE(UZV)#uF)sL3=Ec*OSQ4NqJ$JQS7E6vTP zUPf3b4-;)!qi|Q?*+_!Z-%>IN;|D88q763j&dtY`c1E1W+)+H$XuJAR{B^wHV(;hA z&|g>&1$}S?rVp%afwO?%+YsWp^1W)xZchAu{-xFpQi-)|cMilBBYm9IqFXKfE4s3u3x|TD1I{4e39Dr@rfMRQl)uPYNnzr;rF5p4fW9h?6+hg^X zzD?urx6TjgWPw@FH>*l+rdsnBv6@ffyPxTWnB6pP5`E03EOF@g0%{|cCKgf0S_#UH zF6}}*O5=F^>eOw=O94fYg#V7gmu|d^|NJThA;XUv6Wka<`BkL~=DC1$zpy(0z1PA9 zeeb4Xu&13v8P#wjhkvyUrVjCF>6+1L`dVpr_b|l1s|c5u4YdRv^<5TVkCo|_#N<%p z_AZ2EIfv(}#s%%tbyWD%?4!`XSq}sgy}GWbXS^LW$no{Pk9BJxvne;5ywgQIHAZPB zdCnAT_ztJ??WNpzHW8`Q13h?p7sBsdH+O9C$!%<14HtioqPk;u?E5ysEQwzCPvc1e z_U@u6AWDos(ewj1++{aYdx`6-tID~dD{yj0yd79Pw@I1jMpCJi7WL@!iSvW4b-|c3zKNWOSTIZU-*WLL0^rKLX%@QDho~h zeVK3TJS@QRtBUZaa@*zvlPYXa&|yOk59hV@fr$#y(U}T5HG&@bwYsRS>XQl3ojQ+F z+bA98CMUHixsLgobX=Q~6=@um5-IIwQ#DhN$~*PE?O%pD_G*?etj$52hYzU9Na-&^#D_(B}D`G1;OYTe-aC(FsVRuKML9%(_eJ zWHd}{4p6MKv_-l!8Khj6-*KQP{{d**ZVAxjSaArGKL})ECvfg0)XQ1B{e~+l#4>=_ z*)Z=TMR;N1WQb0G4YpWY>>}5SwRud8X{=FKXI$i0aRV#o02bt|tZ-@n-+S=cLEWL? zeDIpV{6%h87wvf!wBPd}x0hBAMyH1s)~;>}9N`GfT3U2Ryp6h6z!o?@@a?4L-E$-) zd~c$t2$#P7A{yQi>TSlsZN&x4)AUiCFn2l${DV&Q_~vMihjbVXKCrhC5lTRWFzn3c`W?b7UQOo@Ne2v8eSKU&B$0h1B_4kBLJW zG3P_S1aJ7+Es@-wK0Ib4!YR2TR`poG$>x^DzFdCbY8c0GNsL~*KSzWoyoio>gX|LHo%}kMp6oQT z!qeT1Ar@WJ#ku-8?O05|Kl#h7UYKUd?=_@s{wb0_Po24P3{1=+I0I)f&bxtXO0&Mi zO6muHtQ%KW|HKcPmNYc0Dw*5UShxMt)D&BM^CIo0{#^V491Ovsk=M<1bm1zxa|7bH z?tf(Aem^eA0U`oj0c?Ikg2>qLsO~Zw``-SeOnkT)gd?7YzMV=^6PW`Q3ps77!tfYPW>v39*z)_~Vo}w*I-r6m z+Lk!|IZb2soLpe!Yx}K!@wkc2Zl25H%ucfz5gI)$%1d~nS{qO2%vwp-JUXBgKK7=1 z;RuGV(3N32NVWn;hWER@!61Bgb^z8i7`*4eS zvoHWsF7ke&8~TqLkOc~R>D%@k$Ob3_DLc{|fRjtSaMdA!9fJN_&wl_$Y|0Z+dgCGw^j=r&Cm;uX%lo8%vzagwF@xKq$UN$b+tM(3Xk=DNcNOLxIoZMB_iawhnrR=V_k zQVjBrGkH3|v(W1`S4r*#HDDNEdPdiCEv-H9yC@$n9ORO3*6I8#uWGrabix2XGZfHM z5V9*M<85iwRC%b~z0fo4`>ldPr};j8#Dt_J^4IlLD|L5=Aw)@O1j)dI^fcmV>&Gj$ zcf^$V&BX>)|9*P(IKP3)x_ZIVv}-gZvsbzmq)a0{U~-zKS)!DGszquNE%zDOs9#|s z%2^yB+?(r0>Q|>$(SDgP;j$kAcWCUTWS{yx;I9ZKSs{{h+{8+*9KPAvq1uIvp?* zUC~tC5+On2FT(g=SdK>D7%XSf>RgshJNUiuqHh2%H?Hpc${8OFxC+&0~3yZvubmw0;{9(2XDyZ#vMSG6Kze}?0Z~gS80scXQ-o;S^&yEX39@d!hMbt<3Et) z&u@Esh`3c|8VfiDLlN~3i7ZJdhqr0_GQ_6TFwO&4;g&h~2U4}4LPOjhkuqF~8LG6J z9C#34r$kPCjdLC&L{>exQny4~xOjgH`u&!ZEGXm#t>A$t%elGW#l;94ia-+Jf8gV@ z;du+@)Lc26v0y(n|H2S zeqe;Bl?m`jWttHkn`Pf^`N*vXi0!vCaNy%wnp-gYLt2bzbtaTX(bHN{P_WaRQl-pS zWfIy*t#T=7sk-3mWt=sYo$zHD__51O*U4$krksAK0Y0ETvWiXJ^rxGB99?JT-&>5+ zTjqb1?LBfKOniDAe#y{=Xjx~M*`e}ta7C2vm(kZv?=@3TRYMjLpG}wCr77GFD6z!X zON_hS$tXRu>C%4?Kj!NV16!gii}5SD%YDqv$u&?0=?1BPzXhdSa&MP=?mS5b5<&hGqjMO1_TO$R4W6lx@Og zB2DXX5AV!`LdL9yV8GK)XuEF=W9Kij;MT>>&%4!*>DOe530z;6snNm$3XLqV)nnbN z+``th5}%DsMWxb2Dw6;I!JIR|MX~gbvqM3QtN_JeG|RL42C!wLZzOQVaxRrCIwUPe z--Eo}a`w0w(a;07qt>>@m3e{?-HlC>CRgQy#BSYbdY4>q>%i6JWB z{Jc?BTOvyF&x@nFpftQ?WRRP=yEeNcLlA`sBbNs1$DTn(3AGV^w#NopknmQ)G$tq2 zXr0s-EGNvN6p^(vz{Di_7z}D<`oZ7&SAGLFUH7H0Wsy0uYTPiT_M&Z3Bd1Erbn2ER znFyKt>%5^+;_e=F$SzoUoyjPiHY-19$u2`VJm*BOxJOf1xVt6$uh4x~JDlr2GI)6nKB2NHr1rO8KlC91g@QUplYyZAgq$3Z2q;; z&NQk|>2HvQSjnJuOkzez;W4DYL)WT~RW+i-wi8Dl6y=hmVhCcc%0Zk!#$xZ;s8qt{ z4>q9h<-L${Abgg6j|+_srV;o(H8mC64=KX^Q|1?c6y=#jaTigXVdCqALMY}=EuaqP zgKibL_W2XPR_~-Sc-k9#gt@YryDWNci19pQ+kz{+1WqXc&WWGqh$OkTSMN7n0UkR@ ztry`BQS2fy;otuJwv7sHRX)!6WDbqDqTCFP{ezOhoG@bd+d>)oL@Z|TK`69Rh|j^> z+A@m+!{(dO_h-A$QOvsN=@pXN%WdS{={r<}rLy9t&fFc1{_N{~28ong_)jUfC%M1R z0_Jbu@;mEPE*!u&31)yDKhZK^64D5#nBFk(XSv23zF$Qt=&v2*NUEo#^idkZmiB{g z!*}%xXX{EGo%gR+g~Ldtoq%`J46 z31CAAJ6B_pIv*&4luR;%BP#d*o(9po2TJ!@RvuKTYf#QB(iF^>1H6Z9E{Uf~Z&Szh zZzGNTUq)K=<(ZnGG^VW(``FJ&|4BgN#1LU4pnc{qh;fy}}GC8T}+ zAB8l(osACuRnq6f-_$zB+}R9L^BKv|E>5(dH|+j`GyITJw|AW*=htD`TOX zuLmA@de}$4??Ek1m7)9(qyl}F3H+I|{r`5-!v8vHWdC;3;M(xte)#rFzT#d_BanS0 zS2?nDKXd)f!OIO}BBn@a-|*LZ0Ar%c!8-&IQ^A<++j)^>REKCkMb=dwVr{!(gTfD` zEmDl#V6$TeQdor~QKtiLX`~76A%^LQr-fuHt>_hG`&=D4q;`!C{>3ML+!;U1IDJ1i$7U}gvmNir>t!1ODnORTvKjv9?!IwUL39$Dq zm5c_Ktsd(Dc6>4fQ-%vVYYQCJ0VR_EUpI^NX>zA*1b2HtN^-#SXT236_j^hoZJrC~ zwh({B-rw~?6$GjCt1fXgjyog>e7W1JSWq*px3!6rnH)Z^&NImdMrI&@L@KfxT3r$C zMc3-zmOd`$WF0&<-hfi;xhr``>wxlcE&uD@ZpE1|<9(e;Bn217)+O?MbMlce>lg0Ml;t?YfQ{`)SB@yDq5--wq4rtVt`1Nkr0IkKutwK^`3s(S?xQj1N zCC#}5<^dCHkEkGHm%P_ea}PqEqB*e=IcS>P+!lcar`R(GSSik!bznLyBde`=iPtw=fZAvi#P6k^ZG2WN8ufL0(}UBfAD3uS+(2awuhaW75sVR;?XjSjm<=NU3v z8=V&;H&sW8pGnV3nQ+<76k#>X(r+oWAqdaC7sG8`TNgK_Wst%5jDl7CdUI2an^d|f zWR_9FA~?{pxyn+e571|>9ivQmQGNkD9i_b6IW$bM;k-x*=Du(l{r5x6@JQGFgoy!~ zd9hrIvSWldr8c%{e`|L@D4GwO^3Jd>-2c*5#G=^$4n{vUy7;S!FnV5}Lx0Q|;m{oL z?*EsrGgpTDSJ%N)a7DKsj8_*_>5CNA_=9%a`V$}#MC5>!MH29Ny7=TBl^5JMZ@!TV zm~+=6D*!>VU_$gSB}bI!iIfUL=2iHR>~gCr3mF015hvpsGuF?3(J@H&j!;-JXDJ@z%}Lv*W5K>@*{ zjMAv8ww3h1oIy)&6j)7O9Gx^!ic(~vWI1)=Q86=9A71mp3 zhr&T2+l9Nxa)Ym?tU<+T5W$1QvMh1kwMmO{%v{_;k2{R;(2dK*!<>g)oLq~)QxqrQnF;}FD2Ja^k1{KOV=z34KZ8?olRzQ(W5r^CeAvMrxc0uqAJ92^{8oXs z6dZ~*^rz@YHzDF0!~jzLTGC5ww*1mbEISnaZ$d9jKhsR}%mYO*=15m22Y@_pSGIkQ zf-kc~JTsA0t^@ON1|hHJI=Y_5GUu*w*?+T*I_V$)@q;@2_=~Bxza}MT*_o(Np;RrU z0q7Fktg`Bcp62e6g?R|K`&mSC)Ty#^C^Bi3kC%#vm!A;(;^qV$LPfW5h`S0OFI2`B z;TYi~&W2K1qdEB8vvj+_yd(*Yha*kJ##w!!Kd}R`rEF@ZyO;;ghDa)kcE8zP2rXzC zbXzlfyPh6B0f8WE1X!de>8fCL%xlFwfxj0Tkkl}cStE<+%C@i4LvZo~Zj6pk+-Ol% zon9)Galbnt83~#9FZ2*n`ew}RCUwmZRet;t%H|iKBu+UAI%S*bifH3Y0I`|lfsI;6 zN~vp!iF;~0e=g|rB-lZ_bCT_+Xq)~)5{QPucX_K;F4NVu@4FtE9ggrw?OqE#K2ABZ z>_zwQA?z4KqyR+!ZQ3oJPxhhOWVegd)_fWE-mQhvx#qc72{VJu=+(>lhFulj zIq1+Eq?UtVHFbgov^|!^o{@?kc<$+0-{XDir$WZyV}~w|G2At1`n;9 znc?NB{vL8lAdC8EPnl<)jUc%){|XvccYNqr;!v&k#Xw1y^~~^x210SoR9R|JYv{N^ z7tNAx%^7T!`YrjfmdK9J_1j>Xt}ik^XK5fh&!tw6WlH{Ch5Vd3nzfG5Dd0l2693PI z>XFm*Kx#AuKrXP_S)D+U{RX^oykCJei)QxpQm)x_^2XRMP~#!!wRH(Y`6cJY?3d;1 z8JuDQ|7)Q5eCh8%vE?@zE!OIZk!f6l;Sv-l0f;=i_<{M~uo@60Odb&OXpn_vA{@7uaw46s0&I*MQR{i&> zga<;5cL(7W<7tTt@`9baBsceGh9!T?SCnzu-Rv;x;&CEd-p(hD^lFSy35GITqAyVv z#>h1mNzfOOigbu8+fX&FkPuwct07RN!}6b8WHNu0gPA5{=~r(Y0@*jjnDznF@~J}w7ogc1V2E@2&Hgq@LrlPqpyu3}W)Zizw&X*< zGeK=YY)p0{rFAzOi*2jFxBf5z(LdlUT?TJaZ?kj4ybXtM@1tIN6Z)oinm+O-^h-DM zZ0lXfPP^e{qAf@bQIjSLBHHLtcv;093%$Anc%X(RA%tx$k1P#J8*3zYG}1hAh#KZ; zqzsw|kcEHciLEnf)CY#QaebfQn)+{J(6~lG_1W55%%Hi*65k6@rJ#y!J$QF~-PSvB5yNx>`v{H zCL1e=7c-V@l(G#;WJdIx;Gb>?6S5~XXl}y`=rj~X$R|!o=K^snUYQ-4${-AU%<2;% z?f42s4;D0k7nDiDy&ku0Bu?lEg%uorPMu44j+Qv?PBTsy>4M1z@l;j_dHz87jYSc_2i1TahW$Jf3BFBhrpTQCd z_o1v)Rwy9e=MsHAqaEL`70pdmQk_@IF5*j&H8h94{A;K=&Eub;W|^+l5#jBQ9^SAH zX|B+O6$I>f5x?^~uL&{@5d4x(N8LR9;i8Yy-G0fFs2Uns=;`{7!%i}At^2iLcAkQ_4ig3@0af%wLnpIt9^{*J8kSYPjMW99UIrSFI+ZmEw~xa1(dxLt4U9y zl@BIC9ZW~It-=^X-=(kx_2qi$B9k+%h9fn=F+*S^)r=@ExZR%*j072*;9S6W%9+qV5}J z4*Xui06^Dlv)GJ1AwSA5W=fj&)SRBr11=pGUu6n?`qeqBdf0#|@4M(hz*G5aR=jkg z@i*p2MYrh$)j=ifU}=L;l4^n(c(t7}>Gm=1YeL(jLS)Yq(z&k%l0i;H#F(WPcaPu+ zF1Q0+Jf<8_82`(3aAMpL4N3+X=C+<2gI$k2t~D=q3KIcYBen=;3%1;9Hr%KIcV4Yp zBAl`u31=LO3b~Mm*9=a`9sVWYuA5JCTJj7|_}{_fa(U{)rJv^-5|!Z1j|iB|T&6uA3uW1y ziYgaq0msD6&8F>;)Ro#oTe2yMEnQlpLjFT_qn?SHrJecNnfY|}v*(IUt`1Q`5WCVeY>8hXSYgj+ZU^#(Jb4=pj5&|k=8{ZB8|2=B{#%U+Eqdu z!M3|cwpwV{_=S0)_t2{Grv~<7%e%5isMtzv7bRWJIE~~`YA-*A9OkqG?B2E2z3B`8 zcYkUk#;RJsm*&V1s**0~%}{qm39gt9Y|2m%LVSFF1~oXM9-g#HKj+=eo!wIf6NQ;i zG&HN~ld_o`$GQ)^0T38aP3}JOBSwA(>K^HFJr5nFul+!t+3WJH%eD;Tz>24+WMzTC z1>5BQKdCK2e}44n6|=C&X*j_GZyTyOzWRHD{~?(CwCDC8;L(+>M^hdins6qP$pmHT zil(Rl!!CfA$)I2+5EZ7o`Mzc!svSYfOl>!Ya)gvEp8q1qwLIaFXjRqdC6+!cmAo$o zqEQ+FKkC~lpViRrq-K}h4ESPgN%VvjRFdqapBrNQ=D0FIojD_TbvkT) z>Mx+LT+4(CC=T-;dybdyx#mgwQ5azR^FcithU3&u=qqcuYb6%8N+7>moT(0RP^_rZ zO<(P(O~=1l9(U$tPwYq-rMOEQBty^ykV+hF&RBjh448@4RsSz3h-RF5&MPaB!&lAx@(9I+>UC{2Qck*l)I)|N~-$%xIUjP)S$Zus_<%`?g+3$N`g#J>!A*)$K$Km(F}oVVuolj+;@*ZgXAZL$v$ z885*pF8Zr#nWiljkBKhGfUlwOvWHMkslF2I%_$(|g1}v-^V5`;kg9iwS1jzQXAuOD zac!3^!%U1Qn0zpiWY+`l;7K||bzRbZ7$AXQ8;s_?Azs~TKA3Us+9!Gqi0W&HA@X-@ zXZwOFDNp>fU0SU;fPT?jIvE&Nii(D?c(M#ZCABYTVm=M#bE0vm)mDr}6p%fZ48 z|G^4itH*t_w3uhr=z_rm9N>9v#WB<;pdVA@>RUwOC{obmAKO!$IW&u-Uc2n2^$4@F zdz#`e^hWOJ9!0la7Gg~#teX{ArnPqUdYEv(?>QyC=q5o^+Gq5QLT~Ej_u6be7ALM0 z%af7yzKEql|2#n)e7%8-`NLzB<4CbNd9%iHK9v;dY(OR(154_#SYXDq<&6~^p!Y==u*y zur4<9ULR#i?YqL_Zt?l6NLO&ysr?W?D>6DdNs{h~AS;-E0IIxR9>z|dfx25D9V}>Y zu)t2g%y%2ox)&Y+luy$MZ_IeFGl@UX*{I<1uLhN1^Z@d`Mii1*YU?_}8We@eOrd*I;~7NXczB=Ga*hwcd)!P_RN zSi9IysEFLBw1E4HVe{4e$mR`QxucDI^Kt9b3Xa z2fP6X8&elf1KfwCS}lxtyB>X#cl(h>hXJ}1LHau7RpV29t5S7oA%>be1RW;Mx?d1R ztOMg?h_0sS)-yIVF9GVqS3}$rs-eX;9WDAfY`6t(v&(FuG8s54sr!B>K8~xvnJ~WQ zR>n8LFF6!1Vjy72HJuW%ZK?b0qHbyY1+pN`?&K`LU#i=5?$5NOksNQNC$6_FBT2%e zrrVgtINRhq7!#v3_^^|}+Wx$=bPp7wOOw`OJB4qs2`HU^GxKzgHv^5#JbXi!&K8>r zOkbuz?_v@s(xVnCBO@pKk&sCoz$Sl83q<2YD4~azlq0d(Gn3KNdGFn8?dz&88G+ma zRFYFV(RONxU#NV(jrwk(TKG8qz-srcX7)3Wuwz1@kvYpATy+^Z1i868QusUY6-+om zoJj_UV=hGlhm@)$-_)!{Hs~TmQoWFP_sEM#GNa7a;Iqp}JTF^)`QCaw^>MdgwQHZ5IP1}YF$3VEC?`$_MQSBWBTeO@ zz353eel=4-+yJ^BNp-CP`~|Hn_}~s5uSO{Tq!{sPXnmqo|Atkf+exVSedl+3AB;&XKOAfl&uL zJ?0nFMY=mohvOdBHDchrlzYPB@*&pWf!TEe$EvwagLRvP4y;*ql<_Dy7rfH^W<@A+ zsZ%yQK`B)}p2Owh0a64>&;9@(`Z<;TB zcN^X>+xZs3G_Ce8nCCDbIw}wiyJQl0h?{6P)vA!ZCeMR*jdWN8IXS9u{b#nu&>>tz z?xAsFFA>blz0e${Y@!Ejc_uucW#;dTo(xd7n>VEK-lUU@IUVL-!wf}7Mg`EXs_=gx zsLXtGZv7#k8P)CF8j76%=){A;$HaAUbh1($7%{Q%G;?52BU|zF+GjEHXd^ZS>y&p+ zO-&sEFMgQwL6>h{%+f*U$nafo;F@b}LvkCiawEA7*hiPwQwS0Hy+?ITl0jd6WanV1 zNKSF1oU1vd5=ufd#HiU>#^QVb*kUOB4j23}Aei{ppq8&mqa|3P-R}!&cCOH>HX`t( z3Bm}xb*FZ_Of+)I@y2*Xb|BZ>xL=`>5hcA^sQFFl5WBPt{ehzWHsAGSpeedKoWrI( zd+#rw!XM_5`H8w0C*2C2pg-2{Ek|hwG6$kX!IM&mF6$>8%=(UJ$!cKDq63lYfKp@X zgvBVeXZiyZYm*1pNH88zPZ!5+_4_h}Ep3-7@?+p5Soc(^MmysJ|y3QW(L)O`g zO~G#+4mRnCPk4@Zgj0!x>F*`)x(Fbe0w#c@l6ox!yr0xcw_bG1@&0f81oM*3(r%V# zgu~k+q3q|%AWLTCOWc3p6AIdWv4zJ6K)OV*SROQ8Gdq0 zRRf#(?junDsp9-L!%s#YI7xa4GG_lqqNSxrkiCF=;3#1X-76;yH_<1o_$84=xvbyz zZmK$1f-&)cJJ~1 zKNL!Asg=9tqzn2m>pPJ^3~E6&;t)BqyXw|o21O+9+i1!_-f|I}hPqj-oW|)6Q-j8d z)0{hMnO<=&6XxerA-V|2h4fs5$t*c}JI`1ZY-`Tt#Wf$dPS)YfF!p-t-aE`W87N4j zk&cXaJD0maqA_T~u#Hr!i4gyT;W*+$q?h6EJ~Xpmu$0;el&oC+ zc8y^~4pcdHl6XZ9r5>|kgfJ60VSZw@6WEgiHcl}c5D$;HGAAkKHtd=Z527-kRste0 zaTqIm;-tOB3AEowB{_Zv&lvXRpY6VI-E;m(g4t@8peOyw6@E?ibLxQV=f)g-~(-y%lV_51Uz;fHrwl5s7rGdQ%IFveizT_Kr}lvAqe4NPvl% z(D&GNHcuiuUMst75k9dgDJT== zFixV~oRU370y1MXZ@01jMtYXKRKNCPT9}D}?wp(+Y7uJ5Al@8ZH~PHlz^4fJc1y&c zv@$RB?2K*WXDV?q>T>wNCH4@UDo6{mK|&jgPbx3lfh4v9l2qNtM3xpn7uqSvmQBiw zt5nvhIX!jG1e>HMyxskc3&J~zkqt-pbj@%`e23oW#iJ!DBh}xP<(r-`zKo~yiF7Yd zV5LrN*pkYD*XfXqXW zz2Z}H$uCHAB;yxmfeC3l`APj6+AlOhS^^;PE7NdV040=(cS5Ok2H&wP5f>w*ds>yO z6Sf3|035LWMxgV+6USjDYO8zJI?axsQo0|D;Rl=>k5(AJCH#AfL|`=Sm7;+CM|v5e zR{|honTFvggpW#XWErETNy~x`OqLFm1^uuc}mq3z( z8^r5crxwWwE3W=r4QK1}o~ZU%)mJ{yG^7e{#?*?ua4@+s3w9FAR#fKg+t0;zY&=e{ zRf7;#pxr%HytXS1O^lG3bb3`vhHOvK1$BB^pHlF5NK@!T3DFayd-5;L$f$V zg?Yg#i=MNxpaEHAA#kxd>*|H0TcgI4k`+QN%Y18BBwEh|K>b>?tz&_z^m|0a5r3HNj@tB4FsO5Y97b?c2+7tI|HgFFjBtIJobN z_(0>>w{Z|Uhy7r>Gy(OTFW4Bw#CY7@=*J>u$0<^fzAqp=laJ+K3%g3z1l2LN*87aY zt>JVNtA{lBKkXEAvwvg50&(Vx6;L?z+DG4kCU9w@_!l0bp5yI-iTMh6s$0x*%6L7~ z`X0^pRSUymcOI`}4lhA>9<5SBOmH_QNvD`mj=by2*0b}GEKTGUSp zP>x{pYG$gA{tI%8((>>~&1d-8H6C|k!qc4IC#4ptM2)@0??;FH!Wc9_QROSs8~S4$ zDItdK{_lg0g;wi(T zk1b>=6sM<0jpj^br|@%}n5RZ}+I0P$;sE@j$0s>giCMw18}gg!$B_+f{vN3@E9z!6{F_-A$T*X# zutkeO*ybOoO$rO1Um##wy%hKJgN}vIa6kJg7lG6cRHj)=OxXJ*`QK!l1eZDQg#CE3 z+WsN#sXm!Reg$N|b7P|Fa0;CSoA*k_!N5Y5C#^<>D>6~Wc$f<ztG4<_J z0uge!wa0E_E^do?)w+&p7s*7q;A>7hTtSRc@6E=Y7YK1i-S`h%11NY8Hj7>@lP%0U zRY(B`T~&XLvI=ucMW0gu!Q5+_fR%JD99+#v5%L#!8~P zDp)LEqmIhi$-&`g^e9gRr32UD-az?2K{M)7N@y&dFcIUTxQk#uYG1#>v~fQ%Dm;18 zOV=Yt^-{m|erqgRTFO>cC77qatBFl7hK6LzpF5l7<|wff>wY=WBEo|$!9hE zJ5HS(UrMVN@&6j1tCD;~Lrll>)1|G{te%j%*5539#0C6xk) zbZd0cN8zkjhZn~<QczMP4*TFOh@5jfc5_KW@hBr*19*<3xBBGPyP^2B;2lw!>1Fy&Gbz-SoH7mgH zNg;7=*V5`+f}~!GiDo+%rBP<>ssQeegX^d>7}_JVACw9-$*9)Xt3g_InTBkCENyO1O{da^xgVlMN)weEuKwqS z$o|v+r8|k7I*D+;16VKD9%7PFx~_jtx8jqGeNr6#VU(PbZU=9>YAPB+&y zH*3@zGtgz(zM4z03V}(Rgx2otpFtp#_tb&?BfT7ckNTE3WEAy0Z*Cz}_s>P=Il@73 z)Ce`s=4TwsLI~GYM1`1xD_z1%WKRHg$WV_bzXCSscsJ$xB*M^Oh^)xb zsK3!9)rA+>j%QdqE7jB_4_1MD)ZRR-$?3b0SJMGXS>sX%UK{jWn1wH396JUFTX9^X z?|YBHfAPCVV0vwr5FFrX`(Rq7-~mH2RF&AmA;;&oTq#^}F)->=moU~>t|i|p%ih0u z-U*7u#n#M1ZWhiyjIaXQ^x)2`2!}3W3nrG4<`{G56$>j?IID#;_}lo%Olcbto94Gl znYl+h3%oQDW{iYDK}zzx{wfV}D+i7sjkZ>|I9mNtltX4E+nRH6O z1b_PU+6Q0IIUJ9~2~{$(^h9%v67|y-ARQTzQD~k?5Tx7o>mosG|2E={NjUX7a~h+D zST*Na=V(&{@rGN0(ls8opXVJVz>}QiYOHi%+WXSm zw~u*1_PUj`zBp8hRN{q|zkjjRZ(n`v663^fm+|g>Nc-CjFb8L_;iuK;o6u5#JG8ev zpprD7JD&e9KCkF6pT}JHQ>*p)eOvtWK2G~KV$DrQBudz6YQfdKy3T^o+*TO2oS2Yc zIHAJfW6b}}XTY;^z+s{~dcro#OhBg@sQI9>gFxI7Zf{>=8dJ^@&+LlJD&!b1{k?x~ z)0O*Tc?Ae}HR%od=P3w(WriR(N4#yOVKaCJ{czqmGgqvdr`c(j7F~n3^DmEn*;T_4 z%lNu`gA0!GE++UOxy<)NFNKIa9t^+jE;guQ{6JFoG5^^br-IY|0NCfVNW_K4G%eL^ zGgqmXYS^h8Eo zhVW>znGd92M+SH?&BJS!pjMnDGbL_mcK8_UM4wShVP#c{*r2X zu)do)EnXs)ZiomJrH+ab8#G-couPG<0&SG%z-8=kH~|qI`pXPn2GQ9(ByeKoe@SS; z&t225a(~AhCk*B0=(c@KW6g(fQ9BKeW*2z4c%7d>InoSL0pe`F-LR~*#5uV@t9|); zedq%e(va2=1adQ!crwYp)9o%!n1f-DKA+?uo^qg(kO4K@6PdM)6Up`@XI?(YnSHtJ z?u4F#+i++d_L0H)O78xMUw8H(QTEOWjeGcd1wFXyUs`;5{*cEGo(ciYGr{>$-H()U zvigPn0>%ogzY{jVy#UJibz?@kWC=iSyn zRd|c3AV@CLWcO`7eQzf&tEj8??VW{^Knn>WxPgL(12j80Z3qz7P7yu^g5=QK!Q1m1 zYOy-t=vEk#UUzYz5qAfQ+I8l7U=blgx{t^_g>ZM08Af^}c%Q6(AedA+D+)Vqj>-L= zficj|J4Qx-yL`My9Z!0OwhvT{AmSKZDwS@`Q1j4psJ%1&#bDVI$~n74n~z!^8*6T) zAFNQ-zUc@Q*1n4AvRKzM0^%3@vnWK4z=li@1;od8gf;&5<7T8|!ET?%?zh&jhkk>* zOVT+A)WP9>EDHuELV_PaE{v@uDid!-D|1@89zIYC32YHAWBLlLpg)IZaE8wDA5zj2 z1dcKPl;!o|rg{F2!pFj%Cy$n$@XFP@y&Y;%1U$#9j>fn5}nw#jh5LkgKXz(7A?C=T-n_f{&4K+y|? zae{@!01pL&WJ9Ynp!T;Ps(>P*59UvB$B1M0iz$9Y7BOXEz%~kH;WoKhXcA3Q-d9GN z2B4X($DGWFvs1KXGyXD$MUtHwl|X+sqfbG=4$!kjepq=YLNowNQ#i$h z#EG1XLn#Onlo}SPC4$yDVBHVPsUpc-wzQO$LiVrP;QHYZM?wyq>?ZAf>PqK{Fub)f z6Srv6*~Pce3d$wDgD|wW1#!>`x_n%idAxu5Fq1SnW+gR0J!Zmx#0FK$Dgkek%dzW1Z-dZ4O9Ew$ z97zq0n~otOFE2&Yv9$7S!U{R3Af6I(&kyp6zf#0gK> zQ~p#_=YS0UF4Pva(Ow0iy;QD_ZtyTyV{4b)v!uvFMoQyuqnLP;Qp=j{UK4xkP_FlY zoRap-79?8xtJ6wfzr2>|tE^|W`(R3&$|t$ z4}o-M+^y=8*cMi%g5`8U>zf(~)N>C#(AwS)8+Y+8b|%r(&=_CY3^1-0pXvb zt?r4Ut@bV+)#-vK&TclCSSYZHL)MaE5*s5fUp)0nA3Sx-oSqSw@mY|y?1(y`HPrUX z!-)Fnv9eIJkg%P7hSMeGw<`T^VHf9Xe&uuxa65H7V7rz(v zv$!d4TdpOIX+|>48eI~n;BHy-GAD#c#!*GRFAbDid4EKP1W{b`OY5oYX#HUOYhFZp z@66n(?5T+VVhhUuNBfi#cAwrwT3%01=W}bBol@FC|P(~;2x#6Z0>oWYX z#hibI7?%5wgA6)V0xu-ujT;axVb5uCBa%BclZi^moVJ}hf{+e|^i=^_UeXtw!!UA* zHsXTk>$<>$9~N^ua6Lm;2dSz~AwlVnpkQ=CMb?&n@P5*kMKk{|r84Y`)7XJ0g{O}A ztBb*qP{y2QF?h(f`wkpY1sMafRX(1H@He0d=eJy)%Tz=&lXzDZ;HQWw49L|n+wWNX zNI5L2!`8dsiz&`e>x8UfdDh1ms>QA>jau)$ndWvkRqNa(!XvZFG6tc4RPKHWC{3F8 zOi92(SgxokOam{D>_j7Sh94jT{0pQ?`&wF8)+sTr(cVh|EuIrDqGb<-6i|;9B#hW; z61Tw$(?|~JYs~a5t+_|ef}=*F_~LHbWxlIYUYF4~{J*S90}YdXe~T6l(*~K1B_s@B zg?)QTb;@hA0MV15AohgTsw!GA4g8xUSyP^?=to|??sYqM3{wIf19`Fsy|7>j{$O3l zNli$%`SbiZuCtczDH*`d;gp||gs7)7n?=6-s#(52g%a;Vu4n;L=amcnc!5%Ky#r>= zA0mYAcfHn2T}&8MvNMVj!`tGwK1yp;}-e<;AHU2_l zp?2i*uet0bzNYKdEgk(J_^8Gib7g)1?e4_;wJqqP0)j7}U|~ihhgyeX zVgDwn-YL$k-535P@OMhS{!!wx9j7B1vdZoo6&hcb@JY%ch3U=`8d*F&57$1gt@wHl zb~YHLmX!)Nqr&`DYdLO8{I|>Xb|lF}73WcV@H`ui^cq5zQm|B*pt|&F#hjp2KbQiP z=-c@b#=EpLFagAAVTFxWiLZi@YQMm7@n!SE(+jOvWfD<|p7>D{@fMg0TPxc9zMygO zVC-%LqCeuVBXYU2`*Mkv?*$wa5fwTu(K=O($Ur&2_1tKf5<72Oz9zMKViYt!sI1`W z3V6l1mk{l`-(ET23a_2+g|`0;I7*w-5=y4ph7tQ?2Y8wS4tv>=Q&}io zNTZrxmR#!~%d09?##gFD92urr^vc|tgbZu1>2%wP>5Fn#!@PAz;%M|RCeG(CSOS{X z6pkQ$vW+Q&D|5*vUb%}^{G|HM5!9q!OgqAKa9LB_P#@TPtEBSM+SJ$D3>AI8*0a)< zQ&^A{Yl8|d-NS+%E8B-H+a-mad~S@-Tju8yHES#Wk#22MHnVXnr|h6HQuw#(Fbm2U zYd{a6?9g<~)$u z=Y?03J^Qa9YTUS2sx;O?bdprs%nic?O&Y6=4ma}uw-%yocVixMhNrDj0H5{Vh;82F zg~xlgOR)_yS}>_7WyO6c=>7CVn?*q~@tzwR<^M86Cn^_WGSSJ~ml5hgO5xEpa^tFj z>d<94CGXP>F`qjW$uS0k0vQhA!w50|lte|*7;sB+lv5Qd0(YVyT|PB^9@t&>%F z81!;P^-1+$GpLiZyrgJ=T@dmM_$eo~)yq>3STO@%Ji#L_DmGL(rEWcSwc;2VyxKX0 z-|YTBZ(*gu`=)8UhakkGG8->HaBXo#FkutF!e%g`CK>k0NzOkq59~sVm-p1<^$HMzM2-M&Uo96sZ)_fq|J2L`OiMg)xcVyjR}$ z&3CC>GZU4TLC&qo0lzDaMErUEYPUO#M_HX+*}K%XlHe6;*^jm|gGlLGL})s#oNQgn z(M?{OC<_iy+{6_Lw7M9zSN;pAz(24I0QU_F1-zR&fJ)tU%&VCUB38q71vv~V8J}pe z>Ksj$lJKh|iIBo&GSGsx;`;Wai$>j9KpV!ZeKfAI(-#_=2`SP7ksr z_TGaW-G}Bhp}=L3!A?uV?Ui(V(5Y%agU)BFCh*^jliszw2YAT&65yIajKrkXFn-n2 zJt8*|I&WMn5>a})hn>vm6Fq@xP4c9&0ni?Zk^2lT=(`H`O1vUqT884kd7y|JSc#b7 zW6U6Q@1tE_d^YF;3@Xn_$z_U3aol@Vs{LlvXp@)sRdJBP$|H_qLskF_xS`;efN1OJ zHT?c`(j{MB994MGG0hBpHoWNUf-_w?*ff&Gh=SSAgCAv^G+n9!qSGU_-`UrY%%)9S zcaF5S%GK$o2R|AR=rrwa|D*rnlvvEt;=PSm1q*=t)`1^w3f;xBQif(K1nH(iF5@HQ zZ0}ese!b2zFJUOFKRkQ$f46Omep=sI!ULn;q)O3GL7MowuZMb?#@#EGooHn7cNOZ| zMKsQ^R}8MLeL9`ci-1FD-k-bm1c1Qzvl@ET?R*(t;^}>BLaT6jw z2G6Xme+6AVA#83Sf-4ularj4ejLv^f#~3dC-V`_uF_dU_nJhb<_)>g>s`yb4vex>s z{b0&FimfbzbWQiIhTJNA{SIOdCPwr)SU>J3P-q(9`AXL9aG&TQnQ|$kA z6Do5AAjOl)UFTbwHTvE?k6DI5UISG+kxqpz)ecj4ZNch{!Nz*7d9<$z?P;zKsAHry z8m4DU3pNfoMOGU)*O`x`>`+WQ2K7xD+5GUsFmZ=W2`V;u1bU)UYKBwWb|1!r6X;Zl z2#%-K@wD({rs~?G znJpax6(&>*l_?UoUdKC{sbL4SwOKaIH-K4RtObVU$u1eQ$`WEy%x{;c&q=H?6fZe$ zAwcKq5Iu8%jSK=$U&^fS+kCX%WuZw{6{ybN`}`TUz1#dS+AQQk&;kTL_HQel_NQPH zK3F9g;aAC^zK%X*Fv|UX3Q(G@FXU)~3ZfqrV}#NSrr$PwpWz!>GX?J=-RDL@$ZclM zT+`-w$eTmSF9QA?6`lrlND#kMs+uQr05uB2e)V>^v%hhJV6lxke9=OayVV9kz-@Xq z7OsKL`Tu)3BbY-X1JPR*7U`uKDr%QFTt~r}^}PvD26_VYjtli>?E0XuY&fQiCQKa} zigS|n4oi`di&;Z3Z{!BA=4mZ80lABVQ!(ix( zDOq0PnG?KKNlqu%G{l8kXehooxcyn$B}`C!D_534-(Iz>dVh7jtN(Q=f>)36tz(KI zgW9>alX}*WD|9HS&Qg=Jo^${<{ntjIo^5?&$=s^we$gt`nNDX%_-m!;xT6V|-_e1;`+XiK2?UZGJ=GX3K4l1gq4GmWOXk!@#c8EeBSoRaxnm$ z$rq<|GCEe}aMkssp$f`=9l+1!>3wS_1^Bq!J5#9u{2f#2uef{Eym~|QooDMwu7@js z6z=Haer?@yW9hp!vt5S+Tg;m-*<~**s%kb{8nu3QFbBT zM19E$EpmDI4*0q1U=3elwJEx@knP@(d|&wih3}xyEThnj+taT%KO#RysjRH)Ny0Gm zrsq67onG1(EAKe&;bDh$f9AOg;3G-4F;(gF1Y9z1pPYskEB&%vi9cuMcWl4IXyN=J zH!jQSm+dY4IuMH0mjQPj_k-^>_m*QRE!xH_@;gme zy_5-D^`UIM+_uTLB%McH<$3V0dJ|D)>lv5Q+nrh?M9l0QfUw-X@{T~0V5u99a%}iS z92HZAX^NDd6o&u=-pn5<5s%glMBs7DFXgIlTKo|zM!52dB}au$0rS7H4^GWQ z&RT6T3udSpT&_jwd`Vs~L(-;%K}$i|0aJp)Db~sFy`RG`oV`*-+#v&d?0t?aP6sDmN2U#|*%H4CcJ_w}3)^!0Ca2$NiBL2b=xu(t|`l ziv85o->xARNh5)vzZ|2!T`rIKVNn>LSTuugE$qUoHc-DaA&YO~MwOqfvrCC?jZc8! zH8RE-p9C7B6{OEAUn9~zQlBv2wQ(V*TS%d*J%i_NvzPmbYm>0BK!55}5hyFMiV$LD zJ=Z(hr%MLz#|-asbvI;BzB#4!xB%GcWeC$kP=LVO_1iMaHzdey-3Xw^bE+wH%nAQb3OWTa_>5HUD-#4Ny?7?c zrPG__0D6Qt$~8TlbAXm8>6~~oZh+tYI)%rWP-+|I?6%e)oTjue{BvOYr%;J}T=qN& zULBKohnhP#y}a*ZwnoLi{HFIw(XA+zGgR$_5?-n8`J>_!cl|F_p)p&>FjH=YBK(6> z-2@AG#>qoA*zLey+mg+bVMQ^9`x7;_btXkcmwXANU7zj!+AZ`5g6{hlAd!X)sw0!} zX_#({Nvm;{k{EiZlGO$B?C+GuVM}Ugo9nD℞8fuN%)yo^LV*eBv3X-?B_S**h5_ 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 TTyxE}``7+|ppvOs07wl0ocA2q literal 0 HcmV?d00001 diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index ea644c03..31b4748a 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -11,10 +11,10 @@ clusterGroup: external-secrets: image: - tag: v0.9.5-ubi + tag: v0.9.8-ubi webhook: image: - tag: v0.9.5-ubi + tag: v0.9.8-ubi certController: image: - tag: v0.9.5-ubi + tag: v0.9.8-ubi diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index d4a7f257..38744c88 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -278,16 +278,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -350,16 +362,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -376,10 +400,18 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string name: description: Finds secrets based on the name. @@ -414,6 +446,15 @@ spec: - source - target type: object + transform: + description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + properties: + template: + description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + type: string + required: + - template + type: object type: object type: array sourceRef: @@ -506,9 +547,16 @@ spec: type: object engineVersion: default: v2 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string mergePolicy: default: Replace + enum: + - Replace + - Merge type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -534,6 +582,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -556,6 +607,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -569,6 +623,10 @@ spec: type: object target: default: Data + enum: + - Data + - Annotations + - Labels type: string type: object type: array @@ -608,12 +666,16 @@ spec: type: object type: object x-kubernetes-map-type: atomic + namespaces: + description: Choose namespaces by name. This field is ORed with anything that NamespaceSelector ends up choosing. + items: + type: string + type: array refreshTime: - description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. + description: The time in which the controller should reconcile its objects and recheck namespaces for labels. type: string required: - externalSecretSpec - - namespaceSelector type: object status: description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret. @@ -1325,7 +1387,7 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -1371,6 +1433,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -2308,11 +2373,72 @@ spec: - apiKeyRef - userRef type: object - required: - - apikey + jwt: + properties: + account: + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + serviceID: + description: The conjur authn jwt webservice id + type: string + required: + - account + - serviceID + type: object type: object caBundle: type: string + caProvider: + description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object url: type: string required: @@ -2828,6 +2954,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -3734,6 +3863,9 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string key: description: Key is the key used in the Provider, mandatory @@ -3762,6 +3894,9 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string key: description: Key is the key used in the Provider, mandatory @@ -3798,6 +3933,10 @@ spec: creationPolicy: default: Owner description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Merge + - None type: string immutable: description: Immutable defines if the final secret will be immutable @@ -3815,6 +3954,9 @@ spec: engineVersion: default: v1 description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -3960,16 +4102,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -4032,16 +4186,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -4058,10 +4224,18 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string name: description: Finds secrets based on the name. @@ -4096,6 +4270,15 @@ spec: - source - target type: object + transform: + description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + properties: + template: + description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + type: string + required: + - template + type: object type: object type: array sourceRef: @@ -4188,9 +4371,16 @@ spec: type: object engineVersion: default: v2 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string mergePolicy: default: Replace + enum: + - Replace + - Merge type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -4216,6 +4406,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -4238,6 +4431,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -4251,6 +4447,10 @@ spec: type: object target: default: Data + enum: + - Data + - Annotations + - Labels type: string type: object type: array @@ -4636,6 +4836,9 @@ spec: deletionPolicy: default: None description: 'Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".' + enum: + - Delete + - None type: string refreshInterval: description: The Interval to which External Secrets will try to push a secret definition @@ -5439,7 +5642,7 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -5485,6 +5688,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -6422,11 +6628,72 @@ spec: - apiKeyRef - userRef type: object - required: - - apikey + jwt: + properties: + account: + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + serviceID: + description: The conjur authn jwt webservice id + type: string + required: + - account + - serviceID + type: object type: object caBundle: type: string + caProvider: + description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object url: type: string required: @@ -6942,6 +7209,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -8124,10 +8394,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8191,10 +8461,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8300,10 +8570,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -8340,10 +8610,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8384,10 +8654,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8405,10 +8675,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8425,10 +8695,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8461,10 +8731,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8500,10 +8770,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8521,10 +8791,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8545,10 +8815,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8560,10 +8830,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -8578,7 +8848,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8608,10 +8878,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8623,10 +8893,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -8641,7 +8911,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8657,10 +8927,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8672,10 +8942,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -8690,7 +8960,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index a549223c..8c6c5c0f 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -278,16 +278,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -350,16 +362,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -376,10 +400,18 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string name: description: Finds secrets based on the name. @@ -414,6 +446,15 @@ spec: - source - target type: object + transform: + description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + properties: + template: + description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + type: string + required: + - template + type: object type: object type: array sourceRef: @@ -506,9 +547,16 @@ spec: type: object engineVersion: default: v2 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string mergePolicy: default: Replace + enum: + - Replace + - Merge type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -534,6 +582,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -556,6 +607,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -569,6 +623,10 @@ spec: type: object target: default: Data + enum: + - Data + - Annotations + - Labels type: string type: object type: array @@ -608,12 +666,16 @@ spec: type: object type: object x-kubernetes-map-type: atomic + namespaces: + description: Choose namespaces by name. This field is ORed with anything that NamespaceSelector ends up choosing. + items: + type: string + type: array refreshTime: - description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. + description: The time in which the controller should reconcile its objects and recheck namespaces for labels. type: string required: - externalSecretSpec - - namespaceSelector type: object status: description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret. @@ -1325,7 +1387,7 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -1371,6 +1433,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -2308,11 +2373,72 @@ spec: - apiKeyRef - userRef type: object - required: - - apikey + jwt: + properties: + account: + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + serviceID: + description: The conjur authn jwt webservice id + type: string + required: + - account + - serviceID + type: object type: object caBundle: type: string + caProvider: + description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object url: type: string required: @@ -2828,6 +2954,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -3734,6 +3863,9 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string key: description: Key is the key used in the Provider, mandatory @@ -3762,6 +3894,9 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string key: description: Key is the key used in the Provider, mandatory @@ -3798,6 +3933,10 @@ spec: creationPolicy: default: Owner description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Merge + - None type: string immutable: description: Immutable defines if the final secret will be immutable @@ -3815,6 +3954,9 @@ spec: engineVersion: default: v1 description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -3960,16 +4102,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -4032,16 +4186,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -4058,10 +4224,18 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string name: description: Finds secrets based on the name. @@ -4096,6 +4270,15 @@ spec: - source - target type: object + transform: + description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + properties: + template: + description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + type: string + required: + - template + type: object type: object type: array sourceRef: @@ -4188,9 +4371,16 @@ spec: type: object engineVersion: default: v2 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string mergePolicy: default: Replace + enum: + - Replace + - Merge type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -4216,6 +4406,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -4238,6 +4431,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -4251,6 +4447,10 @@ spec: type: object target: default: Data + enum: + - Data + - Annotations + - Labels type: string type: object type: array @@ -4636,6 +4836,9 @@ spec: deletionPolicy: default: None description: 'Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".' + enum: + - Delete + - None type: string refreshInterval: description: The Interval to which External Secrets will try to push a secret definition @@ -5439,7 +5642,7 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -5485,6 +5688,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -6422,11 +6628,72 @@ spec: - apiKeyRef - userRef type: object - required: - - apikey + jwt: + properties: + account: + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + serviceID: + description: The conjur authn jwt webservice id + type: string + required: + - account + - serviceID + type: object type: object caBundle: type: string + caProvider: + description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object url: type: string required: @@ -6942,6 +7209,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -8124,10 +8394,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8191,10 +8461,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8300,10 +8570,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -8340,10 +8610,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8384,10 +8654,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8405,10 +8675,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8425,10 +8695,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8461,10 +8731,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8500,10 +8770,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8521,10 +8791,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8545,10 +8815,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8560,10 +8830,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -8578,7 +8848,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8608,10 +8878,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8623,10 +8893,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -8641,7 +8911,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8657,10 +8927,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8672,10 +8942,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -8690,7 +8960,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index a549223c..8c6c5c0f 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -278,16 +278,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -350,16 +362,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -376,10 +400,18 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string name: description: Finds secrets based on the name. @@ -414,6 +446,15 @@ spec: - source - target type: object + transform: + description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + properties: + template: + description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + type: string + required: + - template + type: object type: object type: array sourceRef: @@ -506,9 +547,16 @@ spec: type: object engineVersion: default: v2 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string mergePolicy: default: Replace + enum: + - Replace + - Merge type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -534,6 +582,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -556,6 +607,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -569,6 +623,10 @@ spec: type: object target: default: Data + enum: + - Data + - Annotations + - Labels type: string type: object type: array @@ -608,12 +666,16 @@ spec: type: object type: object x-kubernetes-map-type: atomic + namespaces: + description: Choose namespaces by name. This field is ORed with anything that NamespaceSelector ends up choosing. + items: + type: string + type: array refreshTime: - description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. + description: The time in which the controller should reconcile its objects and recheck namespaces for labels. type: string required: - externalSecretSpec - - namespaceSelector type: object status: description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret. @@ -1325,7 +1387,7 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -1371,6 +1433,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -2308,11 +2373,72 @@ spec: - apiKeyRef - userRef type: object - required: - - apikey + jwt: + properties: + account: + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + serviceID: + description: The conjur authn jwt webservice id + type: string + required: + - account + - serviceID + type: object type: object caBundle: type: string + caProvider: + description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object url: type: string required: @@ -2828,6 +2954,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -3734,6 +3863,9 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string key: description: Key is the key used in the Provider, mandatory @@ -3762,6 +3894,9 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string key: description: Key is the key used in the Provider, mandatory @@ -3798,6 +3933,10 @@ spec: creationPolicy: default: Owner description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Merge + - None type: string immutable: description: Immutable defines if the final secret will be immutable @@ -3815,6 +3954,9 @@ spec: engineVersion: default: v1 description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -3960,16 +4102,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -4032,16 +4186,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -4058,10 +4224,18 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string name: description: Finds secrets based on the name. @@ -4096,6 +4270,15 @@ spec: - source - target type: object + transform: + description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + properties: + template: + description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + type: string + required: + - template + type: object type: object type: array sourceRef: @@ -4188,9 +4371,16 @@ spec: type: object engineVersion: default: v2 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string mergePolicy: default: Replace + enum: + - Replace + - Merge type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -4216,6 +4406,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -4238,6 +4431,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -4251,6 +4447,10 @@ spec: type: object target: default: Data + enum: + - Data + - Annotations + - Labels type: string type: object type: array @@ -4636,6 +4836,9 @@ spec: deletionPolicy: default: None description: 'Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".' + enum: + - Delete + - None type: string refreshInterval: description: The Interval to which External Secrets will try to push a secret definition @@ -5439,7 +5642,7 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -5485,6 +5688,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -6422,11 +6628,72 @@ spec: - apiKeyRef - userRef type: object - required: - - apikey + jwt: + properties: + account: + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + serviceID: + description: The conjur authn jwt webservice id + type: string + required: + - account + - serviceID + type: object type: object caBundle: type: string + caProvider: + description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object url: type: string required: @@ -6942,6 +7209,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -8124,10 +8394,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8191,10 +8461,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8300,10 +8570,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -8340,10 +8610,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8384,10 +8654,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8405,10 +8675,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8425,10 +8695,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8461,10 +8731,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8500,10 +8770,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8521,10 +8791,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8545,10 +8815,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8560,10 +8830,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -8578,7 +8848,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8608,10 +8878,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8623,10 +8893,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -8641,7 +8911,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8657,10 +8927,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8672,10 +8942,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -8690,7 +8960,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index fa8a268b..bd543b06 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -278,16 +278,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -350,16 +362,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -376,10 +400,18 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string name: description: Finds secrets based on the name. @@ -414,6 +446,15 @@ spec: - source - target type: object + transform: + description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + properties: + template: + description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + type: string + required: + - template + type: object type: object type: array sourceRef: @@ -506,9 +547,16 @@ spec: type: object engineVersion: default: v2 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string mergePolicy: default: Replace + enum: + - Replace + - Merge type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -534,6 +582,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -556,6 +607,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -569,6 +623,10 @@ spec: type: object target: default: Data + enum: + - Data + - Annotations + - Labels type: string type: object type: array @@ -608,12 +666,16 @@ spec: type: object type: object x-kubernetes-map-type: atomic + namespaces: + description: Choose namespaces by name. This field is ORed with anything that NamespaceSelector ends up choosing. + items: + type: string + type: array refreshTime: - description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. + description: The time in which the controller should reconcile its objects and recheck namespaces for labels. type: string required: - externalSecretSpec - - namespaceSelector type: object status: description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret. @@ -1325,7 +1387,7 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -1371,6 +1433,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -2308,11 +2373,72 @@ spec: - apiKeyRef - userRef type: object - required: - - apikey + jwt: + properties: + account: + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + serviceID: + description: The conjur authn jwt webservice id + type: string + required: + - account + - serviceID + type: object type: object caBundle: type: string + caProvider: + description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object url: type: string required: @@ -2828,6 +2954,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -3734,6 +3863,9 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string key: description: Key is the key used in the Provider, mandatory @@ -3762,6 +3894,9 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string key: description: Key is the key used in the Provider, mandatory @@ -3798,6 +3933,10 @@ spec: creationPolicy: default: Owner description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Merge + - None type: string immutable: description: Immutable defines if the final secret will be immutable @@ -3815,6 +3954,9 @@ spec: engineVersion: default: v1 description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -3960,16 +4102,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -4032,16 +4186,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -4058,10 +4224,18 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string name: description: Finds secrets based on the name. @@ -4096,6 +4270,15 @@ spec: - source - target type: object + transform: + description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + properties: + template: + description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + type: string + required: + - template + type: object type: object type: array sourceRef: @@ -4188,9 +4371,16 @@ spec: type: object engineVersion: default: v2 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string mergePolicy: default: Replace + enum: + - Replace + - Merge type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -4216,6 +4406,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -4238,6 +4431,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -4251,6 +4447,10 @@ spec: type: object target: default: Data + enum: + - Data + - Annotations + - Labels type: string type: object type: array @@ -4636,6 +4836,9 @@ spec: deletionPolicy: default: None description: 'Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".' + enum: + - Delete + - None type: string refreshInterval: description: The Interval to which External Secrets will try to push a secret definition @@ -5439,7 +5642,7 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -5485,6 +5688,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -6422,11 +6628,72 @@ spec: - apiKeyRef - userRef type: object - required: - - apikey + jwt: + properties: + account: + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + serviceID: + description: The conjur authn jwt webservice id + type: string + required: + - account + - serviceID + type: object type: object caBundle: type: string + caProvider: + description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object url: type: string required: @@ -6942,6 +7209,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -8124,10 +8394,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8191,10 +8461,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8300,10 +8570,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -8340,10 +8610,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8384,10 +8654,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8405,10 +8675,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8425,10 +8695,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8461,10 +8731,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8500,10 +8770,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8521,10 +8791,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8545,10 +8815,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8560,10 +8830,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -8578,7 +8848,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8608,10 +8878,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8623,10 +8893,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -8641,7 +8911,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8657,10 +8927,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8672,10 +8942,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -8690,7 +8960,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index a549223c..8c6c5c0f 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -278,16 +278,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -350,16 +362,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -376,10 +400,18 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string name: description: Finds secrets based on the name. @@ -414,6 +446,15 @@ spec: - source - target type: object + transform: + description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + properties: + template: + description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + type: string + required: + - template + type: object type: object type: array sourceRef: @@ -506,9 +547,16 @@ spec: type: object engineVersion: default: v2 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string mergePolicy: default: Replace + enum: + - Replace + - Merge type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -534,6 +582,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -556,6 +607,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -569,6 +623,10 @@ spec: type: object target: default: Data + enum: + - Data + - Annotations + - Labels type: string type: object type: array @@ -608,12 +666,16 @@ spec: type: object type: object x-kubernetes-map-type: atomic + namespaces: + description: Choose namespaces by name. This field is ORed with anything that NamespaceSelector ends up choosing. + items: + type: string + type: array refreshTime: - description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. + description: The time in which the controller should reconcile its objects and recheck namespaces for labels. type: string required: - externalSecretSpec - - namespaceSelector type: object status: description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret. @@ -1325,7 +1387,7 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -1371,6 +1433,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -2308,11 +2373,72 @@ spec: - apiKeyRef - userRef type: object - required: - - apikey + jwt: + properties: + account: + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + serviceID: + description: The conjur authn jwt webservice id + type: string + required: + - account + - serviceID + type: object type: object caBundle: type: string + caProvider: + description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object url: type: string required: @@ -2828,6 +2954,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -3734,6 +3863,9 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string key: description: Key is the key used in the Provider, mandatory @@ -3762,6 +3894,9 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string key: description: Key is the key used in the Provider, mandatory @@ -3798,6 +3933,10 @@ spec: creationPolicy: default: Owner description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + enum: + - Owner + - Merge + - None type: string immutable: description: Immutable defines if the final secret will be immutable @@ -3815,6 +3954,9 @@ spec: engineVersion: default: v1 description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -3960,16 +4102,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -4032,16 +4186,28 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string key: description: Key is the key used in the Provider, mandatory type: string metadataPolicy: + default: None description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None + enum: + - None + - Fetch type: string property: description: Used to select a specific property of the Provider value (if a map), if supported @@ -4058,10 +4224,18 @@ spec: conversionStrategy: default: Default description: Used to define a conversion Strategy + enum: + - Default + - Unicode type: string decodingStrategy: default: None description: Used to define a decoding Strategy + enum: + - Auto + - Base64 + - Base64URL + - None type: string name: description: Finds secrets based on the name. @@ -4096,6 +4270,15 @@ spec: - source - target type: object + transform: + description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + properties: + template: + description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + type: string + required: + - template + type: object type: object type: array sourceRef: @@ -4188,9 +4371,16 @@ spec: type: object engineVersion: default: v2 + description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + enum: + - v1 + - v2 type: string mergePolicy: default: Replace + enum: + - Replace + - Merge type: string metadata: description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. @@ -4216,6 +4406,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -4238,6 +4431,9 @@ spec: type: string templateAs: default: Values + enum: + - Values + - KeysAndValues type: string required: - key @@ -4251,6 +4447,10 @@ spec: type: object target: default: Data + enum: + - Data + - Annotations + - Labels type: string type: object type: array @@ -4636,6 +4836,9 @@ spec: deletionPolicy: default: None description: 'Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".' + enum: + - Delete + - None type: string refreshInterval: description: The Interval to which External Secrets will try to push a secret definition @@ -5439,7 +5642,7 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -5485,6 +5688,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -6422,11 +6628,72 @@ spec: - apiKeyRef - userRef type: object - required: - - apikey + jwt: + properties: + account: + type: string + secretRef: + description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + serviceAccountRef: + description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + serviceID: + description: The conjur authn jwt webservice id + type: string + required: + - account + - serviceID + type: object type: object caBundle: type: string + caProvider: + description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + properties: + key: + description: The key where the CA certificate can be found in the Secret or ConfigMap. + type: string + name: + description: The name of the object located at the provider type. + type: string + namespace: + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + type: string + type: + description: The type of provider to use such as "Secret", or "ConfigMap". + enum: + - Secret + - ConfigMap + type: string + required: + - name + - type + type: object url: type: string required: @@ -6942,6 +7209,9 @@ spec: - tenancy - user type: object + principalType: + description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + type: string region: description: Region is the region where vault is located. type: string @@ -8124,10 +8394,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8191,10 +8461,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8300,10 +8570,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -8340,10 +8610,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8384,10 +8654,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8405,10 +8675,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8425,10 +8695,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8461,10 +8731,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8500,10 +8770,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8521,10 +8791,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8545,10 +8815,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8560,10 +8830,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -8578,7 +8848,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8608,10 +8878,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8623,10 +8893,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -8641,7 +8911,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8657,10 +8927,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8672,10 +8942,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.5 + helm.sh/chart: external-secrets-0.9.8 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.5" + app.kubernetes.io/version: "v0.9.8" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -8690,7 +8960,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.5-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.9.8-ubi" imagePullPolicy: IfNotPresent args: - webhook From 744b4951b20a38aa077a74a1461305ba55eda4ed Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 7 Nov 2023 14:16:07 +0100 Subject: [PATCH 1044/1263] Upgrade vault-helm to v0.26.1 --- hashicorp-vault/Chart.yaml | 2 +- hashicorp-vault/charts/vault-0.25.0.tgz | Bin 46359 -> 0 bytes hashicorp-vault/charts/vault-0.26.1.tgz | Bin 0 -> 48057 bytes .../0002-Allow-per-service-annotations.patch | 250 ++---------------- hashicorp-vault/values.yaml | 2 +- ...ault-industrial-edge-factory.expected.yaml | 42 +-- ...rp-vault-industrial-edge-hub.expected.yaml | 42 +-- ...-vault-medical-diagnosis-hub.expected.yaml | 42 +-- tests/hashicorp-vault-naked.expected.yaml | 22 +- tests/hashicorp-vault-normal.expected.yaml | 42 +-- 10 files changed, 125 insertions(+), 319 deletions(-) delete mode 100644 hashicorp-vault/charts/vault-0.25.0.tgz create mode 100644 hashicorp-vault/charts/vault-0.26.1.tgz diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index 84e7edc7..cc575a9b 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -6,5 +6,5 @@ name: hashicorp-vault version: 0.0.1 dependencies: - name: vault - version: "0.25.0" + version: "0.26.1" repository: "https://helm.releases.hashicorp.com" diff --git a/hashicorp-vault/charts/vault-0.25.0.tgz b/hashicorp-vault/charts/vault-0.25.0.tgz deleted file mode 100644 index 62e685dceb5a7e34cc94e8b4db992a70f0335332..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46359 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 literal 0 HcmV?d00001 diff --git a/hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch b/hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch index ba73a467..796b64cf 100644 --- a/hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch +++ b/hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch @@ -1,69 +1,10 @@ -From f62623030374c55410624a00755e9a3c07a411da Mon Sep 17 00:00:00 2001 -From: Michele Baldessari -Date: Tue, 29 Nov 2022 20:06:09 +0100 -Subject: [PATCH] Allow per-service annotations - -We add the 'annotations' field to the existing -vault.service.{active,standby} dictionaries which are relevant for the -active/standby vault ha services. We also add -vault.service.{nonha,internal}.annotations in order to allow per-service -annotations when using the non-ha variant. - -We had to choose 'nonha' as we cannot reuse the existing -vault.service.annotations key, because that gets still applied to all -services and we do not want to break existing installations. - -WIP as we need to add some more docs and maybe some more tests. ---- - templates/_helpers.tpl | 57 ++++++++++++++++++++++++ - templates/server-ha-active-service.yaml | 3 +- - templates/server-ha-standby-service.yaml | 1 + - templates/server-headless-service.yaml | 1 + - templates/server-service.yaml | 1 + - test/unit/server-ha-active-service.bats | 11 +++++ - test/unit/server-ha-standby-service.bats | 11 +++++ - test/unit/server-service.bats | 10 +++++ - values.schema.json | 34 ++++++++++++++ - values.yaml | 22 +++++++++ - 10 files changed, 150 insertions(+), 1 deletion(-) - -diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl -index 3897391..9e98c0b 100644 ---- a/templates/_helpers.tpl -+++ b/templates/_helpers.tpl -@@ -683,6 +683,63 @@ Sets extra vault server Service annotations - {{- end }} +diff -up vault/templates/_helpers.tpl.0.26.1 vault/templates/_helpers.tpl +--- vault/templates/_helpers.tpl.0.26.1 2023-11-07 14:06:52.285821136 +0100 ++++ vault/templates/_helpers.tpl 2023-11-07 14:07:45.445038627 +0100 +@@ -738,6 +738,35 @@ Sets extra vault server Service annotati {{- end -}} -+{{/* -+Sets extra vault server Service active annotations -+*/}} -+{{- define "vault.service.active.annotations" -}} -+ {{- if .Values.server.service.active.annotations }} -+ {{- $tp := typeOf .Values.server.service.active.annotations }} -+ {{- if eq $tp "string" }} -+ {{- tpl .Values.server.service.active.annotations . | nindent 4 }} -+ {{- else }} -+ {{- toYaml .Values.server.service.active.annotations | nindent 4 }} -+ {{- end }} -+ {{- end }} -+{{- end -}} -+ -+{{/* -+Sets extra vault server Service standby annotations -+*/}} -+{{- define "vault.service.standby.annotations" -}} -+ {{- if .Values.server.service.standby.annotations }} -+ {{- $tp := typeOf .Values.server.service.standby.annotations }} -+ {{- if eq $tp "string" }} -+ {{- tpl .Values.server.service.standby.annotations . | nindent 4 }} -+ {{- else }} -+ {{- toYaml .Values.server.service.standby.annotations | nindent 4 }} -+ {{- end }} -+ {{- end }} -+{{- end -}} -+ -+{{/* + {{/* +Sets extra vault server Service internal annotations +*/}} +{{- define "vault.service.internal.annotations" -}} @@ -92,53 +33,25 @@ index 3897391..9e98c0b 100644 + {{- end }} +{{- end -}} + - {{/* ++{{/* Sets PodSecurityPolicy annotations */}} -diff --git a/templates/server-ha-active-service.yaml b/templates/server-ha-active-service.yaml -index 7def2a0..649ffb8 100644 ---- a/templates/server-ha-active-service.yaml -+++ b/templates/server-ha-active-service.yaml -@@ -18,8 +18,9 @@ metadata: - vault-active: "true" - annotations: - {{ template "vault.service.annotations" .}} -+{{ template "vault.service.active.annotations" .}} - spec: -- {{- if .Values.server.service.type}} -+ {{- if .Values.server.service.type }} - type: {{ .Values.server.service.type }} - {{- end}} - {{- if .Values.server.service.clusterIP }} -diff --git a/templates/server-ha-standby-service.yaml b/templates/server-ha-standby-service.yaml -index 50fca4b..cdbfcad 100644 ---- a/templates/server-ha-standby-service.yaml -+++ b/templates/server-ha-standby-service.yaml -@@ -17,6 +17,7 @@ metadata: - app.kubernetes.io/managed-by: {{ .Release.Service }} - annotations: - {{ template "vault.service.annotations" .}} -+{{ template "vault.service.standby.annotations" .}} - spec: - {{- if .Values.server.service.type}} - type: {{ .Values.server.service.type }} -diff --git a/templates/server-headless-service.yaml b/templates/server-headless-service.yaml -index b03f491..25aaa8d 100644 ---- a/templates/server-headless-service.yaml -+++ b/templates/server-headless-service.yaml -@@ -16,6 +16,7 @@ metadata: + {{- define "vault.psp.annotations" -}} +diff -up vault/templates/server-headless-service.yaml.0.26.1 vault/templates/server-headless-service.yaml +--- vault/templates/server-headless-service.yaml.0.26.1 2023-11-07 14:08:24.302197609 +0100 ++++ vault/templates/server-headless-service.yaml 2023-11-07 14:08:48.707297472 +0100 +@@ -21,6 +21,7 @@ metadata: vault-internal: "true" annotations: {{ template "vault.service.annotations" .}} +{{ template "vault.service.internal.annotations" .}} spec: - clusterIP: None - publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} -diff --git a/templates/server-service.yaml b/templates/server-service.yaml -index 913b569..02a1ccd 100644 ---- a/templates/server-service.yaml -+++ b/templates/server-service.yaml -@@ -15,6 +15,7 @@ metadata: + {{- if (semverCompare ">= 1.23-0" .Capabilities.KubeVersion.Version) }} + {{- if .Values.server.service.ipFamilyPolicy }} +diff -up vault/templates/server-service.yaml.0.26.1 vault/templates/server-service.yaml +--- vault/templates/server-service.yaml.0.26.1 2023-11-07 14:09:43.152520231 +0100 ++++ vault/templates/server-service.yaml 2023-11-07 14:09:55.406570360 +0100 +@@ -20,6 +20,7 @@ metadata: app.kubernetes.io/managed-by: {{ .Release.Service }} annotations: {{ template "vault.service.annotations" .}} @@ -146,87 +59,10 @@ index 913b569..02a1ccd 100644 spec: {{- if .Values.server.service.type}} type: {{ .Values.server.service.type }} -diff --git a/test/unit/server-ha-active-service.bats b/test/unit/server-ha-active-service.bats -index d78f5d4..13b5271 100755 ---- a/test/unit/server-ha-active-service.bats -+++ b/test/unit/server-ha-active-service.bats -@@ -13,6 +13,17 @@ load _helpers - [ "${actual}" = "true" ] - } - -+@test "server/ha-active-Service: specific annotations" { -+ cd `chart_dir` -+ local actual=$(helm template \ -+ --show-only templates/server-ha-active-service.yaml \ -+ --set 'server.ha.enabled=true' \ -+ --set 'server.service.active.annotations=vaultIsAwesome: true' \ -+ . | tee /dev/stderr | -+ yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) -+ [ "${actual}" = "true" ] -+} -+ - @test "server/ha-active-Service: disable with ha.enabled false" { - cd `chart_dir` - local actual=$( (helm template \ -diff --git a/test/unit/server-ha-standby-service.bats b/test/unit/server-ha-standby-service.bats -index 6698314..6244565 100755 ---- a/test/unit/server-ha-standby-service.bats -+++ b/test/unit/server-ha-standby-service.bats -@@ -13,6 +13,17 @@ load _helpers - [ "${actual}" = "true" ] - } - -+@test "server/ha-standby-Service: specific annotations string" { -+ cd `chart_dir` -+ local actual=$(helm template \ -+ --show-only templates/server-ha-standby-service.yaml \ -+ --set 'server.ha.enabled=true' \ -+ --set 'server.service.standby.annotations=vaultIsAwesome: true' \ -+ . | tee /dev/stderr | -+ yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) -+ [ "${actual}" = "true" ] -+} -+ - @test "server/ha-standby-Service: generic annotations yaml" { - cd `chart_dir` - local actual=$(helm template \ -diff --git a/test/unit/server-service.bats b/test/unit/server-service.bats -index 70a5445..cc66987 100755 ---- a/test/unit/server-service.bats -+++ b/test/unit/server-service.bats -@@ -153,6 +153,16 @@ load _helpers - [ "${actual}" = "true" ] - } - -+@test "server/Service: specific annotations" { -+ cd `chart_dir` -+ local actual=$(helm template \ -+ --show-only templates/server-service.yaml \ -+ --set 'server.service.nonha.annotations=vaultIsAwesome: true' \ -+ . | tee /dev/stderr | -+ yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) -+ [ "${actual}" = "true" ] -+} -+ - @test "server/Service: publish not ready" { - cd `chart_dir` - local actual=$(helm template \ -diff --git a/values.schema.json b/values.schema.json -index c183957..d0dca34 100644 ---- a/values.schema.json -+++ b/values.schema.json -@@ -854,11 +854,39 @@ - "active": { - "type": "object", - "properties": { -+ "annotations" : { -+ "type": [ -+ "object", -+ "string" -+ ] -+ }, - "enabled": { - "type": "boolean" +diff -up vault/values.schema.json.0.26.1 vault/values.schema.json +--- vault/values.schema.json.0.26.1 2023-11-07 14:10:35.177733085 +0100 ++++ vault/values.schema.json 2023-11-07 14:11:52.244048399 +0100 +@@ -931,6 +931,28 @@ } } }, @@ -255,40 +91,13 @@ index c183957..d0dca34 100644 "annotations": { "type": [ "object", -@@ -890,6 +918,12 @@ - "properties": { - "enabled": { - "type": "boolean" -+ }, -+ "annotations": { -+ "type": [ -+ "object", -+ "string" -+ ] - } - } - }, -diff --git a/values.yaml b/values.yaml -index 2c3d9e2..32d8ea1 100644 ---- a/values.yaml -+++ b/values.yaml -@@ -600,10 +600,32 @@ server: - # have labelled themselves as the cluster leader with `vault-active: "true"` - active: - enabled: true -+ # Extra annotations for the service definition. This can either be YAML or a -+ # YAML-formatted multi-line templated string map of the annotations to apply -+ # to the service. -+ annotations: {} -+ - # Enable or disable the vault-standby service, which selects Vault pods that - # have labelled themselves as a cluster follower with `vault-active: "false"` - standby: - enabled: true -+ # Extra annotations for the service definition. This can either be YAML or a -+ # YAML-formatted multi-line templated string map of the annotations to apply -+ # to the service. -+ annotations: {} +diff -up vault/values.yaml.0.26.1 vault/values.yaml +--- vault/values.yaml.0.26.1 2023-11-07 14:13:00.865329166 +0100 ++++ vault/values.yaml 2023-11-07 14:14:15.318633813 +0100 +@@ -673,6 +673,19 @@ server: + # YAML-formatted multi-line templated string map of the annotations to apply + # to the standby service. + annotations: {} + + nonha: + # Extra annotations for the service definition. This can either be YAML or a @@ -305,6 +114,3 @@ index 2c3d9e2..32d8ea1 100644 # If enabled, the service selectors will include `app.kubernetes.io/instance: {{ .Release.Name }}` # When disabled, services may select Vault pods not deployed from the chart. # Does not affect the headless vault-internal service with `ClusterIP: None` --- -2.38.1 - diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index 780f574a..5d5a1ab9 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -48,4 +48,4 @@ vault: termination: "reencrypt" image: repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.14.0-ubi" + tag: "1.15.1-ubi" diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index ccb5e5bb..b17a36f0 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -4,9 +4,9 @@ apiVersion: v1 kind: ServiceAccount metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -16,9 +16,9 @@ apiVersion: v1 kind: ConfigMap metadata: name: hashicorp-vault-config - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -53,7 +53,7 @@ roleRef: subjects: - kind: ServiceAccount name: hashicorp-vault - namespace: default + namespace: pattern-namespace --- # Source: hashicorp-vault/charts/vault/templates/server-headless-service.yaml # Service for Vault cluster @@ -61,9 +61,9 @@ apiVersion: v1 kind: Service metadata: name: hashicorp-vault-internal - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -93,9 +93,9 @@ apiVersion: v1 kind: Service metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -124,9 +124,9 @@ apiVersion: v1 kind: Service metadata: name: hashicorp-vault-ui - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -148,7 +148,7 @@ apiVersion: apps/v1 kind: StatefulSet metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault @@ -167,7 +167,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.14.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -343,9 +343,9 @@ kind: Route apiVersion: route.openshift.io/v1 metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -364,19 +364,19 @@ spec: apiVersion: v1 kind: Pod metadata: - name: "hashicorp-vault-server-test" - namespace: default + name: hashicorp-vault-server-test + namespace: pattern-namespace annotations: "helm.sh/hook": test spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.14.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR - value: http://hashicorp-vault.default.svc:8200 + value: http://hashicorp-vault.pattern-namespace.svc:8200 - name: "VAULT_ADDR" value: "https://vault.vault.svc.cluster.local:8200" diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index ccb5e5bb..b17a36f0 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -4,9 +4,9 @@ apiVersion: v1 kind: ServiceAccount metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -16,9 +16,9 @@ apiVersion: v1 kind: ConfigMap metadata: name: hashicorp-vault-config - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -53,7 +53,7 @@ roleRef: subjects: - kind: ServiceAccount name: hashicorp-vault - namespace: default + namespace: pattern-namespace --- # Source: hashicorp-vault/charts/vault/templates/server-headless-service.yaml # Service for Vault cluster @@ -61,9 +61,9 @@ apiVersion: v1 kind: Service metadata: name: hashicorp-vault-internal - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -93,9 +93,9 @@ apiVersion: v1 kind: Service metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -124,9 +124,9 @@ apiVersion: v1 kind: Service metadata: name: hashicorp-vault-ui - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -148,7 +148,7 @@ apiVersion: apps/v1 kind: StatefulSet metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault @@ -167,7 +167,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.14.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -343,9 +343,9 @@ kind: Route apiVersion: route.openshift.io/v1 metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -364,19 +364,19 @@ spec: apiVersion: v1 kind: Pod metadata: - name: "hashicorp-vault-server-test" - namespace: default + name: hashicorp-vault-server-test + namespace: pattern-namespace annotations: "helm.sh/hook": test spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.14.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR - value: http://hashicorp-vault.default.svc:8200 + value: http://hashicorp-vault.pattern-namespace.svc:8200 - name: "VAULT_ADDR" value: "https://vault.vault.svc.cluster.local:8200" diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index ccb5e5bb..b17a36f0 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -4,9 +4,9 @@ apiVersion: v1 kind: ServiceAccount metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -16,9 +16,9 @@ apiVersion: v1 kind: ConfigMap metadata: name: hashicorp-vault-config - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -53,7 +53,7 @@ roleRef: subjects: - kind: ServiceAccount name: hashicorp-vault - namespace: default + namespace: pattern-namespace --- # Source: hashicorp-vault/charts/vault/templates/server-headless-service.yaml # Service for Vault cluster @@ -61,9 +61,9 @@ apiVersion: v1 kind: Service metadata: name: hashicorp-vault-internal - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -93,9 +93,9 @@ apiVersion: v1 kind: Service metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -124,9 +124,9 @@ apiVersion: v1 kind: Service metadata: name: hashicorp-vault-ui - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -148,7 +148,7 @@ apiVersion: apps/v1 kind: StatefulSet metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault @@ -167,7 +167,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.14.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -343,9 +343,9 @@ kind: Route apiVersion: route.openshift.io/v1 metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -364,19 +364,19 @@ spec: apiVersion: v1 kind: Pod metadata: - name: "hashicorp-vault-server-test" - namespace: default + name: hashicorp-vault-server-test + namespace: pattern-namespace annotations: "helm.sh/hook": test spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.14.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR - value: http://hashicorp-vault.default.svc:8200 + value: http://hashicorp-vault.pattern-namespace.svc:8200 - name: "VAULT_ADDR" value: "https://vault.vault.svc.cluster.local:8200" diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index aa4f5b87..17c7c3df 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -167,7 +167,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.14.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -345,7 +345,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -364,7 +364,7 @@ spec: apiVersion: v1 kind: Pod metadata: - name: "hashicorp-vault-server-test" + name: hashicorp-vault-server-test namespace: default annotations: "helm.sh/hook": test @@ -372,7 +372,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.14.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index ccb5e5bb..b17a36f0 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -4,9 +4,9 @@ apiVersion: v1 kind: ServiceAccount metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -16,9 +16,9 @@ apiVersion: v1 kind: ConfigMap metadata: name: hashicorp-vault-config - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -53,7 +53,7 @@ roleRef: subjects: - kind: ServiceAccount name: hashicorp-vault - namespace: default + namespace: pattern-namespace --- # Source: hashicorp-vault/charts/vault/templates/server-headless-service.yaml # Service for Vault cluster @@ -61,9 +61,9 @@ apiVersion: v1 kind: Service metadata: name: hashicorp-vault-internal - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -93,9 +93,9 @@ apiVersion: v1 kind: Service metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -124,9 +124,9 @@ apiVersion: v1 kind: Service metadata: name: hashicorp-vault-ui - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -148,7 +148,7 @@ apiVersion: apps/v1 kind: StatefulSet metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault @@ -167,7 +167,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.14.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -343,9 +343,9 @@ kind: Route apiVersion: route.openshift.io/v1 metadata: name: hashicorp-vault - namespace: default + namespace: pattern-namespace labels: - helm.sh/chart: vault-0.25.0 + helm.sh/chart: vault-0.26.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -364,19 +364,19 @@ spec: apiVersion: v1 kind: Pod metadata: - name: "hashicorp-vault-server-test" - namespace: default + name: hashicorp-vault-server-test + namespace: pattern-namespace annotations: "helm.sh/hook": test spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.14.0-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR - value: http://hashicorp-vault.default.svc:8200 + value: http://hashicorp-vault.pattern-namespace.svc:8200 - name: "VAULT_ADDR" value: "https://vault.vault.svc.cluster.local:8200" From 25be40669ca7537c41adb01d6ebb7b27e89a11f3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 7 Nov 2023 17:01:33 +0100 Subject: [PATCH 1045/1263] Parametrize ESO caProvider fields This will allow users to disable using the default kube-root-ca.crt configmap and/or customize things fully. So, for example, an environment which uses letsencrypt for the ingress certs, but does not have that CA included in kube-root-ca.crt, can simply override things with a /values-IBMCloud.yaml file with: golangExternalSecrets: caProvider: enabled: false Which will just use the CA System bundle to authenticate connections from ESO to vault. Tested this and with the steps above, was able to overcome the x509 CA unknown errors. --- ...olang-external-secrets-hub-secretstore.yaml | 18 ++++++++++-------- golang-external-secrets/values.yaml | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml index 0245ebf7..244f22c6 100644 --- a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +++ b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml @@ -10,19 +10,21 @@ spec: path: secret # Version of KV backend version: v2 +{{- if .Values.golangExternalSecrets.caProvider.enabled }} {{ if .Values.clusterGroup.isHubCluster }} caProvider: - type: ConfigMap - name: kube-root-ca.crt - key: ca.crt - namespace: golang-external-secrets + type: {{ .Values.golangExternalSecrets.caProvider.hubCluster.type }} + name: {{ .Values.golangExternalSecrets.caProvider.hubCluster.name }} + key: {{ .Values.golangExternalSecrets.caProvider.hubCluster.key }} + namespace: {{ .Values.golangExternalSecrets.caProvider.hubCluster.namespace }} {{ else }} caProvider: - type: Secret - name: hub-ca - key: hub-kube-root-ca.crt - namespace: imperative + type: {{ .Values.golangExternalSecrets.caProvider.nonhubCluster.type }} + name: {{ .Values.golangExternalSecrets.caProvider.nonhubCluster.name }} + key: {{ .Values.golangExternalSecrets.caProvider.nonhubCluster.key }} + namespace: {{ .Values.golangExternalSecrets.caProvider.nonhubCluster.namespace }} {{ end }} +{{- end }} auth: kubernetes: {{ if .Values.clusterGroup.isHubCluster }} diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index 31b4748a..a7e5f999 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -1,7 +1,23 @@ --- +# Eventually we should aim to move these two under the golangExternalSecrets key mountPath: "hub" mountRole: "hub-role" +golangExternalSecrets: + # This controls how ESO connects to vault + caProvider: + enabled: true # If vault is exposed via a route that is signed by a non internal CA you might want to disable this + hubCluster: + type: ConfigMap + name: kube-root-ca.crt + key: ca.crt + namespace: golang-external-secrets + nonhubCluster: + type: Secret + name: hub-ca + key: hub-kube-root-ca.crt + namespace: imperative + global: hubClusterDomain: hub.example.com clusterDomain: foo.example.com From ad3353d398a03d3700127d721a6ada097d819b03 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 7 Nov 2023 17:42:03 -0700 Subject: [PATCH 1046/1263] Simplify target namespace logic Co-authored-by: Andrew Beekhof --- clustergroup/templates/core/operatorgroup.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index c08e91b7..cd679bd5 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -13,7 +13,7 @@ metadata: namespace: {{ $k }} spec: targetNamespaces: - {{- if or (and $v.operatorGroup (hasKey $v "targetNamespaces")) (and $v.operatorGroup (not (empty $v.targetNamespaces))) }} + {{- if (hasKey $v "targetNamespaces") }} {{- range $v.targetNamespaces }}{{- /* We loop through the list of tergetnamespaces */}} - {{ . }} {{- end }}{{- /* End range targetNamespaces */}} From 205999ec65f4b97684a999a650487825b1e73b18 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 14 Nov 2023 14:53:51 +0100 Subject: [PATCH 1047/1263] Avoid nonhubCluster + hubCluster naming for ESO As mentioned in https://github.com/validatedpatterns/common/pull/391/files#r1391948610 prefer naming that is not related to to the "hub" concept, which hopefuly will disappear one day in the future. --- .../golang-external-secrets-hub-secretstore.yaml | 16 ++++++++-------- golang-external-secrets/values.yaml | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml index 244f22c6..fc0b410f 100644 --- a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +++ b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml @@ -13,16 +13,16 @@ spec: {{- if .Values.golangExternalSecrets.caProvider.enabled }} {{ if .Values.clusterGroup.isHubCluster }} caProvider: - type: {{ .Values.golangExternalSecrets.caProvider.hubCluster.type }} - name: {{ .Values.golangExternalSecrets.caProvider.hubCluster.name }} - key: {{ .Values.golangExternalSecrets.caProvider.hubCluster.key }} - namespace: {{ .Values.golangExternalSecrets.caProvider.hubCluster.namespace }} + type: {{ .Values.golangExternalSecrets.caProvider.vaultHostCluster.type }} + name: {{ .Values.golangExternalSecrets.caProvider.vaultHostCluster.name }} + key: {{ .Values.golangExternalSecrets.caProvider.vaultHostCluster.key }} + namespace: {{ .Values.golangExternalSecrets.caProvider.vaultHostCluster.namespace }} {{ else }} caProvider: - type: {{ .Values.golangExternalSecrets.caProvider.nonhubCluster.type }} - name: {{ .Values.golangExternalSecrets.caProvider.nonhubCluster.name }} - key: {{ .Values.golangExternalSecrets.caProvider.nonhubCluster.key }} - namespace: {{ .Values.golangExternalSecrets.caProvider.nonhubCluster.namespace }} + type: {{ .Values.golangExternalSecrets.caProvider.vaultClientCluster.type }} + name: {{ .Values.golangExternalSecrets.caProvider.vaultClientCluster.name }} + key: {{ .Values.golangExternalSecrets.caProvider.vaultClientCluster.key }} + namespace: {{ .Values.golangExternalSecrets.caProvider.vaultClientCluster.namespace }} {{ end }} {{- end }} auth: diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index a7e5f999..ec3fad3a 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -7,12 +7,12 @@ golangExternalSecrets: # This controls how ESO connects to vault caProvider: enabled: true # If vault is exposed via a route that is signed by a non internal CA you might want to disable this - hubCluster: + vaultHostCluster: type: ConfigMap name: kube-root-ca.crt key: ca.crt namespace: golang-external-secrets - nonhubCluster: + vaultClientCluster: type: Secret name: hub-ca key: hub-kube-root-ca.crt From 57022d4438c7e01a60620475d4f8fdf821daa2e4 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 3 Nov 2023 10:29:10 -0500 Subject: [PATCH 1048/1263] Update for new configmanagement plugin feature --- .../plumbing/argocd-cmp-plugin-cms.yaml | 14 ++++ clustergroup/templates/plumbing/argocd.yaml | 64 +++++++++++-------- clustergroup/values.schema.json | 51 +++++++++++++++ clustergroup/values.yaml | 5 ++ 4 files changed, 107 insertions(+), 27 deletions(-) create mode 100644 clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml diff --git a/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml b/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml new file mode 100644 index 00000000..52ae7c50 --- /dev/null +++ b/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml @@ -0,0 +1,14 @@ +{{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} +{{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} +{{- if $cmp.pluginConfig }} +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: "argocd-cmp-{{ $cmp.name }}" + namespace: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} +data: + "plugin.yaml": | {{ $cmp.pluginConfig | nindent 4 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index de83b53c..df09bfe5 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -39,33 +39,9 @@ spec: # Not the greatest way to pass git/quay info to sub-applications, but it will do until # we can support helmChart with kustomize # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - configManagementPlugins: | - - name: kustomize-version - generate: - command: ["sh", "-c"] - args: ["kustomize version 1>&2 && exit 1"] - - name: kustomize-with-helm - generate: - command: ["kustomize"] - args: ["build", "--enable-helm"] - - name: helm-with-kustomize - init: - command: ["/bin/sh", "-c"] - args: ["helm dependency build"] - generate: - command: ["/bin/bash", "-c"] - args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f $(git rev-parse --show-toplevel)/values-global.yaml - -f $(git rev-parse --show-toplevel)/values-{{ .Values.clusterGroup.name }}.yaml - --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL - --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION - --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern={{ .Values.global.pattern }} - --set global.clusterDomain={{ .Values.global.clusterDomain }} - --set global.hubClusterDomain={{ .Values.global.hubClusterDomain }} - --set global.localClusterDomain={{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }} - --set clusterGroup.name={{ .Values.clusterGroup.name }} - --post-renderer ./kustomize"] + {{- if len $.Values.clusterGroup.argoCD.legacyConfigManagementPlugins }} + configManagementPlugins: | {{ $.Values.clusterGroup.argoCD.legacyConfigManagementPlugins | nindent 4 }} + {{- end }} applicationSet: resources: limits: @@ -98,6 +74,40 @@ spec: rbac: defaultPolicy: role:admin repo: +{{- if len $.Values.clusterGroup.argoCD.initContainers }} + initContainers: {{ $.Values.clusterGroup.argoCD.initContainers | toPrettyJson }} +{{- end }} +{{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} + sidecarContainers: +{{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} + - name: {{ $cmp.name }} + command: [/var/run/argocd/argocd-cmp-server] + image: {{ $cmp.image }} + imagePullPolicy: {{ coalesce $cmp.imagePullPolicy "Always" }} + securityContext: + runAsNonRoot: true + volumeMounts: + - mountPath: /var/run/argocd + name: var-files + - mountPath: /home/argocd/cmp-server/plugins + name: plugins + - mountPath: /tmp + name: tmp +{{- if $cmp.pluginConfig }} + - mountPath: /home/argocd/cmp-server/config/plugin.yaml + subPath: plugin.yaml + name: {{ $cmp.name }} +{{- end }} +{{- end }} +{{- end }} +{{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} + volumes: +{{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} + - configMap: + name: "argocd-cmp-{{ $cmp.name }}" + name: {{ $cmp.name }} +{{- end }} +{{- end }} resources: limits: cpu: "1" diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 863b8489..e66e74f6 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -317,6 +317,9 @@ "$ref": "#/definitions/Applications" } }, + "argoCD": { + "$ref": "#/definitions/ArgoCD" + }, "imperative": { "$ref": "#/definitions/Imperative" }, @@ -488,6 +491,54 @@ ], "title": "Applications" }, + "ArgoCD": { + "type": "object", + "description": "Details for configuring ArgoCD instances in particular", + "additionalProperties": false, + "properties": { + "legacyConfigManagementPlugins": { + "type": "string", + "description": "The legacy (and not deprecated) string-based configManagementPlugins config" + }, + "configManagementPlugins": { + "type": "array", + "items": { + "$ref": "#/definitions/ArgoCDConfigManagementPlugin" + }, + "description": "The new configManagementPlugins array, will also generate configMaps to inject into the plugins" + }, + "initContainers": { + "type": "array", + "description": "A list of initContainers to add to the repo-server if needed" + } + } + }, + "ArgoCDConfigManagementPlugin": { + "type": "object", + "additionalProperties": true, + "properties": { + "name": { + "type": "string", + "description": "Name for the config management plugin" + }, + "image": { + "type": "string", + "description": "Image for a sidecar container" + }, + "imagePullPolicy": { + "type": "string", + "description": "Image pull policy for the sidecar. Defaults to 'Always'" + }, + "pluginConfig": { + "type": "string", + "description": "Configuration file to project into sidecar container. This will create a configMap if specified" + } + }, + "required": [ + "name", + "image" + ] + }, "IndexImages": { "type": "object", "description": "Details for overriding default catalog sources", diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index e9720d20..e63fab44 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -18,6 +18,11 @@ clusterGroup: targetCluster: in-cluster sharedValueFiles: [] + argoCD: + initContainers: [] + legacyConfigManagementPlugins: "" + configManagementPlugins: [] + imperative: jobs: [] # This image contains ansible + kubernetes.core by default and is used to run the jobs From 59f1305be36fc587d781da280a4b985af0032f19 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 3 Nov 2023 11:02:41 -0500 Subject: [PATCH 1049/1263] Remove obsolete comment and update tests --- clustergroup/templates/plumbing/argocd.yaml | 3 -- ...roup-industrial-edge-factory.expected.yaml | 34 +++---------------- ...tergroup-industrial-edge-hub.expected.yaml | 34 +++---------------- ...rgroup-medical-diagnosis-hub.expected.yaml | 34 +++---------------- tests/clustergroup-naked.expected.yaml | 34 +++---------------- tests/clustergroup-normal.expected.yaml | 34 +++---------------- 6 files changed, 20 insertions(+), 153 deletions(-) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index df09bfe5..aba995ab 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -36,9 +36,6 @@ spec: return hs applicationInstanceLabelKey: argocd.argoproj.io/instance - # Not the greatest way to pass git/quay info to sub-applications, but it will do until - # we can support helmChart with kustomize - # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION {{- if len $.Values.clusterGroup.argoCD.legacyConfigManagementPlugins }} configManagementPlugins: | {{ $.Values.clusterGroup.argoCD.legacyConfigManagementPlugins | nindent 4 }} {{- end }} diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 3eed3296..f559ee4f 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -83,6 +83,10 @@ data: namespace: manuela-factory-ml-workspace path: charts/datacenter/opendatahub project: factory + argoCD: + configManagementPlugins: [] + initContainers: [] + legacyConfigManagementPlugins: "" imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role @@ -481,36 +485,6 @@ spec: return hs applicationInstanceLabelKey: argocd.argoproj.io/instance - # Not the greatest way to pass git/quay info to sub-applications, but it will do until - # we can support helmChart with kustomize - # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - configManagementPlugins: | - - name: kustomize-version - generate: - command: ["sh", "-c"] - args: ["kustomize version 1>&2 && exit 1"] - - name: kustomize-with-helm - generate: - command: ["kustomize"] - args: ["build", "--enable-helm"] - - name: helm-with-kustomize - init: - command: ["/bin/sh", "-c"] - args: ["helm dependency build"] - generate: - command: ["/bin/bash", "-c"] - args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f $(git rev-parse --show-toplevel)/values-global.yaml - -f $(git rev-parse --show-toplevel)/values-factory.yaml - --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL - --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION - --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern=mypattern - --set global.clusterDomain=region.example.com - --set global.hubClusterDomain=apps.hub.example.com - --set global.localClusterDomain=apps.region.example.com - --set clusterGroup.name=factory - --post-renderer ./kustomize"] applicationSet: resources: limits: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 12e1ee28..57b25ce9 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -204,6 +204,10 @@ data: project: datacenter repoURL: https://helm.releases.hashicorp.com targetRevision: v0.20.1 + argoCD: + configManagementPlugins: [] + initContainers: [] + legacyConfigManagementPlugins: "" imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role @@ -1150,36 +1154,6 @@ spec: return hs applicationInstanceLabelKey: argocd.argoproj.io/instance - # Not the greatest way to pass git/quay info to sub-applications, but it will do until - # we can support helmChart with kustomize - # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - configManagementPlugins: | - - name: kustomize-version - generate: - command: ["sh", "-c"] - args: ["kustomize version 1>&2 && exit 1"] - - name: kustomize-with-helm - generate: - command: ["kustomize"] - args: ["build", "--enable-helm"] - - name: helm-with-kustomize - init: - command: ["/bin/sh", "-c"] - args: ["helm dependency build"] - generate: - command: ["/bin/bash", "-c"] - args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f $(git rev-parse --show-toplevel)/values-global.yaml - -f $(git rev-parse --show-toplevel)/values-datacenter.yaml - --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL - --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION - --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern=mypattern - --set global.clusterDomain=region.example.com - --set global.hubClusterDomain=apps.hub.example.com - --set global.localClusterDomain=apps.region.example.com - --set clusterGroup.name=datacenter - --post-renderer ./kustomize"] applicationSet: resources: limits: diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 9efc2431..538bb902 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -215,6 +215,10 @@ data: namespace: xraylab-1 path: charts/all/medical-diagnosis/xray-init project: medical-diagnosis + argoCD: + configManagementPlugins: [] + initContainers: [] + legacyConfigManagementPlugins: "" imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role @@ -1359,36 +1363,6 @@ spec: return hs applicationInstanceLabelKey: argocd.argoproj.io/instance - # Not the greatest way to pass git/quay info to sub-applications, but it will do until - # we can support helmChart with kustomize - # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - configManagementPlugins: | - - name: kustomize-version - generate: - command: ["sh", "-c"] - args: ["kustomize version 1>&2 && exit 1"] - - name: kustomize-with-helm - generate: - command: ["kustomize"] - args: ["build", "--enable-helm"] - - name: helm-with-kustomize - init: - command: ["/bin/sh", "-c"] - args: ["helm dependency build"] - generate: - command: ["/bin/bash", "-c"] - args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f $(git rev-parse --show-toplevel)/values-global.yaml - -f $(git rev-parse --show-toplevel)/values-hub.yaml - --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL - --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION - --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern=mypattern - --set global.clusterDomain=region.example.com - --set global.hubClusterDomain=apps.hub.example.com - --set global.localClusterDomain=apps.region.example.com - --set clusterGroup.name=hub - --post-renderer ./kustomize"] applicationSet: resources: limits: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 75359902..3d95ff59 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -38,6 +38,10 @@ data: values.yaml: | clusterGroup: applications: {} + argoCD: + configManagementPlugins: [] + initContainers: [] + legacyConfigManagementPlugins: "" imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role @@ -293,36 +297,6 @@ spec: return hs applicationInstanceLabelKey: argocd.argoproj.io/instance - # Not the greatest way to pass git/quay info to sub-applications, but it will do until - # we can support helmChart with kustomize - # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - configManagementPlugins: | - - name: kustomize-version - generate: - command: ["sh", "-c"] - args: ["kustomize version 1>&2 && exit 1"] - - name: kustomize-with-helm - generate: - command: ["kustomize"] - args: ["build", "--enable-helm"] - - name: helm-with-kustomize - init: - command: ["/bin/sh", "-c"] - args: ["helm dependency build"] - generate: - command: ["/bin/bash", "-c"] - args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f $(git rev-parse --show-toplevel)/values-global.yaml - -f $(git rev-parse --show-toplevel)/values-example.yaml - --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL - --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION - --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern=common - --set global.clusterDomain= - --set global.hubClusterDomain= - --set global.localClusterDomain= - --set clusterGroup.name=example - --post-renderer ./kustomize"] applicationSet: resources: limits: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 1e7d8e8f..2424c92f 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -123,6 +123,10 @@ data: namespace: application-ci path: charts/datacenter/pipelines project: datacenter + argoCD: + configManagementPlugins: [] + initContainers: [] + legacyConfigManagementPlugins: "" imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role @@ -967,36 +971,6 @@ spec: return hs applicationInstanceLabelKey: argocd.argoproj.io/instance - # Not the greatest way to pass git/quay info to sub-applications, but it will do until - # we can support helmChart with kustomize - # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - configManagementPlugins: | - - name: kustomize-version - generate: - command: ["sh", "-c"] - args: ["kustomize version 1>&2 && exit 1"] - - name: kustomize-with-helm - generate: - command: ["kustomize"] - args: ["build", "--enable-helm"] - - name: helm-with-kustomize - init: - command: ["/bin/sh", "-c"] - args: ["helm dependency build"] - generate: - command: ["/bin/bash", "-c"] - args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f $(git rev-parse --show-toplevel)/values-global.yaml - -f $(git rev-parse --show-toplevel)/values-example.yaml - --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL - --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION - --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern=mypattern - --set global.clusterDomain=region.example.com - --set global.hubClusterDomain=apps.hub.example.com - --set global.localClusterDomain=apps.region.example.com - --set clusterGroup.name=example - --post-renderer ./kustomize"] applicationSet: resources: limits: From 597aaf96d4e68251ec105a184cd8c93c155e05d8 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 3 Nov 2023 11:46:16 -0500 Subject: [PATCH 1050/1263] Update schema --- .../templates/plumbing/argocd-cmp-plugin-cms.yaml | 2 +- clustergroup/templates/plumbing/argocd.yaml | 2 +- clustergroup/values.schema.json | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml b/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml index 52ae7c50..c4b599da 100644 --- a/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml +++ b/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml @@ -8,7 +8,7 @@ metadata: name: "argocd-cmp-{{ $cmp.name }}" namespace: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} data: - "plugin.yaml": | {{ $cmp.pluginConfig | nindent 4 }} + {{ $cmp.pluginConfig | indent 2 }} {{- end }} {{- end }} {{- end }} diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index aba995ab..f913962c 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -92,7 +92,7 @@ spec: name: tmp {{- if $cmp.pluginConfig }} - mountPath: /home/argocd/cmp-server/config/plugin.yaml - subPath: plugin.yaml + subPath: {{ coalesce $cmp.subPath "plugin.yaml" }} name: {{ $cmp.name }} {{- end }} {{- end }} diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index e66e74f6..42cfb546 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -514,6 +514,10 @@ } }, "ArgoCDConfigManagementPlugin": { + "required": [ + "name", + "image" + ], "type": "object", "additionalProperties": true, "properties": { @@ -530,14 +534,10 @@ "description": "Image pull policy for the sidecar. Defaults to 'Always'" }, "pluginConfig": { - "type": "string", + "type": "object", "description": "Configuration file to project into sidecar container. This will create a configMap if specified" } - }, - "required": [ - "name", - "image" - ] + } }, "IndexImages": { "type": "object", From 4b6a8b39919dbaeb30acf3a78c597902e2d9ce5e Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 3 Nov 2023 11:56:34 -0500 Subject: [PATCH 1051/1263] Require plugin.yaml --- clustergroup/argocd-cmp-plugin-cms.yaml | 14 ++ clustergroup/argocd.yaml | 162 ++++++++++++++++++++++++ clustergroup/values.schema.json | 12 +- 3 files changed, 182 insertions(+), 6 deletions(-) create mode 100644 clustergroup/argocd-cmp-plugin-cms.yaml create mode 100644 clustergroup/argocd.yaml diff --git a/clustergroup/argocd-cmp-plugin-cms.yaml b/clustergroup/argocd-cmp-plugin-cms.yaml new file mode 100644 index 00000000..52ae7c50 --- /dev/null +++ b/clustergroup/argocd-cmp-plugin-cms.yaml @@ -0,0 +1,14 @@ +{{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} +{{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} +{{- if $cmp.pluginConfig }} +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: "argocd-cmp-{{ $cmp.name }}" + namespace: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} +data: + "plugin.yaml": | {{ $cmp.pluginConfig | nindent 4 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/clustergroup/argocd.yaml b/clustergroup/argocd.yaml new file mode 100644 index 00000000..df09bfe5 --- /dev/null +++ b/clustergroup/argocd.yaml @@ -0,0 +1,162 @@ +{{- if (eq .Values.enabled "all") }} +{{- $namespace := print $.Values.global.pattern "-" $.Values.clusterGroup.name }} +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + finalizers: + - argoproj.io/finalizer + # Changing the name affects the ClusterRoleBinding, the generated secret, + # route URL, and argocd.argoproj.io/managed-by annotations + name: {{ .Values.clusterGroup.name }}-gitops + namespace: {{ $namespace }} + annotations: + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: +# Adding health checks to argocd to prevent pvc resources +# that aren't bound state from blocking deployments + resourceHealthChecks: + - kind: PersistentVolumeClaim + check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs + + applicationInstanceLabelKey: argocd.argoproj.io/instance + # Not the greatest way to pass git/quay info to sub-applications, but it will do until + # we can support helmChart with kustomize + # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION + {{- if len $.Values.clusterGroup.argoCD.legacyConfigManagementPlugins }} + configManagementPlugins: | {{ $.Values.clusterGroup.argoCD.legacyConfigManagementPlugins | nindent 4 }} + {{- end }} + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + controller: + processors: {} + resources: + limits: + cpu: "4" + memory: 4Gi + requests: + cpu: 500m + memory: 2Gi + sso: + provider: dex + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + rbac: + defaultPolicy: role:admin + repo: +{{- if len $.Values.clusterGroup.argoCD.initContainers }} + initContainers: {{ $.Values.clusterGroup.argoCD.initContainers | toPrettyJson }} +{{- end }} +{{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} + sidecarContainers: +{{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} + - name: {{ $cmp.name }} + command: [/var/run/argocd/argocd-cmp-server] + image: {{ $cmp.image }} + imagePullPolicy: {{ coalesce $cmp.imagePullPolicy "Always" }} + securityContext: + runAsNonRoot: true + volumeMounts: + - mountPath: /var/run/argocd + name: var-files + - mountPath: /home/argocd/cmp-server/plugins + name: plugins + - mountPath: /tmp + name: tmp +{{- if $cmp.pluginConfig }} + - mountPath: /home/argocd/cmp-server/config/plugin.yaml + subPath: plugin.yaml + name: {{ $cmp.name }} +{{- end }} +{{- end }} +{{- end }} +{{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} + volumes: +{{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} + - configMap: + name: "argocd-cmp-{{ $cmp.name }}" + name: {{ $cmp.name }} +{{- end }} +{{- end }} + resources: + limits: + cpu: "1" + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + tls: + insecureEdgeTerminationPolicy: Redirect + termination: reencrypt + service: + type: "" + tls: + ca: {} +status: +--- +apiVersion: console.openshift.io/v1 +kind: ConsoleLink +metadata: + name: {{ .Values.clusterGroup.name }}-gitops-link + namespace: {{ $namespace }} +spec: + applicationMenu: + section: OpenShift GitOps + imageURL: data:image/png;base64,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 + href: 'https://{{ .Values.clusterGroup.name }}-gitops-server-{{ $namespace }}.{{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }}' + location: ApplicationMenu + text: '{{ title .Values.clusterGroup.name }} ArgoCD' +{{- end }} diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 42cfb546..e66e74f6 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -514,10 +514,6 @@ } }, "ArgoCDConfigManagementPlugin": { - "required": [ - "name", - "image" - ], "type": "object", "additionalProperties": true, "properties": { @@ -534,10 +530,14 @@ "description": "Image pull policy for the sidecar. Defaults to 'Always'" }, "pluginConfig": { - "type": "object", + "type": "string", "description": "Configuration file to project into sidecar container. This will create a configMap if specified" } - } + }, + "required": [ + "name", + "image" + ] }, "IndexImages": { "type": "object", From 342fef9f7b9c4f491477297f2717d860112d415c Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 14 Nov 2023 08:44:30 -0600 Subject: [PATCH 1052/1263] Add tmpdir to sidecar mounts --- clustergroup/templates/plumbing/argocd.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index f913962c..5f09c3e4 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -89,7 +89,7 @@ spec: - mountPath: /home/argocd/cmp-server/plugins name: plugins - mountPath: /tmp - name: tmp + name: cmp-tmp {{- if $cmp.pluginConfig }} - mountPath: /home/argocd/cmp-server/config/plugin.yaml subPath: {{ coalesce $cmp.subPath "plugin.yaml" }} @@ -99,6 +99,8 @@ spec: {{- end }} {{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} volumes: + - emptyDir: {} + name: cmp-tmp {{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} - configMap: name: "argocd-cmp-{{ $cmp.name }}" From 2fa4d6ea12628472797676e4131fb870d7cb7168 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 14 Nov 2023 08:48:02 -0600 Subject: [PATCH 1053/1263] True up to test code --- clustergroup/argocd-cmp-plugin-cms.yaml | 14 -------------- .../templates/plumbing/argocd-cmp-plugin-cms.yaml | 2 +- 2 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 clustergroup/argocd-cmp-plugin-cms.yaml diff --git a/clustergroup/argocd-cmp-plugin-cms.yaml b/clustergroup/argocd-cmp-plugin-cms.yaml deleted file mode 100644 index 52ae7c50..00000000 --- a/clustergroup/argocd-cmp-plugin-cms.yaml +++ /dev/null @@ -1,14 +0,0 @@ -{{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} -{{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} -{{- if $cmp.pluginConfig }} ---- -kind: ConfigMap -apiVersion: v1 -metadata: - name: "argocd-cmp-{{ $cmp.name }}" - namespace: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} -data: - "plugin.yaml": | {{ $cmp.pluginConfig | nindent 4 }} -{{- end }} -{{- end }} -{{- end }} diff --git a/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml b/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml index c4b599da..2a8a458c 100644 --- a/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml +++ b/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml @@ -8,7 +8,7 @@ metadata: name: "argocd-cmp-{{ $cmp.name }}" namespace: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} data: - {{ $cmp.pluginConfig | indent 2 }} + "plugin.yaml": | {{ tpl $cmp.pluginConfig $ | indent 4 }} {{- end }} {{- end }} {{- end }} From 44ac765e22dd96ebf4a6b77d08fb052a2c04be73 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 14 Nov 2023 12:59:53 -0600 Subject: [PATCH 1054/1263] Use nindent as appropriate --- clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml b/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml index 2a8a458c..d62cb652 100644 --- a/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml +++ b/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml @@ -8,7 +8,7 @@ metadata: name: "argocd-cmp-{{ $cmp.name }}" namespace: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} data: - "plugin.yaml": | {{ tpl $cmp.pluginConfig $ | indent 4 }} + "plugin.yaml": | {{ tpl $cmp.pluginConfig $ | nindent 4 }} {{- end }} {{- end }} {{- end }} From 25145c406c70c3f4d56024de0cd06469493c3ae9 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 14 Nov 2023 13:07:27 -0600 Subject: [PATCH 1055/1263] Remove stray files --- clustergroup/argocd.yaml | 162 --------------------------------------- clustergroup/test.yaml | 100 ------------------------ 2 files changed, 262 deletions(-) delete mode 100644 clustergroup/argocd.yaml delete mode 100644 clustergroup/test.yaml diff --git a/clustergroup/argocd.yaml b/clustergroup/argocd.yaml deleted file mode 100644 index df09bfe5..00000000 --- a/clustergroup/argocd.yaml +++ /dev/null @@ -1,162 +0,0 @@ -{{- if (eq .Values.enabled "all") }} -{{- $namespace := print $.Values.global.pattern "-" $.Values.clusterGroup.name }} -apiVersion: argoproj.io/v1alpha1 -kind: ArgoCD -metadata: - finalizers: - - argoproj.io/finalizer - # Changing the name affects the ClusterRoleBinding, the generated secret, - # route URL, and argocd.argoproj.io/managed-by annotations - name: {{ .Values.clusterGroup.name }}-gitops - namespace: {{ $namespace }} - annotations: - argocd.argoproj.io/compare-options: IgnoreExtraneous -spec: -# Adding health checks to argocd to prevent pvc resources -# that aren't bound state from blocking deployments - resourceHealthChecks: - - kind: PersistentVolumeClaim - check: | - hs = {} - if obj.status ~= nil then - if obj.status.phase ~= nil then - if obj.status.phase == "Pending" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - elseif obj.status.phase == "Bound" then - hs.status = "Healthy" - hs.message = obj.status.phase - return hs - end - end - end - hs.status = "Progressing" - hs.message = "Waiting for PVC" - return hs - - applicationInstanceLabelKey: argocd.argoproj.io/instance - # Not the greatest way to pass git/quay info to sub-applications, but it will do until - # we can support helmChart with kustomize - # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - {{- if len $.Values.clusterGroup.argoCD.legacyConfigManagementPlugins }} - configManagementPlugins: | {{ $.Values.clusterGroup.argoCD.legacyConfigManagementPlugins | nindent 4 }} - {{- end }} - applicationSet: - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 250m - memory: 512Mi - controller: - processors: {} - resources: - limits: - cpu: "4" - memory: 4Gi - requests: - cpu: 500m - memory: 2Gi - sso: - provider: dex - dex: - openShiftOAuth: true - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 250m - memory: 128Mi - initialSSHKnownHosts: {} - rbac: - defaultPolicy: role:admin - repo: -{{- if len $.Values.clusterGroup.argoCD.initContainers }} - initContainers: {{ $.Values.clusterGroup.argoCD.initContainers | toPrettyJson }} -{{- end }} -{{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} - sidecarContainers: -{{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} - - name: {{ $cmp.name }} - command: [/var/run/argocd/argocd-cmp-server] - image: {{ $cmp.image }} - imagePullPolicy: {{ coalesce $cmp.imagePullPolicy "Always" }} - securityContext: - runAsNonRoot: true - volumeMounts: - - mountPath: /var/run/argocd - name: var-files - - mountPath: /home/argocd/cmp-server/plugins - name: plugins - - mountPath: /tmp - name: tmp -{{- if $cmp.pluginConfig }} - - mountPath: /home/argocd/cmp-server/config/plugin.yaml - subPath: plugin.yaml - name: {{ $cmp.name }} -{{- end }} -{{- end }} -{{- end }} -{{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} - volumes: -{{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} - - configMap: - name: "argocd-cmp-{{ $cmp.name }}" - name: {{ $cmp.name }} -{{- end }} -{{- end }} - resources: - limits: - cpu: "1" - memory: 512Mi - requests: - cpu: 250m - memory: 256Mi - resourceExclusions: | - - apiGroups: - - tekton.dev - kinds: - - TaskRun - - PipelineRun - server: - autoscale: - enabled: false - grpc: - ingress: - enabled: false - ingress: - enabled: false - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 125m - memory: 128Mi - route: - enabled: true - tls: - insecureEdgeTerminationPolicy: Redirect - termination: reencrypt - service: - type: "" - tls: - ca: {} -status: ---- -apiVersion: console.openshift.io/v1 -kind: ConsoleLink -metadata: - name: {{ .Values.clusterGroup.name }}-gitops-link - namespace: {{ $namespace }} -spec: - applicationMenu: - section: OpenShift GitOps - imageURL: data:image/png;base64,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 - href: 'https://{{ .Values.clusterGroup.name }}-gitops-server-{{ $namespace }}.{{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }}' - location: ApplicationMenu - text: '{{ title .Values.clusterGroup.name }} ArgoCD' -{{- end }} diff --git a/clustergroup/test.yaml b/clustergroup/test.yaml deleted file mode 100644 index 3c0afc6f..00000000 --- a/clustergroup/test.yaml +++ /dev/null @@ -1,100 +0,0 @@ -clusterGroup: - name: hub - isHubCluster: true - - namespaces: - - open-cluster-management - - vault - - golang-external-secrets - - config-demo - - indexImages: - - name: snr - image: quay.io/mshitrit/self-node-remediation-manager-index:0.0.104 - - subscriptions: - acm: - name: advanced-cluster-management - namespace: open-cluster-management - channel: release-2.5 - csv: advanced-cluster-management.v2.5.0 - - projects: - - hub - - config-demo - - applications: - acm: - name: acm - namespace: open-cluster-management - project: hub - path: common/acm - ignoreDifferences: - - group: internal.open-cluster-management.io - kind: ManagedClusterInfo - jsonPointers: - - /spec/loggingCA - - vault: - name: vault - namespace: vault - project: hub - chart: vault - repoURL: https://helm.releases.hashicorp.com - targetRevision: v0.21.0 - overrides: - - name: global.openshift - value: "true" - - name: injector.enabled - value: "false" - - name: ui.enabled - value: "true" - - name: ui.serviceType - value: LoadBalancer - - name: server.route.enabled - value: "true" - - name: server.route.host - value: null - - name: server.route.tls.termination - value: edge - - name: server.image.repository - value: "registry.connect.redhat.com/hashicorp/vault" - - name: server.image.tag - value: "1.11.2-ubi" - - golang-external-secrets: - name: golang-external-secrets - namespace: golang-external-secrets - project: hub - path: common/golang-external-secrets - - config-demo: - name: config-demo - namespace: config-demo - project: config-demo - path: charts/all/config-demo - - imperative: - # NOTE: We *must* use lists and not hashes. As hashes lose ordering once parsed by helm - # The default schedule is every 10 minutes: imperative.schedule - # Total timeout of all jobs is 1h: imperative.activeDeadlineSeconds - # imagePullPolicy is set to always: imperative.imagePullPolicy - # For additional overrides that apply to the jobs, please refer to - # https://hybrid-cloud-patterns.io/imperative-actions/#additional-job-customizations - jobs: - - name: regional-ca - # ansible playbook to be run - playbook: ansible/playbooks/on-hub-get-regional-ca.yml - # per playbook timeout in seconds - timeout: 234 - # verbosity: "-v" - - managedClusterGroups: - region-one: - name: region-one - hostedArgoSites: - - perth - - sydney - helmOverrides: - - name: clusterGroup.isHubCluster - value: false From b60d760d562ae2f703ac4c603a09919f924425a0 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 14 Nov 2023 13:09:52 -0600 Subject: [PATCH 1056/1263] Plugin config is plugin.yaml --- clustergroup/templates/plumbing/argocd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 5f09c3e4..4d5f2123 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -92,7 +92,7 @@ spec: name: cmp-tmp {{- if $cmp.pluginConfig }} - mountPath: /home/argocd/cmp-server/config/plugin.yaml - subPath: {{ coalesce $cmp.subPath "plugin.yaml" }} + subPath: plugin.yaml name: {{ $cmp.name }} {{- end }} {{- end }} From 34a17d3aff47c4bc05df2404ad8377016a334557 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Tue, 14 Nov 2023 13:39:21 -0600 Subject: [PATCH 1057/1263] Remove now-obsolete kustomize-renderer example --- examples/kustomize-renderer/Chart.yaml | 6 ---- examples/kustomize-renderer/environment.yaml | 34 ------------------- .../kustomize-renderer/kustomization.yaml | 5 --- examples/kustomize-renderer/kustomize | 14 -------- .../templates/environment.yaml | 34 ------------------- examples/kustomize-renderer/values.yaml | 12 ------- 6 files changed, 105 deletions(-) delete mode 100644 examples/kustomize-renderer/Chart.yaml delete mode 100644 examples/kustomize-renderer/environment.yaml delete mode 100644 examples/kustomize-renderer/kustomization.yaml delete mode 100755 examples/kustomize-renderer/kustomize delete mode 100644 examples/kustomize-renderer/templates/environment.yaml delete mode 100644 examples/kustomize-renderer/values.yaml diff --git a/examples/kustomize-renderer/Chart.yaml b/examples/kustomize-renderer/Chart.yaml deleted file mode 100644 index 88a786c9..00000000 --- a/examples/kustomize-renderer/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -description: A Helm chart to demonstrate how to use with kustomize -keywords: -- pattern -name: example -version: 0.0.1 diff --git a/examples/kustomize-renderer/environment.yaml b/examples/kustomize-renderer/environment.yaml deleted file mode 100644 index de4c48a9..00000000 --- a/examples/kustomize-renderer/environment.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: environment -data: - IMAGE_PROVIDER: {{ .Values.global.imageregistry.hostname }} - IMAGE_ACCOUNT: {{ .Values.global.imageregistry.account }} - GIT_EMAIL: {{ .Values.global.git.email }} - GIT_DEV_REPO_URL: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }}/manuela-dev.git - GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} - GIT_OPS_REPO_TEST_URL: {{ .Values.global.repoURL }} - GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.targetRevision }} - GIT_OPS_REPO_PROD_URL: {{ .Values.global.repoURL }} - GIT_OPS_REPO_PROD_REVISION: {{ .Values.global.targetRevision }} - IOT_CONSUMER_IMAGE: iot-consumer - IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag - IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml - IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml - IOT_FRONTEND_IMAGE: iot-frontend - IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag - IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml - IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml - IOT_SWSENSOR_IMAGE: iot-software-sensor - IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag - IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml - IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml - IOT_ANOMALY_IMAGE: iot-anomaly-detection - IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag - IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml - IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/examples/kustomize-renderer/kustomization.yaml b/examples/kustomize-renderer/kustomization.yaml deleted file mode 100644 index 8d8bcd10..00000000 --- a/examples/kustomize-renderer/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -resources: - - environment.yaml - -patches: -- helm.patch.yaml diff --git a/examples/kustomize-renderer/kustomize b/examples/kustomize-renderer/kustomize deleted file mode 100755 index 3266d453..00000000 --- a/examples/kustomize-renderer/kustomize +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -x - -BASE=`dirname $0` -if [ $BASE = $PWD ]; then - BASE=./ -fi - -cat <&0 > "$BASE/helm.yaml" - -# Including at least one log to stderr allows us to see the full -x output -echo $HOME $PWD 1>&2 -ls -al 1>&2 - -kustomize build "$BASE" && rm "$BASE/helm.yaml" diff --git a/examples/kustomize-renderer/templates/environment.yaml b/examples/kustomize-renderer/templates/environment.yaml deleted file mode 100644 index de4c48a9..00000000 --- a/examples/kustomize-renderer/templates/environment.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: environment -data: - IMAGE_PROVIDER: {{ .Values.global.imageregistry.hostname }} - IMAGE_ACCOUNT: {{ .Values.global.imageregistry.account }} - GIT_EMAIL: {{ .Values.global.git.email }} - GIT_DEV_REPO_URL: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }}/manuela-dev.git - GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} - GIT_OPS_REPO_TEST_URL: {{ .Values.global.repoURL }} - GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.targetRevision }} - GIT_OPS_REPO_PROD_URL: {{ .Values.global.repoURL }} - GIT_OPS_REPO_PROD_REVISION: {{ .Values.global.targetRevision }} - IOT_CONSUMER_IMAGE: iot-consumer - IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag - IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml - IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml - IOT_FRONTEND_IMAGE: iot-frontend - IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag - IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml - IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml - IOT_SWSENSOR_IMAGE: iot-software-sensor - IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag - IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml - IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml - IOT_ANOMALY_IMAGE: iot-anomaly-detection - IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag - IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml - IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/examples/kustomize-renderer/values.yaml b/examples/kustomize-renderer/values.yaml deleted file mode 100644 index cb80a03a..00000000 --- a/examples/kustomize-renderer/values.yaml +++ /dev/null @@ -1,12 +0,0 @@ -global: - git: - provider: github.com - account: PLAINTEXT - username: PLAINTEXT - email: SOMEWHERE@EXAMPLE.COM - dev_revision: main - - imageregistry: - provider: quay.io - account: PLAINTEXT - From b703d8993facf8811d61af1d453522f7918bbfbc Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 16 Nov 2023 10:51:21 -0600 Subject: [PATCH 1058/1263] Allow pluginArgs to be set and add schema --- clustergroup/templates/plumbing/argocd.yaml | 3 +++ clustergroup/values.schema.json | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 4d5f2123..0dfd8338 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -79,6 +79,9 @@ spec: {{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} - name: {{ $cmp.name }} command: [/var/run/argocd/argocd-cmp-server] +{{- if $cmp.pluginArgs }} + args: {{ $cmp.pluginArgs | toPrettyJson }} +{{- end }} image: {{ $cmp.image }} imagePullPolicy: {{ coalesce $cmp.imagePullPolicy "Always" }} securityContext: diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index e66e74f6..95bc46a1 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -532,6 +532,10 @@ "pluginConfig": { "type": "string", "description": "Configuration file to project into sidecar container. This will create a configMap if specified" + }, + "pluginArgs": { + "type": "array", + "description": "Additional args to pass to the cmpserver command, usually loglevel" } }, "required": [ From 5125aa83928faebef47b6a85657fdd29e4158789 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 16 Nov 2023 20:03:53 -0600 Subject: [PATCH 1059/1263] Remove redundancy --- clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml b/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml index d62cb652..6f86c316 100644 --- a/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml +++ b/clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml @@ -1,4 +1,3 @@ -{{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} {{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} {{- if $cmp.pluginConfig }} --- @@ -11,4 +10,3 @@ data: "plugin.yaml": | {{ tpl $cmp.pluginConfig $ | nindent 4 }} {{- end }} {{- end }} -{{- end }} From 6a2f3bfe6085dd6a00fddd22ba35d3c977b103e4 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 16 Nov 2023 20:04:21 -0600 Subject: [PATCH 1060/1263] Revert "Remove now-obsolete kustomize-renderer example" This reverts commit 34a17d3aff47c4bc05df2404ad8377016a334557. --- examples/kustomize-renderer/Chart.yaml | 6 ++++ examples/kustomize-renderer/environment.yaml | 34 +++++++++++++++++++ .../kustomize-renderer/kustomization.yaml | 5 +++ examples/kustomize-renderer/kustomize | 14 ++++++++ .../templates/environment.yaml | 34 +++++++++++++++++++ examples/kustomize-renderer/values.yaml | 12 +++++++ 6 files changed, 105 insertions(+) create mode 100644 examples/kustomize-renderer/Chart.yaml create mode 100644 examples/kustomize-renderer/environment.yaml create mode 100644 examples/kustomize-renderer/kustomization.yaml create mode 100755 examples/kustomize-renderer/kustomize create mode 100644 examples/kustomize-renderer/templates/environment.yaml create mode 100644 examples/kustomize-renderer/values.yaml diff --git a/examples/kustomize-renderer/Chart.yaml b/examples/kustomize-renderer/Chart.yaml new file mode 100644 index 00000000..88a786c9 --- /dev/null +++ b/examples/kustomize-renderer/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +description: A Helm chart to demonstrate how to use with kustomize +keywords: +- pattern +name: example +version: 0.0.1 diff --git a/examples/kustomize-renderer/environment.yaml b/examples/kustomize-renderer/environment.yaml new file mode 100644 index 00000000..de4c48a9 --- /dev/null +++ b/examples/kustomize-renderer/environment.yaml @@ -0,0 +1,34 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: environment +data: + IMAGE_PROVIDER: {{ .Values.global.imageregistry.hostname }} + IMAGE_ACCOUNT: {{ .Values.global.imageregistry.account }} + GIT_EMAIL: {{ .Values.global.git.email }} + GIT_DEV_REPO_URL: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }}/manuela-dev.git + GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} + GIT_OPS_REPO_TEST_URL: {{ .Values.global.repoURL }} + GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.targetRevision }} + GIT_OPS_REPO_PROD_URL: {{ .Values.global.repoURL }} + GIT_OPS_REPO_PROD_REVISION: {{ .Values.global.targetRevision }} + IOT_CONSUMER_IMAGE: iot-consumer + IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag + IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml + IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml + IOT_FRONTEND_IMAGE: iot-frontend + IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag + IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml + IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml + IOT_SWSENSOR_IMAGE: iot-software-sensor + IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag + IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml + IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml + IOT_ANOMALY_IMAGE: iot-anomaly-detection + IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag + IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml + IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/examples/kustomize-renderer/kustomization.yaml b/examples/kustomize-renderer/kustomization.yaml new file mode 100644 index 00000000..8d8bcd10 --- /dev/null +++ b/examples/kustomize-renderer/kustomization.yaml @@ -0,0 +1,5 @@ +resources: + - environment.yaml + +patches: +- helm.patch.yaml diff --git a/examples/kustomize-renderer/kustomize b/examples/kustomize-renderer/kustomize new file mode 100755 index 00000000..3266d453 --- /dev/null +++ b/examples/kustomize-renderer/kustomize @@ -0,0 +1,14 @@ +#!/bin/bash -x + +BASE=`dirname $0` +if [ $BASE = $PWD ]; then + BASE=./ +fi + +cat <&0 > "$BASE/helm.yaml" + +# Including at least one log to stderr allows us to see the full -x output +echo $HOME $PWD 1>&2 +ls -al 1>&2 + +kustomize build "$BASE" && rm "$BASE/helm.yaml" diff --git a/examples/kustomize-renderer/templates/environment.yaml b/examples/kustomize-renderer/templates/environment.yaml new file mode 100644 index 00000000..de4c48a9 --- /dev/null +++ b/examples/kustomize-renderer/templates/environment.yaml @@ -0,0 +1,34 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: environment +data: + IMAGE_PROVIDER: {{ .Values.global.imageregistry.hostname }} + IMAGE_ACCOUNT: {{ .Values.global.imageregistry.account }} + GIT_EMAIL: {{ .Values.global.git.email }} + GIT_DEV_REPO_URL: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }}/manuela-dev.git + GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} + GIT_OPS_REPO_TEST_URL: {{ .Values.global.repoURL }} + GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.targetRevision }} + GIT_OPS_REPO_PROD_URL: {{ .Values.global.repoURL }} + GIT_OPS_REPO_PROD_REVISION: {{ .Values.global.targetRevision }} + IOT_CONSUMER_IMAGE: iot-consumer + IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag + IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml + IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml + IOT_FRONTEND_IMAGE: iot-frontend + IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag + IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml + IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml + IOT_SWSENSOR_IMAGE: iot-software-sensor + IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag + IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml + IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml + IOT_ANOMALY_IMAGE: iot-anomaly-detection + IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag + IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml + IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml + IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/examples/kustomize-renderer/values.yaml b/examples/kustomize-renderer/values.yaml new file mode 100644 index 00000000..cb80a03a --- /dev/null +++ b/examples/kustomize-renderer/values.yaml @@ -0,0 +1,12 @@ +global: + git: + provider: github.com + account: PLAINTEXT + username: PLAINTEXT + email: SOMEWHERE@EXAMPLE.COM + dev_revision: main + + imageregistry: + provider: quay.io + account: PLAINTEXT + From ab9b4f270ece5193071da6996fb8be8df5e42a0d Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 16 Nov 2023 20:08:02 -0600 Subject: [PATCH 1061/1263] Remove legacy configManagementPlugins support --- clustergroup/templates/plumbing/argocd.yaml | 3 --- clustergroup/values.schema.json | 4 ---- clustergroup/values.yaml | 1 - tests/clustergroup-industrial-edge-factory.expected.yaml | 1 - tests/clustergroup-industrial-edge-hub.expected.yaml | 1 - tests/clustergroup-medical-diagnosis-hub.expected.yaml | 1 - tests/clustergroup-naked.expected.yaml | 1 - tests/clustergroup-normal.expected.yaml | 1 - 8 files changed, 13 deletions(-) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 0dfd8338..a85fb6f9 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -36,9 +36,6 @@ spec: return hs applicationInstanceLabelKey: argocd.argoproj.io/instance - {{- if len $.Values.clusterGroup.argoCD.legacyConfigManagementPlugins }} - configManagementPlugins: | {{ $.Values.clusterGroup.argoCD.legacyConfigManagementPlugins | nindent 4 }} - {{- end }} applicationSet: resources: limits: diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 95bc46a1..e88fc5a9 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -496,10 +496,6 @@ "description": "Details for configuring ArgoCD instances in particular", "additionalProperties": false, "properties": { - "legacyConfigManagementPlugins": { - "type": "string", - "description": "The legacy (and not deprecated) string-based configManagementPlugins config" - }, "configManagementPlugins": { "type": "array", "items": { diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index e63fab44..4c408463 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -20,7 +20,6 @@ clusterGroup: argoCD: initContainers: [] - legacyConfigManagementPlugins: "" configManagementPlugins: [] imperative: diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index f559ee4f..2a2e2d34 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -86,7 +86,6 @@ data: argoCD: configManagementPlugins: [] initContainers: [] - legacyConfigManagementPlugins: "" imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 57b25ce9..d820345a 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -207,7 +207,6 @@ data: argoCD: configManagementPlugins: [] initContainers: [] - legacyConfigManagementPlugins: "" imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 538bb902..6b8b88ca 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -218,7 +218,6 @@ data: argoCD: configManagementPlugins: [] initContainers: [] - legacyConfigManagementPlugins: "" imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 3d95ff59..5d12b04d 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -41,7 +41,6 @@ data: argoCD: configManagementPlugins: [] initContainers: [] - legacyConfigManagementPlugins: "" imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 2424c92f..62136abe 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -126,7 +126,6 @@ data: argoCD: configManagementPlugins: [] initContainers: [] - legacyConfigManagementPlugins: "" imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role From 3c1e3923c13599bbe4b3cc1d9be4cc0c0aea08f8 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 17 Nov 2023 08:30:10 -0600 Subject: [PATCH 1062/1263] Add configManagementPlugins to tests for industrial edge --- examples/industrial-edge-factory.yaml | 31 +++++++ examples/industrial-edge-hub.yaml | 31 +++++++ ...roup-industrial-edge-factory.expected.yaml | 88 ++++++++++++++++++- ...tergroup-industrial-edge-hub.expected.yaml | 88 ++++++++++++++++++- 4 files changed, 236 insertions(+), 2 deletions(-) diff --git a/examples/industrial-edge-factory.yaml b/examples/industrial-edge-factory.yaml index 9ed1e8d3..c60d0960 100644 --- a/examples/industrial-edge-factory.yaml +++ b/examples/industrial-edge-factory.yaml @@ -36,6 +36,37 @@ clusterGroup: projects: - factory + argoCD: + configManagementPlugins: + - name: helm-with-kustomize + image: quay.io/hybridcloudpatterns/utility-container:latest + pluginArgs: + - '--loglevel=debug' + pluginConfig: | + apiVersion: argoproj.io/v1alpha1 + kind: ConfigManagementPlugin + metadata: + name: helm-with-kustomize + spec: + preserveFileMode: true + init: + command: ["/bin/sh", "-c"] + args: ["helm dependency build"] + generate: + command: ["/bin/bash", "-c"] + args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f $(git rev-parse --show-toplevel)/values-global.yaml + -f $(git rev-parse --show-toplevel)/values-{{ .Values.clusterGroup.name }}.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern={{ .Values.global.pattern }} + --set global.clusterDomain={{ .Values.global.clusterDomain }} + --set global.hubClusterDomain={{ .Values.global.hubClusterDomain }} + --set global.localClusterDomain={{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }} + --set clusterGroup.name={{ .Values.clusterGroup.name }} + --post-renderer ./kustomize"] + applications: - name: stormshift project: factory diff --git a/examples/industrial-edge-hub.yaml b/examples/industrial-edge-hub.yaml index 3dfd2fc8..e48c4013 100644 --- a/examples/industrial-edge-hub.yaml +++ b/examples/industrial-edge-hub.yaml @@ -65,6 +65,37 @@ clusterGroup: - golang-external-secrets - vault + argoCD: + configManagementPlugins: + - name: helm-with-kustomize + image: quay.io/hybridcloudpatterns/utility-container:latest + pluginArgs: + - '--loglevel=debug' + pluginConfig: | + apiVersion: argoproj.io/v1alpha1 + kind: ConfigManagementPlugin + metadata: + name: helm-with-kustomize + spec: + preserveFileMode: true + init: + command: ["/bin/sh", "-c"] + args: ["helm dependency build"] + generate: + command: ["/bin/bash", "-c"] + args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f $(git rev-parse --show-toplevel)/values-global.yaml + -f $(git rev-parse --show-toplevel)/values-{{ .Values.clusterGroup.name }}.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern={{ .Values.global.pattern }} + --set global.clusterDomain={{ .Values.global.clusterDomain }} + --set global.hubClusterDomain={{ .Values.global.hubClusterDomain }} + --set global.localClusterDomain={{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }} + --set clusterGroup.name={{ .Values.clusterGroup.name }} + --post-renderer ./kustomize"] + applications: acm: name: acm diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 2a2e2d34..8d55b696 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -84,7 +84,35 @@ data: path: charts/datacenter/opendatahub project: factory argoCD: - configManagementPlugins: [] + configManagementPlugins: + - image: quay.io/hybridcloudpatterns/utility-container:latest + name: helm-with-kustomize + pluginArgs: + - --loglevel=debug + pluginConfig: | + apiVersion: argoproj.io/v1alpha1 + kind: ConfigManagementPlugin + metadata: + name: helm-with-kustomize + spec: + preserveFileMode: true + init: + command: ["/bin/sh", "-c"] + args: ["helm dependency build"] + generate: + command: ["/bin/bash", "-c"] + args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f $(git rev-parse --show-toplevel)/values-global.yaml + -f $(git rev-parse --show-toplevel)/values-factory.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern=mypattern + --set global.clusterDomain=region.example.com + --set global.hubClusterDomain=apps.hub.example.com + --set global.localClusterDomain=apps.region.example.com + --set clusterGroup.name=factory + --post-renderer ./kustomize"] initContainers: [] imperative: activeDeadlineSeconds: 3600 @@ -168,6 +196,38 @@ data: kind: ClusterSecretStore name: vault-backend --- +# Source: clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml +kind: ConfigMap +apiVersion: v1 +metadata: + name: "argocd-cmp-helm-with-kustomize" + namespace: mypattern-factory +data: + "plugin.yaml": | + apiVersion: argoproj.io/v1alpha1 + kind: ConfigManagementPlugin + metadata: + name: helm-with-kustomize + spec: + preserveFileMode: true + init: + command: ["/bin/sh", "-c"] + args: ["helm dependency build"] + generate: + command: ["/bin/bash", "-c"] + args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f $(git rev-parse --show-toplevel)/values-global.yaml + -f $(git rev-parse --show-toplevel)/values-factory.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern=mypattern + --set global.clusterDomain=region.example.com + --set global.hubClusterDomain=apps.hub.example.com + --set global.localClusterDomain=apps.region.example.com + --set clusterGroup.name=factory + --post-renderer ./kustomize"] +--- # Source: clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -516,6 +576,32 @@ spec: rbac: defaultPolicy: role:admin repo: + sidecarContainers: + - name: helm-with-kustomize + command: [/var/run/argocd/argocd-cmp-server] + args: [ + "--loglevel=debug" +] + image: quay.io/hybridcloudpatterns/utility-container:latest + imagePullPolicy: Always + securityContext: + runAsNonRoot: true + volumeMounts: + - mountPath: /var/run/argocd + name: var-files + - mountPath: /home/argocd/cmp-server/plugins + name: plugins + - mountPath: /tmp + name: cmp-tmp + - mountPath: /home/argocd/cmp-server/config/plugin.yaml + subPath: plugin.yaml + name: helm-with-kustomize + volumes: + - emptyDir: {} + name: cmp-tmp + - configMap: + name: "argocd-cmp-helm-with-kustomize" + name: helm-with-kustomize resources: limits: cpu: "1" diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index d820345a..f09d1a75 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -205,7 +205,35 @@ data: repoURL: https://helm.releases.hashicorp.com targetRevision: v0.20.1 argoCD: - configManagementPlugins: [] + configManagementPlugins: + - image: quay.io/hybridcloudpatterns/utility-container:latest + name: helm-with-kustomize + pluginArgs: + - --loglevel=debug + pluginConfig: | + apiVersion: argoproj.io/v1alpha1 + kind: ConfigManagementPlugin + metadata: + name: helm-with-kustomize + spec: + preserveFileMode: true + init: + command: ["/bin/sh", "-c"] + args: ["helm dependency build"] + generate: + command: ["/bin/bash", "-c"] + args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f $(git rev-parse --show-toplevel)/values-global.yaml + -f $(git rev-parse --show-toplevel)/values-datacenter.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern=mypattern + --set global.clusterDomain=region.example.com + --set global.hubClusterDomain=apps.hub.example.com + --set global.localClusterDomain=apps.region.example.com + --set clusterGroup.name=datacenter + --post-renderer ./kustomize"] initContainers: [] imperative: activeDeadlineSeconds: 3600 @@ -329,6 +357,38 @@ data: kind: ClusterSecretStore name: vault-backend --- +# Source: clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml +kind: ConfigMap +apiVersion: v1 +metadata: + name: "argocd-cmp-helm-with-kustomize" + namespace: mypattern-datacenter +data: + "plugin.yaml": | + apiVersion: argoproj.io/v1alpha1 + kind: ConfigManagementPlugin + metadata: + name: helm-with-kustomize + spec: + preserveFileMode: true + init: + command: ["/bin/sh", "-c"] + args: ["helm dependency build"] + generate: + command: ["/bin/bash", "-c"] + args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} + -f $(git rev-parse --show-toplevel)/values-global.yaml + -f $(git rev-parse --show-toplevel)/values-datacenter.yaml + --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL + --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION + --set global.namespace=$ARGOCD_APP_NAMESPACE + --set global.pattern=mypattern + --set global.clusterDomain=region.example.com + --set global.hubClusterDomain=apps.hub.example.com + --set global.localClusterDomain=apps.region.example.com + --set clusterGroup.name=datacenter + --post-renderer ./kustomize"] +--- # Source: clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -1185,6 +1245,32 @@ spec: rbac: defaultPolicy: role:admin repo: + sidecarContainers: + - name: helm-with-kustomize + command: [/var/run/argocd/argocd-cmp-server] + args: [ + "--loglevel=debug" +] + image: quay.io/hybridcloudpatterns/utility-container:latest + imagePullPolicy: Always + securityContext: + runAsNonRoot: true + volumeMounts: + - mountPath: /var/run/argocd + name: var-files + - mountPath: /home/argocd/cmp-server/plugins + name: plugins + - mountPath: /tmp + name: cmp-tmp + - mountPath: /home/argocd/cmp-server/config/plugin.yaml + subPath: plugin.yaml + name: helm-with-kustomize + volumes: + - emptyDir: {} + name: cmp-tmp + - configMap: + name: "argocd-cmp-helm-with-kustomize" + name: helm-with-kustomize resources: limits: cpu: "1" From 0c93a29c75d52fdf416ef63b4865a18d1226ff10 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 20 Nov 2023 07:52:49 +0100 Subject: [PATCH 1063/1263] Clustergroup 0.0.5 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 1256786b..b54d8855 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.0.4 +version: 0.0.5 From 36092755ddee197e0c413aabb68c93c4a043859b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 20 Nov 2023 08:11:43 +0100 Subject: [PATCH 1064/1263] Small whitespace test --- clustergroup/.github/workflows/update-helm-repo.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/clustergroup/.github/workflows/update-helm-repo.yml b/clustergroup/.github/workflows/update-helm-repo.yml index 8c658a18..7189efa1 100644 --- a/clustergroup/.github/workflows/update-helm-repo.yml +++ b/clustergroup/.github/workflows/update-helm-repo.yml @@ -9,6 +9,7 @@ # - Contents: r/w # - Deployments: r/w # - Pages: r/w +# name: vp-patterns/update-helm-repo on: From 6144e864475c62db6b011801b275f1169377b908 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 20 Nov 2023 08:19:30 +0100 Subject: [PATCH 1065/1263] Stop referencing remote actions via @main. Use a specific commit Reason is that even though we've updated workflows in helm-chart, the charts seem to still reference the an old commit: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@refs/tags/main (ee7ec78f30b8f72463633b781527dfa186d3e980) --- acm/.github/workflows/update-helm-repo.yml | 4 ++-- clustergroup/.github/workflows/update-helm-repo.yml | 4 ++-- .../.github/workflows/update-helm-repo.yml | 4 ++-- hashicorp-vault/.github/workflows/update-helm-repo.yml | 4 ++-- letsencrypt/.github/workflows/update-helm-repo.yml | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/acm/.github/workflows/update-helm-repo.yml b/acm/.github/workflows/update-helm-repo.yml index 8c658a18..c12af2b5 100644 --- a/acm/.github/workflows/update-helm-repo.yml +++ b/acm/.github/workflows/update-helm-repo.yml @@ -18,12 +18,12 @@ on: jobs: helmlint: - uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@main + uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@985ba37e0eb50b1b35ec194fc999eae2d0ae1486 permissions: contents: read update-helm-repo: needs: [helmlint] - uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@main + uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@985ba37e0eb50b1b35ec194fc999eae2d0ae1486 permissions: read-all secrets: inherit diff --git a/clustergroup/.github/workflows/update-helm-repo.yml b/clustergroup/.github/workflows/update-helm-repo.yml index 7189efa1..fa1d6247 100644 --- a/clustergroup/.github/workflows/update-helm-repo.yml +++ b/clustergroup/.github/workflows/update-helm-repo.yml @@ -19,12 +19,12 @@ on: jobs: helmlint: - uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@main + uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@985ba37e0eb50b1b35ec194fc999eae2d0ae1486 permissions: contents: read update-helm-repo: needs: [helmlint] - uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@main + uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@985ba37e0eb50b1b35ec194fc999eae2d0ae1486 permissions: read-all secrets: inherit diff --git a/golang-external-secrets/.github/workflows/update-helm-repo.yml b/golang-external-secrets/.github/workflows/update-helm-repo.yml index 8c658a18..c12af2b5 100644 --- a/golang-external-secrets/.github/workflows/update-helm-repo.yml +++ b/golang-external-secrets/.github/workflows/update-helm-repo.yml @@ -18,12 +18,12 @@ on: jobs: helmlint: - uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@main + uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@985ba37e0eb50b1b35ec194fc999eae2d0ae1486 permissions: contents: read update-helm-repo: needs: [helmlint] - uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@main + uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@985ba37e0eb50b1b35ec194fc999eae2d0ae1486 permissions: read-all secrets: inherit diff --git a/hashicorp-vault/.github/workflows/update-helm-repo.yml b/hashicorp-vault/.github/workflows/update-helm-repo.yml index 8c658a18..c12af2b5 100644 --- a/hashicorp-vault/.github/workflows/update-helm-repo.yml +++ b/hashicorp-vault/.github/workflows/update-helm-repo.yml @@ -18,12 +18,12 @@ on: jobs: helmlint: - uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@main + uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@985ba37e0eb50b1b35ec194fc999eae2d0ae1486 permissions: contents: read update-helm-repo: needs: [helmlint] - uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@main + uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@985ba37e0eb50b1b35ec194fc999eae2d0ae1486 permissions: read-all secrets: inherit diff --git a/letsencrypt/.github/workflows/update-helm-repo.yml b/letsencrypt/.github/workflows/update-helm-repo.yml index 8c658a18..c12af2b5 100644 --- a/letsencrypt/.github/workflows/update-helm-repo.yml +++ b/letsencrypt/.github/workflows/update-helm-repo.yml @@ -18,12 +18,12 @@ on: jobs: helmlint: - uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@main + uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@985ba37e0eb50b1b35ec194fc999eae2d0ae1486 permissions: contents: read update-helm-repo: needs: [helmlint] - uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@main + uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@985ba37e0eb50b1b35ec194fc999eae2d0ae1486 permissions: read-all secrets: inherit From e71c5ef6f855e2b134d656969786219ecb3d5bfd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 20 Nov 2023 17:17:43 +0100 Subject: [PATCH 1066/1263] Updated ESO to v0.9.9 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.9.8.tgz | Bin 84651 -> 0 bytes .../charts/external-secrets-0.9.9.tgz | Bin 0 -> 85534 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 186 +++++++++++++----- ...-secrets-industrial-edge-hub.expected.yaml | 186 +++++++++++++----- ...ecrets-medical-diagnosis-hub.expected.yaml | 186 +++++++++++++----- ...olang-external-secrets-naked.expected.yaml | 186 +++++++++++++----- ...lang-external-secrets-normal.expected.yaml | 186 +++++++++++++----- 9 files changed, 699 insertions(+), 239 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.8.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.9.9.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 54675dd3..c8a4a3a6 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.3 dependencies: - name: external-secrets - version: "0.9.8" + version: "0.9.9" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.9.8.tgz b/golang-external-secrets/charts/external-secrets-0.9.8.tgz deleted file mode 100644 index 7270f40c9ac985e948dc32b3ad58da5e869b5b68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 84651 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%0A|S!OFKV8)vpTld-oc4y=Mzh( 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℞8fuN%)yo^LV*eBv3X-?B_S**h5_ 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 TTyxE}``7+|ppvOs07wl0ocA2q diff --git a/golang-external-secrets/charts/external-secrets-0.9.9.tgz b/golang-external-secrets/charts/external-secrets-0.9.9.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e39ffdd0bfd16df9d38ff11020d688ff486d83d9 GIT binary patch literal 85534 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�A=@R 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 N%30Vr5iW@u- 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 Date: Mon, 20 Nov 2023 18:27:45 +0100 Subject: [PATCH 1067/1263] Updated vault-helm to v0.27.0 --- hashicorp-vault/Chart.yaml | 2 +- hashicorp-vault/charts/vault-0.26.1.tgz | Bin 48057 -> 0 bytes hashicorp-vault/charts/vault-0.27.0.tgz | Bin 0 -> 48690 bytes hashicorp-vault/values.yaml | 2 +- ...ault-industrial-edge-factory.expected.yaml | 21 +++++++++--------- ...rp-vault-industrial-edge-hub.expected.yaml | 21 +++++++++--------- ...-vault-medical-diagnosis-hub.expected.yaml | 21 +++++++++--------- tests/hashicorp-vault-naked.expected.yaml | 21 +++++++++--------- tests/hashicorp-vault-normal.expected.yaml | 21 +++++++++--------- 9 files changed, 57 insertions(+), 52 deletions(-) delete mode 100644 hashicorp-vault/charts/vault-0.26.1.tgz create mode 100644 hashicorp-vault/charts/vault-0.27.0.tgz diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index cc575a9b..b9a20124 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -6,5 +6,5 @@ name: hashicorp-vault version: 0.0.1 dependencies: - name: vault - version: "0.26.1" + version: "0.27.0" repository: "https://helm.releases.hashicorp.com" diff --git a/hashicorp-vault/charts/vault-0.26.1.tgz b/hashicorp-vault/charts/vault-0.26.1.tgz deleted file mode 100644 index f7bb362e96af6e08aa651b1f11174c7c57071f80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48057 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 diff --git a/hashicorp-vault/charts/vault-0.27.0.tgz b/hashicorp-vault/charts/vault-0.27.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..574b3e743442466c1d05f9c38bf1e69c0f82f037 GIT binary patch literal 48690 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 literal 0 HcmV?d00001 diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index 5d5a1ab9..25740d98 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -48,4 +48,4 @@ vault: termination: "reencrypt" image: repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.15.1-ubi" + tag: "1.15.2-ubi" diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index b17a36f0..0c68e832 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -167,7 +167,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -317,6 +317,7 @@ spec: - metadata: name: data + spec: accessModes: - ReadWriteOnce @@ -345,7 +346,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -372,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index b17a36f0..0c68e832 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -167,7 +167,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -317,6 +317,7 @@ spec: - metadata: name: data + spec: accessModes: - ReadWriteOnce @@ -345,7 +346,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -372,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index b17a36f0..0c68e832 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -167,7 +167,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -317,6 +317,7 @@ spec: - metadata: name: data + spec: accessModes: - ReadWriteOnce @@ -345,7 +346,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -372,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index 17c7c3df..e09ac08f 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -167,7 +167,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -317,6 +317,7 @@ spec: - metadata: name: data + spec: accessModes: - ReadWriteOnce @@ -345,7 +346,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -372,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index b17a36f0..0c68e832 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -42,7 +42,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +63,7 @@ metadata: name: hashicorp-vault-internal namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +95,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +126,7 @@ metadata: name: hashicorp-vault-ui namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -167,7 +167,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -317,6 +317,7 @@ spec: - metadata: name: data + spec: accessModes: - ReadWriteOnce @@ -345,7 +346,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.26.1 + helm.sh/chart: vault-0.27.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -372,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR From 73d1804df17716093e51ea36293b0e88f593c87a Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Mon, 20 Nov 2023 02:45:44 +0000 Subject: [PATCH 1068/1263] Prevent ArgoCD from writing ESO CRs to clusters that need full support --- clustergroup/templates/plumbing/argocd.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index a85fb6f9..7c4edb6a 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -120,6 +120,12 @@ spec: kinds: - TaskRun - PipelineRun +{{ if .Values.global.excludeESO }} + - apiGroups: + - external-secrets.io + kinds: + - ExternalSecret +{{ end }} server: autoscale: enabled: false From 151bc41fb3f042fc685f6ddbac8d150e2e9f9720 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 21 Nov 2023 11:04:55 +0100 Subject: [PATCH 1069/1263] Fix whitespaces --- clustergroup/templates/plumbing/argocd.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 7c4edb6a..b0977e9c 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -120,12 +120,12 @@ spec: kinds: - TaskRun - PipelineRun -{{ if .Values.global.excludeESO }} +{{- if .Values.global.excludeESO }} - apiGroups: - external-secrets.io kinds: - ExternalSecret -{{ end }} +{{- end }} server: autoscale: enabled: false From 6423803b4d89c3a4e3cb031102b16808f363b5a0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 21 Nov 2023 11:12:26 +0100 Subject: [PATCH 1070/1263] Release clustergroup v0.8.0 We're fairly feature complete at this point in time, let's signal that by upping the version substantially. --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index b54d8855..3a4ee397 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.0.5 +version: 0.8.0 From 9c1d47b44dd7c148b146a5334fa47253a687cea4 Mon Sep 17 00:00:00 2001 From: Andrew Beekhof Date: Tue, 28 Nov 2023 14:04:28 +1100 Subject: [PATCH 1071/1263] Document preview limitations --- scripts/preview.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/preview.sh b/scripts/preview.sh index 8bafb43f..379f240d 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -1,5 +1,13 @@ #!/bin/bash +# DISCLAIMER +# +# - Parsing of applications needs to be more clever. Currently the code assumes that all +# targets will be local charts. This is not true, for example, in industrial-edge. +# - There is currently not a mechanism to actually preview against multiple clusters +# (i.e. a hub and a remote). All previews will be done against the current. +# - Make output can be included in the YAML. + SITE=$1; shift APP=$1; shift GIT_REPO=$1; shift From 50be2fd7bfd14c2ab4fae199ec8149187bcb8021 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 29 Nov 2023 15:29:47 +0100 Subject: [PATCH 1072/1263] Add support for private repos This needs the corresponding PR from the operator (https://www.github.com/validatedpatterns/patterns-operator/pull/139). The way it works is that if "global.privateRepo" is set to true, we add an acm policy on the hub only that reads the secret from the openshift-gitops namespaces and copies it to the open-cluster-manager. And then we use another policy that pushes the secret just copied to open-cluster-managemnt to the openshift-gitops + pattern-name-group-one namespaces so that the two argo instances can consume the private repositories. Tested end to end with both https and ssh private repository. --- .../policies/private-repo-policies.yaml | 161 ++++++++++++++++++ clustergroup/templates/_helpers.tpl | 2 + .../templates/imperative/_helpers.tpl | 29 +++- 3 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 acm/templates/policies/private-repo-policies.yaml diff --git a/acm/templates/policies/private-repo-policies.yaml b/acm/templates/policies/private-repo-policies.yaml new file mode 100644 index 00000000..0b7db0da --- /dev/null +++ b/acm/templates/policies/private-repo-policies.yaml @@ -0,0 +1,161 @@ +# We copy the vp-private-repo-credentials from the "openshift-gitops" namespace +# to the "open-cluster-management" via the "private-hub-policy" +# +# Then we copy the secret from the "open-cluster-management" namespace to the +# managed clusters "openshift-gitops" instance +# +# And we also copy the same secret to the namespaced argo's namespace +{{ if $.Values.global.privateRepo }} +{{ if .Values.clusterGroup.isHubCluster }} +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: private-hub-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: private-hub-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: Secret + apiVersion: v1 + type: Opaque + metadata: + name: vp-private-repo-credentials + namespace: open-cluster-management + labels: + argocd.argoproj.io/secret-type: repository + data: '{{ `{{copySecretData "openshift-gitops" "vp-private-repo-credentials"}}` }}' +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: private-hub-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: private-hub-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: private-hub-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: private-hub-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: In + values: + - 'true' +--- +{{ end }}{{- /* if .Values.clusterGroup.isHubCluster */}} +{{- range .Values.clusterGroup.managedClusterGroups }} +{{- $group := . }} +{{- if not .hostedArgoSites }} +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: private-{{ .name }}-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: private-{{ .name }}-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: Secret + apiVersion: v1 + type: Opaque + metadata: + name: vp-private-repo-credentials + namespace: openshift-gitops + labels: + argocd.argoproj.io/secret-type: repository + data: '{{ `{{hub copySecretData "open-cluster-management" "vp-private-repo-credentials" hub}}` }}' + - complianceType: mustonlyhave + objectDefinition: + kind: Secret + apiVersion: v1 + type: Opaque + metadata: + name: vp-private-repo-credentials + namespace: {{ $.Values.global.pattern }}-{{ .name }} + labels: + argocd.argoproj.io/secret-type: repository + data: '{{ `{{hub copySecretData "open-cluster-management" "vp-private-repo-credentials" hub}}` }}' +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: private-{{ .name }}-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: private-{{ .name }}-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: private-{{ .name }}-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: private-{{ .name }}-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +{{- end }}{{- /* if not .hostedArgoSites */}} +{{- end }}{{- /* range .Values.clusterGroup.managedClusterGroups */}} +{{- end }}{{- /* if $.Values.global.privateRepo */}} diff --git a/clustergroup/templates/_helpers.tpl b/clustergroup/templates/_helpers.tpl index e7736a6c..83b06a04 100644 --- a/clustergroup/templates/_helpers.tpl +++ b/clustergroup/templates/_helpers.tpl @@ -20,6 +20,8 @@ Default always defined top-level variables for helm charts value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} +- name: global.privateRepo + value: {{ $.Values.global.privateRepo | quote }} {{- end }} {{/* clustergroup.globalvaluesparameters */}} diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl index 8a946b3c..f75e781e 100644 --- a/clustergroup/templates/imperative/_helpers.tpl +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -1,3 +1,9 @@ +# Pseudo-code +# 1. Get the pattern's CR +# 2. If there is a secret called vp-private-repo-credentials in the current namespace, fetch it +# 3. If it is an http secret, generate the correct URL +# 4. If it is an ssh secret, create the private ssh key and make sure the git clone works + {{/* git-init InitContainer */}} {{- define "imperative.initcontainers.gitinit" }} - name: git-init @@ -9,7 +15,28 @@ command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- {{ $.Values.global.repoURL }} /git/repo;chmod 0770 /git/{repo,home}" + - >- + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials &> /dev/null; then + URL="{{ $.Values.global.repoURL }}"; + else + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.sshPrivateKey | base64decode}}` }}' &>/dev/null; then + U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.username | base64decode }}` }}')"; + P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.password | base64decode }}` }}')"; + URL=$(echo {{ $.Values.global.repoURL }} | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); + echo "USER/PASS: ${URL}"; + else + S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.sshPrivateKey | base64decode }}` }}')"; + mkdir -p --mode 0700 "${HOME}/.ssh"; + echo "${S}" > "${HOME}/.ssh/id_rsa"; + chmod 0600 "${HOME}/.ssh/id_rsa"; + URL=$(echo {{ $.Values.global.repoURL }} | sed -E "s/(https?:\/\/)/\1git@/"); + git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; + echo "SSH: ${URL}"; + fi; + fi; + mkdir /git/{repo,home}; + git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- "${URL}" /git/repo; + chmod 0770 /git/{repo,home}; volumeMounts: - name: git mountPath: "/git" From e8800f87a7c03cd2faad67f9004575a2d3b2abb6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 29 Nov 2023 15:33:00 +0100 Subject: [PATCH 1073/1263] Amend tests --- .../acm-industrial-edge-factory.expected.yaml | 9 +++ tests/acm-industrial-edge-hub.expected.yaml | 9 +++ tests/acm-medical-diagnosis-hub.expected.yaml | 9 +++ tests/acm-naked.expected.yaml | 9 +++ tests/acm-normal.expected.yaml | 9 +++ ...roup-industrial-edge-factory.expected.yaml | 25 ++++++- ...tergroup-industrial-edge-hub.expected.yaml | 60 +++++++++++++++- ...rgroup-medical-diagnosis-hub.expected.yaml | 72 ++++++++++++++++++- tests/clustergroup-naked.expected.yaml | 23 +++++- tests/clustergroup-normal.expected.yaml | 50 ++++++++++++- 10 files changed, 267 insertions(+), 8 deletions(-) diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index 86d7277d..2210b4cf 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -5,6 +5,15 @@ # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io --- +# Source: acm/templates/policies/private-repo-policies.yaml +# We copy the vp-private-repo-credentials from the "openshift-gitops" namespace +# to the "open-cluster-management" via the "private-hub-policy" +# +# Then we copy the secret from the "open-cluster-management" namespace to the +# managed clusters "openshift-gitops" instance +# +# And we also copy the same secret to the namespaced argo's namespace +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 6bb30bac..f9627771 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -2,6 +2,15 @@ # Source: acm/templates/policies/acm-hub-ca-policy.yaml # This pushes out the HUB's Certificate Authorities on to the imported clusters --- +# Source: acm/templates/policies/private-repo-policies.yaml +# We copy the vp-private-repo-credentials from the "openshift-gitops" namespace +# to the "open-cluster-management" via the "private-hub-policy" +# +# Then we copy the secret from the "open-cluster-management" namespace to the +# managed clusters "openshift-gitops" instance +# +# And we also copy the same secret to the namespaced argo's namespace +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 2361be7a..cea5a1dc 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -2,6 +2,15 @@ # Source: acm/templates/policies/acm-hub-ca-policy.yaml # This pushes out the HUB's Certificate Authorities on to the imported clusters --- +# Source: acm/templates/policies/private-repo-policies.yaml +# We copy the vp-private-repo-credentials from the "openshift-gitops" namespace +# to the "open-cluster-management" via the "private-hub-policy" +# +# Then we copy the secret from the "open-cluster-management" namespace to the +# managed clusters "openshift-gitops" instance +# +# And we also copy the same secret to the namespaced argo's namespace +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index cb73d733..5ba9bd60 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -5,6 +5,15 @@ # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io --- +# Source: acm/templates/policies/private-repo-policies.yaml +# We copy the vp-private-repo-credentials from the "openshift-gitops" namespace +# to the "open-cluster-management" via the "private-hub-policy" +# +# Then we copy the secret from the "open-cluster-management" namespace to the +# managed clusters "openshift-gitops" instance +# +# And we also copy the same secret to the namespaced argo's namespace +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index a83284bb..55553a79 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -22,6 +22,15 @@ type: Opaque # Source: acm/templates/policies/acm-hub-ca-policy.yaml # This pushes out the HUB's Certificate Authorities on to the imported clusters --- +# Source: acm/templates/policies/private-repo-policies.yaml +# We copy the vp-private-repo-credentials from the "openshift-gitops" namespace +# to the "open-cluster-management" via the "private-hub-policy" +# +# Then we copy the secret from the "open-cluster-management" namespace to the +# managed clusters "openshift-gitops" instance +# +# And we also copy the same secret to the namespaced argo's namespace +--- # Source: acm/templates/provision/clusterpool.yaml apiVersion: hive.openshift.io/v1 kind: ClusterClaim diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 8d55b696..352d8400 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -360,7 +360,28 @@ spec: command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - >- + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials &> /dev/null; then + URL="https://github.com/pattern-clone/mypattern"; + else + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode}}' &>/dev/null; then + U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; + P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); + echo "USER/PASS: ${URL}"; + else + S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; + mkdir -p --mode 0700 "${HOME}/.ssh"; + echo "${S}" > "${HOME}/.ssh/id_rsa"; + chmod 0600 "${HOME}/.ssh/id_rsa"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); + git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; + echo "SSH: ${URL}"; + fi; + fi; + mkdir /git/{repo,home}; + git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + chmod 0770 /git/{repo,home}; volumeMounts: - name: git mountPath: "/git" @@ -502,6 +523,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index f09d1a75..b38ac727 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -521,7 +521,28 @@ spec: command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - >- + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials &> /dev/null; then + URL="https://github.com/pattern-clone/mypattern"; + else + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode}}' &>/dev/null; then + U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; + P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); + echo "USER/PASS: ${URL}"; + else + S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; + mkdir -p --mode 0700 "${HOME}/.ssh"; + echo "${S}" > "${HOME}/.ssh/id_rsa"; + chmod 0600 "${HOME}/.ssh/id_rsa"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); + git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; + echo "SSH: ${URL}"; + fi; + fi; + mkdir /git/{repo,home}; + git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + chmod 0770 /git/{repo,home}; volumeMounts: - name: git mountPath: "/git" @@ -595,7 +616,28 @@ spec: command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - >- + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials &> /dev/null; then + URL="https://github.com/pattern-clone/mypattern"; + else + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode}}' &>/dev/null; then + U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; + P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); + echo "USER/PASS: ${URL}"; + else + S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; + mkdir -p --mode 0700 "${HOME}/.ssh"; + echo "${S}" > "${HOME}/.ssh/id_rsa"; + chmod 0600 "${HOME}/.ssh/id_rsa"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); + git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; + echo "SSH: ${URL}"; + fi; + fi; + mkdir /git/{repo,home}; + git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + chmod 0770 /git/{repo,home}; volumeMounts: - name: git mountPath: "/git" @@ -775,6 +817,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: ignoreDifferences: [ { "group": "internal.open-cluster-management.io", @@ -836,6 +880,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -888,6 +934,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -940,6 +988,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: ignoreDifferences: [ { "group": "apps", @@ -1022,6 +1072,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -1074,6 +1126,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -1153,6 +1207,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: - name: global.openshift value: "true" - name: injector.enabled diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 6b8b88ca..b5641fb2 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -448,7 +448,28 @@ spec: command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - >- + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials &> /dev/null; then + URL="https://github.com/pattern-clone/mypattern"; + else + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode}}' &>/dev/null; then + U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; + P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); + echo "USER/PASS: ${URL}"; + else + S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; + mkdir -p --mode 0700 "${HOME}/.ssh"; + echo "${S}" > "${HOME}/.ssh/id_rsa"; + chmod 0600 "${HOME}/.ssh/id_rsa"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); + git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; + echo "SSH: ${URL}"; + fi; + fi; + mkdir /git/{repo,home}; + git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + chmod 0770 /git/{repo,home}; volumeMounts: - name: git mountPath: "/git" @@ -522,7 +543,28 @@ spec: command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - >- + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials &> /dev/null; then + URL="https://github.com/pattern-clone/mypattern"; + else + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode}}' &>/dev/null; then + U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; + P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); + echo "USER/PASS: ${URL}"; + else + S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; + mkdir -p --mode 0700 "${HOME}/.ssh"; + echo "${S}" > "${HOME}/.ssh/id_rsa"; + chmod 0600 "${HOME}/.ssh/id_rsa"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); + git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; + echo "SSH: ${URL}"; + fi; + fi; + mkdir /git/{repo,home}; + git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + chmod 0770 /git/{repo,home}; volumeMounts: - name: git mountPath: "/git" @@ -660,6 +702,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -712,6 +756,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -764,6 +810,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -816,6 +864,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -868,6 +918,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -920,6 +972,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -972,6 +1026,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -1024,6 +1080,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: - name: global.openshift value: "true" - name: injector.enabled @@ -1094,6 +1152,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -1146,6 +1206,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: @@ -1198,6 +1260,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: ignoreDifferences: [ { "group": "apps.openshift.io", @@ -1259,6 +1323,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: ignoreDifferences: [ { "group": "apps.openshift.io", @@ -1320,6 +1386,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 5d12b04d..b682e994 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -213,7 +213,28 @@ spec: command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- /git/repo;chmod 0770 /git/{repo,home}" + - >- + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials &> /dev/null; then + URL=""; + else + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode}}' &>/dev/null; then + U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; + P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; + URL=$(echo | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); + echo "USER/PASS: ${URL}"; + else + S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; + mkdir -p --mode 0700 "${HOME}/.ssh"; + echo "${S}" > "${HOME}/.ssh/id_rsa"; + chmod 0600 "${HOME}/.ssh/id_rsa"; + URL=$(echo | sed -E "s/(https?:\/\/)/\1git@/"); + git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; + echo "SSH: ${URL}"; + fi; + fi; + mkdir /git/{repo,home}; + git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + chmod 0770 /git/{repo,home}; volumeMounts: - name: git mountPath: "/git" diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 62136abe..0c1db0f2 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -410,7 +410,28 @@ spec: command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - >- + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials &> /dev/null; then + URL="https://github.com/pattern-clone/mypattern"; + else + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode}}' &>/dev/null; then + U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; + P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); + echo "USER/PASS: ${URL}"; + else + S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; + mkdir -p --mode 0700 "${HOME}/.ssh"; + echo "${S}" > "${HOME}/.ssh/id_rsa"; + chmod 0600 "${HOME}/.ssh/id_rsa"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); + git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; + echo "SSH: ${URL}"; + fi; + fi; + mkdir /git/{repo,home}; + git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + chmod 0770 /git/{repo,home}; volumeMounts: - name: git mountPath: "/git" @@ -484,7 +505,28 @@ spec: command: - 'sh' - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + - >- + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials &> /dev/null; then + URL="https://github.com/pattern-clone/mypattern"; + else + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode}}' &>/dev/null; then + U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; + P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); + echo "USER/PASS: ${URL}"; + else + S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; + mkdir -p --mode 0700 "${HOME}/.ssh"; + echo "${S}" > "${HOME}/.ssh/id_rsa"; + chmod 0600 "${HOME}/.ssh/id_rsa"; + URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); + git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; + echo "SSH: ${URL}"; + fi; + fi; + mkdir /git/{repo,home}; + git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + chmod 0770 /git/{repo,home}; volumeMounts: - name: git mountPath: "/git" @@ -624,6 +666,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: ignoreDifferences: [ { "group": "internal.open-cluster-management.io", @@ -688,6 +732,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: apps.region.example.com + - name: global.privateRepo + value: syncPolicy: automated: {} retry: From df468d60883ac8fe279c57d57a84c2b251a63024 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Dec 2023 10:47:03 +0100 Subject: [PATCH 1074/1263] Check for rc attribute to exist Sometimes CI would error out with: [localhost]: FAILED! => {"msg": "The conditional check 'vault_role_cmd.rc == 0' failed. The error was: error while evaluating conditional (vault_role_cmd.rc == 0): 'dict object' has no attribute 'rc'. 'dict object' has no attribute 'rc'"} This can ahepn when a call returns error 500 for whatever reason. Let's make sure we catch this situation and keep trying and don't give up due to this spurious error. --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 9255a0ca..31d2878b 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -30,7 +30,9 @@ command: sh -c "vault list auth/{{ vault_hub }}/role | grep '{{ vault_hub }}-role'" register: vault_role_cmd - until: vault_role_cmd.rc == 0 + until: + - vault_role_cmd.rc is defined + - vault_role_cmd.rc == 0 retries: 20 delay: 45 changed_when: false From cc56214bf4e199cc54a985be55383d8582560c8b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Dec 2023 11:35:49 +0100 Subject: [PATCH 1075/1263] Upgrade default imperative image Tested in MCG and everything deployed correctly (imperative ansible jobs ran without issues) --- clustergroup/values.yaml | 2 +- ...stergroup-industrial-edge-factory.expected.yaml | 8 ++++---- .../clustergroup-industrial-edge-hub.expected.yaml | 14 +++++++------- ...lustergroup-medical-diagnosis-hub.expected.yaml | 14 +++++++------- tests/clustergroup-naked.expected.yaml | 8 ++++---- tests/clustergroup-normal.expected.yaml | 14 +++++++------- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 4c408463..bb3a6e27 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -25,7 +25,7 @@ clusterGroup: imperative: jobs: [] # This image contains ansible + kubernetes.core by default and is used to run the jobs - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest namespace: "imperative" # configmap name in the namespace that will contain all helm values valuesConfigMap: "helm-values-configmap" diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 8d55b696..e9fc7484 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -119,7 +119,7 @@ data: clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' jobName: imperative-job @@ -352,7 +352,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -365,7 +365,7 @@ spec: - name: git mountPath: "/git" - name: test - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -388,7 +388,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always command: - 'sh' diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index f09d1a75..b8b2fe82 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -240,7 +240,7 @@ data: clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' jobName: imperative-job @@ -513,7 +513,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -526,7 +526,7 @@ spec: - name: git mountPath: "/git" - name: test - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -549,7 +549,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always command: - 'sh' @@ -587,7 +587,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -600,7 +600,7 @@ spec: - name: git mountPath: "/git" - name: unseal-playbook - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -625,7 +625,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always command: - 'sh' diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 6b8b88ca..d5b5644b 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -223,7 +223,7 @@ data: clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' jobName: imperative-job @@ -440,7 +440,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -453,7 +453,7 @@ spec: - name: git mountPath: "/git" - name: test - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -476,7 +476,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always command: - 'sh' @@ -514,7 +514,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -527,7 +527,7 @@ spec: - name: git mountPath: "/git" - name: unseal-playbook - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -552,7 +552,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always command: - 'sh' diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 5d12b04d..595dedde 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -46,7 +46,7 @@ data: clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' jobName: imperative-job @@ -205,7 +205,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -218,7 +218,7 @@ spec: - name: git mountPath: "/git" - name: unseal-playbook - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -243,7 +243,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always command: - 'sh' diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 62136abe..2196680c 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -131,7 +131,7 @@ data: clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' jobName: imperative-job @@ -402,7 +402,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -415,7 +415,7 @@ spec: - name: git mountPath: "/git" - name: test - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -438,7 +438,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always command: - 'sh' @@ -476,7 +476,7 @@ spec: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - name: git-init - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -489,7 +489,7 @@ spec: - name: git mountPath: "/git" - name: unseal-playbook - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always env: - name: HOME @@ -514,7 +514,7 @@ spec: subPath: values.yaml containers: - name: "done" - image: registry.redhat.io/ansible-automation-platform-23/ee-supported-rhel8:latest + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always command: - 'sh' From e4867d72221aece54e8f868fc8d44ffdc34a4b2d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Dec 2023 11:45:10 +0100 Subject: [PATCH 1076/1263] Release clustergroup v0.8.1 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 3a4ee397..31c94318 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.8.0 +version: 0.8.1 From 9893d1d921cf2ff2795c0b9a01cd827fa6f89463 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 5 Dec 2023 12:17:36 +0100 Subject: [PATCH 1077/1263] Update pattern operator CRD --- .../crds/gitops.hybrid-cloud-patterns.io_patterns.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml index 1806661d..37626bdd 100644 --- a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml +++ b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml @@ -116,9 +116,9 @@ spec: used when developing the clustergroup helm chart) type: string clusterGroupChartVersion: - default: 0.0.* + default: 0.8.* description: Which chart version for the clustergroup helm chart - Defaults to "0.0.*" + Defaults to "0.8.*" type: string clusterGroupGitRepoUrl: description: The url when deploying the clustergroup helm chart @@ -126,7 +126,7 @@ spec: (Only used when developing the clustergroup helm chart) type: string enabled: - default: false + default: true description: (EXPERIMENTAL) Enable multi-source support when deploying the clustergroup argo application type: boolean From 2ff8ab186e5142e98dc56c51b42624435b6ae9f2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 6 Dec 2023 15:43:11 +0100 Subject: [PATCH 1078/1263] Update CRD from the operator --- .../crds/gitops.hybrid-cloud-patterns.io_patterns.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml index 37626bdd..20203625 100644 --- a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml +++ b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml @@ -116,8 +116,7 @@ spec: used when developing the clustergroup helm chart) type: string clusterGroupChartVersion: - default: 0.8.* - description: Which chart version for the clustergroup helm chart + description: Which chart version for the clustergroup helm chart. Defaults to "0.8.*" type: string clusterGroupGitRepoUrl: @@ -131,9 +130,8 @@ spec: the clustergroup argo application type: boolean helmRepoUrl: - default: https://charts.validatedpatterns.io/ description: The helm chart url to fetch the helm charts from - in order to deploy the pattern Defaults to https://charts.validatedpatterns.io/ + in order to deploy the pattern. Defaults to https://charts.validatedpatterns.io/ type: string type: object required: From 59cd59a88e8e2a35694522a8923b8a2089c955e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 19:08:37 +0000 Subject: [PATCH 1079/1263] Bump actions/setup-python from 4 to 5 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ansible-unittest.yml | 2 +- .github/workflows/jsonschema.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ansible-unittest.yml b/.github/workflows/ansible-unittest.yml index 90d4507f..c9f7485a 100644 --- a/.github/workflows/ansible-unittest.yml +++ b/.github/workflows/ansible-unittest.yml @@ -38,7 +38,7 @@ jobs: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/jsonschema.yaml b/.github/workflows/jsonschema.yaml index ad83173a..e47de928 100644 --- a/.github/workflows/jsonschema.yaml +++ b/.github/workflows/jsonschema.yaml @@ -38,7 +38,7 @@ jobs: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} From 82a4afaa48681528a174b73eeef70ab7d5fe68c5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 12 Dec 2023 15:49:53 +0100 Subject: [PATCH 1080/1263] Release clustergroup v0.8.2 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 31c94318..345b8175 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.8.1 +version: 0.8.2 From d27b94fa8f4dd3fccff2ddd50181a192e06acb5a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 12 Dec 2023 16:19:51 +0100 Subject: [PATCH 1081/1263] Update CRD from the operator --- .../gitops.hybrid-cloud-patterns.io_patterns.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml index 20203625..68be225f 100644 --- a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml +++ b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml @@ -98,12 +98,22 @@ spec: type: integer targetRepo: description: Git repo containing the pattern to deploy. Must use - https/http + https/http or, for ssh, git@server:foo/bar.git type: string targetRevision: description: 'Branch, tag, or commit to deploy. Does not support short-sha''s. Default: HEAD' type: string + tokenSecret: + description: Optional. K8s secret name where the info for connecting + to git can be found. The supported secrets are modeled after + the private repositories in argo (https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#repositories) + currently ssh and username+password are supported + type: string + tokenSecretNamespace: + description: Optional. K8s secret namespace where the token for + connecting to git can be found + type: string required: - targetRepo type: object @@ -211,6 +221,8 @@ spec: lastStep: description: Last action related to the pattern type: string + path: + type: string version: description: Number of updates to the pattern type: integer From df739e2f2f57918525dfd9a5ab11c070fb26033f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 14 Dec 2023 21:38:41 +0100 Subject: [PATCH 1082/1263] Small clarification in IIB --- ansible/roles/iib_ci/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 828daa05..98355ad9 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -54,7 +54,7 @@ export CHANNEL=$(oc get -n openshift-marketplace packagemanifests -l "catalog=ii make EXTRA_HELM_OPTS="--set main.extraParameters[0].name=clusterGroup.subscriptions.acm.source --set main.extraParameters[0].value=iib-${IIB} --set main.extraParameters[1].name=clusterGroup.subscriptions.acm.channel --set main.extraParameters[1].value=${CHANNEL}" install ``` -*Note*: This needs VP operator version >= 0.0.14 +*Note*: In this case `acm` is the name of the subscription in `values-hub.yaml` ### OCP 4.13 and onwards From 5e514ab9555538f2f7a1d582ecdcc232ff9e511e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 17 Dec 2023 19:03:55 +0100 Subject: [PATCH 1083/1263] Switch imageDigestMirrors to AllowContactingSource Currently when we load a preview operator via the IIB mechanism we redirect all images making up the operator bundle to the cluster-internal registry. This is all fine and well, except these redirects (done via an ImageDigestMirrorSet) are based on image names without any specific hashes. (This is because OCP won't allow you to specify hashes). The problem arises when there is a prerelease operator which includes an image that is used by the other non-prerelease operators. So if AAP prerelease uses the image "registry.redhat.io/public/redis-6" we redirect all these redis 6 images towards the internal registry. But if another operator needs the redis-6 image with a hash that is not the exact same that is used by AAP prerelease, it will be unable to find it on the internal registry because we never uploaded it. This is an example error: 2023-12-13 07:18:06,216 INFO Warning Failed 64m (x6 over 66m) kubelet Error: ImagePullBackOff 2023-12-13 07:18:06,216 INFO Normal BackOff 83s (x286 over 66m) kubelet Back-off pulling image "registry.redhat.io/rhel8/redis-6@sha256:edbd40185ed8c20ee61ebdf9f2e1e1d7594598fceff963b4dee3201472d6deda" And this is a relevant /etc/containers/registries.conf : [[registry]] prefix = "" location = "registry.redhat.io/rhel8/redis-6" blocked = true [[registry.mirror]] location = "default-route-openshift-image-registry.apps.mcg-hub.blueprints.rhecoeng.com/openshift-marketplace/redis-6" insecure = true pull-from-mirror = "digest-only" If we change the `mirrorSourcePolicy` from `NeverContactSource` to `AllowContactingSource` we actually avoid this problem entirely. OCP will try to pull the images from both the internal registry and the original source and use the one it was able to find. Tested both on AAP and Gitops prerelease and both deployed correctly which was not the case before. --- ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 b/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 index d23ab9f2..1b04f321 100644 --- a/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 +++ b/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 @@ -10,9 +10,9 @@ spec: - mirrors: - {{ item.mirrordest_nosha }} source: {{ item.source_nosha }} - mirrorSourcePolicy: NeverContactSource + mirrorSourcePolicy: AllowContactingSource - mirrors: - {{ item.mirrordest_nosha }} source: {{ item.image_nosha }} - mirrorSourcePolicy: NeverContactSource + mirrorSourcePolicy: AllowContactingSource {% endfor %} From a3c26275832626dc1bc2b23e48eda08c567b9968 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 18 Dec 2023 09:58:30 +0100 Subject: [PATCH 1084/1263] Upgrade ESO to v0.9.10 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.9.10.tgz | Bin 0 -> 86339 bytes .../charts/external-secrets-0.9.9.tgz | Bin 85534 -> 0 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 148 ++++++++++++------ ...-secrets-industrial-edge-hub.expected.yaml | 148 ++++++++++++------ ...ecrets-medical-diagnosis-hub.expected.yaml | 148 ++++++++++++------ ...olang-external-secrets-naked.expected.yaml | 148 ++++++++++++------ ...lang-external-secrets-normal.expected.yaml | 148 ++++++++++++------ 9 files changed, 489 insertions(+), 259 deletions(-) create mode 100644 golang-external-secrets/charts/external-secrets-0.9.10.tgz delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.9.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index c8a4a3a6..613a9a96 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.3 dependencies: - name: external-secrets - version: "0.9.9" + version: "0.9.10" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.9.10.tgz b/golang-external-secrets/charts/external-secrets-0.9.10.tgz new file mode 100644 index 0000000000000000000000000000000000000000..dc04b636814218c1f0d03356d4e07b27341dfc45 GIT binary patch literal 86339 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 z znKO0YA@z)YdeZGy*6YVOw!rrFtKZKYKjA2L##4d!;P^_CkaXp}Mo>G$;qR6lYS2XV zU!J7Rs(@lGQS1XzkT^tEXk{KmwdI7tIq2lfH+Z38d;?g}`Au?&uzr@g-%(+>FuM&+f^jn96DMzJ_h0 z@kl)E>SL930gT5jJ`;`FzMc1R=HC{Gdl?}i!)IC@OOqxz!MC(@`R?oJVVbh&EaR@>DPY_CMR(^H%yan%qnP27L#jlox50+ z+G)zuIg$#1#^ojzhf6<6Y=*mQHLP)}0}veZvMtAHxRowbqWz@0c>$UwzZWc09-<1d z%G#|=A{RnFEH9?M8r$ly+iJA#sWz|9uuGuMCVBsiLk94%Z#gwEU?KM6e zf*EGFPwg`vgi}uK9We-}M^sin5*F+lDl9tq1+xYvvzS6VNxQg50@ z{ks9FGsz*TQ1SIVdT83o66D5GOoXqzr5Tslwbz6FI!4}IzVh4L2) zlTLkR-i#B`I0~T}6#5?`MS<a4%AzNk?2pflW- z41537=ib@fe#2MPyNU!x<3ix$FEH_tavagCl3aDi&J%I(l!!(iOw|I+F(#JkKFM2q zR5|USgt2|9Oce43KNq!2)9FoP7o95GhRj#OOqY5Ois260kLROFCKbW>jdU#tAC!Xl ze)J9MmuI`Hka0$wrB%!HqSP>E-C|*bJ)HPcXC58W5eyssHI}ej3M@l+a49CS4NaUd zNrT)n2Z(88uLnVnC28CgtEvVT`}85I#c&O2$>14u0^mD7=c%(PjZXKqpQEnKZ@~y9 zNAu3l4=?_3TnFCFj;;g)RUVJG^dm1%#WE=A4z`5g*4w;1&DQ|KN;yF9o(f*`FGPr* z7G8*+6@G{wt~YDohFOSSt5Io@R_Q?5ynCj93)FYUtRlXrMeOaLjg?MjXxdB_JtXR= zM<27)C8_djK|WGiz(aHb7$|jR84oeYmsWxZNQfQyvBkgP`T~ZKtI18(V!r}Pg z%B@XKnDi+I^+a_PI(JQ~a&>MU#@$)VYt|zkH^rpqetg^g^9ee4Tx=U5D{M@R)uO*+ z8UZ!`J_FVJwrX@PAgYf~JU&!GZG8;&gwB_1#bP_t;5c<+W!LCqsI zij!E_ZDCAd8~sF@?S+c>TRP_t)SK}27UlY?sjf!7>E@w~o0M~s*M1HNXb`dvwM>p! zen-2-JP8sQW?Z8=rJMwGFwjRdXEa?=GvL5x;No&f4#7fCCI~;4CcdDivUHKNuVk0P zpkd(zQExUD^HXtlT-vLZeCl*ui1o*#R^{71UHvAy4JhdSN;DI=zj8=B(LK8wG=9;0 z{E#804m-cp6^?1zl*+OCP&Zzsdk z5%djQWZI?yzONej4H9HsF*#sF(3VyN9@b&GR%VPB!%*oBfTN6qb8_>h0+XOBqvcQ)nd8nF!MDTt-YAZ6?%`1QjD+!f9c>dR1fxm8rqj!UrjimN=eL!sLHL z3ZVw-67^6{E?g&41YCTl-8~TpyCQDCjD7DpB=wH25zljR($AHX)<<Tf(WB|4YLlaTa0D`7Bt9P19e z)WF7ka~b70AofmBuQQQNFG9Kq(#gg^JJ>H2m@)kMAV9~)bq;e$eRi{9x->xjd=gfO zN~e_#mD&xZVk&e}1D9HjjA?a!SZyIF$(p@iEi&pSf^N6GB zyQYB}!b2{lqafjSwvK6~Xe(fT#tdUyKH}{!I`cLm=&p2C z7FO~%a6cZg2|<5S8GG745o!z)dDQ7jz6R6}<>vB9|Ds&D1#&&LCUEg@tk*jz@@hzZ zX;gwar?gdAR^LBJi*ToAxRZO7>kdI%m@`{yDyFKOcLQ^UzUOb{R~|Q$4c*Lo?I82D zCQs-m!Fpd0a2wfxk%lsRP#Qe6Yw{4>o-%;YCb1_xhwI-CT_ghflY0>$X%U#Z%1%e8 z%xGl#5y$yk0@vx|q&d}%AcW6d4HsP{;#fl1S&jzr^o}u}dKB2ZP;?5Cv7X2D`!T6X zMiwA!MsoHUN8(Zw$JQ}&ch8$g-f#dOE{5zY{bWuEdEB`T1MG1l!h9 z_-}RXAClPfkZM;ZxONx@=i?k8C^OZ$Cx{wN7Q`WzOjZTIk{lLbvW_sNY!oP|oen%d z(njun0GyRBzP}{!QI)^%GK;oI#6}cWt`Q%O$9Ufoh{%)y$&egtwY4_z@D0xSNxR!y zZDT7cZ$@WjUOCdnU2t=-qjZpER9}ryRUKOL9S7mmelF4q<_7b?cb)+`f)bNlw4fCxljWx(& z;8EPnq-cbPhyrpB>3K1n!$!vb)|tUUOLI-CC$nX+{d?N?nsEq7VD_j_YB}2U39<*T zU+Qw{(%hPZ&%mV9P_{&xdF4ILYi1IZj6rqlDvWN^+a+cN9_(4drz3!e)iw?*IC-+)*8143!@-Y5h68K1KWv%MH) zD=+J7VBj>`Q>(oc^v-ihQif_8fz3KaAUT`IUzd4?qngNiF=1V)J{@PIOXDnMhDz;y zHY!ZL!!kgHtl1BZBdOWdz$eqU z?&29n>@zy|z$S;quUN^B`C5|S+1NksclL^iTfHo=T0oCWXBbHFnHen#4>RSl2;4xq zyLV>@bkC6~IbW`;0l;eX%60&Xz$M?1w3q}OQg}P*r54i=r-`H1y5c=k%Pn{3694=| zImb6E-ha)bFA&CgbA-plS)niAHFGqU8YDbZ&QzLGi6AEJrO<5s!W2*e(X7k+<1PAS zAoIsCBc1&C1hO~@<&qpT*>Zz5X z$`kb?k7zxGl7`%tCZgN3vQ@_^GTmJU{*97oM=qa$QRO)U zRkvC`f_lAz+xijB1d%xwQ@26| z=`1vHJo+(BY4=ki9(E$7t#x9YdA!L1Qgr&72eT^Go1uSoyLU9XfAnDR2HR%OB(_~z zexD*rc+y?_LqUB>9fl=XM+}g}<{oA{xW)BbCgsN5h85D~UUcFEn_mPf4sCf?+^DB` z-b!FZ@<(bhZ8(y@dh^V2$F7?+rfEn>7>*a6&mFFN&k?@G<|;n>dOxV_L>xj*iPPAi z_~498sBG}s2rA2;Ucb<-NgBnPDcz#h*nBrL*L82ddFB`2n>305oBVkKC}=r<;n#5+ z$a{%{!goaI&XNnErWxVlja7FL-Lu3S-P1I=iKy-+qtcA@h`~xtm?%rSA~dOnk{t5qr9j8{Shf^K>myjMcLMrPWQcFmYf zAxwZtnF2c9>nF5!IOQ>XXD?DFe}kf9KLw=GA~zpz9*N)|e%c1(V*Y{QyyEKNJhF9X zj)h;|l~9LuQzThOx}RwE4RkEM)Y891(|)QT*~Q%wb#HXRV|z=En~~N1EAzr~?s-tE zGxmx60hO11(??l@Fs{Gq_bBM=1vQQST$%XCj=L^oL;pFioOJo~!m+UV%CT^%;CiT$ zQ$b@4UFwho)#P8lfKV8XoY6YQ#Aly6U=Z(}vcnLjc<&9Z4pPY+XEJL!fadIl5koeU zN^gK+mgo9%m09fz&t#6Jt%#<0;oof(X3P5?{$aCDlPZ(Gt{YN=2EZ|RoX>p*G1~}M z3^A@tt8c;Ki4w6cBYRi3_O<={a&^!#{`uMxD)I*C9K+^N+#0DV{r^|ew zW}Uh7jkc>0*wE9M6NR(y%|}UDo&Q9$DX$`(<#K4JY5-OhcY)~!Z{{-Z>QXS@NsW^_R} z8bLitEK+ZoRTt+F2R+k=(Nu|k{P2oV>6 zW0t5Ow=pK50SV2>)Hp}4KWY%jpoep*29Kd3@KnFE_mB{g2l9z9rAhiEIDl(VrH2}%N;O)^2#y7!MdKhZtH_6C31T>Ct4kmp2$)VD=VW+TREJdDMDo8cS zlt~;~Lk0|@gUrRzEhdE;M|J~KiCe#per|7cZzs@)a=n=qH*!jXCzA`N+B}q}z3b*i zIdR+ZAv#pN+hY^qGL5;nAASjSh zVV#tyM2vd@EE}z=ZZ+%FZ$h|9FqX|%x7K%6F8DehWAWM%S$RTb1*eoYB+d-mootuS z%*#Dr&&)e+!pV4D&&s<#X{s*Tk3Zc(oA2${zrBDJ&C~Ixmfp*a+po(i==JUdKs&5K zh&>RDV%NC-#uqoOgf&fslbbk3G=-Jb)oW^yh1loV1#(KXi?ihTr)iLx9hL4AIl{hA zf9&s<QTDhNJyrs%KRg=F<(yY)oV<$$?Zr{nn-5sDWB zN^ETnl1Y^Tk?xUJ>wmM5 zW_l%JMbzK}PceL2q7pDWAdzIZAMajnoX)qPmdbTNMv5H_H^*Q|L_VCp|L)2;@|Jcc zKST-$>hj~B4!m(-yl`L^pW8D;CY2za#Li#ogC@9N$4=qY}Dnc?r+rfGZ;;NWjks|HMHw! zR;j0aw@00%8H3N_^8sD4#wwWm{3$vr*fw(i8I4C~`IfE{gIoRL7G zMhp5)M<+Lps9|=?0w>PnrLMBk?~?z9gcxrEaS-%YN+;n&q|PysCc|NzpI3O-m9o{_ zsOct&gC0+G|F=CUNgsZqx)OE;i2C%2zjb`AY4#*jcD1bZ!Ki~fRLR7hK-B2|=e?CP z$yR=tP%N_G>$Ze6nQ7!-c3zexYBMc;M@FHftbUG&>f}C{%)5BQLTpn-61B~zF}`Z_ z0Z@+H7xuuVK}@8|Z6qgY=Th=^aZ=uk$^|qs?B>{b^M@*g_#)l6B8HTdi2!c4;Bw`s`C=LsN*yQuR&LaNlV)y1-; zMexI*EG3M7o>uaN91%?)UJqoLrjb1K`ipX7P0FhQ#VF53DF&YEq^Yn=*vFg=l5tlg zz48UpHR2tDwLxe}n^UhLmG-Y?CwU?FPx{#%gazhc+tTvMTGkkD~+)5)FehR7wDnG|4D07!xLX`q*S*Bh`r=f zOm{6QU{5<$fMFh5+l{%i@2a_;)<=#FLH6AJaf3tp{Z|I4Q0$~t4mxT5Oq^X1NgR6R z_Zr_rRcHc?ElT7;r$1XS0vxb)@1_LAu%ZV8g5r&}Ki!YcItyr(U~R6Iv@3m^1HV-E ziLEVDM>7QL($B7uI1Au0<>ol|?X_wSr{|k~pFrshh;9Ym4K*`EeB~EM-;J9~44gU6 zVUj1fQKB&T2lMIK9n*_|%)mb~E%*b9J0q%(WS9r(7L_7ZDDS6FXSSnb?16JADt*@f=>)L^UTLXw_n zj#i>}(hQ(2BQgTXr4353Cyypkzto`xID&UB% zsQ~+W!;q~jzXy*t@hrV%gd45p;;ij*&hx(e7qk}FZ1a&L-UtF++@Z@ZexJi?Sc9A8 zcxD4V`lxAKqqz%grKfsAenuGm#$Xd>tS-)JAElml#hMXLCA!zIVU3&k`qeOPGw*$z zkKG^ww?cAgETn3^^r?>ph0Fdb;a-q7_)Hzd<9UuC)deAXhZI_@YZn!6o)0uDN3!gTov@37!P4O z&%XDCsEj)H!D5r7a+7fa0Ze*Rt%a_*W_RC3vVA&O0s^s}7am^%m7xspXpVvK_2N|hAb&xIY6#6F9THj*3m+C@hGGm)%?KszEZ?ei$Hh<Ve` zl)sPJ^$wK5Zq?oWqi#1Mch$hn*tw?sS8Q8xoOCzs5< zG_!GmjZ3rAu)%($Sy_CYZ`MH$mX^b1K@g5}h#d_2DG*&QF(EmTo2|ZSjPNA4`FogI zAeEiv(YS8kiU!lWY{Pz=$oXfEE28u6zLsFlt%xBu>y<-e0*pO-h1CX1d~5eig5r0?sok zmG#g4Z;~@w-h0{jS-5A{^b4Lcs9%nb87WK&FHG8`J%{WxDG9$E_x>cp;H{m%FLlw%z7OhAp1rB3jt14ztn`t?3cR*xf-$P zzNxoF{+ppdtCraI+pFBQx*4ez0~*Wc5|j#40#Ty8FF#KKjCeu>!C2a~-Y)9&{jZ6=Q&x#~1FxX3zFWiId`nY8r6gf63^F+KDNwF=s8J0swfp7=#E3zuFtrIXPueD7HAJ;SY|SEfv7jo2!e^*d>i3 zZAQG{j1R~K6%#9DgS`MT>lC`f4Bj-Q`-^*_Oco?*E{AQjYczk|dNgae`H>PHemkos z@9pifY4%PhO?XfYCI_YXb!`tXYOq%30!cN=c+G|$)`a%076DUD{#CQ07(-RMZfVC= zO?;JOrm5MILJO|>bAyJh9ae?4_P~kl6o!?0>&o>wl=hyc`6n1Cv*QTLa63X0$KQtLT$~|F52Y}TZtVloTMa#n^eVg?#aQj5c9qhD zB{i*mTv>IA_njX0>TIV0#?Gk3oznuxLF*km)2wo(9_JX`lyPOYDn9#dnx}C*DWhBj zR^OHU{MtDYV%`?V6Qvjg-Ev`!!@!7`uD=JO_U{}9`sm*UiuidGV zzSmzZX3+8_-yv?(dRkoGp(Tso`}*Ib&oTHZAN+XN{XUuB!*F=V-@70DIB~e1+!nUxy?}TYJF?)866mvF7PSe9e~FP{71 zfo0mf+1-BhysjkG8VaMgO-G*0ELO@(3qY<*HR3Y64H)QBcS;W0*1@7fa8r{%2;ng^~wGbc%lB z)t~K&zZHtjl!s!Lyy^#!wt%vt$c}jl-h7dz!^^-B6yFWx-J4wLl7G+g zW;<#(=@or-6RIv@SWU9Vwt#Q~AE}VB^iFHbayoh8vsh! zkF5ZA9T>`-e+|bY$AeQ?_B^D(KP#LekVaYd<5VphD0B|EVcs1qR_9-rWxNE#+_JEA zu`3KY99eecV(Z^3nevw*-Ebg}iIMY3z+MiNMPHBbvQB;ahKKh3Q`70b%@8n!y>LV@qfnja{dO*!`w7I(bJ!SZ71XV}ow(4P6WYU8-OTavo&Z@R%7`A%U_ zMcdf7A3r=UVYwmpwn9qMhRJmE>lVF0R4;VMa(5yFz(PC!R=8~X(1cdj4w?L!>@mx$ zNi}F}q$(Eu!&>1JJvq-Zs!8RDCST^yMZ8w*Mw`Q~UU(Afldq)inH?5=vfxR~C;XQa zsa+%%QOde+J)tOJbM6=zh2Gb_UXGXOoGESNR|6tx-IkgmhV$HE%g5f4v&|r-+H!76 zZR6jvj-s;1(czNsNbA&Z^SzLqNw28%(~xePcrZn$A38y+cG(*qP0)C2g(u?R7aH{i zSc?uUZ9j$3cyUwxPK-S-{g?VuV&8BBp3+ytIoCOvvE6n!ZW}P&tC8;PI202b1LmEu z{=!^tej@4!NB7B8x5vh-E&g)^*Y_VZ6)V|Av+PS-TH80al-qowiekWJv_xHXvNh}W z)C7X`-hA#PLjF6KbRWnwhgS4v3?q~Q?Fi}*e~}SSMn9lwmKlYb7U%$si1ue^lf>|j zQb*-hRW6)|U%l$2PD*KS<21O#>@7gwE?(H=7}5+P-_wNPo?K)hy|>K|_}6=rw;5(X zqKqM77lrUv?I8bmJc}+>&qDZ(d)3|x{)`90b8?%(<9-IcdAr_ZVSB-Uq>9v+I^nZ# zWORckE=rM9=MwYo6dOiWRSll8Q9wbTX$w43;1Wf;MwH8Y5rtX{5!5=O(7S`G_s$89 z7*&0-EdFiVX3meY#Sz>+bj+uJw(^51H5tx40CV=}0jVG}bft|$QslgZE!8hfSL8e9 zNZZK9@!NrtZ{&}&HY!~2G`A}VfLoRRB}RYbxn(|nxX8?YM4vic<>YnMm42&q^!Q55 z%yxWcZKH!?zqKbJ7mZZ!%@XQxBTzV0_n`R4B0kpL_bQ5>9kYin>u!-3Mhc=8dHUA` z`{h9G9b}6zcWFwDUnyU=-?Tfk^TK)+GL(LRUuQ1S1s$1lu~9DUhDq=!dInh0>qqz9 zniP#}dssoH-o@K_6;9Cx1oBFbqaQ=2;KY2M8NX_9jsu20OY#QL1r2A{3;3|`!c0Up z(+WEP?hD0%ITfzbSSq5H-X0R&q9w0zBH00Xlfj?KPHFy#+IZd-D#?D#Hf7=o0~Bhj zd`+jT2!^?G0pgN!9>sYqze1o#%HMII+^8Ld0TKh7&Ix0*-h@e=ZwcP$o9b|Z9N zQm0OXKa-)9@4rDh`rb|4WA47fSZp}QPp^y$8ewumFOWF%Vf6JQ=td7LyvMw=S67U1 zz}mox1gmmnZ@y%eUU5c1Jjm|^=x;%hvw_%i6vA-b(9)cg%8$aCz~!@eH8L==a(rV= zt-gZFRJ$(>9511fwcR&vI>yDzx;3`u>BNrByt)fUlV+&rWup^x1==91l(k6{UQ3t~ z%OaBzt}tHP%gE+$zgL>Zzpv;RBK)G@yLheN-0v(7dB>V(=3n^CT%Ho6d$>t};=SnV z7Z5{cv$@5kI@2d@rfoYs4_J-i3~4kLBq9hffDxL$$70KR&|k1Yd(Q+##BrMfk4EVZ zXzn^P`zYXSE>VVVY;v^B8{1zHlKWxDevSvsiXb|z*J}@;=syQ4xqD8`Fw6Z~`*AO# zulv3e1-*&!{g6;!$WM+3>v7jZ$p5BAJ4wK3f;&T1jcG=WF|K5cNy*tG_*}5;t;BsduoR7l5Gt)zc%Rrc z&Wfdgc=VbjXK=FCeZ@rRDV4rtfbLDe8s5jI3*V=STz3&KQG$F(gBE`r*Xj-9RV zPrOjz3t<8LFl&AU*>|A~j8XqB$d1=3H_6z+pW;ge$!So;RkX})&~1&KMU26hYjfU9 z?q!}_CAK!B|MAqpsI+(;o%d`xKpxHV3r{Q5ts;~t@k=Jm;KN`G=v_2cgKS-JtAuO7 zk@7GT!v?8qM+?2Qir^yuK3f?hJsoqJfdu``9Sx)2LFwtyfVut*h4#AUgixAHEoe-z z1m!#u=gD+{`po2l##%wMZt6PJVh1Xjg;HGAqjoiCC4IkZe$>-gEnVL=^uzkqA1g>cZ}?CRi9$5%Ch5|zIzcAEJPt`(MKBa zINSJ4F~GeA)%xk?CSv)$Sft)A*X}(JjB#^w^G!`%U0nR@u#&q@sIPCYOrSe29Orsn zG|4(x{`+K91QSC$ieTn_a!xz#m59lJ1rOofzZowdJR$&E>6@zD6 z)NYIl_Im^9t!otRhQ6j9oF#(|!=<;yd6#arRGRH9K8lS-m=xP)yzKPnCr|LG7@)iJ zyOo7{rZplqU8QH@?}4+wWlL^SA2r=J}8j@n(#8R=o~J8hbIf8isi z6z?%Nd_%Yh9f$Utn{FGKIHq&loD&BO34kLq zn`I_4u!dK%PgRVH#0C>+%%JB1iAGUK8AL^yX`t zKeq3z+)J48Z(9NM2x0%uq27NKi5IfY6^~n_b;OefbCg30bq< zh%tQbRzBm7D`ka7$Js21Tx@$=W&(k_}@Y~lB0{wJb z7i4Kcf3!qVESjJw+E!p?DV)@P&o&m ziX6m|eSGH>zY+!uC{u`_?DT>BDT|mxv2S!`kE|aRj0(_`|MycH=8FAOI(?l?val*W zzvyyAoNK&0AsD7%HXkY&4bICfK0E~v0E)bKPhaA?4&$(^+xV5z?)~+&_Lxh`-_0WH zYfckxT|KFVYQrFjnBHsCEPjg&r_6#wxu$P#N_KF0!E}*HhF2d6@K{eXgVvszR& zjAl-xZ(blj0#TmlSi?Z6EQ)N%WIqbhgf!|#1=1=?PSD-Wc9=exn5Ff7N8WP0;CK!B zvj?myCdGsRzgFjVVU*wUZZ($$LIpesh-gKzF7>wcQPESQihB7tkF9(mi%;o}T7GuBI6I4Hm7)6+ZGorHq%%O~M7A zq4A-0m66Y<_*ma}TZnIm;YRPwCMciGU|Nr?M9gm1&f2O9ghOxSEy?~6{PP)g#gWZs zltsqqzjxra4;ZuuREsKhzMk9GpAU?Z;H_v#Q9#2s6h~K~;fsIf6i7RZyH$a5^N1(r z2_I|RD-?7L!9Y+VnQ&ZrG-`4wE#Nnjwee=a(Gk73E`bbaAuc2e@Mff^3wd}x z;TU-sbxpL94b^8fxrcq4!%X@j$M`)1U)aa&93btt{32fM@5?&-6<79^#FJ<5-bMfY8Soted zIR13cKl@zCr+oQPGBw%k0Wa--Kk|Mk&VA$ad``XkTprlJ`ILOuoO~Y^Z8sk2d8^I8 z=A7{#u6e&YicZ%QZ0?^-)A*2_zfOm~9~&pjBhlIC^2I(z+?F9+NW|q_;XIqJgmt8l=f= zENGf$4foNY%h*7)nHuLzswwQH7}SncDz8KRVhC!l&O$mDj^Cmsvv(^5V+c*7a-qP* zd4o0=;wxJFr8+r0Dz`|0uFrLSw z^~hi0G^JNn18BMYv#isjwp;J%o5$_!fuGx*MP)Pqi*%;{LoNEAV)=*?2kHvwX2v3{ zOP4lI2v%$??~z65mEEZ*mdnSaG1fy-1tv*sj)ng$D(^}hUW#zAPZ6gYO;qz!3roC2 z1o*zPjcK2ajM*2vNnMrVaW~=^)!Vcf-G~Zi;x$1ryJtV=`*o*VY~?%zAIwa>!~EuF zyp%nhe^++zuoZ$e<*ZDaqKwPA7^jSz#8p|urQ{|{6(jL}zFZM=x5Q>yP>j%#euvHD zTTMbujCGjJ@fSrhE3jLUFAsFQn3mKEn)%OQseYI%YLCm9dqD!FTGl~5sh`?J%c}Cg z#5dI~IsxRWiX2w~pE`Ch1H4=%z(5xiQCd=Sjg0(sK~eY`w6m}-Yc6A++HGCAMyUSl zDv7)CzSsS0*%^n0QD-EpnWgTfL{({HkwqAtrZ~CV8J+Db5{66(FdP{J#!*T^T~hB_ zdwm>wov#cNo;l>OMsi)5%llv&fe*L(X?Q4Lc3#jDH@_eeiwC^AwDD*s!iwdus;cBW z-O{Fdw>C`)@50v^RHPBO^^JSnL- z;5Zd&9>pdQ7c;mCRf^5Yc_ugU875ia1$xKT^ECFIL9wBiUO`1_zMbyHpkj9NHtQXQCg>!g7n>H%d~}5E}D1gBl3>sAu?orv&H7Zgj6jDav+lBB6}A zPY-o~@UE82=k3}4w`Jl`cBAA!DiLRGhza=GVsB{)Z8UA~kR)(l1#R-B~#3Z^!#)t;Qls4}0E!5XY( zVnr(x^qg&Iax;Fd8&R`O1KH%+Z;|D7PCz*o2EB_e9-{1>ToNjMD-ujga}J-t5bl=Z zeM#iNLkk-J7I2aVzmn&m1`Hka?NYF(R?Ym`84gtf`+rRnz1dRCS}n-omI|Q5fSuTt@<(rF!4WY!!RB8LA@t4;G$y(- zs>@`LGFBrpu9B^fCaDcUs8$eaPbBY0ye(f=XZF!|dEqg_hZ(M0g>Z}!lfQu+CDTg+ z^<}6D!nd~yFLcxbHO1pr;^0X0!TFG&-N}_NV!)Uh!yN zUQ$Y}1RV)W%NiqXD(I`Mj=2DN8K@cum8pBZ3>fQ0E;Tdchj%4RHTV|7zDiNRYtYM@ zPtIv8+kE@%t8{{Hj-TL`r@{BxH1wiC~U{}5x%XK&DMxqv3sq4;=~4Yn!XUzX=mVOqIOzeXqeB_3c`e2A!YG|qxK%`}(W&Tqr0=eH!Tf_|i=^`!^6faq90r|2tj5=Vtj{fnEQVy2lOEyYJE0 z=Voq9XuqJ1VvAjhP?^PPVBMO)>8FwLilw#nSUw%AQLkkmwa+OA+?1kAAXodLVnQWiM>?lh8GCR%|`fQCmKP^ z9a=%|nsAu|wxpiYK-bvw_g*+~5k&G%Y@q{(6dh&hn0ysE1D<3V5Z&zIyiKN00X@sLEY{jA%kEclgww??05iWv380H|HH^-IlkkTr11| z;Psr|IV{LXDlO3DX&m#uGaDXtjSNK)yvM8k)$wr1CD#NSO2bKMO1r0JhJLs^DMq zA!9n!qCY|WI$I9l*T=RLl>{#_-LaIc^ZMOmQZ&`BwJ7z6JJibB2+P@?fL+c%<*4Q~ zFdoJ8(W$V`N4VzPHZfY+?)m{rl~29w;P6Ji1q_t;7EqQe1|7Wvxvja*D2s`<7#(zI1xyQ#A{a5G*yvgEx7;YLC(BjY%)__ALa{PUiS#j~zFWTu0 zNlNI4w3g35jT_|oq~uGdDC{|HX|apQYI`@aemr`Jr^HIDNby;YHwI`QEYjTV#*h*( zlS9lh1t2yC&$WeI#no^xJFaV~&xict0hqX3PpcU0aru^5$V+3_kpO0>w7+-xDaVUaw*-!#))A ztJ(i!|0GuP=bsOopO6*qBhM@u=}8y_?wW#3zSauT@Yqmc-e>m(TcSr2`|V?}`$#&# zQOHR8xEwvQST*{keP+qi7{XEJ(BvT;Bhpc3d)sCE$pC#5C<-&Z-SY>nf9<4ztvTYA zvY!0QZ%`fafoQoT98LSra0>iuH;HYCP&h}n-6SKtC6x{4pWDCSHsPOJBO~WLhgtLm zqa;5rq%lupkI20m3gMZeU78=}Zueg&ryIsbrraP+<3qt$v|V)3@Je;1MSA9dC?4o~gE`B4pvsRTL)1rMv_2+#ax zurN-O)X}JRQnKl-&CaJNK`E1-oIi&fO9!T^TxRFQLmFxF%~T{!k%6f$Vd1^rL*z1> zc*5HGW%XnhTHIsu1My;aS63emO5rvH(-mk^gp_q2fMc^vbA-`;VYyW$k+alp_dO%& ze@hL5#+OAb$!2qjs(Ce^0wW9JoXLGwp)<&$J8eU^u4Y} z3%B;87=xApmBrUn-oM*9W3Q(H20bfd&vQF%ju$(f;f8S8VDV6jrjPL}X~H-^YP%~; zV+67f3(+lOJmSzzW)5)Jd+1QiD=!v_sLnhE73V*BlNv1`^gt0EJm06dXa1WI;%1v9 zfzDuhMdlZ!gI@Y@{KTjlZ|LZDaORhX_ml1v;vllWj>8ZncSc2xX2z;@JqNC=UL{=_ zS^4yVTY8F}p@6o}t%@MJz?{rfWLIxs*{Kq(@Zv%iL5n{<#Ja9s0~aERP&@-7MBb_< z>Q|ra$0a@+2T9xKYh8Xd%DeK@?=RNVD@I5}#Q=yky{-WH5G2k4=Pn8trE^qmKn<wDL+~_Ll7N)t!DLXopigP zXghK4GDL>>JEr-zo2W}-7%XxzDa9F44cI;6IKmH4W_&?G9PWbblNfy7no!x&R-pgLf zthR$2Z@rY!cX3fLKj4U6)$S`e%NG_{+sgW{9b%Z7`&eDcTt2FgpTc*fM5kdlRby+{ zVNU2=IwgcB0tjdt*1G~cZ#%^Ywl<>bF+UE>kvFhcE~A%W4?r<^nmCsrJmjK8u$rQ? zqj1cbHim)qRfF9o@?VEP&&QIXc3F)e;idp)sIn#MtcxQ&dv0iJpEzqO<+Eok;YYG_n7i+yqfCap&UeGV6-jH2FbQ zk7x*`hC4o`HKZCKTdcPn~a#;8GjF^*aq##6zcSxMYQ)v{Pz4kZ-iPW4=IvN zHg5T*^6s%x?%7E>LA>=(&9T)82yD3hn4zz1Wx(LFg1K|(=6SV^7orEg^?vC2VI9HL ziTqL2etnu5}dm!N~>wzn6}VO$-}er*S8CouWv}`&j=NXH#a8R z>}-wF*iT7t+IQ9{2_zSA3C~eoRDyCJ<_c4t06Rh>J$SOQwE)ib$I@eYz2Sf1)aupX zcgSkxm$?3-KTWk$VdD-FxHziF-Yr#fn$4Bg(t7-eZ=5+pGket?*=SaEv-cNBZiW|F zn09!VW!r7TMff5(=tE)>febZWgf5Vw7QOfX3%)%N6)xt2Bq_q*w?QO;OBk0N6W?Fw z;QVXwIFB$c0qGcHlT~`ZxkjDGJIPa(9J6r8s_VSi_yR~#Gp)@?fF=Uu*Qg6@1@>ym zEaU;|Hi~-GWig35EE;}s@YVOD6%rbEjUc7*bnVH;3p(Z;g+-vK*UmeGQpr=>eY_VH zc|BHh;t+w1wA985=u>*PoeI zE@+HuWH&P@QuC4XNMqC#Qxm=@hb9a52;Puglo&@06nXAIHY7L$7h46*mK4>GWrqeETV>#s7;c3(#wnJ^>R{`=#7+2(=sqKBw;-GMnlq{|PIL&!K2#K_@?D zNF$H6=3FqDlG39!#`tAeL>ij>-EoQFL!YxkGv^87(RO0FR@B z_u{u{t(!n%Sv8?&NrU%hnIA`FJF#MdaHe8Nu9o@c)aSowW$~#n4TU--sz#8}%^ zw%2M#mkkS>yJ$V8EeA@oO8?*Z@`ru4#qNHM6|6VdekgU{RTb2FUf0l^3`Ra zvm_xT0E6n?8jDBdU9rI4A^6$JsQ$`ek6fay-b(6$N@MCj6+#0{zj;J217#?s~cxkTqXQHF@97p(Gkf*H>3B|qTVEDDJ0yEd2BEkm{Sn3=*7QoXack$De`NzwRt;uOOV{krVW0%}|-gWrn z#gf~tCeVVMN0-bXO}CN+^do+;s}ik1>P*03F+M;gUyPl0{#xQ&AAy7sav@})q zM!O0IwcHG1XkeW-b!yqN+_oa0Md}Cd}pIaaY|D0`_%e5%rn&ovWa0zICwMJl_#$Q1AMb}QBoezQ;bP3zB zO!{Y6X{FEbB+`Ty7^4~XEUbDC#LJ3FNkKa`twmbCXs!OSF)JEseLOJvuk2c`I> zN#V69D|`{boRArkA+G~rV9bY`M68X38pxK93P1tvkti}z!ZxV^od z42~!WJ(_rElBn4`C&6M99*t%c$To+2-!Z@sy*z0(N@nf+Z{$ zx1Ntrvl4k)L)x|Y4pJ+1@m(+-%!&MY&?`R({abmMuUa>T=?q=~esqG$iAl*O)HRqE z=`IYQo&)2+DgDK|UnrxMnK=M$3SZTMx{KJ`rcz)aYauk2IZ-oe6q__ti4#l5?#n7@zHYgMgk)mm^{aetC zFZrgA0?+UtXWx+UzYBs7lFg@ZSL==jxY&Q<2Q9{+%1x^5J}TPSbMa4XCgGbQPc5oH zL+&vqxXpg^jmvyj{-~Taoh+rto_I)kBf=@N4`h$Wp8I;U4h(E~MMcvp_U92*3w#)N zUvL_MM32?vYJ0-7Erar0M^@f{hT?(-H&t@{HXxA!I4>dZwaHGBDOku2t%q`uyQYEE zmkY-{kK2=eV0kYN30#s+1buC3Ki?MZ+Q+Q?^ zy>92M?u)BI6|49OJ??YAr%`~%Zlv%VZ}3&rnD!0)be zf2s2w-G4K24RvvHP7|+7ZFv1+c{5!x*?drGci=E0YpE}x$v!7U%K(ZSlk&0y0fCLy z#Jta`{m%I=d?%8^ku&NzEw<_~>OW7r;BdJe$e{@pFP_7}Y%w2K8^DUpjnMXE*+wP~ z2#{|jwHII0kP=+Q8`4*>sO0espO|b$Yw+EdxubJI`?#J9c)D427s!3zWERiFw^i_r zyV#z-_;zZ!%K1E(toCBy>g`Ehx-4%G9SBVb4Duo4QlK)o(`H-qI$wI||62;X81McTd#1 z1u+7>8gBM}JJ!6cqKY1qSrG{wLJwkJ(^F^`#=;v!Tv)d%S=B^Z=rY0V#_;>Tfgj%! znIyQBNGN3zfUv@R;wN)^jCW~xA&a?`=I!gcC? zufHF2(0@4w4TV0Kg%ex52ZjRUU+!xBE_!+lTBVAfeC;PFMR4}R>kxd^|1SF7K8>g& z;r^xVb^1%-VRO|YiClj!D;>07&XZs;FSi}cwa4wLdsdVE?gL|_~;9A4vAljV1`063p|2mfaU~)+ZR3=6zx3`6ZXi&j1qPe%MqNNoJ8A2 zTJInsC(Bw}aLC_$7d6wyeK|D8j^^St;gsxGL~-Cy5%w#mNR6!#A5@(>SuOv1vVM*= zcX6DM?Z6pm9h|ECsY~8l*O0&mjI$VKdd}=nTxGu zLd+BsgD{Urg#BTpYr|;c^fLLcUy7%5Li^!Lxe4+^ACm?=ivzXN-rhDT-%n@)cb@T20QknUn!=D5N&P&K2HHVWfG5L;Bc9J*!3*%z!G zpr--IE*q%%WN531>I=K+;JgG|%_YANFFKsrl9$z1)(h+Jdnxy4xds7ra-rplgO3jQ zQw;kh1gh~u3TslW=X?pRUJ6f%z{;dLl*Sqh2Y)`w-Nc?QH5&5XNRF`{hQNe}*qHNc z@El@f6>*@bzC>!5y2>hypfq(jVn~SvHcnsLj-eC<3%QTCAW%h+7FvBpusM>5&APV9 zayN-~rMzkhb7KX#h8IW$ama%>CbK!zO@E{8T(oyJ`~}NlO(C+IC_3lP5LRtON_xg> zrTZ?XEBx=+*|344;7JH&XEFFa)%S-=N)JV!SNnibrz^1lCx$Am5T@bngMWYE85IpuNC0 zTzT8+>2BD;qiOPr>Mfy{N3l&Evr$`?Am8J?4@fXf?Ew4CBqME{flb}mTZsQWbFvmT zS{uwiapo0PC_d4kB2MQULCmP8px8#W$KI`+-@(rG67 z(R~j}{z<17OS_e75reK(u?qen>(diUlc3g40K4}bV2d=MUS^$qZ9^_BX|N=ca91|{ zN4pk*a_)lKLa-ntGN**j>(P2n;>dr*lt(HE|Adsj<++#r6*#N+K7{CAf@>s8N_oyq zW<9P&|8~c#4DK+I`uBG!Glu@n+-C_%oC>h%PMBLrHAiIbMEJ_U{eb@`56m3iqqyTb zJ4|`e`*}(yGFhmF7DywQb*AW6(91MZ#e!}Cr^ooh1y5OSjeFQb|G_7J$<~nzQ}A*n zj@7LUjJa?^PMLJ5ehqsrzHk?GJ-G1ySqtu~^R^8*U_w|-+<$nJr;~_VY1XCS`P6cOssF&;}1#PIKWY zES~Y$)Q#Mg?q7jgwmSEM<~e_0WkNIR>XF3@$!*BEFR64gJEaEyGX(o%`%ehg3Jk$;*N)9j z5s?9E(Vc}M^IQ`qHlL96q22M>mD@o0E&m^}x1Q+#5PLVwGrAl0I)95~OD9p2dFsgu z9@MeSBp~wT8=3psnP_xL$et`o5+*tU=Lrp8qP91KRV+1L-j;>_(X{`^{e1-11?QJZ z5Ci>y2}tRQo+~1aD=g=ZEVX6i=In5P$fwiuP*pnWTve&(LONEx7}RuS+iz1zR|mV; z+4cxf_M6$?;+ED-x5IQ;jdVii5{{b130A+Ja;Z**=$`-)_2+rXt8$Y40J@voE01c( z6>1IiSJH|sg3y_9i z5>$ykP|DV$JD~XkEeoXc-y(2^_IOi`*<#M%HdCijNJd9JbWSZ|{h!pJ4R^k3m)=`7 zE$y8EuaW!h{g?~>qI_ug832D#KV!fn0LU)t6+T~Yi)Kc~rAdNQvG@?##jwReuH^?F zXr=dy;aw;P$cI73nJJq~16AP5p>KdR@ccwIBtOFObymrSpa{=U)!Z~mhDW1WPsWws@tdAVy$V)T(`bV*Cj4^` zqFm*unLM@2@>E}WJ4F>ptzKz$c?zu%=#y}yQWqisgmg~QGo z+TGe|%fjo|+$b*k43r=W!Z&Q&Bu#UEiDO_Oa-e@KI`&xQ@tEw5oqa%qgpmjN0ussu z;>6ZoTG)p!FL~HJxGFANtT7V`1X*}>9MTgWJ@Z=^Wl)`h^*dk1O*Irj%Ty$mqq2?; z_|m48m0Z{Fiyf`Lig{#5M=S5`Im<^g6G?auRZ(h~GARn-aE1E0GqJ47+Fc2lLW4Qk zIC9~sB}<|q6QyXpdR^pMVz#%qgkfa245AuwEL!)lV zT}^^Dm^OqxP0%ibpG0}E9HDrfPZ?{Pyj3fNa;IrqIs|6_hXwwGEz}_}!0wbQ)SqA7 z<}uUV(`!S|<$jY!Oh#|s*NK?TBU!vL-&1%lz# zekkI@<+n;H=fl|83SXVSe(%zpaE%$z{O*9%pXPVE+OsCLiE{1O-Ms^7+1YWl-%NbN zWHQr&V`!YU!n__#>plpm$51v@{++(9``^M*owAGQU)2#f0fzq>j@r>jZSwwa!%=Ka zt3?B6VkwON(}1w7q%Yb@f5^=Lk&X%u$j5R1XFBSh#BS~AuFQdK23Ydw$_X1tE9r|z zUVS+9k}ar6`8~|-x^t?NVJ>f>2p)5eB_n&gZA{bLg)o{2_+nXe1$~;8D*3FGh;sV& z?OTo0oi10V3(jaex3M5}8pH-7yeKDZxBn^S--Gr%cL!Bou~~q#lS(5BHd)r=$bz zeS&75u=O-gmKOvIou$MaIC2Y|zyI7SFCMfJ=$uXm!4wbC%%Nr6AoHL*aagcDH3kjJ zdtTR{!H_~Wu)=CS9sSE4O)3<>oTcCasWCU%i8#p_J3*BhGQ;Apjzc;pnU_VR8|>xp8+n)FGbUT+XD*5f9@=jAa=E6|A!S_c97#S@;$)GGoPX}`4RsPRjyY>v)!1a1QqFp3BZWu1 zNms1hxu+AGwFKKNk&l*IM;#MVPvE%NxsX$a2Ukpz@GIReKp$u`dFa=-E-nt+;MJ(p zSu#-IiW&tW0QW|DYWS@F06m9Re?}{yR1;PFY$u?he55m&yae(FI`d4``M*CF-A6`f z{=n3nOzQtM7U#6CQ^>4n3F5Qnl=-0Fsh%}=hW@=*vmUCQ+ z{nHS7O5z|PYON{S5X4Qoyctj!j5@U1g%Dx=u3K!m=|R)_LtbB+clH?tS>Zq#Dbae8 z#)uLd$v~M#kO8B`{<7TJJ#oV{S7obIB$@zf#pBIIiyr-mnV8`c_lYfCo{_`5luu7X zyJnKhf!vH*=r=!PB_+>^%XMcW)2;F04y;arRVeF;UHECJh!{+E=pHH?r28Xjdt5&~ z*W#p787&0v7#b~nwUjvo;AdX?Yd9S*oxI%gx8$;c?`GUialpC2s}#u@U$45QpYB4_ z6+c3BOm^cT?Mg^S-7BT3dZ8ypw29d#1}3L%AaWve@8wBN^3KTvOHP71nCY{ctD0Xp zMTrpd2nl#(#&O{`GDXgOkzX)2D{Rqzyz6ZyV|_zbz+JW7Rcu$&8me{`>Xg(-0ESnM;S=xCdP zFF0|X8H=TAP-*srA+o37282ZLBF~1k2v2*)d^1ryDjn4yHEJTZnGCWX+#tzaX=5?c zfRbQ2q-$fph1BA}RI0XEL=nC84+70iwAYJS&i5aRk3>{WK%2B8$V!RR|>W)mWM+gBGW zT3<2MlKh5|Fd19A^q{4w8;R}WhiQ7kET8I%*MuN*z$u%hTSK<`a}-wjMZFcZz9mTw z8aU$q1H4b%BgkcGOLaA0FgXciPtIicEZ-Lu(9g;Lbs6VBDFMOu(|#(8UyEuTvXfna zaePE2v0&iwE>z#%eVJm@WEapn(GscgxP>gjVcW(|LK{w2J)X3va~Bk)&J!K&9Wvvt zzJq3(6mD-yNeZe7 zQ+og=e{J4D>Oumy>~QLz$q~12hf2Ih{0@sHa>)jLEYn#@Sulx~ASbyl8pNUGzz?rV zOHCzDzSJqyiv^+jt9>i9u?FdY(%!5OO>;{0{x`O~Inithr((%5eI11-Ty9*C8R8#X zn#E&|-QdtO%GC{HQ%h~0*M-^Ikl&pO#}E7I|50ZtvDvz2ScjegyC7`gKqOtN7{zSx z*6t}J@C2y@Bi_W@@Ez~%o_I|G3Fe4pLNjNy^j28NuAI}(3SuaT^#Sj-W6bpHT15-n zeqU^QFZgBy zC~L3u791CN=eO*xyjaFn67lkDfyP2j3Uwi$r!xmDfj7UivFm#(2YD*}&P#DgDeMYI)AzUW6n^%mt_imc zpHv(YF_V5^^@i^GOerQy9x_XMdcf7e#n&&a?r+-1^+}&debXS76x%VtoN>#;&yn3z zfm!D`QA2wI=xo8{0~TU|%iDe8`X zt9R^fiq|wII*E!Cj4nDvMU5=3xZFherH#|WdyNgKY3MmpqFmSv7pNi%ei^m7yyGrT z3BEOv$$;HH)9aZk+|l1wUv{a4Xed6Ee@&`&`Ae0~{y>--mg>scO?{$rE#dl-T9h=- zHXpfZ(lUp4o>;PZZ(Oun)%vhkv8n!NFXs(ez`tbD6{E)39f9s1<$G%PY{ls5YVDzn zh03!U(P-wE?-z7OC@Q>#;=2k;qSKR*5`n-B5EUGt;Dn5R`F zCg|!@DfbRTA)WX>_l_vkl1DdQECFWjOAX}tim=$JSlDTRGo?a}j|J#*-0@Ztw(uGM zf~KYx5<$u+*Mot;qNJ^h{;}!1$j~+S#FotNCDVY+F7SUs$w)a;{Oy2RP_o`Ye`@b= zxg}dW=z%x0(fr2CDT)#M3M0Yb48`0}Z+b`t;?ZB?gmUp==)1+f{(V zH~QerplfeBZ@VN=uiTGJ&lDFbJ>5Hv+L=)b`^{QHntO!h<(Tj22Hq3Bb3?5O^)Z|d z)xQsb#Ug_O4YhD7#sf5o0pYPwD!zHq+lHl^5{wWP@`p=b5x@Z>z{hYPN%ROEL zivKPRWpZZQ#wsujO?VSUw+^H1=9Z?KnKY3qvdodL>A_$o*m0~F4U21&r=;KMWQ1~r z6B#HyT&b04{@JVettUvnZqJy*)IeUV!CGG0lgN)s-g_R3x-7MueGdwD;n5{~nt$?* zEb4uMy|Z+~y`v^GMcAhIX!_ip8rarzHlz3QQ@z~UbWQv4STLEfYdnlbEyMj;e}yj{ zl$K@#Yz+A%PT|tgZY0-Ml=NIrLnHJ68$(&vD-G7A;Ywi3hZJi*($xsk-P*s=l?5_$ zEo`lsP#7y6)@I6_XN>0;K%D;m$a9z4LLacqYRg1w$-VYGH1asYqB&RE=JzTO$9k6o%FE6G>pPfYR)7J`h&Pcev{~l$&z1bG`Sh)j zAFLh+*Cd18RBH~{=e*5Kz_^#wP#2N%+ z4+d}d9xKP^Y2jZCY;q4SjCf8Zw;3Cm)2+KceU!W{F64eM%;6=fX-yXM<-|Mbc)D(E z#=}A>$XQuzO#KWEEGm2%_qvAuv=v+~0v|o>T-{!#R}y-F!=N{i_{RF7st?0xQR5lW z1Bcw^1~%@@CHfwK<|@K92a{vel}*p&rNI4G-&7n;5Qu}T9$`U&_4hhWnJO|>M!r=m z+3;sF8BS)SixXC+Ir^$GiE8lJmvaacD4e!y`evlIVw08|v z^M>S7M_Ez`7u~96a6X!$K74vG1K1&zWI#3adEgdn@=3(U4@SJc;>(>5SRG}~YGrvs zRpRElW==J)vvLiR+^BI$2=?;Zqv!Zk*R|hPx?w?{n=%X^O^l1RjxqB(^(+dQop1OTk-(7)uvRUAJCZwq#a>jB-(f zj-zKLXPSyUp+_h==dXFm8N^cZIE|Z^$FLNLKc6l0DcN5rM3FgN-y(DD*AA4`UK?rC z#&}cRbA12kO)qxML;R#y!L(pBEbz_wA|1Kt#-WYXoAY4nLNZfA<~a_h)fO33`9f%F zgBQy6!eZN2nTaXwnTE272lmZiMH%;0d>J4A-8WPAIt3`Vi|qjWX7x=;3`LU~wCixQ zs$xmJiuNb8Q!m3NXpu5+1|dV_z*ho;F>1rCc!op`s^0RQ34e~TSznk*Uij`ow_6vO z;CPXZvxJU#Bm7qaMcbaQL4x88LdH1ZxFfP@-kVIPL}VnWXW^jST4;Nc~h} zny}$?bU<5X7V>I}ns@9+NcgmFchH=g)!QW8!!NZAjr?p%O&1Fp!OHn=7ST14qH{vHLT^$=_mFNU={U)JMR3I_Da`unxv2@|me*Ir`OkL3*&~4`i9f+29#RP7dDAMk&8o!)nHx z;uL-^B|pPKUStvRFytiin z9Zgc#Yc;wuG)xAoTAizCG7lEt(2ce} zkJNGoA?<^x%11Wv_f}p$e#^!!3!TKqd1pX3GB+rOi#~3YinQ&F$^^~Nb&bh@Dr$u- z?>rd$+kk-975ocxWWjzZH7W7bVn7sPVCh%kWbrsx=DGn7Nv*wKDm17;eUpZ&Y`l8| zGCPm1Nu9(JVrIB|D7Ej~uq}Ld4b-ucP{r{BYg0*0EswMX9?=Z@hwfnX?BkXE-0UNL z=J9aP-0Gt}SFQ;rVj%xRThtpQQlOe0_xg%#*27aA%4|xE&O{qOjDN zL2ys=f>m?0XqrMQp$EedYz*OR;gU^Ujwhtu|pg>)~jKMx*CbCW{h zf=z{W#o31-@_2&#=xdrbQrEt~#uUxZ-Wf|}L`}~$f5<^C#{gMYY%yO6fL^>GS#Ixa zk<6}zivCG@+JyR8VnhjA{Y}U?8)tBSAoCgkdAB-Jddj`^Elq7`vZs;nM&GeK6DE5XLTiw$(&jFk*l> zN$-j)t}R=VtImPip!MxJu6v*8&RL~Z@;NH{b1k$7&Ubv{rWIDa3%+)8YTAX-qB@a5 z5r9qvTU!enm60eknFyMJ07`|~#{s9J2oTAZ{|bnEHN#Vd-B5Tf0Gl=^7WB9;P@|UBsiy& zYC|~Qp9gKAxga*#dVnUkVBadx$1Pv8_XQ_*JIuFh#Y6x zOL)CDflYYjwC&S7*~+@nj2gJx)tsrg8q;8JrWSI4`@l4AwuzH{E){bU?OnOvZeY$j zm_0+n^6}hFex`)G3F9DN(-b39sny2l>g?bakTTC7S>h(RyEV~pLEehKkQ0~AAW6c$ zDD5I%h&Dc8wqiX_|?r!2VhOaH#`g_D^wxO)Q&!9E=e^(;l7a&$(5Vp_GHB0)D6?$L^K@TTx%>iPb8=G?7~*tzc3o zr9OX*srRHlv=rCUn!+CNZq8tpcn4nP2}8AbX&xV?{X33_VqhcxqPesNO6v@Umlhkwes+ZpLb})#nNZcR7;Z)RXOe=Hdi@wP`INGD_?X2j4uckxox}vgFWCsnw!Ps z4-KWJ-r5|wh#M65a_urnudIFYjdDFw<6;frBn20Xkg*hbcI(wgikn~O{E;p_7-`UV ziYNn~tlUCDZ!lA(o%%@)Stm88u>cs3a5pYhm-T-tn=t|yn7&dX)1^NxC}>SJM{zb7 za59~AfT=`(49DPAKrih2PyV0i#B)o&z68?8xF$eyoIz02V<^h`1oiL*vU{#YA zpX#5gCXaN-1S@P3Uhyd}gSul4(GG+R0fTY3&HH>isUmBS3UkUc+$Mw=H$nrX7a0kG z)cJt0_&_skSFOaWJDho_wcKgOi){eFuZVMF>&(^DhS0VJdT=Gd9c zzXi51&Q!>uB>KiY9E{hH8X%SA6nfn z8+zD?@=jD9K_c;f{)yTWMgD#ebL4;7G)V0MkOA zV2k1sfr@``cDVw`~YMWv)Wa5WF`!?ZXh!VLh-ihjQQTCR(+QyBdQ=VmaKdL^4&~?QqH(?s`o0bFlNOk8T+pO;dTP2aM0j6I47{Oc?PcA zc0pzf!94`jjp8E3Cd(^}q+m^L077UT2yju$iq+hrT%gkbF>j{ zc{Hf}v@p1K({ynHt7o^PcuqxUi_u?v*!l#tDay_g`Ax?As^ZKx1rUmGZPtUc;7HNoa)|J3L=b z_+OEk)#3%UJpqPJyHjV+K9JD@5Th`npmK-xZD?ccZyyHoN|-%=t+RTgQWV7Aj>jIq3YJgFBnl!XC6yTa(pkCX6d&t$Gq7 zaaFA|$RG?LTGpZS{fO~uQ{A~0c|4pbAdr;B#IJH*XZ2=$ydTY-wqoan1FRrC1k%p* zJqHa^+zb_!()1C!+{Tj%`92*@0N#0;g~gPHhdJxwRhJIs+5@B>vW)L@?U(DaT?t z-+Q{3{!{9#R>f1-alZjK>?rJJPjmeBjZ#wyel1|m7ujIf4L*S7Jt4Nr#k&6cCejQC z=Wp;C2Wt_IqoE;hAAx8gZRS0u?};AVjvFig4_EILQnSJ}1$|+-c~o*}7vhU%VU-jeMSpf6%+b z=)PJq$}$JuSs>5b_QvLVv+}(cur5 zvY5mdYDmdHIv3LXP+nVzJOwkN(LEU8K=qMZQwrI1vPYG7$PEuW`M-PJJMCE?x&i=% z1GR7V6pcT01{F-hh5+`!GlUA?S5CEH(CFz;M9>dW&WT66n;tbreQwQ8ZwmJboCgyV zFn-vM_XoMJoSoK@XUdx$^|rL=JNf(EoHaaaO@<>GQ8#NOO&=rt>=L=`T|b0wfUO>Y zo@a;u>w50{2Nu`u_4##B^6R!|=aS#^^Q-S=$NfV~(`gSlB%L(-1+LN#UaYQ8XXRt& zk&=)InPA|51V5OebC3%ia;kzH2Muw`Lmg}Qcs#$ab`l`FJ@k-o{dMmeMDx5yK8w&n{m(}HY|U>Ki^=ptD!>Lq+DRSvxndJ8 zUHICP$8tGM(CQ0q8O#Nk+KZtD($0$ofO6%JDpq=Qe4(ZQnp#8I6--nyQ1 zlQ)(x%`&g8u>;2j(GhhR3&L$dd}WOi;q*k|ZNOkw5W<0i*x|EvsDiEgA`t(S|K$AF z=B)~dEnY+uHf5sYFal$Kv%Z;Y5{XvcSBjdZnip-|V`q%rnS!a(15X>2TyU>b2mWru zSpbJ5qvi?kME&@`HD1A}R72~}|DywNcKe?Wz=L|Ena#1qnMunaT1;v1SjlZKzgmDQ zfijDizh_pGjBr(?G2Sx$xK~sF7|% zF`dF}nnK?TF-PhiQht%m|6Ac*wvcT4lSM#RoABKjF%E6#3O)W(BpJL?h-jHb(&zUh z+AOGW|DD_Zj@^W#hUv`vADS?YMTF($EJV_we(_x4T0Rk#}bq50Q)(Sr<{Q zRXJ*N#aro-N=vBxF~G#HdVGx8lDoj^qWK)y{e^=(3+-4wn8#1@A`({O4?OY@lV zro6OaKJVTa(UYvb`MG$h20VQ{(ayeLlnSgRyNX(%l|!z|>`)f0{U0>-ij{ew)(ur^ zSLf*){XE@h6-k$hI>qyjA%SE~&1ozB_mu@SnhwLAK29J@Jb1rZo|LsX^cOfI1D)$q z=mXF5_GQqxGk=QcNhs8*Jnyx`X@$BfW9UGl5JM!I4b|xS1}P3I-d)UR<#l@?V%%r2 z|4I|=@K~9w>PBUT7~8@yJof!!h$)CVz2^g`)JJN?O>w{$VXMpu$hE)g(E{7?>Wl0Om8U z1lx(mEQyx7GQrF=he30>QxW-5IhY|xnbJSZ@KY?Z8+mEN#aZUVECZrumSznCs?2^o zt>eSCj38*Hwd2daHB zCTc&rE(eXBy+5Hiq*E|~kId8G4 z6Pge4akR$h1zPZ=YtPS)igz@MD0go1KiZ8_$h?$HHty`|*;=o&JR!l^QE0z4JhKmE zQGvjaMdUSHpPJZE1Nnn(Y1^TA;|!-=_OgE<+2uFfXbZ>S^Qm| zyh0K?m}z!qHEsp17ifRM^N-F43r`Yf{Fn2v+>G4V=pWnxzlvnL8hiz9s z`A`!HH@=$G{Y7pl`d=v`3Ktvpj<1uUK3oWUOnn?Fd(7U_Hbw~XIh;?BqnW1~fR8WW zAurvQ&uqN#O*fev4D+HAbiIFk6bvRr|4}hhU-EYrtE3OVo9=MCH1V#Fn023TEHRBS z3m>O9>+$xfQKiX;A=>1p1H*?3&Y>FayISglJZEU^e`ysZ)HQ?3$^k>tPkZ+VlS&C? zAKG#TrFF{yE!A}W1+Wqid_)u;Cd`c?#5Wlg!A7?@ViR_NkGUXo>u)4gSxQg%1mV&? zD;qHFD_W(9__u3N^b5>re_j`ww?mk6<2aN`nib*rA?8SWN1q z=*%9(s5hvq49NVFF6SJYfm65^2RwVz9uE8+7*OM96uwumW-eDr{H?;SX_>{lendhu{7{FYyx`lwBn z)A&X`qi9t z1MQj}@|T2WajHK|SK{^8|5b2?3_PAunVSSq5Yd5$X%Enk@^k4+n)Ez2EiRX?mZQHO z`jIcj6xzUPmF7wuf$pP8l9gmB?lA6T zetSE#^_%za2XL0qsiI2GG1Wv?3~nz6=zgv7sM-<{*yjPtTN zxGZ9A>@(4V0=nq1v0IZazxx{3KzqY&w=Qctu%!FOe5+`d5~Z^o<*Z_p+xU56+frHl zH@D&#vY~#FFCj}do`QWHagfWS;lCNAaa0=t$0U*LA4@q+SZ<=4p^7+%E1NMp?>UPV zzvvB(A6Nf7xjrUEq)i)6k|iWFR&+MfH%;F))6p`le~hABSVR^f59o@vrZ+@b-ISS< zzQ;yK+{5bX@2p(xH?JUJYAt&*zXD;;Dv7R1=H$&!PzBPJ@>_5JpW=T(6|(ZyODS4+ zTCZ{9Zi&g}92!|nHFx_qkB!(0P6l>xsmA$2I)l8NWD^-)VvP5*r4}T~C?%(1OW<@9 z&cxEsfCoe}xL0+;q;yJPoG(}oO60Y~AoX?JIWQ;0B#+#Bz2JMsVChHeU0}gH`|w2L zO^K9Wpd+@=SfCNi+{}VLqbp=Y+y|@k3(@xfi>SV4{ufbc3{@$oLi)=bR<|c%ifg~F zeH&M04V6@UB{qg6%ViVcorSV(d3$Gi%fE2E<=R~9agZ`6Balco_apwKOPjlKJ1*~4 z&;2R3Z_8J|d5Z<(5!Vw7O^0=Nhon?bmt{>KoLBqX$`PiDfQLUP>x%BW>bohrjKnL- zunmT1u&J>F2TpzWdHKQlhF?v;iMXtL->=gvhBU1Em&QpKKrY*~K0i}>w=|iWU49KQ zw#2ltrZyYd;$&Qhw6PvrEs+A=-8ZJml%9!~=4{g)>Y#@n)ck{s8L2`apf{1<%fFj# zbLmmHpSOkgvSt@g(pAkLe+Cr4*`l?cTmGB<3RnNkeg}{cvXq;cN?pTRF~S~G2+>n} z`O0CC!ywUNoyZ9CRe4GL*CR^*^)UHJK)0oLv%~8ge&{g=ClP zyzBi_9CT5;&D4;YG{jc;epRVi$7=B6iW0@4`EmhUx-ll5Lg!jO-H9KVO*0{FLB@Kp z$IldjLK}7XaPfxGw{}r>7tAwFpFE%}i*)3kqd>_=wDS;NMfBdVGguRoDai%#nYp(@ zEHkkY^{`1-*2`rnJ!v+P5ni?`Am{i;!fE>>;cUQ>m){h?ixS+B{*iE|uOrk|t9n$m z#l3Hu>N8TJY)b-bmJeg=`}Sh~Z(Ph5I}hZ_V;R*%Z9OBm&<%$qW^BrbxotE&r70Tw-zrTi6rkSAVQRg93#AWES;(BL&W*ueXvLY;W> zdL_gEYy6L)){7zXNbUSP2|?CN7sW@{@+{H}TNwoA5sfJoRf7YdOe)7jIawSiVc?r{ zxlD@;SP}Y>sCa0s>aie$AiMxv)vUl$zv77LC#fP;H~B(qgbJ|-0=oQ3s>DVAORDbb zq1q|`OR7$@%mq%^{!dax#@X~R-z0I!I^SepMwFJ8(umA(vitT{6<=v$!bK_T1nk~R z&9TYTU)(PdIC3y<`PXBtp%3$@1sEXR-rfnzhp5o=*l>n>;}3gUWa;h=qAnr_&Tr_G zT@TK`I~hC6r)Zw(NIZbRDw>3a+G*P0=f8Uof8#>zB~O)-=LRfL=!_f00w4ZQwN~Vu zzE)^6kvx%H(%(N7oZt}XGdJ>|C(o7VeffPN=j2GKd4OYMyx(Z4!5?29pURCE!$B5D zXSOb-^*A`WO18uG)Bs}IhCkG;77o@<#b`z^brd=K$S$JtzctzzG#5t!y+Gg}n0kQA zn%L);Ul;gE8SnX7Z^(ZkE|25Jh)gWx>cm}8tZGt3sq`_vfS$0ocaQZ2tOuruTXQwb zQGklS5 z@)CsH98-1YZ05U%K-Tw0#knC0=~@UFW1~;NwjQFLT>o07_0uaoCnl51C&qT|R4VtG zRH2Dq*i*(v0xJ$Zj0#!=%;Nk7zXU{BMl9oX9|y7jtXREW)csgA;k6$OtmL&}(K2*B zm+S{EeT$X2s28&a!+>0mF#LYb>iPNJo?&-tXUsqPz?&p0KX$ayV#)cp8*d6A2y~ln zuj8(T2)Rh1#s5L|0c5v!JLnPkPUaM-1QF4Qv_BZd_@Wzc@+7yDl2+dum9lF2i9xhAsg(o15 z4~@p({d+1t0hMP~lhVOBd4W+|LpS70JuBABQ_%DR#&hOb=)b8VNc8L{4*#ImzS;Mc z2!qApWzMZ&E%930;rzpaZ>jfRrENAh?B(wCACSJ*9806Xuln1@-FBsJzB`e-1EewO zMP^{(@Z4`Le~PbblUm4n#S}}>Z56J72PlvozO0_b-9>Q9wJ#I6!R&i_@kxSi`SL!U zfD@){6`*iA@Kjo$FRN`CP$~c+O{HyC!mS!!&&pH|Xn{C*yP0NMg|1KMeVRUS;s+|- zU>l<562}ocj$j3*;9dJij4F5OhGa+UxFPcMg(LGBc{9hf_!ISX6Rd|~2G|-N0~nUH zjGoxukSIgih{f~zjP1c;JA=v9OK07#5e(XBC}%wm-jqf>(r|fpO8@zMaZwXpjY;^g z*m6CZN+mopM4cv+dAYE^`z!*+Ab8m-x@MQqe56Crb>jm^isONP-<%YF;IWsK!@sxD za>qxs%2SpkK=DA*9|T-?+ow;$Qhp!zI962U!>;4(Rx+ZME{+wjb>CH0j`!CjFT*xt zxdKgft;g1)Kygb~&xa04Fu$H@xFE5@P(0&!4iKKygmzB-tEKOK2vuZ#TkoRrDF%x> zFF|$r66{H_>00NDTyH2AjOfYva(7>BwKoYK@xdlb4Y9=j>w7;y3ZvB1B?qO^^i-VG zD>vj(I+QzJbM$G=_W_}fEuHHw%xz`}oWOeQ{L@ClL(vdQc;dHpoPFrsC_wZ{4YB6YmcJl0<9~c}Mlgd!4yd-wBiv5fmES11 zvx17O_P+5`1bPB=_6c^H@w=lfuG%LH$BrNBi*k^3_KqzzS+;i!ZR2ECj>mE^?bJAj zS?=OHJ5uOI3C%0UjHh#5h%N1JUA%~_n`neh%X*ueg> znR_@b=IF%VdK}SF?k-c78**jEF5~0V=`4cs!U?Si20*V(&V$)DvYU+4n$2>=FHN2w zyA-;QI=-yOT1~NrG;2(kdOM>RX%{57$n(2Y^4(YtOR?JpHLJpZ1q!djJ}&N01UFy| z{n#5rgey$nYj7msl5XlICJ6e)_p~{XdmcZsXyCsJX=JSUpTC++aEI00PNZ1a`ZWZy z?BrYJqh;8#!(HQ(B@h#Y|0;J-=&ISt^`>0dKa7kYh!&*Jv4 zde2gyo_o?$ay|yZu5EVwhk{VKqI2)AZDL5qe>OYWf5SV&#E~WS?|E?d;#EdtJA8cCpoQ12^N%Lai8O_z- zZ^!P-G*$w9D9$R<7509B9zL!A;KDXl@%WM2F~-sjaTXKYcLS6mNDcOcKVgKvy#%`o z@s;j&w0Dut;x}Z=jni!cR5bpL;YW>7*+Uhp%EMVfDtdK@w=o@gpL!svK2R`F3`HL( zm2I`Y=D*{TsqX1BR^1fcVTma`e0<#V4`uTb-d>vF>T8�JwfB;?m;H*wO9&Y9`
      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�A=@R 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 N%30Vr5iW@u- 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 Date: Mon, 18 Dec 2023 11:53:14 +0100 Subject: [PATCH 1085/1263] Add initial support for deploying private repos via CLI directly Tested with: export EXTRA_HELM_OPTS="--set main.tokenSecret=private-repo --set main.tokenSecretNamespace=openshift-operators" ./pattern.sh make install Note that this is currently only working with https URLs because we have logic in the Makefile to rewrite ssh-based git URLs into https ones. --- operator-install/templates/pattern.yaml | 4 ++++ operator-install/values.yaml | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index d0227e58..e70f391d 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -16,6 +16,10 @@ spec: {{- if .Values.main.analyticsUUID }} analyticsUUID: {{ .Values.main.analyticsUUID }} {{- end }} {{/* if .Values.main.analyticsUUID */}} +{{- if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace }} + tokenSecret: {{ .Values.main.tokenSecret }} + tokenSecretNamespace: {{ .Values.main.tokenSecretNamespace }} +{{- end }} {{/* if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace */}} {{- if .Values.main.extraParameters }} extraParameters: {{- range .Values.main.extraParameters }} diff --git a/operator-install/values.yaml b/operator-install/values.yaml index 01605b21..0e92559d 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -18,3 +18,8 @@ main: source: community-operators clusterGroupName: default + + # If you are using a private repository define the secret where + # credentials to access the private repository are + # tokenSecret: + # tokenSecretNamespace: From c547ffe55a8df36fccb8e0bf264d843f15e31e45 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 19 Dec 2023 18:45:21 +0100 Subject: [PATCH 1086/1263] Add support for private repo deployments via CLI A normal non-private deployment: ./pattern.sh make show helm template common/operator-install/ --name-template multicloud-gitops -f values-global.yaml --set main.git.repoURL="https://github.com/mbaldessari/multicloud-gitops.git" --set main.git.revision=private-repo-cli ... apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern metadata: name: multicloud-gitops namespace: openshift-operators spec: clusterGroupName: hub gitSpec: targetRepo: https://github.com/mbaldessari/multicloud-gitops.git targetRevision: private-repo-cli gitOpsSpec: operatorChannel: gitops-1.8 operatorSource: redhat-operators multiSourceConfig: enabled: true When we set the TOKEN_SECRET AND TOKEN_NAMESPACE env variables: ./pattern.sh make TOKEN_SECRET=foo TOKEN_NAMESPACE=bar show helm template common/operator-install/ --name-template multicloud-gitops -f values-global.yaml --set main.tokenSecret=foo --set main.tokenSecretNamespace=bar --set main.git.repoURL="git@github.com:mbaldessari/multicloud-gitops.git" --set main.git.revision=private-repo-cli ... apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 kind: Pattern metadata: name: multicloud-gitops namespace: openshift-operators spec: clusterGroupName: hub gitSpec: targetRepo: git@github.com:mbaldessari/multicloud-gitops.git targetRevision: private-repo-cli gitOpsSpec: operatorChannel: gitops-1.8 operatorSource: redhat-operators multiSourceConfig: enabled: true tokenSecret: foo tokenSecretNamespace: bar In the latter case we do not rewrite the URL as it might an ssh-based one. --- Makefile | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index af2c82f1..d07ca5cd 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,18 @@ ifneq ("$(wildcard $(UUID_FILE))","") UUID_HELM_OPTS := --set main.analyticsUUID=$(UUID) endif -HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) $(UUID_HELM_OPTS) $(EXTRA_HELM_OPTS) +# Set the secret name *and* its namespace when deploying from private repositories +# The format of said secret is documented here: https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#repositories +TOKEN_SECRET ?= +TOKEN_NAMESPACE ?= + +ifeq ($(TOKEN_SECRET),) + HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) $(UUID_HELM_OPTS) $(EXTRA_HELM_OPTS) +else + # When we are working with a private repository we do not escape the git URL as it might be using an ssh secret which does not use https:// + TARGET_CLEAN_REPO=$(shell git ls-remote --get-url --symref $(TARGET_ORIGIN)) + HELM_OPTS=-f values-global.yaml --set main.tokenSecret=$(TOKEN_SECRET) --set main.tokenSecretNamespace=$(TOKEN_NAMESPACE) --set main.git.repoURL="$(TARGET_CLEAN_REPO)" --set main.git.revision=$(TARGET_BRANCH) $(TARGET_SITE_OPT) $(UUID_HELM_OPTS) $(EXTRA_HELM_OPTS) +endif ##@ Pattern Common Tasks From cc067ff091ffc49098a523bedcd3b71dfc73918d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 20 Dec 2023 11:33:16 +0100 Subject: [PATCH 1087/1263] Fix placement of tokenSecret material --- operator-install/templates/pattern.yaml | 8 ++++---- ...operator-install-industrial-edge-factory.expected.yaml | 2 +- tests/operator-install-industrial-edge-hub.expected.yaml | 2 +- .../operator-install-medical-diagnosis-hub.expected.yaml | 2 +- tests/operator-install-naked.expected.yaml | 2 +- tests/operator-install-normal.expected.yaml | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index e70f391d..bf35b3cd 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -11,15 +11,15 @@ spec: gitOpsSpec: operatorChannel: {{ default "gitops-1.8" .Values.main.gitops.channel }} operatorSource: {{ default "redhat-operators" .Values.main.gitops.operatorSource }} +{{- if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace }} + tokenSecret: {{ .Values.main.tokenSecret }} + tokenSecretNamespace: {{ .Values.main.tokenSecretNamespace }} +{{- end }} {{/* if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace */}} multiSourceConfig: enabled: {{ .Values.main.multiSourceConfig.enabled }} {{- if .Values.main.analyticsUUID }} analyticsUUID: {{ .Values.main.analyticsUUID }} {{- end }} {{/* if .Values.main.analyticsUUID */}} -{{- if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace }} - tokenSecret: {{ .Values.main.tokenSecret }} - tokenSecretNamespace: {{ .Values.main.tokenSecretNamespace }} -{{- end }} {{/* if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace */}} {{- if .Values.main.extraParameters }} extraParameters: {{- range .Values.main.extraParameters }} diff --git a/tests/operator-install-industrial-edge-factory.expected.yaml b/tests/operator-install-industrial-edge-factory.expected.yaml index 1c57fe6b..6c6b10a9 100644 --- a/tests/operator-install-industrial-edge-factory.expected.yaml +++ b/tests/operator-install-industrial-edge-factory.expected.yaml @@ -27,7 +27,7 @@ spec: targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 - operatorSource: redhat-operators + operatorSource: redhat-operators multiSourceConfig: enabled: false --- diff --git a/tests/operator-install-industrial-edge-hub.expected.yaml b/tests/operator-install-industrial-edge-hub.expected.yaml index 1c57fe6b..6c6b10a9 100644 --- a/tests/operator-install-industrial-edge-hub.expected.yaml +++ b/tests/operator-install-industrial-edge-hub.expected.yaml @@ -27,7 +27,7 @@ spec: targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 - operatorSource: redhat-operators + operatorSource: redhat-operators multiSourceConfig: enabled: false --- diff --git a/tests/operator-install-medical-diagnosis-hub.expected.yaml b/tests/operator-install-medical-diagnosis-hub.expected.yaml index 1c57fe6b..6c6b10a9 100644 --- a/tests/operator-install-medical-diagnosis-hub.expected.yaml +++ b/tests/operator-install-medical-diagnosis-hub.expected.yaml @@ -27,7 +27,7 @@ spec: targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 - operatorSource: redhat-operators + operatorSource: redhat-operators multiSourceConfig: enabled: false --- diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index 90fca51e..c7ae9159 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -27,7 +27,7 @@ spec: targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 - operatorSource: redhat-operators + operatorSource: redhat-operators multiSourceConfig: enabled: false --- diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index 1c57fe6b..6c6b10a9 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -27,7 +27,7 @@ spec: targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 - operatorSource: redhat-operators + operatorSource: redhat-operators multiSourceConfig: enabled: false --- From eef7bb4544ede0909a7c11c117b097f052c457f7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 20 Dec 2023 11:47:32 +0100 Subject: [PATCH 1088/1263] Fix placement of tokenSecret material in the right section --- operator-install/templates/pattern.yaml | 6 +++--- .../operator-install-industrial-edge-factory.expected.yaml | 4 ++-- tests/operator-install-industrial-edge-hub.expected.yaml | 4 ++-- tests/operator-install-medical-diagnosis-hub.expected.yaml | 4 ++-- tests/operator-install-naked.expected.yaml | 4 ++-- tests/operator-install-normal.expected.yaml | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index bf35b3cd..3dc1948a 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -8,13 +8,13 @@ spec: gitSpec: targetRepo: {{ .Values.main.git.repoURL }} targetRevision: {{ .Values.main.git.revision }} - gitOpsSpec: - operatorChannel: {{ default "gitops-1.8" .Values.main.gitops.channel }} - operatorSource: {{ default "redhat-operators" .Values.main.gitops.operatorSource }} {{- if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace }} tokenSecret: {{ .Values.main.tokenSecret }} tokenSecretNamespace: {{ .Values.main.tokenSecretNamespace }} {{- end }} {{/* if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace */}} + gitOpsSpec: + operatorChannel: {{ default "gitops-1.8" .Values.main.gitops.channel }} + operatorSource: {{ default "redhat-operators" .Values.main.gitops.operatorSource }} multiSourceConfig: enabled: {{ .Values.main.multiSourceConfig.enabled }} {{- if .Values.main.analyticsUUID }} diff --git a/tests/operator-install-industrial-edge-factory.expected.yaml b/tests/operator-install-industrial-edge-factory.expected.yaml index 6c6b10a9..71a8523e 100644 --- a/tests/operator-install-industrial-edge-factory.expected.yaml +++ b/tests/operator-install-industrial-edge-factory.expected.yaml @@ -24,10 +24,10 @@ spec: clusterGroupName: example gitSpec: targetRepo: https://github.com/pattern-clone/mypattern - targetRevision: main + targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 - operatorSource: redhat-operators + operatorSource: redhat-operators multiSourceConfig: enabled: false --- diff --git a/tests/operator-install-industrial-edge-hub.expected.yaml b/tests/operator-install-industrial-edge-hub.expected.yaml index 6c6b10a9..71a8523e 100644 --- a/tests/operator-install-industrial-edge-hub.expected.yaml +++ b/tests/operator-install-industrial-edge-hub.expected.yaml @@ -24,10 +24,10 @@ spec: clusterGroupName: example gitSpec: targetRepo: https://github.com/pattern-clone/mypattern - targetRevision: main + targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 - operatorSource: redhat-operators + operatorSource: redhat-operators multiSourceConfig: enabled: false --- diff --git a/tests/operator-install-medical-diagnosis-hub.expected.yaml b/tests/operator-install-medical-diagnosis-hub.expected.yaml index 6c6b10a9..71a8523e 100644 --- a/tests/operator-install-medical-diagnosis-hub.expected.yaml +++ b/tests/operator-install-medical-diagnosis-hub.expected.yaml @@ -24,10 +24,10 @@ spec: clusterGroupName: example gitSpec: targetRepo: https://github.com/pattern-clone/mypattern - targetRevision: main + targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 - operatorSource: redhat-operators + operatorSource: redhat-operators multiSourceConfig: enabled: false --- diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index c7ae9159..7523ec96 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -24,10 +24,10 @@ spec: clusterGroupName: default gitSpec: targetRepo: https://github.com/pattern-clone/mypattern - targetRevision: main + targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 - operatorSource: redhat-operators + operatorSource: redhat-operators multiSourceConfig: enabled: false --- diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index 6c6b10a9..71a8523e 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -24,10 +24,10 @@ spec: clusterGroupName: example gitSpec: targetRepo: https://github.com/pattern-clone/mypattern - targetRevision: main + targetRevision: main gitOpsSpec: operatorChannel: gitops-1.8 - operatorSource: redhat-operators + operatorSource: redhat-operators multiSourceConfig: enabled: false --- From cb96d55a7cacb603d819e1cea806643f5874bd74 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 2 Jan 2024 13:40:49 +0100 Subject: [PATCH 1089/1263] Upgrade ESO to v0.9.11 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.9.10.tgz | Bin 86339 -> 0 bytes .../charts/external-secrets-0.9.11.tgz | Bin 0 -> 86686 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 181 +++++++++++++----- ...-secrets-industrial-edge-hub.expected.yaml | 181 +++++++++++++----- ...ecrets-medical-diagnosis-hub.expected.yaml | 181 +++++++++++++----- ...olang-external-secrets-naked.expected.yaml | 181 +++++++++++++----- ...lang-external-secrets-normal.expected.yaml | 181 +++++++++++++----- 9 files changed, 694 insertions(+), 219 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.10.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.9.11.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 613a9a96..b60b499c 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.3 dependencies: - name: external-secrets - version: "0.9.10" + version: "0.9.11" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.9.10.tgz b/golang-external-secrets/charts/external-secrets-0.9.10.tgz deleted file mode 100644 index dc04b636814218c1f0d03356d4e07b27341dfc45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86339 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 z 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�JwfB;?m;H*wO9&Y9`

      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 Date: Wed, 3 Jan 2024 09:08:13 +0100 Subject: [PATCH 1090/1263] Make the container to run with the UID and GID of the user running pattern.sh This allows us to drop the /root bind mount and it will also show any errors related to paths in the proper folder. E.g. any permission problem of KUBECONFIG files won't be shown as /root/kubeconfig (inside the container) but as the proper path inside the /home folder. Tested on F38, F39, RHEL8.9 and RHEL9.2 --- scripts/pattern-util.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index bc833866..b5549d07 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -25,15 +25,17 @@ fi # $HOME is mounted as itself for any files that are referenced with absolute paths # $HOME is mounted to /root because the UID in the container is 0 and that's where SSH looks for credentials -# Do not quote the ${KUBECONF_ENV} below, otherwise we will pass '' to podman -# which will be confused +# We do not rely on bash's $UID and $GID because on MacOSX $GID is not set +MYUID=$(id -u) +MYGID=$(id -g) podman run -it --rm --pull=newer \ --security-opt label=disable \ + --user "${MYUID}:${MYGID}" \ + --userns "keep-id:uid=${MYUID},gid=${MYGID}" \ -e EXTRA_HELM_OPTS \ -e KUBECONFIG \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ - -v "${HOME}":/root \ -w "$(pwd)" \ "$PATTERN_UTILITY_CONTAINER" \ $@ From b86f13b397d02b88218011fb52679381de2b92d7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 3 Jan 2024 09:09:40 +0100 Subject: [PATCH 1091/1263] Error out nicely if podman is not present If podman is not installed we get the following unfriendly output: [michele@rhel1]~/multicloud-gitops% ./pattern.sh ./pattern.sh: line 10: podman: command not found ./pattern.sh: line 10: podman: command not found ./pattern.sh: line 32: podman: command not found Let's bail out and have a generic function to check for that in case we need to add other requirements --- scripts/pattern-util.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index b5549d07..5cdd1c6d 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -1,9 +1,15 @@ #!/bin/bash +function is_available { + command -v $1 >/dev/null 2>&1 || { echo >&2 "$1 is required but it's not installed. Aborting."; exit 1; } +} if [ -z "$PATTERN_UTILITY_CONTAINER" ]; then PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/utility-container" fi +readonly commands=(podman) +for cmd in ${commands[@]}; do is_available "$cmd"; done + UNSUPPORTED_PODMAN_VERSIONS="1.6 1.5" for i in ${UNSUPPORTED_PODMAN_VERSIONS}; do # We add a space From ce1548e27586ebf0f673471b9cdfd36934ba4dc4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 4 Jan 2024 16:01:36 +0100 Subject: [PATCH 1092/1263] Account for podman versions older than 4.3.0 The addition of --userns keep-id:uid=...,gid=... is supported only on podman versions >= 4.3.0 [1] If we have an older version, let's just keep the same logic as before. [1] https://github.com/containers/podman/blob/main/troubleshooting.md#39-podman-run-fails-with-error-unrecognized-namespace-mode-keep-iduid1000gid1000-passed --- scripts/pattern-util.sh | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 5cdd1c6d..745131b5 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -1,8 +1,13 @@ #!/bin/bash + function is_available { command -v $1 >/dev/null 2>&1 || { echo >&2 "$1 is required but it's not installed. Aborting."; exit 1; } } +function version { + echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }' +} + if [ -z "$PATTERN_UTILITY_CONTAINER" ]; then PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/utility-container" fi @@ -11,15 +16,30 @@ readonly commands=(podman) for cmd in ${commands[@]}; do is_available "$cmd"; done UNSUPPORTED_PODMAN_VERSIONS="1.6 1.5" +PODMAN_VERSION_STR=$(podman --version) for i in ${UNSUPPORTED_PODMAN_VERSIONS}; do # We add a space - if podman --version | grep -q -E "\b${i}"; then - echo "Unsupported podman version. We recommend >= 4.2.0" + if echo "${PODMAN_VERSION_STR}" | grep -q -E "\b${i}"; then + echo "Unsupported podman version. We recommend > 4.3.0" podman --version exit 1 fi done +# podman --version outputs: +# podman version 4.8.2 +PODMAN_VERSION=$(echo "${PODMAN_VERSION_STR}" | awk '{ print $NF }') + +# podman < 4.3.0 do not support keep-id:uid=... +if [ $(version "${PODMAN_VERSION}") -lt $(version "4.3.0") ]; then + PODMAN_ARGS="-v ${HOME}:/root" +else + # We do not rely on bash's $UID and $GID because on MacOSX $GID is not set + MYUID=$(id -u) + MYGID=$(id -g) + PODMAN_ARGS="--user ${MYUID}:${MYGID} --userns keep-id:uid=${MYUID},gid=${MYGID}" +fi + if [ -n "$KUBECONFIG" ]; then if [[ ! "${KUBECONFIG}" =~ ^$HOME* ]]; then echo "${KUBECONFIG} is pointing outside of the HOME folder, this will make it unavailable from the container." @@ -31,17 +51,13 @@ fi # $HOME is mounted as itself for any files that are referenced with absolute paths # $HOME is mounted to /root because the UID in the container is 0 and that's where SSH looks for credentials -# We do not rely on bash's $UID and $GID because on MacOSX $GID is not set -MYUID=$(id -u) -MYGID=$(id -g) podman run -it --rm --pull=newer \ --security-opt label=disable \ - --user "${MYUID}:${MYGID}" \ - --userns "keep-id:uid=${MYUID},gid=${MYGID}" \ -e EXTRA_HELM_OPTS \ -e KUBECONFIG \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ + ${PODMAN_ARGS} \ -w "$(pwd)" \ "$PATTERN_UTILITY_CONTAINER" \ $@ From 6f1b236da635b7af07108464a034b38452251a25 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 Jan 2024 16:49:56 +0100 Subject: [PATCH 1093/1263] Switch default gitops channel to gitops-1.11 Tested on both ocp 4.14 and 4.12. 4.12 is the oldest supported version at this point in time. --- operator-install/values.yaml | 2 +- tests/operator-install-industrial-edge-factory.expected.yaml | 4 ++-- tests/operator-install-industrial-edge-hub.expected.yaml | 4 ++-- tests/operator-install-medical-diagnosis-hub.expected.yaml | 4 ++-- tests/operator-install-naked.expected.yaml | 4 ++-- tests/operator-install-normal.expected.yaml | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/operator-install/values.yaml b/operator-install/values.yaml index 0e92559d..9c2f7386 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -7,7 +7,7 @@ main: revision: main gitops: - channel: "gitops-1.8" + channel: "gitops-1.11" operatorSource: redhat-operators multiSourceConfig: diff --git a/tests/operator-install-industrial-edge-factory.expected.yaml b/tests/operator-install-industrial-edge-factory.expected.yaml index 71a8523e..0ef51dc0 100644 --- a/tests/operator-install-industrial-edge-factory.expected.yaml +++ b/tests/operator-install-industrial-edge-factory.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.8 + gitops.channel: gitops-1.11 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan @@ -26,7 +26,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: gitops-1.8 + operatorChannel: gitops-1.11 operatorSource: redhat-operators multiSourceConfig: enabled: false diff --git a/tests/operator-install-industrial-edge-hub.expected.yaml b/tests/operator-install-industrial-edge-hub.expected.yaml index 71a8523e..0ef51dc0 100644 --- a/tests/operator-install-industrial-edge-hub.expected.yaml +++ b/tests/operator-install-industrial-edge-hub.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.8 + gitops.channel: gitops-1.11 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan @@ -26,7 +26,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: gitops-1.8 + operatorChannel: gitops-1.11 operatorSource: redhat-operators multiSourceConfig: enabled: false diff --git a/tests/operator-install-medical-diagnosis-hub.expected.yaml b/tests/operator-install-medical-diagnosis-hub.expected.yaml index 71a8523e..0ef51dc0 100644 --- a/tests/operator-install-medical-diagnosis-hub.expected.yaml +++ b/tests/operator-install-medical-diagnosis-hub.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.8 + gitops.channel: gitops-1.11 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan @@ -26,7 +26,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: gitops-1.8 + operatorChannel: gitops-1.11 operatorSource: redhat-operators multiSourceConfig: enabled: false diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index 7523ec96..024ea7ea 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.8 + gitops.channel: gitops-1.11 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan @@ -26,7 +26,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: gitops-1.8 + operatorChannel: gitops-1.11 operatorSource: redhat-operators multiSourceConfig: enabled: false diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index 71a8523e..0ef51dc0 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.8 + gitops.channel: gitops-1.11 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan @@ -26,7 +26,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: gitops-1.8 + operatorChannel: gitops-1.11 operatorSource: redhat-operators multiSourceConfig: enabled: false From c6ab55ecef3fb1fa8d17723787f2d6da96af4ed6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 Jan 2024 14:38:04 +0100 Subject: [PATCH 1094/1263] Set the user's passwd entry inside the container The reason for this is somewhat multi-faceted, but boils down to the fact that openssh does not consult the $HOME variable to find .ssh/* files but only relies to the home folder entry in /etc/passwd. So what might happen is the following scenario: 1. The remote is ssh based: `origin git@github.com:validatedpatterns/industrial-edge` 2. The main Makefile invokes `git remote show origin` which triggers an ssh connection 3. The ssh connection fails because ssh ignores the $HOME variable and instead relies on the home in `getent passwd`. Which is set to: ``` fedora:*:1000:1000:fedora Cloud User:/home/fedora/industrial-edge:/bin/sh ``` 4. Newer podmans set the user's home folder automagically to the folder that is passed as current working directory (in our case we pass `-w $(pwd)`) Under these circumstances ssh connection will fail because git+ssh will look for ssh files in the current folder (aka entry in /etc/passwd): debug1: identity file /home/fedora/industrial-edge/.ssh/id_rsa type -1 debug1: identity file /home/fedora/industrial-edge/.ssh/id_rsa-cert type -1 Fix this by making sure we force an /etc/passwd entry for the user running podman that points to the $HOME directory (aka /pattern-home inside the container). --- scripts/pattern-util.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 745131b5..9cec19fa 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -35,9 +35,10 @@ if [ $(version "${PODMAN_VERSION}") -lt $(version "4.3.0") ]; then PODMAN_ARGS="-v ${HOME}:/root" else # We do not rely on bash's $UID and $GID because on MacOSX $GID is not set + MYNAME=$(id -n -u) MYUID=$(id -u) MYGID=$(id -g) - PODMAN_ARGS="--user ${MYUID}:${MYGID} --userns keep-id:uid=${MYUID},gid=${MYGID}" + PODMAN_ARGS="--passwd-entry ${MYNAME}:x:${MYUID}:${MYGID}:/pattern-home:/bin/bash --user ${MYUID}:${MYGID} --userns keep-id:uid=${MYUID},gid=${MYGID}" fi if [ -n "$KUBECONFIG" ]; then From 23969cc1ddd365533e671ef2cc8b3f9810ec7952 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 Jan 2024 21:04:23 +0100 Subject: [PATCH 1095/1263] Upgrade helm to v3.13.2 This is the version we use in gitops-1.11 which is the new default --- .github/workflows/linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 947cc127..39aa63cb 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -36,7 +36,7 @@ jobs: - name: Setup helm uses: azure/setup-helm@v3 with: - version: 'v3.12.3' + version: 'v3.13.2' ################################ From e67832844e5f3e4ddab97db8fe20a9164b6ace45 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 Jan 2024 16:29:05 +0100 Subject: [PATCH 1096/1263] Drop old patch around null subkeys Now that we switched to gitops-1.11, the helm version is recent enough that we're not affected by the subkey null bug any longer. --- hashicorp-vault/README.md | 6 ---- hashicorp-vault/charts/vault-0.27.0.tgz | Bin 48690 -> 49088 bytes ... 0001-Allow-per-service-annotations.patch} | 0 .../0001-patch-server-route.patch | 28 ------------------ hashicorp-vault/update-helm-dependency.sh | 2 +- 5 files changed, 1 insertion(+), 35 deletions(-) rename hashicorp-vault/local-patches/{0002-Allow-per-service-annotations.patch => 0001-Allow-per-service-annotations.patch} (100%) delete mode 100644 hashicorp-vault/local-patches/0001-patch-server-route.patch diff --git a/hashicorp-vault/README.md b/hashicorp-vault/README.md index 84065ffd..26252b7e 100644 --- a/hashicorp-vault/README.md +++ b/hashicorp-vault/README.md @@ -10,12 +10,6 @@ ## Patches -### Issue 9136 - -**IMPORTANT**: Due to the fact that 'null' values do not work in helm charts -([GH#9136](https://github.com/helm/helm/issues/9136)), we need to patch the -chart to skip setting the host. - ### Issue 674 In order to be able to use vault ssl we need to patch the helm chart to fix diff --git a/hashicorp-vault/charts/vault-0.27.0.tgz b/hashicorp-vault/charts/vault-0.27.0.tgz index 574b3e743442466c1d05f9c38bf1e69c0f82f037..24a07991517b476c1391b305d0953e9e231a030c 100644 GIT binary patch delta 48301 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 diff --git a/hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch b/hashicorp-vault/local-patches/0001-Allow-per-service-annotations.patch similarity index 100% rename from hashicorp-vault/local-patches/0002-Allow-per-service-annotations.patch rename to hashicorp-vault/local-patches/0001-Allow-per-service-annotations.patch diff --git a/hashicorp-vault/local-patches/0001-patch-server-route.patch b/hashicorp-vault/local-patches/0001-patch-server-route.patch deleted file mode 100644 index edc22c57..00000000 --- a/hashicorp-vault/local-patches/0001-patch-server-route.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff -up vault/values.yaml.orig vault/values.yaml ---- vault/values.yaml.orig 2022-09-05 20:42:02.468428184 +0200 -+++ vault/values.yaml 2022-09-05 20:42:05.218435871 +0200 -@@ -406,7 +406,8 @@ server: - - labels: {} - annotations: {} -- host: chart-example.local -+ #host: chart-example.local -+ host: null - # tls will be passed directly to the route's TLS config, which - # can be used to configure other termination methods that terminate - # TLS at the router -diff -up vault/values.schema.json.orig vault/values.schema.json ---- vault/values.schema.json.orig 2022-09-11 21:00:34.834334961 +0200 -+++ vault/values.schema.json 2022-09-11 21:00:57.190368032 +0200 -@@ -838,7 +838,10 @@ - "type": "boolean" - }, - "host": { -- "type": "string" -+ "type": [ -+ "null", -+ "string" -+ ] - }, - "labels": { - "type": "object" diff --git a/hashicorp-vault/update-helm-dependency.sh b/hashicorp-vault/update-helm-dependency.sh index 76e4ac14..2551d888 100755 --- a/hashicorp-vault/update-helm-dependency.sh +++ b/hashicorp-vault/update-helm-dependency.sh @@ -1,5 +1,5 @@ #!/bin/bash -set -eu +set -eu -o pipefail # Get the version of the dependency and then unquote it TMPVER=$(sed -e '1,/^version:/ d' "Chart.yaml" | grep "version:" | awk '{ print $2 }') From aa72a78bf7d411a2636cd4b0070d90cd68c92004 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 23 Jan 2024 16:15:50 +0100 Subject: [PATCH 1097/1263] Do check for remote existance all the time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At the time we disabled the `validate-origin` target when running from inside the container as it apparently caused issues for some folks. I think now that we run as the user inside the container, the chances of this not working are reduced, so let's reenable this. Tested as follows: ❯ ./pattern.sh make TARGET_ORIGIN=upstream validate-origin Checking repository: https://github.com/hybrid-cloud-patterns/multicloud-gitops - branch 'nonexisting': NOT FOUND make: *** [Makefile:12: validate-origin] Error 2 ❯ ./pattern.sh make TARGET_ORIGIN=upstream validate-origin Checking repository: https://github.com/hybrid-cloud-patterns/multicloud-gitops - branch 'main': OK ❯ ./pattern.sh make validate-origin Checking repository: https://github.com/mbaldessari/multicloud-gitops.git - branch 'main': OK ❯ ./pattern.sh make validate-origin Checking repository: https://github.com/mbaldessari/multicloud-gitops.git - branch 'nonexisting': NOT FOUND make: *** [Makefile:12: validate-origin] Error 2 --- Makefile | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index d07ca5cd..50f886e1 100644 --- a/Makefile +++ b/Makefile @@ -99,14 +99,9 @@ load-iib: ## CI target to install Index Image Bundles .PHONY: validate-origin validate-origin: ## verify the git origin is available @echo "Checking repository:" - @echo -n " $(TARGET_REPO) - branch $(TARGET_BRANCH): " - @if [ ! -f /run/.containerenv ]; then\ - git ls-remote --exit-code --heads $(TARGET_REPO) $(TARGET_BRANCH) >/dev/null &&\ - echo "OK" ||\ - (echo "NOT FOUND"; exit 1);\ - else\ - echo "Running inside a container: Skipping git ssh checks";\ - fi + @echo -n " $(TARGET_REPO) - branch '$(TARGET_BRANCH)': " + @git ls-remote --exit-code --heads $(TARGET_REPO) $(TARGET_BRANCH) >/dev/null &&\ + echo "OK" || (echo "NOT FOUND"; exit 1) .PHONY: validate-cluster validate-cluster: ## Do some cluster validations before installing From 05c51c1556663bd1672d666b2a4594ea97ddb99e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 23 Jan 2024 16:30:44 +0100 Subject: [PATCH 1098/1263] Run validate-prereq only when not in a container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no point in testing the requirements when we use the container, as we guarantee that those exist in there. Tested as follows: ❯ make validate-prereq make -f common/Makefile validate-prereq make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Checking prerequisites: Check for 'git helm oc ansible': OK Check for python-kubernetes: OK Check for kubernetes.core collection: OK make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' ❯ ./pattern.sh make validate-prereq make -f common/Makefile validate-prereq make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Skipping prerequisites check as we're running inside a container make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' --- Makefile | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 50f886e1..f7a2d5a7 100644 --- a/Makefile +++ b/Makefile @@ -125,15 +125,19 @@ validate-schema: ## validates values files against schema in common/clustergroup .PHONY: validate-prereq validate-prereq: ## verify pre-requisites - @echo "Checking prerequisites:" - @for t in $(EXECUTABLES); do if ! which $$t > /dev/null 2>&1; then echo "No $$t in PATH"; exit 1; fi; done - @echo " Check for '$(EXECUTABLES)': OK" - @echo -n " Check for python-kubernetes: " - @if ! ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1; then echo "Not found"; exit 1; fi - @echo "OK" - @echo -n " Check for kubernetes.core collection: " - @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi - @echo "OK" + @if [ ! -f /run/.containerenv ]; then\ + echo "Checking prerequisites:";\ + for t in $(EXECUTABLES); do if ! which $$t > /dev/null 2>&1; then echo "No $$t in PATH"; exit 1; fi; done;\ + echo " Check for '$(EXECUTABLES)': OK";\ + echo -n " Check for python-kubernetes: ";\ + if ! ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1; then echo "Not found"; exit 1; fi;\ + echo "OK";\ + echo -n " Check for kubernetes.core collection: ";\ + if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi;\ + echo "OK";\ + else\ + echo "Skipping prerequisites check as we're running inside a container";\ + fi .PHONY: argo-healthcheck argo-healthcheck: ## Checks if all argo applications are synced From b4eb4d999970a44c0904eb1e5d8e54c371a05b65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:55:26 +0000 Subject: [PATCH 1099/1263] Bump dorny/paths-filter from 2 to 3 Bumps [dorny/paths-filter](https://github.com/dorny/paths-filter) from 2 to 3. - [Release notes](https://github.com/dorny/paths-filter/releases) - [Changelog](https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md) - [Commits](https://github.com/dorny/paths-filter/compare/v2...v3) --- updated-dependencies: - dependency-name: dorny/paths-filter dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/chart-branches.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/chart-branches.yml b/.github/workflows/chart-branches.yml index d93b1dbb..1a4fb455 100644 --- a/.github/workflows/chart-branches.yml +++ b/.github/workflows/chart-branches.yml @@ -32,7 +32,7 @@ jobs: - name: Checkout Code uses: actions/checkout@v4 - - uses: dorny/paths-filter@v2 + - uses: dorny/paths-filter@v3 id: filter with: filters: | From 4023800aa9a13e148649ea0e459180637269515e Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 4 Dec 2023 14:23:03 -0600 Subject: [PATCH 1100/1263] Add support for parsing secrets into intermediate structure and creating k8s secret objects Ensure only push_secrets runs from vault_utils Update makefile - remove extra targets and make fix none Revert version bump as we only add fields Conditionalize check change output Start module to load parsed secrets into vault New machinery for vault secrets loading Make the linters pass again Inject policies Add some more code to test readiness to load Correct typo Add vault_hub Add vaultMount Rename new modules to v2 Update inject_field method Correct field typo Temporarily print command Add more logic Hopefully fix secret loading issue with counter Count per secret Pick stuff out of secret that we need Fix lint issue Refactor tests to use fixture constants Correctly spell exclusion for ansible-lint Provide a target to exercise legacy code path Add error exists for missing args and update docs Reverse test for override Also process base64 for generated secrets Be more explicit about what we load Test framework for loading parsed_secret data Fix linting errors Finish test suite Last linter stuff Change schema; code and tests to follow Add target_namespaces phase 1 more passing, but some still fail Passing again All pass Check the correct variable in golang-external-secrets chart Update YAML parsing to do decodes right Add tests and tighten up code for retrieving block yaml quotes Add test for kubernetes secret object and block yaml --- .ansible-lint | 3 + .gitignore | 1 + Makefile | 30 +- ansible/playbooks/k8s_secrets/k8s_secrets.yml | 9 + .../process_secrets/display_secrets_info.yml | 29 + .../process_secrets/process_secrets.yml | 50 + ansible/playbooks/vault/vault.yaml | 2 + .../module_utils/load_secrets_common.py | 20 + .../plugins/module_utils/parse_secrets_v2.py | 527 ++++++++++ ansible/plugins/modules/parse_secrets_info.py | 149 +++ .../modules/vault_load_parsed_secrets.py | 302 ++++++ .../roles/cluster_pre_check/defaults/main.yml | 3 + .../tasks/main.yml} | 0 ansible/roles/find_vp_secrets/tasks/main.yml | 87 ++ .../roles/k8s_secret_utils/defaults/main.yml | 2 + .../tasks/inject_k8s_secret.yml | 15 + .../tasks/inject_k8s_secrets.yml | 5 + ansible/roles/k8s_secret_utils/tasks/main.yml | 6 + .../k8s_secret_utils/tasks/parse_secrets.yml | 12 + .../tasks/push_parsed_secrets.yaml | 43 + .../roles/vault_utils/tasks/push_secrets.yaml | 2 - .../roles/vault_utils/tasks/vault_init.yaml | 2 - .../vault_utils/tasks/vault_secrets_init.yaml | 3 - .../vault_utils/tasks/vault_spokes_init.yaml | 3 - .../roles/vault_utils/tasks/vault_unseal.yaml | 2 - .../vault_utils/values-secrets.v2.schema.json | 32 +- ansible/tests/unit/test_parse_secrets.py | 981 ++++++++++++++++++ .../tests/unit/test_util_datastructures.py | 205 ++++ .../unit/test_vault_load_parsed_secrets.py | 320 ++++++ ansible/tests/unit/v2/test-file-contents | 1 + ansible/tests/unit/v2/test-file-contents.b64 | 1 + .../v2/values-secret-v2-base-k8s-backend.yaml | 9 + .../values-secret-v2-base-none-backend.yaml | 11 + ...values-secret-v2-base-unknown-backend.yaml | 9 + .../v2/values-secret-v2-block-yamlstring.yaml | 16 + .../values-secret-v2-default-annotations.yaml | 13 + .../v2/values-secret-v2-default-labels.yaml | 11 + .../values-secret-v2-default-namespace.yaml | 8 + .../values-secret-v2-file-contents-b64.yaml | 9 + ...es-secret-v2-file-contents-double-b64.yaml | 9 + .../v2/values-secret-v2-file-contents.yaml | 8 + ...values-secret-v2-generic-onlygenerate.yaml | 33 + .../v2/values-secret-v2-ini-file-b64.yaml | 23 + .../v2/values-secret-v2-more-namespaces.yaml | 11 + ...values-secret-v2-nondefault-namespace.yaml | 8 + ...es-secret-v2-none-no-targetnamespaces.yaml | 33 + .../v2/values-secret-v2-override-labels.yaml | 13 + .../values-secret-v2-override-namespace.yaml | 10 + .../values-secret-v2-override-type-none.yaml | 14 + .../v2/values-secret-v2-override-type.yaml | 12 + .../values-secret-v2-secret-binary-b64.yaml | 10 + .../templates/imperative/unsealjob.yaml | 2 + clustergroup/values.yaml | 2 + ...lang-external-secrets-hub-secretstore.yaml | 40 - .../golang-external-secrets-hub-role.yaml | 22 + ...lang-external-secrets-hub-secretstore.yaml | 34 + ...lang-external-secrets-hub-secretstore.yaml | 44 + golang-external-secrets/values.yaml | 24 +- scripts/determine-main-clustergroup.sh | 16 + scripts/determine-pattern-name.sh | 15 + scripts/determine-secretstore-backend.sh | 15 + scripts/display-secrets-info.sh | 30 + scripts/load-k8s-secrets.sh | 19 + scripts/manage-secret-app.sh | 49 + scripts/manage-secret-namespace.sh | 28 + scripts/process-secrets.sh | 20 + scripts/set-secret-backend.sh | 5 + ...roup-industrial-edge-factory.expected.yaml | 2 + ...tergroup-industrial-edge-hub.expected.yaml | 2 + ...rgroup-medical-diagnosis-hub.expected.yaml | 2 + tests/clustergroup-naked.expected.yaml | 2 + tests/clustergroup-normal.expected.yaml | 2 + ...rets-industrial-edge-factory.expected.yaml | 2 +- ...-secrets-industrial-edge-hub.expected.yaml | 2 +- ...ecrets-medical-diagnosis-hub.expected.yaml | 2 +- ...olang-external-secrets-naked.expected.yaml | 2 +- ...lang-external-secrets-normal.expected.yaml | 2 +- values-global.yaml | 3 + 78 files changed, 3439 insertions(+), 66 deletions(-) create mode 100644 ansible/playbooks/k8s_secrets/k8s_secrets.yml create mode 100644 ansible/playbooks/process_secrets/display_secrets_info.yml create mode 100644 ansible/playbooks/process_secrets/process_secrets.yml create mode 100644 ansible/plugins/module_utils/parse_secrets_v2.py create mode 100644 ansible/plugins/modules/parse_secrets_info.py create mode 100644 ansible/plugins/modules/vault_load_parsed_secrets.py create mode 100644 ansible/roles/cluster_pre_check/defaults/main.yml rename ansible/roles/{vault_utils/tasks/pre_check.yaml => cluster_pre_check/tasks/main.yml} (100%) create mode 100644 ansible/roles/find_vp_secrets/tasks/main.yml create mode 100644 ansible/roles/k8s_secret_utils/defaults/main.yml create mode 100644 ansible/roles/k8s_secret_utils/tasks/inject_k8s_secret.yml create mode 100644 ansible/roles/k8s_secret_utils/tasks/inject_k8s_secrets.yml create mode 100644 ansible/roles/k8s_secret_utils/tasks/main.yml create mode 100644 ansible/roles/k8s_secret_utils/tasks/parse_secrets.yml create mode 100644 ansible/roles/vault_utils/tasks/push_parsed_secrets.yaml create mode 100644 ansible/tests/unit/test_parse_secrets.py create mode 100644 ansible/tests/unit/test_util_datastructures.py create mode 100644 ansible/tests/unit/test_vault_load_parsed_secrets.py create mode 100644 ansible/tests/unit/v2/test-file-contents create mode 100644 ansible/tests/unit/v2/test-file-contents.b64 create mode 100644 ansible/tests/unit/v2/values-secret-v2-base-k8s-backend.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-base-none-backend.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-base-unknown-backend.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-block-yamlstring.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-default-annotations.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-default-labels.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-default-namespace.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-file-contents-b64.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-file-contents-double-b64.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-file-contents.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-generic-onlygenerate.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-ini-file-b64.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-more-namespaces.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-nondefault-namespace.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-none-no-targetnamespaces.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-override-labels.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-override-namespace.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-override-type-none.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-override-type.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-secret-binary-b64.yaml delete mode 100644 golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml create mode 100644 golang-external-secrets/templates/kubernetes/golang-external-secrets-hub-role.yaml create mode 100644 golang-external-secrets/templates/kubernetes/golang-external-secrets-hub-secretstore.yaml create mode 100644 golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml create mode 100755 scripts/determine-main-clustergroup.sh create mode 100755 scripts/determine-pattern-name.sh create mode 100755 scripts/determine-secretstore-backend.sh create mode 100755 scripts/display-secrets-info.sh create mode 100755 scripts/load-k8s-secrets.sh create mode 100755 scripts/manage-secret-app.sh create mode 100755 scripts/manage-secret-namespace.sh create mode 100755 scripts/process-secrets.sh create mode 100755 scripts/set-secret-backend.sh diff --git a/.ansible-lint b/.ansible-lint index 353222eb..aaffc6b5 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -14,4 +14,7 @@ skip_list: exclude_paths: - ./ansible/playbooks/vault/vault.yaml - ./ansible/playbooks/iib-ci/iib-ci.yaml + - ./ansible/playbooks/k8s_secrets/k8s_secrets.yml + - ./ansible/playbooks/process_secrets/process_secrets.yml + - ./ansible/playbooks/process_secrets/display_secrets_info.yml - ./ansible/roles/vault_utils/tests/test.yml diff --git a/.gitignore b/.gitignore index 9e5051a8..454efc9e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ __pycache__/ *.swo values-secret.yaml .*.expected.yaml +.vscode pattern-vault.init pattern-vault.init.bak super-linter.log diff --git a/Makefile b/Makefile index f7a2d5a7..0d5d0a36 100644 --- a/Makefile +++ b/Makefile @@ -77,9 +77,37 @@ uninstall: ## runs helm uninstall @oc delete csv -n openshift-operators $(CSV) .PHONY: load-secrets -load-secrets: ## loads the secrets into the vault +load-secrets: ## loads the secrets into the backend determined by values-global setting + common/scripts/process-secrets.sh $(NAME) + +.PHONY: legacy-load-secrets +legacy-load-secrets: ## loads the secrets into vault (only) common/scripts/vault-utils.sh push_secrets $(NAME) +.PHONY: secrets-backend-vault +secrets-backend-vault: ## Edits values files to use default Vault+ESO secrets config + common/scripts/set-secret-backend.sh vault + common/scripts/manage-secret-app.sh vault present + common/scripts/manage-secret-app.sh golang-external-secrets present + common/scripts/manage-secret-namespace.sh validated-patterns-secrets absent + @git diff --exit-code || echo "Secrets backend set to vault, please review changes, commit, and push to activate in the pattern" + +.PHONY: secrets-backend-kubernetes +secrets-backend-kubernetes: ## Edits values file to use Kubernetes+ESO secrets config + common/scripts/set-secret-backend.sh kubernetes + common/scripts/manage-secret-namespace.sh validated-patterns-secrets present + common/scripts/manage-secret-app.sh vault absent + common/scripts/manage-secret-app.sh golang-external-secrets present + @git diff --exit-code || echo "Secrets backend set to kubernetes, please review changes, commit, and push to activate in the pattern" + +.PHONY: secrets-backend-none +secrets-backend-none: ## Edits values files to remove secrets manager + ESO + common/scripts/set-secret-backend.sh none + common/scripts/manage-secret-app.sh vault absent + common/scripts/manage-secret-app.sh golang-external-secrets absent + common/scripts/manage-secret-namespace.sh validated-patterns-secrets absent + @git diff --exit-code || echo "Secrets backend set to none, please review changes, commit, and push to activate in the pattern" + .PHONY: load-iib load-iib: ## CI target to install Index Image Bundles @set -e; if [ x$(INDEX_IMAGES) != x ]; then \ diff --git a/ansible/playbooks/k8s_secrets/k8s_secrets.yml b/ansible/playbooks/k8s_secrets/k8s_secrets.yml new file mode 100644 index 00000000..989a498a --- /dev/null +++ b/ansible/playbooks/k8s_secrets/k8s_secrets.yml @@ -0,0 +1,9 @@ +--- +- name: Secrets parsing and direct loading + hosts: localhost + connection: local + gather_facts: false + roles: + - find_vp_secrets + - cluster_pre_check + - k8s_secret_utils diff --git a/ansible/playbooks/process_secrets/display_secrets_info.yml b/ansible/playbooks/process_secrets/display_secrets_info.yml new file mode 100644 index 00000000..4d972359 --- /dev/null +++ b/ansible/playbooks/process_secrets/display_secrets_info.yml @@ -0,0 +1,29 @@ +--- +- name: Parse and display secrets + hosts: localhost + connection: local + gather_facts: false + vars: + secrets_backing_store: "vault" + tasks: + # Set the VALUES_SECRET environment variable to the file to parse + - name: Find and decrypt secrets if needed + ansible.builtin.include_role: + name: find_vp_secrets + + # find_vp_secrets will return a plaintext data structure called values_secrets_data + # This will allow us to determine schema version and which backend to use + - name: Determine how to load secrets + ansible.builtin.set_fact: + secrets_yaml: '{{ values_secrets_data | from_yaml }}' + + - name: Parse secrets data + no_log: '{{ override_no_log | default(true) }}' + parse_secrets_info: + values_secrets_plaintext: "{{ values_secrets_data }}" + secrets_backing_store: "{{ secrets_backing_store }}" + register: secrets_results + + - name: Display secrets data + ansible.builtin.debug: + var: secrets_results diff --git a/ansible/playbooks/process_secrets/process_secrets.yml b/ansible/playbooks/process_secrets/process_secrets.yml new file mode 100644 index 00000000..ecc1b565 --- /dev/null +++ b/ansible/playbooks/process_secrets/process_secrets.yml @@ -0,0 +1,50 @@ +--- +- name: Parse and load secrets + hosts: localhost + connection: local + gather_facts: false + vars: + secrets_role: 'vault_utils' + pattern_name: 'common' + pattern_dir: '.' + secrets_backing_store: 'vault' + tasks_from: 'push_parsed_secrets' + tasks: + - name: "Run secret-loading pre-requisites" + ansible.builtin.include_role: + name: '{{ item }}' + loop: + - cluster_pre_check + - find_vp_secrets + + # find_vp_secrets will return a plaintext data structure called values_secrets_data + # This will allow us to determine schema version and which backend to use + - name: Determine how to load secrets + ansible.builtin.set_fact: + secrets_yaml: '{{ values_secrets_data | from_yaml }}' + + - name: Parse secrets data + no_log: '{{ override_no_log | default(true) }}' + parse_secrets_info: + values_secrets_plaintext: "{{ values_secrets_data }}" + secrets_backing_store: "{{ secrets_backing_store }}" + register: secrets_results + + # Use the k8s secrets loader when explicitly requested + - name: Determine role to use to load secrets + ansible.builtin.set_fact: + secrets_role: 'k8s_secret_utils' + tasks_from: 'inject_k8s_secrets' + when: + - secrets_backing_store == "kubernetes" or secrets_backing_store == "none" + - secrets_yaml['version'] | default('2.0') >= '2.0' + + # secrets_role will have been changed from the default if needed + - name: Load secrets using designated role and tasks + ansible.builtin.include_role: + name: '{{ secrets_role }}' + tasks_from: '{{ tasks_from }}' + vars: + kubernetes_secret_objects: "{{ secrets_results['kubernetes_secret_objects'] }}" + vault_policies: "{{ secrets_results['vault_policies'] }}" + parsed_secrets: "{{ secrets_results['parsed_secrets'] }}" diff --git a/ansible/playbooks/vault/vault.yaml b/ansible/playbooks/vault/vault.yaml index 64711e47..b0da9405 100644 --- a/ansible/playbooks/vault/vault.yaml +++ b/ansible/playbooks/vault/vault.yaml @@ -4,4 +4,6 @@ connection: local gather_facts: false roles: + - find_vp_secrets + - cluster_pre_check - vault_utils diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index 1652a287..b4ebc816 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -102,3 +102,23 @@ def get_ini_value(inifile, inisection, inikey): config = configparser.ConfigParser() config.read(inifile) return config.get(inisection, inikey, fallback=None) + + +def stringify_dict(input_dict): + """ + Return a dict whose keys and values are all co-erced to strings, for creating labels and annotations in the + python Kubernetes module + + Parameters: + input_dict(dict): A dictionary of keys and values + + Returns: + + obj: The same dict in the same order but with the keys coerced to str + """ + output_dict = {} + + for key, value in input_dict.items(): + output_dict[str(key)] = str(value) + + return output_dict diff --git a/ansible/plugins/module_utils/parse_secrets_v2.py b/ansible/plugins/module_utils/parse_secrets_v2.py new file mode 100644 index 00000000..512f75ef --- /dev/null +++ b/ansible/plugins/module_utils/parse_secrets_v2.py @@ -0,0 +1,527 @@ +# Copyright 2022, 2023 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Module that implements V2 of the values-secret.yaml spec +""" + +import base64 +import getpass +import os + +from ansible.module_utils.load_secrets_common import ( + find_dupes, + get_ini_value, + get_version, + stringify_dict, +) + +default_vp_vault_policies = { + "validatedPatternDefaultPolicy": ( + "length=20\n" + 'rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\n' + 'rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\n' + 'rule "charset" { charset = "0123456789" min-chars = 1 }\n' + 'rule "charset" { charset = "!@#%^&*" min-chars = 1 }\n' + ) +} + +secret_store_namespace = "validated-patterns-secrets" + + +class ParseSecretsV2: + def __init__(self, module, syaml, secrets_backing_store): + self.module = module + self.syaml = syaml + self.secrets_backing_store = str(secrets_backing_store) + self.secret_store_namespace = None + self.parsed_secrets = {} + self.kubernetes_secret_objects = [] + self.vault_policies = {} + + def _get_backingstore(self): + """ + Backing store is now influenced by the caller more than the file. Setting + Return the backingStore: of the parsed yaml object. In most cases the file + key was not set anyway - since vault was the only supported option. Since + we are introducing new options now, this method of defining behavior is + deprecated, but if the file key is included it must match the option defined + by values-global in the pattern, or there is an error. The default remains + 'vault' if the key is unspecified. + + Returns: + ret(str): The value of the top-level 'backingStore:' key + """ + file_backing_store = str(self.syaml.get("backingStore", "unset")) + + if file_backing_store == "unset": + pass + else: + if file_backing_store != self.secrets_backing_store: + self.module.fail_json( + f"Secrets file specifies '{file_backing_store}' backend but pattern config " + f"specifies '{self.secrets_backing_store}'." + ) + + return self.secrets_backing_store + + def _get_vault_policies(self, enable_default_vp_policies=True): + # We start off with the hard-coded default VP policy and add the user-defined ones + if enable_default_vp_policies: + policies = default_vp_vault_policies.copy() + else: + policies = {} + + # This is useful for embedded newlines, which occur with YAML + # flow-type scalars (|, |- for example) + for name, policy in self.syaml.get("vaultPolicies", {}).items(): + policies[name] = self._sanitize_yaml_value(policy) + + return policies + + def _get_secrets(self): + return self.syaml.get("secrets", {}) + + def _get_field_on_missing_value(self, f): + # By default if 'onMissingValue' is missing we assume we need to + # error out whenever the value is missing + return f.get("onMissingValue", "error") + + def _get_field_value(self, f): + return f.get("value", None) + + def _get_field_path(self, f): + return f.get("path", None) + + def _get_field_ini_file(self, f): + return f.get("ini_file", None) + + def _get_field_annotations(self, f): + return f.get("annotations", {}) + + def _get_field_labels(self, f): + return f.get("labels", {}) + + def _get_field_kind(self, f): + # value: null will be interpreted with None, so let's just + # check for the existence of the field, as we use 'value: null' to say + # "we want a value/secret and not a file path" + found = [] + for i in ["value", "path", "ini_file"]: + if i in f: + found.append(i) + + if len(found) > 1: # you can only have one of value, path and ini_file + self.module.fail_json( + f"Both '{found[0]}' and '{found[1]}' cannot be used " + f"in field {f['name']}" + ) + + if len(found) == 0: + return "" + return found[0] + + def _get_field_prompt(self, f): + return f.get("prompt", None) + + def _get_field_base64(self, f): + return bool(f.get("base64", False)) + + def _get_field_override(self, f): + return bool(f.get("override", False)) + + def _get_secret_store_namespace(self): + return str(self.syaml.get("secretStoreNamespace", secret_store_namespace)) + + def _get_vault_prefixes(self, s): + return list(s.get("vaultPrefixes", ["hub"])) + + def _get_default_labels(self): + return self.syaml.get("defaultLabels", {}) + + def _get_default_annotations(self): + return self.syaml.get("defaultAnnotations", {}) + + def _append_kubernetes_secret(self, secret_obj): + self.kubernetes_secret_objects.append(secret_obj) + + def _sanitize_yaml_value(self, value): + # This is useful for embedded newlines, which occur with YAML + # flow-type scalars (|, |- for example) + if value is not None: + sanitized_value = bytes(value, "utf-8").decode("unicode_escape") + else: + sanitized_value = None + + return sanitized_value + + def _create_k8s_secret(self, sname, secret_type, namespace, labels, annotations): + return { + "type": secret_type, + "kind": "Secret", + "apiVersion": "v1", + "metadata": { + "name": sname, + "namespace": namespace, + "annotations": annotations, + "labels": labels, + }, + "stringData": {}, + } + + # This does what inject_secrets used to (mostly) + def parse(self): + self.sanitize_values() + self.vault_policies = self._get_vault_policies() + self.secret_store_namespace = self._get_secret_store_namespace() + backing_store = self._get_backingstore() + secrets = self._get_secrets() + + total_secrets = 0 # Counter for all the secrets uploaded + for s in secrets: + total_secrets += 1 + counter = 0 # This counter is to use kv put on first secret and kv patch on latter + sname = s.get("name") + fields = s.get("fields", []) + vault_prefixes = self._get_vault_prefixes(s) + secret_type = s.get("type", "Opaque") + vault_mount = s.get("vaultMount", "secret") + target_namespaces = s.get("targetNamespaces", []) + labels = stringify_dict(s.get("labels", self._get_default_labels())) + annotations = stringify_dict( + s.get("annotations", self._get_default_annotations()) + ) + + self.parsed_secrets[sname] = { + "name": sname, + "fields": {}, + "vault_mount": vault_mount, + "vault_policies": {}, + "vault_prefixes": vault_prefixes, + "override": [], + "generate": [], + "paths": {}, + "base64": [], + "ini_file": {}, + "type": secret_type, + "target_namespaces": target_namespaces, + "labels": labels, + "annotations": annotations, + } + + for i in fields: + self._inject_field(sname, i) + counter += 1 + + if backing_store == "kubernetes": + k8s_namespaces = [self._get_secret_store_namespace()] + else: + k8s_namespaces = target_namespaces + + for tns in k8s_namespaces: + k8s_secret = self._create_k8s_secret( + sname, secret_type, tns, labels, annotations + ) + k8s_secret["stringData"] = self.parsed_secrets[sname]["fields"] + self.kubernetes_secret_objects.append(k8s_secret) + + return total_secrets + + # This function could use some rewriting and it should call a specific validation function + # for each type (value, path, ini_file) + def _validate_field(self, f): + # These fields are mandatory + try: + _ = f["name"] + except KeyError: + return (False, f"Field {f} is missing name") + + on_missing_value = self._get_field_on_missing_value(f) + if on_missing_value not in ["error", "generate", "prompt"]: + return (False, f"onMissingValue: {on_missing_value} is invalid") + + value = self._get_field_value(f) + path = self._get_field_path(f) + ini_file = self._get_field_ini_file(f) + kind = self._get_field_kind(f) + if kind == "ini_file": + # if we are using ini_file then at least ini_key needs to be defined + # ini_section defaults to 'default' when omitted + ini_key = f.get("ini_key", None) + if ini_key is None: + return ( + False, + "ini_file requires at least ini_key to be defined", + ) + + # Test if base64 is a correct boolean (defaults to False) + _ = self._get_field_base64(f) + _ = self._get_field_override(f) + + vault_policy = f.get("vaultPolicy", None) + if vault_policy is not None and vault_policy not in self._get_vault_policies(): + return ( + False, + f"Secret has vaultPolicy set to {vault_policy} but no such policy exists", + ) + + if on_missing_value in ["error"]: + if ( + (value is None or len(value) < 1) + and (path is None or len(path) < 1) + and (ini_file is None or len(ini_file) < 1) + ): + return ( + False, + "Secret has onMissingValue set to 'error' and has neither value nor path nor ini_file set", + ) + if path is not None and not os.path.isfile(os.path.expanduser(path)): + return (False, f"Field has non-existing path: {path}") + + if ini_file is not None and not os.path.isfile( + os.path.expanduser(ini_file) + ): + return (False, f"Field has non-existing ini_file: {ini_file}") + + if on_missing_value in ["prompt"]: + # When we prompt, the user needs to set one of the following: + # - value: null # prompt for a secret without a default value + # - value: 123 # prompt for a secret but use a default value + # - path: null # prompt for a file path without a default value + # - path: /tmp/ca.crt # prompt for a file path with a default value + if "value" not in f and "path" not in f: + return ( + False, + "Secret has onMissingValue set to 'prompt' but has no value nor path fields", + ) + + if "override" in f: + return ( + False, + "'override' attribute requires 'onMissingValue' to be set to 'generate'", + ) + + return (True, "") + + def _validate_secrets(self): + backing_store = self._get_backingstore() + secrets = self._get_secrets() + if len(secrets) == 0: + self.module.fail_json("No secrets found") + + names = [] + for s in secrets: + # These fields are mandatory + for i in ["name"]: + try: + _ = s[i] + except KeyError: + return (False, f"Secret {s['name']} is missing {i}") + names.append(s["name"]) + + vault_prefixes = s.get("vaultPrefixes", ["hub"]) + # This checks for the case when vaultPrefixes: is specified but empty + if vault_prefixes is None or len(vault_prefixes) == 0: + return (False, f"Secret {s['name']} has empty vaultPrefixes") + + namespaces = s.get("targetNamespaces", []) + if not isinstance(namespaces, list): + return (False, f"Secret {s['name']} targetNamespaces must be a list") + + if backing_store == "none" and namespaces == []: + return ( + False, + f"Secret {s['name']} targetNamespaces cannot be empty for secrets backend {backing_store}", + ) # noqa: E501 + + labels = s.get("labels", {}) + if not isinstance(labels, dict): + return (False, f"Secret {s['name']} labels must be a dictionary") + + annotations = s.get("annotations", {}) + if not isinstance(annotations, dict): + return (False, f"Secret {s['name']} annotations must be a dictionary") + + fields = s.get("fields", []) + if len(fields) == 0: + return (False, f"Secret {s['name']} does not have any fields") + + field_names = [] + for i in fields: + (ret, msg) = self._validate_field(i) + if not ret: + return (False, msg) + field_names.append(i["name"]) + field_dupes = find_dupes(field_names) + if len(field_dupes) > 0: + return (False, f"You cannot have duplicate field names: {field_dupes}") + + dupes = find_dupes(names) + if len(dupes) > 0: + return (False, f"You cannot have duplicate secret names: {dupes}") + return (True, "") + + def sanitize_values(self): + """ + Sanitizes the secrets YAML object version 2.0 + + Parameters: + + Returns: + Nothing: Updates self.syaml(obj) if needed + """ + v = get_version(self.syaml) + if v not in ["2.0"]: + self.module.fail_json(f"Version is not 2.0: {v}") + + backing_store = self._get_backingstore() + if backing_store not in [ + "kubernetes", + "vault", + "none", + ]: # we currently only support vault + self.module.fail_json( + f"Currently only the 'vault', 'kubernetes' and 'none' backingStores are supported: {backing_store}" + ) + + (ret, msg) = self._validate_secrets() + if not ret: + self.module.fail_json(msg) + + def _get_secret_value(self, name, field): + on_missing_value = self._get_field_on_missing_value(field) + # We cannot use match + case as RHEL8 has python 3.9 (it needs 3.10) + # We checked for errors in _validate_secrets() already + if on_missing_value == "error": + return self._sanitize_yaml_value(field.get("value")) + elif on_missing_value == "prompt": + prompt = self._get_field_prompt(field) + if prompt is None: + prompt = f"Type secret for {name}/{field['name']}: " + value = self._get_field_value(field) + if value is not None: + prompt += f" [{value}]" + prompt += ": " + return getpass.getpass(prompt) + return None + + def _get_file_path(self, name, field): + on_missing_value = self._get_field_on_missing_value(field) + if on_missing_value == "error": + return os.path.expanduser(field.get("path")) + elif on_missing_value == "prompt": + prompt = self._get_field_prompt(field) + path = self._get_field_path(field) + if path is None: + path = "" + + if prompt is None: + text = f"Type path for file {name}/{field['name']} [{path}]: " + else: + text = f"{prompt} [{path}]: " + + newpath = getpass.getpass(text) + if newpath == "": # Set the default if no string was entered + newpath = path + + if os.path.isfile(os.path.expanduser(newpath)): + return newpath + self.module.fail_json(f"File {newpath} not found, exiting") + + self.module.fail_json("File with wrong onMissingValue") + + def _inject_field(self, secret_name, f): + on_missing_value = self._get_field_on_missing_value(f) + override = self._get_field_override(f) + kind = self._get_field_kind(f) + b64 = self._get_field_base64(f) + + if kind in ["value", ""]: + if on_missing_value == "generate": + self.parsed_secrets[secret_name]["generate"].append(f["name"]) + if self._get_backingstore() != "vault": + self.module.fail_json( + "You cannot have onMissingValue set to 'generate' unless using vault backingstore " + f"for secret {secret_name} field {f['name']}" + ) + else: + if kind in ["path", "ini_file"]: + self.module.fail_json( + "You cannot have onMissingValue set to 'generate' with a path or ini_file" + f" for secret {secret_name} field {f['name']}" + ) + + vault_policy = f.get("vaultPolicy", "validatedPatternDefaultPolicy") + + if override: + self.parsed_secrets[secret_name]["override"].append(f["name"]) + + if b64: + self.parsed_secrets[secret_name]["base64"].append(f["name"]) + + self.parsed_secrets[secret_name]["fields"][f["name"]] = None + self.parsed_secrets[secret_name]["vault_policies"][ + f["name"] + ] = vault_policy + + return + + # If we're not generating the secret inside the vault directly we either read it from the file ("error") + # or we are prompting the user for it + secret = self._get_secret_value(secret_name, f) + if b64: + secret = base64.b64encode(secret.encode()).decode("utf-8") + self.parsed_secrets[secret_name]["base64"].append(f["name"]) + + self.parsed_secrets[secret_name]["fields"][f["name"]] = secret + + elif kind == "path": # path. we upload files + path = self._get_file_path(secret_name, f) + self.parsed_secrets[secret_name]["paths"][f["name"]] = path + + binfile = False + + # Default to UTF-8 + try: + secret = open(path, encoding="utf-8").read() + except UnicodeDecodeError: + secret = open(path, "rb").read() + binfile = True + + if b64: + self.parsed_secrets[secret_name]["base64"].append(f["name"]) + if binfile: + secret = base64.b64encode(bytes(secret)).decode("utf-8") + else: + secret = base64.b64encode(secret.encode()).decode("utf-8") + + self.parsed_secrets[secret_name]["fields"][f["name"]] = secret + elif kind == "ini_file": # ini_file. we parse an ini_file + ini_file = os.path.expanduser(f.get("ini_file")) + ini_section = f.get("ini_section", "default") + ini_key = f.get("ini_key") + secret = get_ini_value(ini_file, ini_section, ini_key) + if b64: + self.parsed_secrets[secret_name]["base64"].append(f["name"]) + secret = base64.b64encode(secret.encode()).decode("utf-8") + + self.parsed_secrets[secret_name]["ini_file"][f["name"]] = { + "ini_file": ini_file, + "ini_section": ini_section, + "ini_key": ini_key, + } + self.parsed_secrets[secret_name]["fields"][f["name"]] = secret + + return diff --git a/ansible/plugins/modules/parse_secrets_info.py b/ansible/plugins/modules/parse_secrets_info.py new file mode 100644 index 00000000..b962271a --- /dev/null +++ b/ansible/plugins/modules/parse_secrets_info.py @@ -0,0 +1,149 @@ +# Copyright 2022,2023 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Ansible plugin module that loads secrets from a yaml file and pushes them +inside the HashiCorp Vault in an OCP cluster. The values-secrets.yaml file is +expected to be in the following format: +--- +# version is optional. When not specified it is assumed it is 1.0 +version: 1.0 + +# These secrets will be pushed in the vault at secret/hub/test The vault will +# have secret/hub/test with secret1 and secret2 as keys with their associated +# values (secrets) +secrets: + test: + secret1: foo + secret2: bar + +# This will create the vault key secret/hub/testfoo which will have two +# properties 'b64content' and 'content' which will be the base64-encoded +# content and the normal content respectively +files: + testfoo: ~/ca.crt + +# These secrets will be pushed in the vault at secret/region1/test The vault will +# have secret/region1/test with secret1 and secret2 as keys with their associated +# values (secrets) +secrets.region1: + test: + secret1: foo1 + secret2: bar1 + +# This will create the vault key secret/region2/testbar which will have two +# properties 'b64content' and 'content' which will be the base64-encoded +# content and the normal content respectively +files.region2: + testbar: ~/ca.crt +""" + +import yaml +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.parse_secrets_v2 import ParseSecretsV2 + +ANSIBLE_METADATA = { + "metadata_version": "1.2", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = """ +--- +module: parse_secrets_info +short_description: Parses a Validated Patterns Secrets file for later loading +version_added: "2.50" +author: "Martin Jackson" +description: + - Takes a values-secret.yaml file, parses and returns values for secrets loading. The goal here is to do all the + work of reading and interpreting the file and resolving the content pointers (that is, creating content where it + is given) such that that content is then available for secrets vaults to load. It does not attempt to load the + content or interpret the content beyond the conventions of the file format. (So, it knows how to retrieve + ini-keys, about paths, and about base64 but leaves interaction with backends to backend-specific code. +options: + values_secrets_plaintext: + description: + - The unencrypted content of the values-secrets file + required: true + type: str + secrets_backing_store: + description: + - The secrets backing store that will be used for parsed secrets (i.e. vault, kubernetes, none) + required: false + default: vault + type: str +""" + +RETURN = """ +""" + +EXAMPLES = """ +- name: Parse secrets file into objects - backingstore defaults to vault + parse_secrets_info: + values_secrets_plaintext: '{{ }}' + register: secrets_info + +- name: Parse secrets file into data structures + parse_secrets_info: + values_secrets_plaintext: '{{ }}' + secrets_backing_store: 'kubernetes' + register: secrets_info + +- name: Parse secrets file into data structures + parse_secrets_info: + values_secrets_plaintext: '{{ }}' + secrets_backing_store: 'none' + register: secrets_info +""" + + +def run(module): + """Main ansible module entry point""" + results = dict(changed=False) + + args = module.params + values_secrets_plaintext = args.get("values_secrets_plaintext", "") + secrets_backing_store = args.get("secrets_backing_store", "vault") + + syaml = yaml.safe_load(values_secrets_plaintext) + + if syaml is None: + syaml = {} + + parsed_secret_obj = ParseSecretsV2(module, syaml, secrets_backing_store) + parsed_secret_obj.parse() + + results["failed"] = False + results["changed"] = False + + results["vault_policies"] = parsed_secret_obj.vault_policies + results["parsed_secrets"] = parsed_secret_obj.parsed_secrets + results["kubernetes_secret_objects"] = parsed_secret_obj.kubernetes_secret_objects + results["secret_store_namespace"] = parsed_secret_obj.secret_store_namespace + + module.exit_json(**results) + + +def main(): + """Main entry point where the AnsibleModule class is instantiated""" + module = AnsibleModule( + argument_spec=yaml.safe_load(DOCUMENTATION)["options"], + supports_check_mode=True, + ) + run(module) + + +if __name__ == "__main__": + main() diff --git a/ansible/plugins/modules/vault_load_parsed_secrets.py b/ansible/plugins/modules/vault_load_parsed_secrets.py new file mode 100644 index 00000000..cfcf9732 --- /dev/null +++ b/ansible/plugins/modules/vault_load_parsed_secrets.py @@ -0,0 +1,302 @@ +# Copyright 2022 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Ansible plugin module that loads secrets and policies once parsed and pushes them +into a HashiCorp Vault in an OCP cluster. The values-secrets.yaml file is +expected to be in the following format: +--- +# version is optional. When not specified it is assumed it is 2.0 +version: 2.0 + +""" + +import os +import time + +import yaml +from ansible.module_utils.basic import AnsibleModule + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = """ +--- +module: vault_load_parsed_secrets +short_description: Loads secrets into the HashiCorp Vault +version_added: "2.50" +author: "Martin Jackson" +description: + - Takes parsed secrets objects and vault policies (as delivered by parse_secrets_info) and runs the commands to + load them into a vault instance. The relevent metadata will exist in the parsed secrets object. Returns count + of secrets injected. +options: + parsed_secrets: + description: + - A structure containing the secrets, fields, and their metadata + required: true + type: dict + vault_policies: + description: + - Vault policies to inject into the instance. + required: true + type: dict + namespace: + description: + - Namespace where the vault is running + required: false + type: str + default: vault + pod: + description: + - Name of the vault pod to use to inject secrets + required: false + type: str + default: vault-0 +""" + +RETURN = """ +""" + +EXAMPLES = """ +- name: Loads secrets file into the vault of a cluster + vault_load_parsed_secrets: + parsed_secrets: "{{ parsed_secrets_structure_from_parse_secrets_info }}" + vault_policies: "{{ parsed_vault_policies_structure_from_parse_secrets_info }}" +""" + + +class VaultSecretLoader: + def __init__( + self, + module, + parsed_secrets, + vault_policies, + namespace, + pod, + ): + self.module = module + self.parsed_secrets = parsed_secrets + self.vault_policies = vault_policies + self.namespace = namespace + self.pod = pod + + def _run_command(self, command, attempts=1, sleep=3, checkrc=True): + """ + Runs a command on the host ansible is running on. A failing command + will raise an exception in this function directly (due to check=True) + + Parameters: + command(str): The command to be run. + attempts(int): Number of times to retry in case of Error (defaults to 1) + sleep(int): Number of seconds to wait in between retry attempts (defaults to 3s) + + Returns: + ret(subprocess.CompletedProcess): The return value from run() + """ + for attempt in range(attempts): + ret = self.module.run_command( + command, + check_rc=checkrc, + use_unsafe_shell=True, + environ_update=os.environ.copy(), + ) + if ret[0] == 0: + return ret + if attempt >= attempts - 1: + return ret + time.sleep(sleep) + + def _vault_secret_attr_exists(self, mount, prefix, secret_name, attribute): + cmd = ( + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f'"vault kv get -mount={mount} -field={attribute} {prefix}/{secret_name}"' + ) + # we ignore stdout and stderr + (ret, _, _) = self._run_command(cmd, attempts=1, checkrc=False) + if ret == 0: + return True + + return False + + def load_vault(self): + injected_secret_count = 0 + + self.inject_vault_policies() + + for secret_name, secret in self.parsed_secrets.items(): + self.inject_secret(secret_name, secret) + injected_secret_count += 1 + + return injected_secret_count + + def inject_field( + self, + secret_name, + soverride, + sbase64, + sgenerate, + spaths, + svault_policies, + fieldname, + fieldvalue, + mount, + vault_prefixes, + first=False, + ): + # Special cases: + # generate w|wo override + # path (w|wo b64) + # + # inifile secrets will be resolved by parser + # values (including base64'd ones) will be resolved by parser + # And we just ignore k8s or other fields + + override = True if fieldname in soverride else False + b64 = True if fieldname in sbase64 else False + generate = True if fieldname in sgenerate else False + path = spaths.get(fieldname, False) + prefixes = vault_prefixes + verb = "put" if first else "patch" + policy = svault_policies.get(fieldname, False) + + # "generate" secrets are created with policies and may be overridden or not + if generate: + gen_cmd = ( + f"vault read -field=password sys/policies/password/{policy}/generate" + ) + if b64: + gen_cmd += " | base64 --wrap=0" + for prefix in prefixes: + # if the override field is False and the secret attribute exists at the prefix then we just + # skip, as we do not want to overwrite the existing secret + if not override and self._vault_secret_attr_exists( + mount, prefix, secret_name, fieldname + ): + continue + cmd = ( + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f'"{gen_cmd} | vault kv {verb} -mount={mount} {prefix}/{secret_name} {fieldname}=-"' + ) + self._run_command(cmd, attempts=3) + return + + if path: + for prefix in prefixes: + if b64: + b64_cmd = "| base64 --wrap=0" + else: + b64_cmd = "" + cmd = ( + f"cat '{path}' | oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f"'cat - {b64_cmd}> /tmp/vcontent'; " + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c '" + f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {fieldname}=@/tmp/vcontent; " + f"rm /tmp/vcontent'" + ) + self._run_command(cmd, attempts=3) + return + + for prefix in prefixes: + cmd = ( + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f"\"vault kv {verb} -mount={mount} {prefix}/{secret_name} {fieldname}='{fieldvalue}'\"" + ) + self._run_command(cmd, attempts=3) + return + + def inject_secret(self, secret_name, secret): + mount = secret.get("vault_mount", "secret") + vault_prefixes = secret.get("vault_prefixes", ["hub"]) + + counter = 0 + # In this structure, each field will have one value + for fname, fvalue in secret.get("fields").items(): + self.inject_field( + secret_name=secret_name, + soverride=secret["override"], + sbase64=secret["base64"], + sgenerate=secret["generate"], + spaths=secret["paths"], + svault_policies=secret["vault_policies"], + fieldname=fname, + fieldvalue=fvalue, + mount=mount, + vault_prefixes=vault_prefixes, + first=counter == 0, + ) + counter += 1 + return + + def inject_vault_policies(self): + for name, policy in self.vault_policies.items(): + cmd = ( + f"echo '{policy}' | oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f"'cat - > /tmp/{name}.hcl';" + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c 'vault write sys/policies/password/{name} " + f" policy=@/tmp/{name}.hcl'" + ) + self._run_command(cmd, attempts=3) + + +def run(module): + """Main ansible module entry point""" + results = dict(changed=False) + + args = module.params + + vault_policies = args.get("vault_policies", {}) + parsed_secrets = args.get("parsed_secrets", {}) + namespace = args.get("namespace", "vault") + pod = args.get("pod", "vault-0") + + if vault_policies == {}: + results["failed"] = True + module.fail_json("Must pass vault_policies") + + if parsed_secrets == {}: + results["failed"] = True + module.fail_json("Must pass parsed_secrets") + + loader = VaultSecretLoader( + module, + parsed_secrets, + vault_policies, + namespace, + pod, + ) + + nr_secrets = loader.load_vault() + + results["failed"] = False + results["changed"] = True + results["msg"] = f"{nr_secrets} secrets injected" + module.exit_json(**results) + + +def main(): + """Main entry point where the AnsibleModule class is instantiated""" + module = AnsibleModule( + argument_spec=yaml.safe_load(DOCUMENTATION)["options"], + supports_check_mode=False, + ) + run(module) + + +if __name__ == "__main__": + main() diff --git a/ansible/roles/cluster_pre_check/defaults/main.yml b/ansible/roles/cluster_pre_check/defaults/main.yml new file mode 100644 index 00000000..fd6cdd5c --- /dev/null +++ b/ansible/roles/cluster_pre_check/defaults/main.yml @@ -0,0 +1,3 @@ +--- +kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" +kubeconfig_backup: "{{ lookup('env', 'HOME') }}/.kube/config" diff --git a/ansible/roles/vault_utils/tasks/pre_check.yaml b/ansible/roles/cluster_pre_check/tasks/main.yml similarity index 100% rename from ansible/roles/vault_utils/tasks/pre_check.yaml rename to ansible/roles/cluster_pre_check/tasks/main.yml diff --git a/ansible/roles/find_vp_secrets/tasks/main.yml b/ansible/roles/find_vp_secrets/tasks/main.yml new file mode 100644 index 00000000..ce847a01 --- /dev/null +++ b/ansible/roles/find_vp_secrets/tasks/main.yml @@ -0,0 +1,87 @@ +--- +# Once V1 support is dropped we can remove the whole secret_template support +- name: Set secret_template fact + no_log: "{{ override_no_log | default(true) }}" + ansible.builtin.set_fact: + secret_template: "{{ pattern_dir }}/values-secret.yaml.template" + +- name: Is a VALUES_SECRET env variable set? + ansible.builtin.set_fact: + custom_env_values_secret: "{{ lookup('ansible.builtin.env', 'VALUES_SECRET') }}" + +- name: Check if VALUES_SECRET file exists + ansible.builtin.stat: + path: "{{ custom_env_values_secret }}" + register: custom_file_values_secret + when: custom_env_values_secret | default('') | length > 0 + +- name: Set values-secret yaml file to {{ custom_file_values_secret.stat.path }} + ansible.builtin.set_fact: + found_file: "{{ custom_file_values_secret.stat.path }}" + when: + - custom_env_values_secret | default('') | length > 0 + - custom_file_values_secret.stat.exists + +# FIXME(bandini): Eventually around end of 2023(?) we should drop +# ~/values-secret-{{ pattern_name }}.yaml and ~/values-secret.yaml +- name: Find first existing values-secret yaml file + ansible.builtin.set_fact: + found_file: "{{ lookup('ansible.builtin.first_found', findme) }}" + vars: + findme: + - "~/.config/hybrid-cloud-patterns/values-secret-{{ pattern_name }}.yaml" + - "~/.config/validated-patterns/values-secret-{{ pattern_name }}.yaml" + - "~/values-secret-{{ pattern_name }}.yaml" + - "~/values-secret.yaml" + - "{{ pattern_dir }}/values-secret.yaml.template" + when: custom_env_values_secret | default('') | length == 0 + +- name: Is found values secret file encrypted + no_log: "{{ override_no_log | default(true) }}" + ansible.builtin.shell: | + set -o pipefail + head -1 "{{ found_file }}" | grep -q \$ANSIBLE_VAULT + changed_when: false + register: encrypted + failed_when: (encrypted.rc not in [0, 1]) + +# When HOME is set we replace it with '~' in this debug message +# because when run from inside the container the HOME is /pattern-home +# which is confusing for users +- name: Is found values secret file encrypted + ansible.builtin.debug: + msg: "Using {{ (lookup('env', 'HOME') | length > 0) | ternary(found_file | regex_replace('^' + lookup('env', 'HOME'), '~'), found_file) }} to parse secrets" + +- name: Set encryption bool fact + no_log: "{{ override_no_log | default(true) }}" + ansible.builtin.set_fact: + is_encrypted: "{{ encrypted.rc == 0 | bool }}" + +- name: Get password for "{{ found_file }}" + ansible.builtin.pause: + prompt: "Input the password for {{ found_file }}" + echo: false + when: is_encrypted + register: vault_pass + +- name: Get decrypted content if {{ found_file }} was encrypted + no_log: "{{ override_no_log | default(true) }}" + ansible.builtin.shell: + ansible-vault view --vault-password-file <(cat <<<"{{ vault_pass.user_input }}") "{{ found_file }}" + register: values_secret_plaintext + when: is_encrypted + changed_when: false + +- name: Normalize secrets format (un-encrypted) + no_log: '{{ override_no_log | default(true) }}' + ansible.builtin.set_fact: + values_secrets_data: "{{ lookup('file', found_file) | from_yaml }}" + when: not is_encrypted + changed_when: false + +- name: Normalize secrets format (encrypted) + no_log: '{{ override_no_log | default(true) }}' + ansible.builtin.set_fact: + values_secrets_data: "{{ values_secret_plaintext.stdout | from_yaml }}" + when: is_encrypted + changed_when: false diff --git a/ansible/roles/k8s_secret_utils/defaults/main.yml b/ansible/roles/k8s_secret_utils/defaults/main.yml new file mode 100644 index 00000000..7ebda207 --- /dev/null +++ b/ansible/roles/k8s_secret_utils/defaults/main.yml @@ -0,0 +1,2 @@ +--- +secrets_ns: 'validated-patterns-secrets' diff --git a/ansible/roles/k8s_secret_utils/tasks/inject_k8s_secret.yml b/ansible/roles/k8s_secret_utils/tasks/inject_k8s_secret.yml new file mode 100644 index 00000000..283fb6a2 --- /dev/null +++ b/ansible/roles/k8s_secret_utils/tasks/inject_k8s_secret.yml @@ -0,0 +1,15 @@ +--- +- name: Check for secrets namespace + no_log: false + kubernetes.core.k8s_info: + kind: Namespace + name: "{{ item['metadata']['namespace'] }}" + register: secrets_ns_rc + until: secrets_ns_rc.resources | length > 0 + retries: 20 + delay: 45 + +- name: Inject k8s secret + no_log: '{{ override_no_log | default(True) }}' + kubernetes.core.k8s: + definition: '{{ item }}' diff --git a/ansible/roles/k8s_secret_utils/tasks/inject_k8s_secrets.yml b/ansible/roles/k8s_secret_utils/tasks/inject_k8s_secrets.yml new file mode 100644 index 00000000..a2299734 --- /dev/null +++ b/ansible/roles/k8s_secret_utils/tasks/inject_k8s_secrets.yml @@ -0,0 +1,5 @@ +--- +- name: Inject secrets + no_log: '{{ override_no_log | default(True) }}' + ansible.builtin.include_tasks: inject_k8s_secret.yml + loop: '{{ kubernetes_secret_objects }}' diff --git a/ansible/roles/k8s_secret_utils/tasks/main.yml b/ansible/roles/k8s_secret_utils/tasks/main.yml new file mode 100644 index 00000000..d72de7ae --- /dev/null +++ b/ansible/roles/k8s_secret_utils/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- name: Parse and extract k8s secrets from values-secret file + ansible.builtin.include_tasks: parse_secrets.yml + +- name: Inject k8s secrets + ansible.builtin.include_tasks: inject_k8s_secrets.yml diff --git a/ansible/roles/k8s_secret_utils/tasks/parse_secrets.yml b/ansible/roles/k8s_secret_utils/tasks/parse_secrets.yml new file mode 100644 index 00000000..b1755cc2 --- /dev/null +++ b/ansible/roles/k8s_secret_utils/tasks/parse_secrets.yml @@ -0,0 +1,12 @@ +--- +- name: Parse secrets data + # no_log: '{{ override_no_log | default(true) }}' + parse_secrets_info: + values_secrets_plaintext: "{{ values_secrets_data }}" + secrets_backing_store: "{{ secrets_backing_store }}" + register: secrets_results + +- name: Return kubernetes objects + no_log: '{{ override_no_log | default(true) }}' + ansible.builtin.set_fact: + kubernetes_secret_objects: "{{ secrets_results['kubernetes_secret_objects'] }}" diff --git a/ansible/roles/vault_utils/tasks/push_parsed_secrets.yaml b/ansible/roles/vault_utils/tasks/push_parsed_secrets.yaml new file mode 100644 index 00000000..cbca15e0 --- /dev/null +++ b/ansible/roles/vault_utils/tasks/push_parsed_secrets.yaml @@ -0,0 +1,43 @@ +--- +- name: "Do pre-checks for Vault" + ansible.builtin.include_role: + name: vault_utils + tasks_from: vault_status + +# Unfortunately we cannot loop vault_status and just check if the vault is unsealed +# https://github.com/ansible/proposals/issues/136 +# So here we keep running the 'vault status' command until sealed is set to false +- name: If the vault is still sealed we need to retry + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: vault status -format=json + register: vault_status_json + until: "'stdout' in vault_status_json and (not (vault_status_json.stdout | from_json)['sealed'] | bool)" + retries: 20 + delay: 45 + failed_when: "'stdout_lines' not in vault_status_json" + +# This step is not really needed when running make vault-init + load-secrets as +# everything is sequential +# It is needed when the vault is unsealed/configured inside the cluster and load-secrets +# gets run *while* the cronjob configures the vault. I.e. it might be half configured and return +# errors +- name: Make sure that the vault auth policy exists + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: + sh -c "vault list auth/{{ vault_hub }}/role | grep '{{ vault_hub }}-role'" + register: vault_role_cmd + until: + - vault_role_cmd.rc is defined + - vault_role_cmd.rc == 0 + retries: 20 + delay: 45 + changed_when: false + +- name: Load parsed secrets into cluster vault + vault_load_parsed_secrets: + vault_policies: "{{ vault_policies }}" + parsed_secrets: "{{ parsed_secrets }}" diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 31d2878b..7954dc47 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -1,6 +1,4 @@ --- -- name: Vault pre checks - ansible.builtin.include_tasks: pre_check.yaml - name: Vault status check ansible.builtin.include_tasks: vault_status.yaml diff --git a/ansible/roles/vault_utils/tasks/vault_init.yaml b/ansible/roles/vault_utils/tasks/vault_init.yaml index 16ce73df..38e1e911 100644 --- a/ansible/roles/vault_utils/tasks/vault_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_init.yaml @@ -1,6 +1,4 @@ --- -- name: Vault pre checks - ansible.builtin.include_tasks: pre_check.yaml - name: Vault status check ansible.builtin.include_tasks: vault_status.yaml diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 7e0741aa..35327d58 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -1,7 +1,4 @@ --- -- name: Vault pre checks - ansible.builtin.include_tasks: pre_check.yaml - - name: Is secrets backend already enabled kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" diff --git a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml index d4310e7f..e930252a 100644 --- a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml @@ -1,7 +1,4 @@ --- -- name: Vault pre checks - ansible.builtin.include_tasks: pre_check.yaml - - name: Find managed clusters kubernetes.core.k8s_info: kind: ManagedCluster diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index 862f19d8..43232ac7 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -1,6 +1,4 @@ --- -- name: Vault pre checks - ansible.builtin.include_tasks: pre_check.yaml - name: Vault status check ansible.builtin.include_tasks: vault_status.yaml diff --git a/ansible/roles/vault_utils/values-secrets.v2.schema.json b/ansible/roles/vault_utils/values-secrets.v2.schema.json index c9723d6f..c8b5c020 100644 --- a/ansible/roles/vault_utils/values-secrets.v2.schema.json +++ b/ansible/roles/vault_utils/values-secrets.v2.schema.json @@ -10,7 +10,7 @@ "title": "Hybrid Cloud Patterns - values-secret.yaml files schema V2", "description": "This schema defines the values-secret.yaml file as used by [Validated Patterns](https://hybrid-cloud-patterns.io)", "type": "object", - "examples": [ + "examples": [ { "version": "2.0", "backingStore": "vault", @@ -105,6 +105,19 @@ "$ref": "#/definitions/VaultPolicies", "description": "A dictionary of {name}:{policy} of custom vault password policies" }, + "secretStoreNamespace": { + "type": "string", + "description": "Namespace to store secrets in for kubernetes loader", + "default": "validated-patterns-secrets" + }, + "defaultLabels": { + "type": "object", + "description": "Default labels to add to secret objects for kubernetes loader" + }, + "defaultAnnotations": { + "type": "object", + "description": "Default labels to add to secret objects for kubernetes loader" + }, "secrets": { "$ref": "#/definitions/Secrets", "description": "The list of actual secrets to be uploaded in the vault" @@ -166,6 +179,23 @@ }, "default": [ "hub" ] }, + "targetNamespaces": { + "type": "array", + "description": "The namespace(s) that the secret will be injected into, ignored by configs using ESO", + "items": { + "type": "string", + "minItems": 1, + "uniqueItems": true + } + }, + "annotations": { + "type": "object", + "description": "Annotations to add to the kubernetes secret object, which override defaults" + }, + "labels": { + "type": "object", + "description": "Labels to add to the kubernetes secret object, which override defaults" + }, "fields": { "type": "array", "description": "This is the list of actual secret material that will be placed in a vault key's attributes", diff --git a/ansible/tests/unit/test_parse_secrets.py b/ansible/tests/unit/test_parse_secrets.py new file mode 100644 index 00000000..0cfef1b6 --- /dev/null +++ b/ansible/tests/unit/test_parse_secrets.py @@ -0,0 +1,981 @@ +# Copyright 2022, 2023 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Simple module to test parse_secret_info +""" + +import base64 +import configparser +import json +import os +import sys +import unittest +from unittest import mock +from unittest.mock import patch + +from ansible.module_utils import basic +from ansible.module_utils.common.text.converters import to_bytes +from test_util_datastructures import ( + DEFAULT_KUBERNETES_METADATA, + DEFAULT_KUBERNETES_SECRET_OBJECT, + DEFAULT_PARSED_SECRET_VALUE, + DEFAULT_VAULT_POLICIES, +) + +# from unittest.mock import call, patch + +# TODO(bandini): I could not come up with something better to force the imports to be existing +# when we "import parse_secrets_info" +sys.path.insert(1, "./ansible/plugins/module_utils") +sys.path.insert(1, "./ansible/plugins/modules") + +import load_secrets_common # noqa: E402 + +sys.modules["ansible.module_utils.load_secrets_common"] = load_secrets_common + +import parse_secrets_v2 # noqa: E402 + +sys.modules["ansible.module_utils.parse_secrets_v2"] = parse_secrets_v2 + +import parse_secrets_info # noqa: E402 + +sys.modules["ansible.modules.parse_secrets_info"] = parse_secrets_info + + +def set_module_args(args): + """prepare arguments so that they will be picked up during module creation""" + args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class BytesEncoder(json.JSONEncoder): + def default(self, o): + if isinstance(o, bytes): + return base64.b64encode(o).decode("ascii") + else: + return super().default(o) + + +def json_str(a): + return json.dumps(a, sort_keys=True, cls=BytesEncoder) + + +def ds_eq(a, b): + """ + This function takes two arbitrary data structures, sorts their keys, stringifies them into JSON + and compares them. The idea here is to test data structure difference without having to write + an involved recursive data structure parser. If the function returns true, the two data + structures are equal. + """ + print("a=" + json_str(a)) + print("b=" + json_str(b)) + return json_str(a) == json_str(b) + + +class AnsibleExitJson(Exception): + """Exception class to be raised by module.exit_json and caught by the test case""" + + pass + + +class AnsibleFailJson(Exception): + """Exception class to be raised by module.fail_json and caught by the test case""" + + pass + + +def exit_json(*args, **kwargs): + """function to patch over exit_json; package return data into an exception""" + if "changed" not in kwargs: + kwargs["changed"] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + """function to patch over fail_json; package return data into an exception""" + kwargs["failed"] = True + kwargs["args"] = args + raise AnsibleFailJson(kwargs) + + +@mock.patch("getpass.getpass") +class TestMyModule(unittest.TestCase): + def create_inifile(self): + self.inifile = open("/tmp/awscredentials", "w") + config = configparser.ConfigParser() + config["default"] = { + "aws_access_key_id": "123123", + "aws_secret_access_key": "abcdefghi", + } + config["foobar"] = { + "aws_access_key_id": "345345", + "aws_secret_access_key": "rstuvwxyz", + } + with self.inifile as configfile: + config.write(configfile) + + def create_testbinfile(self): + with open(self.binfilename, "wb") as f: + f.write(bytes([8, 6, 7, 5, 3, 0, 9])) + f.close() + + def setUp(self): + self.binfilename = "/tmp/testbinfile.bin" + self.mock_module_helper = patch.multiple( + basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json + ) + self.mock_module_helper.start() + self.addCleanup(self.mock_module_helper.stop) + self.testdir_v2 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v2") + self.testfile = open("/tmp/ca.crt", "w") + self.create_inifile() + self.create_testbinfile() + # For ~/expanduser tests + self.orig_home = os.environ["HOME"] + os.environ["HOME"] = self.testdir_v2 + + def tearDown(self): + os.environ["HOME"] = self.orig_home + self.testfile.close() + try: + os.remove("/tmp/ca.crt") + os.remove(self.binfilename) + # os.remove("/tmp/awscredentials") + except OSError: + pass + + def get_file_as_stdout(self, filename, openmode="r"): + with open(filename, mode=openmode, encoding="utf-8") as f: + return f.read() + + def test_module_fail_when_required_args_missing(self, getpass): + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + parse_secrets_info.main() + + def test_module_parse_base(self, getpass): + getpass.return_value = "/tmp/ca.crt" + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-base.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + } + ) + parse_secrets_info.main() + + ret = result.exception.args[0] + self.assertTrue( + (ret["failed"] is False) + and (ret["changed"] is False) + and (len(ret["parsed_secrets"])) == 1 + and (len(ret["kubernetes_secret_objects"]) == 0) + ) + + def test_module_parse_base_parsed_secrets(self, getpass): + getpass.return_value = "/tmp/ca.crt" + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-base.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + } + ) + parse_secrets_info.main() + + vp = DEFAULT_VAULT_POLICIES | { + "basicPolicy": 'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n', # noqa: E501 + "advancedPolicy": 'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n', # noqa: E501 + } + + # Beware reading this structure aloud to your cat... + pspsps = { + "config-demo": DEFAULT_PARSED_SECRET_VALUE + | { + "name": "config-demo", + "fields": { + "secret": None, + "secret2": "/tmp/ca.crt", + "ca_crt": "", + "ca_crt2": "", + }, + "base64": ["ca_crt2"], + "generate": ["secret"], + "override": ["secret"], + "vault_policies": { + "secret": "basicPolicy", + }, + "vault_prefixes": [ + "region-one", + "snowflake.blueprints.rhecoeng.com", + ], + "paths": { + "ca_crt": "/tmp/ca.crt", + "ca_crt2": "/tmp/ca.crt", + }, + }, + } + + ret = result.exception.args[0] + self.assertTrue( + (ret["failed"] is False) + and (ret["changed"] is False) + and (ds_eq(vp, ret["vault_policies"])) + and (ds_eq(pspsps, ret["parsed_secrets"])) + ) + + def test_module_parsed_secret_ini_files(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-ini-file.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + } + ) + parse_secrets_info.main() + + ps = { + "aws": DEFAULT_PARSED_SECRET_VALUE + | { + "name": "aws", + "fields": { + "aws_access_key_id": "123123", + "aws_secret_access_key": "abcdefghi", + }, + "ini_file": { + "aws_access_key_id": { + "ini_file": "/tmp/awscredentials", + "ini_section": "default", + "ini_key": "aws_access_key_id", + }, + "aws_secret_access_key": { + "ini_file": "/tmp/awscredentials", + "ini_section": "default", + "ini_key": "aws_secret_access_key", + }, + }, + }, + "awsfoobar": DEFAULT_PARSED_SECRET_VALUE + | { + "name": "awsfoobar", + "fields": { + "aws_access_key_id": "345345", + "aws_secret_access_key": "rstuvwxyz", + }, + "ini_file": { + "aws_access_key_id": { + "ini_file": "/tmp/awscredentials", + "ini_section": "foobar", + "ini_key": "aws_access_key_id", + }, + "aws_secret_access_key": { + "ini_file": "/tmp/awscredentials", + "ini_section": "foobar", + "ini_key": "aws_secret_access_key", + }, + }, + }, + } + + ret = result.exception.args[0] + self.assertTrue( + (ret["failed"] is False) + and (ret["changed"] is False) + and (len(ret["parsed_secrets"]) == 2) + and (ds_eq(ps, ret["parsed_secrets"])) + ) + + def test_module_parsed_secret_ini_files_base64(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-ini-file-b64.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + } + ) + parse_secrets_info.main() + + ps = { + "aws": DEFAULT_PARSED_SECRET_VALUE + | { + "name": "aws", + "fields": { + "aws_access_key_id": "A123456789012345678A", + "aws_secret_access_key": "A12345678901234567890123456789012345678A", + }, + "ini_file": { + "aws_access_key_id": { + "ini_file": f"{os.environ['HOME']}/aws-example.ini", + "ini_section": "default", + "ini_key": "aws_access_key_id", + }, + "aws_secret_access_key": { + "ini_file": f"{os.environ['HOME']}/aws-example.ini", + "ini_section": "default", + "ini_key": "aws_secret_access_key", + }, + }, + }, + "awsb64": DEFAULT_PARSED_SECRET_VALUE + | { + "name": "awsb64", + "fields": { + "aws_access_key_id": "QTEyMzQ1Njc4OTAxMjM0NTY3OEE=", + "aws_secret_access_key": "QTEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4QQ==", + }, + "base64": [ + "aws_access_key_id", + "aws_secret_access_key", + ], + "ini_file": { + "aws_access_key_id": { + "ini_file": f"{os.environ['HOME']}/aws-example.ini", + "ini_section": "default", + "ini_key": "aws_access_key_id", + }, + "aws_secret_access_key": { + "ini_file": f"{os.environ['HOME']}/aws-example.ini", + "ini_section": "default", + "ini_key": "aws_secret_access_key", + }, + }, + }, + } + + ret = result.exception.args[0] + self.assertTrue( + (ret["failed"] is False) + and (ret["changed"] is False) + and (len(ret["parsed_secrets"]) == 2) + and (len(ret["kubernetes_secret_objects"]) == 0) + and (ds_eq(ps, ret["parsed_secrets"])) + ) + + def test_module_parsed_secret_ini_files_base64_kubernetes(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-ini-file-b64.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "kubernetes", + } + ) + parse_secrets_info.main() + + ps = { + "aws": DEFAULT_PARSED_SECRET_VALUE + | { + "name": "aws", + "fields": { + "aws_access_key_id": "A123456789012345678A", + "aws_secret_access_key": "A12345678901234567890123456789012345678A", + }, + "ini_file": { + "aws_access_key_id": { + "ini_file": f"{os.environ['HOME']}/aws-example.ini", + "ini_section": "default", + "ini_key": "aws_access_key_id", + }, + "aws_secret_access_key": { + "ini_file": f"{os.environ['HOME']}/aws-example.ini", + "ini_section": "default", + "ini_key": "aws_secret_access_key", + }, + }, + }, + "awsb64": DEFAULT_PARSED_SECRET_VALUE + | { + "name": "awsb64", + "fields": { + "aws_access_key_id": "QTEyMzQ1Njc4OTAxMjM0NTY3OEE=", + "aws_secret_access_key": "QTEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4QQ==", + }, + "base64": [ + "aws_access_key_id", + "aws_secret_access_key", + ], + "ini_file": { + "aws_access_key_id": { + "ini_file": f"{os.environ['HOME']}/aws-example.ini", + "ini_section": "default", + "ini_key": "aws_access_key_id", + }, + "aws_secret_access_key": { + "ini_file": f"{os.environ['HOME']}/aws-example.ini", + "ini_section": "default", + "ini_key": "aws_secret_access_key", + }, + }, + }, + } + + ret = result.exception.args[0] + self.assertTrue( + (ret["failed"] is False) + and (ret["changed"] is False) + and (len(ret["parsed_secrets"]) == 2) + and (len(ret["kubernetes_secret_objects"]) == 2) + and (ds_eq(ps, ret["parsed_secrets"])) + ) + + def test_module_default_labels(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-default-labels.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "kubernetes", + } + ) + parse_secrets_info.main() + + ret = result.exception.args[0] + self.assertTrue( + ds_eq( + ret["kubernetes_secret_objects"][0], + DEFAULT_KUBERNETES_SECRET_OBJECT + | { + "metadata": DEFAULT_KUBERNETES_METADATA + | { + "name": "test-secret", + "labels": {"testlabel": "4"}, + "namespace": "validated-patterns-secrets", + }, + "stringData": {"username": "user"}, + }, + ) + ) + + def test_module_override_labels(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-override-labels.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "kubernetes", + } + ) + parse_secrets_info.main() + ret = result.exception.args[0] + self.assertTrue( + ds_eq( + ret["kubernetes_secret_objects"][0], + DEFAULT_KUBERNETES_SECRET_OBJECT + | { + "metadata": DEFAULT_KUBERNETES_METADATA + | { + "name": "test-secret", + "labels": {"overridelabel": "42"}, + }, + "stringData": {"username": "user"}, + }, + ) + ) + + def test_module_override_namespace(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-override-namespace.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "kubernetes", + } + ) + parse_secrets_info.main() + ret = result.exception.args[0] + self.assertTrue( + len(ret["kubernetes_secret_objects"]) == 1 + and ds_eq( + ret["kubernetes_secret_objects"][0], + DEFAULT_KUBERNETES_SECRET_OBJECT + | { + "metadata": DEFAULT_KUBERNETES_METADATA + | { + "name": "test-secret", + "namespace": "overridden-namespace", + }, + "stringData": {"username": "user"}, + }, + ) + ) + + def test_module_none_extra_namespaces(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-more-namespaces.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "none", + } + ) + parse_secrets_info.main() + ret = result.exception.args[0] + self.assertTrue( + len(ret["kubernetes_secret_objects"]) == 2 + and ds_eq( + ret["kubernetes_secret_objects"][0], + DEFAULT_KUBERNETES_SECRET_OBJECT + | { + "metadata": DEFAULT_KUBERNETES_METADATA + | { + "name": "test-secret", + "namespace": "default", + }, + "stringData": {"username": "user"}, + }, + ) + and ds_eq( + ret["kubernetes_secret_objects"][1], + DEFAULT_KUBERNETES_SECRET_OBJECT + | { + "metadata": DEFAULT_KUBERNETES_METADATA + | { + "name": "test-secret", + "namespace": "extra", + }, + "stringData": {"username": "user"}, + }, + ) + ) + + def test_module_override_type_kubernetes(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-override-type.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "kubernetes", + } + ) + parse_secrets_info.main() + ret = result.exception.args[0] + self.assertTrue( + len(ret["kubernetes_secret_objects"]) == 1 + and ds_eq( + ret["kubernetes_secret_objects"][0], + DEFAULT_KUBERNETES_SECRET_OBJECT + | { + "type": "user-specified", + "metadata": DEFAULT_KUBERNETES_METADATA + | { + "name": "test-secret", + }, + "stringData": {"username": "user"}, + }, + ) + ) + + def test_module_override_type_none(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-override-type-none.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "none", + } + ) + parse_secrets_info.main() + ret = result.exception.args[0] + self.assertTrue( + len(ret["kubernetes_secret_objects"]) == 1 + and ds_eq( + ret["kubernetes_secret_objects"][0], + DEFAULT_KUBERNETES_SECRET_OBJECT + | { + "type": "user-specified", + "metadata": DEFAULT_KUBERNETES_METADATA + | {"name": "test-secret", "namespace": "default"}, + "stringData": {"username": "user"}, + }, + ) + ) + + def test_module_secret_file_contents(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-file-contents.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "kubernetes", + } + ) + parse_secrets_info.main() + ret = result.exception.args[0] + self.assertTrue( + len(ret["kubernetes_secret_objects"]) == 1 + and ds_eq( + ret["kubernetes_secret_objects"][0], + DEFAULT_KUBERNETES_SECRET_OBJECT + | { + "metadata": DEFAULT_KUBERNETES_METADATA + | { + "name": "test-secret", + }, + "stringData": {"username": "This space intentionally left blank\n"}, + }, + ) + ) + + def test_module_secret_file_contents_b64(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-file-contents-b64.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "kubernetes", + } + ) + parse_secrets_info.main() + ret = result.exception.args[0] + self.assertTrue( + len(ret["kubernetes_secret_objects"]) == 1 + and ds_eq( + ret["kubernetes_secret_objects"][0], + DEFAULT_KUBERNETES_SECRET_OBJECT + | { + "metadata": DEFAULT_KUBERNETES_METADATA + | { + "name": "test-secret", + }, + "stringData": { + "username": "VGhpcyBzcGFjZSBpbnRlbnRpb25hbGx5IGxlZnQgYmxhbmsK" + }, + }, + ) + ) + + def test_module_secret_file_contents_double_b64(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join( + self.testdir_v2, "values-secret-v2-file-contents-double-b64.yaml" + ) + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "kubernetes", + } + ) + parse_secrets_info.main() + ret = result.exception.args[0] + self.assertTrue( + len(ret["kubernetes_secret_objects"]) == 1 + and ds_eq( + ret["kubernetes_secret_objects"][0], + DEFAULT_KUBERNETES_SECRET_OBJECT + | { + "metadata": DEFAULT_KUBERNETES_METADATA + | { + "name": "test-secret", + }, + "stringData": { + "username": "VkdocGN5QnpjR0ZqWlNCcGJuUmxiblJwYjI1aGJHeDVJR3hsWm5RZ1lteGhibXNL" + }, + }, + ) + ) + + def test_module_secret_file_contents_binary_b64(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-secret-binary-b64.yaml") + ) + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "kubernetes", + } + ) + parse_secrets_info.main() + ret = result.exception.args[0] + + # The binary bytes are [ 8, 6, 7, 5, 3, 0, 9 ] (IYKYK) + self.assertTrue( + len(ret["kubernetes_secret_objects"]) == 1 + and ds_eq( + ret["kubernetes_secret_objects"][0], + DEFAULT_KUBERNETES_SECRET_OBJECT + | { + "metadata": DEFAULT_KUBERNETES_METADATA + | { + "name": "secret", + }, + "stringData": {"secret": "CAYHBQMACQ=="}, + }, + ) + ) + + def test_ensure_success_retrieving_block_yaml_policy(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-defaultvp-policy.yaml") + ) + with self.assertRaises(AnsibleExitJson) as ansible_err: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "vault", + } + ) + parse_secrets_info.main() + + ret = ansible_err.exception.args[0] + self.assertTrue( + ds_eq( + ret["vault_policies"], + { + "basicPolicy": 'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n', # noqa: E501 + "validatedPatternDefaultPolicy": 'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n', # noqa: E501 + }, + ) + ) + + def test_ensure_success_retrieving_block_yaml_value(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-block-yamlstring.yaml") + ) + with self.assertRaises(AnsibleExitJson) as ansible_err: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "vault", + } + ) + parse_secrets_info.main() + + ret = ansible_err.exception.args[0] + self.assertTrue( + ds_eq( + ret["parsed_secrets"], + { + "config-demo": DEFAULT_PARSED_SECRET_VALUE + | { + "fields": { + "sshprivkey": "ssh-rsa oNb/kAvwdQl+FKdwzzKo5rnGIB68UOxWoaKPnKdgF/ts67CDBslWGnpUZCpp8TdaxfHmpoyA6nutMwQw8OAMEUybxvilDn+ZVJ/5qgfRBdi8wLKRLTIj0v+ZW7erN9yuZG53xUQAaQjivM3cRyNLIZ9torShYaYwD1UTTDkV97RMfNDlWI5f5FGRvfy429ZfCwbUWUbijrcv/mWc/uO3x/+MBXwa4f8ubzEYlrt4yH/Vbpzs67kE9UJ9z1zurFUFJydy1ZDAdKSiBS91ImI3ccKnbz0lji2bgSYR0Wp1IQhzSpjyJU2rIu9HAEUh85Rwf2jakfLpMcg/hSBer3sG kilroy@example.com", # noqa: E501 + "sshpubkey": "-----BEGIN OPENSSH PRIVATE KEY-----\nTtzxGgWrNerAr1hzUqPW2xphF/Aur1rQXSLv4J7frEJxNED6u/eScsNgwJMGXwRx7QYVohh0ARHVhJdUzJK7pEIphi4BGw==\nwlo+oQsi828b47SKZB8/K9dbeLlLiXh9/hu47MGpeGHZsKbjAdauncuw+YUDDN2EADJjasNMZHjxYhXKtqDjXTIw1X1n0Q==\n-----END OPENSSH PRIVATE KEY-----", # noqa: E501 + }, + "name": "config-demo", + } + }, + ) + ) + + def test_ensure_kubernetes_object_block_yaml_value(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-block-yamlstring.yaml") + ) + with self.assertRaises(AnsibleExitJson) as ansible_err: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "kubernetes", + } + ) + parse_secrets_info.main() + + ret = ansible_err.exception.args[0] + self.assertTrue( + ds_eq( + ret["kubernetes_secret_objects"][0], + DEFAULT_KUBERNETES_SECRET_OBJECT + | { + "metadata": DEFAULT_KUBERNETES_METADATA + | { + "name": "config-demo", + }, + "stringData": { + "sshprivkey": "ssh-rsa oNb/kAvwdQl+FKdwzzKo5rnGIB68UOxWoaKPnKdgF/ts67CDBslWGnpUZCpp8TdaxfHmpoyA6nutMwQw8OAMEUybxvilDn+ZVJ/5qgfRBdi8wLKRLTIj0v+ZW7erN9yuZG53xUQAaQjivM3cRyNLIZ9torShYaYwD1UTTDkV97RMfNDlWI5f5FGRvfy429ZfCwbUWUbijrcv/mWc/uO3x/+MBXwa4f8ubzEYlrt4yH/Vbpzs67kE9UJ9z1zurFUFJydy1ZDAdKSiBS91ImI3ccKnbz0lji2bgSYR0Wp1IQhzSpjyJU2rIu9HAEUh85Rwf2jakfLpMcg/hSBer3sG kilroy@example.com", # noqa: E501 + "sshpubkey": "-----BEGIN OPENSSH PRIVATE KEY-----\nTtzxGgWrNerAr1hzUqPW2xphF/Aur1rQXSLv4J7frEJxNED6u/eScsNgwJMGXwRx7QYVohh0ARHVhJdUzJK7pEIphi4BGw==\nwlo+oQsi828b47SKZB8/K9dbeLlLiXh9/hu47MGpeGHZsKbjAdauncuw+YUDDN2EADJjasNMZHjxYhXKtqDjXTIw1X1n0Q==\n-----END OPENSSH PRIVATE KEY-----", # noqa: E501 + }, + }, + ) + ) + + def test_ensure_kubernetes_backend_allowed(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-base-k8s-backend.yaml") + ) + with self.assertRaises(AnsibleExitJson) as ansible_err: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "kubernetes", + } + ) + parse_secrets_info.main() + + ret = ansible_err.exception.args[0] + self.assertFalse(ret["failed"]) + + def test_ensure_none_backend_allowed(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-base-none-backend.yaml") + ) + with self.assertRaises(AnsibleExitJson) as ansible_err: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "none", + } + ) + parse_secrets_info.main() + + ret = ansible_err.exception.args[0] + self.assertFalse(ret["failed"]) + + def test_ensure_error_conflicting_backends(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-base-k8s-backend.yaml") + ) + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "vault", + } + ) + parse_secrets_info.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert ( + ret["args"][1] + == "Secrets file specifies 'kubernetes' backend but pattern config specifies 'vault'." + ) + + def test_ensure_error_unknown_backends(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-base-unknown-backend.yaml") + ) + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "unknown", + } + ) + parse_secrets_info.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert ( + ret["args"][1] + == "Currently only the 'vault', 'kubernetes' and 'none' backingStores are supported: unknown" + ) + + def test_ensure_error_secrets_same_name(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-same-secret-names.yaml") + ) + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + } + ) + parse_secrets_info.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert ( + ret["args"][1] == "You cannot have duplicate secret names: ['config-demo']" + ) + + def test_ensure_error_fields_same_name(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-same-field-names.yaml") + ) + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + } + ) + parse_secrets_info.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert ret["args"][1] == "You cannot have duplicate field names: ['secret']" + + def test_ensure_generate_errors_on_kubernetes(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-generic-onlygenerate.yaml") + ) + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "kubernetes", + } + ) + parse_secrets_info.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert ( + ret["args"][1] + == "You cannot have onMissingValue set to 'generate' unless using vault backingstore for secret config-demo field secret" # noqa: E501 + ) + + def test_ensure_generate_errors_on_none_generate(self, getpass): + testfile_output = self.get_file_as_stdout( + os.path.join(self.testdir_v2, "values-secret-v2-generic-onlygenerate.yaml") + ) + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets_plaintext": testfile_output, + "secrets_backing_store": "none", + } + ) + parse_secrets_info.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert ( + ret["args"][1] + == "You cannot have onMissingValue set to 'generate' unless using vault backingstore for secret config-demo field secret" # noqa: E501 + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/ansible/tests/unit/test_util_datastructures.py b/ansible/tests/unit/test_util_datastructures.py new file mode 100644 index 00000000..11d7cdae --- /dev/null +++ b/ansible/tests/unit/test_util_datastructures.py @@ -0,0 +1,205 @@ +DEFAULT_PARSED_SECRET_VALUE = { + "name": "overwrite-me", + "fields": {}, + "base64": [], + "ini_file": {}, + "generate": [], + "override": [], + "vault_mount": "secret", + "vault_policies": {}, + "vault_prefixes": ["hub"], + "type": "Opaque", + "target_namespaces": [], + "labels": {}, + "annotations": {}, + "paths": {}, +} + +DEFAULT_KUBERNETES_METADATA = { + "name": "overwrite-me", + "labels": {}, + "annotations": {}, + "namespace": "validated-patterns-secrets", +} +DEFAULT_KUBERNETES_SECRET_OBJECT = { + "kind": "Secret", + "type": "Opaque", + "apiVersion": "v1", + "metadata": DEFAULT_KUBERNETES_METADATA, + "stringData": {}, +} + +DEFAULT_VAULT_POLICIES = { + "validatedPatternDefaultPolicy": ( + "length=20\n" + 'rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\n' # noqa: E501 + 'rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\n' # noqa: E501 + 'rule "charset" { charset = "0123456789" min-chars = 1 }\n' + 'rule "charset" { charset = "!@#%^&*" min-chars = 1 }\n' + ), +} + +GENERATE_POLICY_B64_TEST = { + "vault_policies": { + "basicPolicy": 'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n', # noqa: E501 + "validatedPatternDefaultPolicy": 'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n', # noqa: E501 + }, + "parsed_secrets": { + "config-demo": { + "annotations": {}, + "base64": ["secret"], + "fields": {"secret": None}, + "generate": ["secret"], + "ini_file": {}, + "labels": {}, + "name": "config-demo", + "namespace": "validated-patterns-secrets", + "override": ["secret"], + "paths": {}, + "type": "Opaque", + "vault_mount": "secret", + "vault_policies": {"secret": "basicPolicy"}, + "vault_prefixes": ["region-one", "snowflake.blueprints.rhecoeng.com"], + } + }, +} + +PARSED_SECRET_VALUE_TEST = { + "parsed_secrets": { + "config-demo": { + "annotations": {}, + "base64": [], + "fields": {"secret": "value123"}, + "generate": [], + "ini_file": {}, + "labels": {}, + "name": "config-demo", + "namespace": "validated-patterns-secrets", + "override": [], + "paths": {}, + "type": "Opaque", + "vault_mount": "secret", + "vault_policies": {}, + "vault_prefixes": ["hub"], + } + }, + "vault_policies": { + "validatedPatternDefaultPolicy": 'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n' # noqa: E501 + }, +} + +PARSED_SECRET_B64_VALUE_TEST = { + "parsed_secrets": { + "config-demo": { + "annotations": {}, + "base64": ["secret"], + "fields": {"secret": "dmFsdWUxMjMK"}, + "generate": [], + "ini_file": {}, + "labels": {}, + "name": "config-demo", + "namespace": "validated-patterns-secrets", + "override": [], + "paths": {}, + "type": "Opaque", + "vault_mount": "secret", + "vault_policies": {}, + "vault_prefixes": ["hub"], + } + }, + "vault_policies": { + "validatedPatternDefaultPolicy": 'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n' # noqa: E501 + }, +} + +PARSED_SECRET_FILE_INJECTION_TEST = { + "parsed_secrets": { + "config-demo": { + "annotations": {}, + "base64": [], + "fields": {"secret": "value123"}, + "generate": [], + "ini_file": {}, + "labels": {}, + "name": "config-demo", + "namespace": "validated-patterns-secrets", + "override": [], + "paths": {}, + "type": "Opaque", + "vault_mount": "secret", + "vault_policies": {}, + "vault_prefixes": [ + "secret/region-one", + "secret/snowflake.blueprints.rhecoeng.com", + ], + }, + "config-demo-file": { + "annotations": {}, + "base64": [], + "fields": {"test": ""}, + "generate": [], + "ini_file": {}, + "labels": {}, + "name": "config-demo-file", + "namespace": "validated-patterns-secrets", + "override": [], + "paths": {"test": "/tmp/footest"}, + "type": "Opaque", + "vault_mount": "secret", + "vault_policies": {}, + "vault_prefixes": [ + "secret/region-two", + "secret/snowflake.blueprints.rhecoeng.com", + ], + }, + }, + "vault_policies": { + "validatedPatternDefaultPolicy": 'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n' # noqa: 501 + }, +} + +PARSED_SECRET_FILE_B64_INJECTION_TEST = { + "parsed_secrets": { + "config-demo": { + "annotations": {}, + "base64": [], + "fields": {"secret": "value123"}, + "generate": [], + "ini_file": {}, + "labels": {}, + "name": "config-demo", + "namespace": "validated-patterns-secrets", + "override": [], + "paths": {}, + "type": "Opaque", + "vault_mount": "secret", + "vault_policies": {}, + "vault_prefixes": [ + "secret/region-one", + "secret/snowflake.blueprints.rhecoeng.com", + ], + }, + "config-demo-file": { + "annotations": {}, + "base64": ["test"], + "fields": {"test": ""}, + "generate": [], + "ini_file": {}, + "labels": {}, + "name": "config-demo-file", + "namespace": "validated-patterns-secrets", + "override": [], + "paths": {"test": "/tmp/footest"}, + "type": "Opaque", + "vault_mount": "secret", + "vault_policies": {}, + "vault_prefixes": [ + "secret/region-two", + "secret/snowflake.blueprints.rhecoeng.com", + ], + }, + }, + "vault_policies": { + "validatedPatternDefaultPolicy": 'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n' # noqa: 501 + }, +} diff --git a/ansible/tests/unit/test_vault_load_parsed_secrets.py b/ansible/tests/unit/test_vault_load_parsed_secrets.py new file mode 100644 index 00000000..ca37de94 --- /dev/null +++ b/ansible/tests/unit/test_vault_load_parsed_secrets.py @@ -0,0 +1,320 @@ +# Copyright 2022 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Simple module to test vault_load_parsed_secrets +""" + +import json +import os +import sys +import unittest +from unittest.mock import call, patch + +import test_util_datastructures +from ansible.module_utils import basic +from ansible.module_utils.common.text.converters import to_bytes + +# TODO(bandini): I could not come up with something better to force the imports to be existing +# when we 'import vault_load_secrets' +sys.path.insert(1, "./ansible/plugins/module_utils") +sys.path.insert(1, "./ansible/plugins/modules") + +import vault_load_parsed_secrets # noqa: E402 + +sys.modules["ansible.modules.vault_load_parsed_secrets"] = vault_load_parsed_secrets + + +def set_module_args(args): + """prepare arguments so that they will be picked up during module creation""" + args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + """Exception class to be raised by module.exit_json and caught by the test case""" + + pass + + +class AnsibleFailJson(Exception): + """Exception class to be raised by module.fail_json and caught by the test case""" + + pass + + +def exit_json(*args, **kwargs): + """function to patch over exit_json; package return data into an exception""" + if "changed" not in kwargs: + kwargs["changed"] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + """function to patch over fail_json; package return data into an exception""" + kwargs["failed"] = True + kwargs["args"] = args + raise AnsibleFailJson(kwargs) + + +class TestMyModule(unittest.TestCase): + def setUp(self): + self.mock_module_helper = patch.multiple( + basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json + ) + self.mock_module_helper.start() + self.addCleanup(self.mock_module_helper.stop) + self.testdir_v2 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v2") + + def tearDown(self): + return + + def test_module_fail_when_required_args_missing(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + vault_load_parsed_secrets.main() + + # For these tests, we need the data structures that parse_secrets_info outputs. + # Several have been saved in the test_util_datastructures module for this purpose + def test_ensure_value_injection_works(self): + set_module_args( + { + "parsed_secrets": test_util_datastructures.PARSED_SECRET_VALUE_TEST[ + "parsed_secrets" + ], + "vault_policies": test_util_datastructures.PARSED_SECRET_VALUE_TEST[ + "vault_policies" + ], + } + ) + with patch.object( + vault_load_parsed_secrets.VaultSecretLoader, "_run_command" + ) as mock_run_command: + stdout = "" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_parsed_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 2 + + calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret hub/config-demo secret='value123'\"", + attempts=3, + ), + ] + print(mock_run_command.mock_calls) + mock_run_command.assert_has_calls(calls) + + def test_ensure_b64_value_injection_works(self): + set_module_args( + { + "parsed_secrets": test_util_datastructures.PARSED_SECRET_B64_VALUE_TEST[ + "parsed_secrets" + ], + "vault_policies": test_util_datastructures.PARSED_SECRET_B64_VALUE_TEST[ + "vault_policies" + ], + } + ) + with patch.object( + vault_load_parsed_secrets.VaultSecretLoader, "_run_command" + ) as mock_run_command: + stdout = "" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_parsed_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 2 + + calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret hub/config-demo secret='dmFsdWUxMjMK'\"", # noqa: E501 + attempts=3, + ), + ] + print(mock_run_command.mock_calls) + mock_run_command.assert_has_calls(calls) + + def test_ensure_file_injection_works(self): + set_module_args( + { + "parsed_secrets": test_util_datastructures.PARSED_SECRET_FILE_INJECTION_TEST[ + "parsed_secrets" + ], + "vault_policies": test_util_datastructures.PARSED_SECRET_FILE_INJECTION_TEST[ + "vault_policies" + ], + } + ) + with patch.object( + vault_load_parsed_secrets.VaultSecretLoader, "_run_command" + ) as mock_run_command: + stdout = "" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_parsed_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 5 + + calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/region-one/config-demo secret='value123'\"", # noqa: E501 + attempts=3, + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret='value123'\"", # noqa: E501 + attempts=3, + ), + call( + "cat '/tmp/footest' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv put -mount=secret secret/region-two/config-demo-file test=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, + ), + call( + "cat '/tmp/footest' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file test=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, + ), + ] + print(mock_run_command.mock_calls) + mock_run_command.assert_has_calls(calls) + + def test_ensure_file_b64_injection_works(self): + set_module_args( + { + "parsed_secrets": test_util_datastructures.PARSED_SECRET_FILE_B64_INJECTION_TEST[ + "parsed_secrets" + ], + "vault_policies": test_util_datastructures.PARSED_SECRET_FILE_B64_INJECTION_TEST[ + "vault_policies" + ], + } + ) + with patch.object( + vault_load_parsed_secrets.VaultSecretLoader, "_run_command" + ) as mock_run_command: + stdout = "" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_parsed_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 5 + + calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/region-one/config-demo secret='value123'\"", # noqa: E501 + attempts=3, + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret='value123'\"", # noqa: E501 + attempts=3, + ), + call( + "cat '/tmp/footest' | oc exec -n vault vault-0 -i -- sh -c 'cat - | base64 --wrap=0> /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv put -mount=secret secret/region-two/config-demo-file test=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, + ), + call( + "cat '/tmp/footest' | oc exec -n vault vault-0 -i -- sh -c 'cat - | base64 --wrap=0> /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file test=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + attempts=3, + ), + ] + print(mock_run_command.mock_calls) + mock_run_command.assert_has_calls(calls) + + def test_ensure_b64_generate_passwords_works(self): + set_module_args( + { + "parsed_secrets": test_util_datastructures.GENERATE_POLICY_B64_TEST[ + "parsed_secrets" + ], + "vault_policies": test_util_datastructures.GENERATE_POLICY_B64_TEST[ + "vault_policies" + ], + } + ) + with patch.object( + vault_load_parsed_secrets.VaultSecretLoader, "_run_command" + ) as mock_run_command: + stdout = "" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_parsed_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 4 + + calls = [ + call( + 'echo \'length=10\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/basicPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/basicPolicy policy=@/tmp/basicPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | base64 --wrap=0 | vault kv put -mount=secret region-one/config-demo secret=-"', # noqa: E501 + attempts=3, + ), + call( + 'oc exec -n vault vault-0 -i -- sh -c "vault read -field=password sys/policies/password/basicPolicy/generate | base64 --wrap=0 | vault kv put -mount=secret snowflake.blueprints.rhecoeng.com/config-demo secret=-"', # noqa: E501 + attempts=3, + ), + ] + print(mock_run_command.mock_calls) + mock_run_command.assert_has_calls(calls) + + +if __name__ == "__main__": + unittest.main() diff --git a/ansible/tests/unit/v2/test-file-contents b/ansible/tests/unit/v2/test-file-contents new file mode 100644 index 00000000..49c9a88c --- /dev/null +++ b/ansible/tests/unit/v2/test-file-contents @@ -0,0 +1 @@ +This space intentionally left blank diff --git a/ansible/tests/unit/v2/test-file-contents.b64 b/ansible/tests/unit/v2/test-file-contents.b64 new file mode 100644 index 00000000..da896ba7 --- /dev/null +++ b/ansible/tests/unit/v2/test-file-contents.b64 @@ -0,0 +1 @@ +VGhpcyBzcGFjZSBpbnRlbnRpb25hbGx5IGxlZnQgYmxhbmsK \ No newline at end of file diff --git a/ansible/tests/unit/v2/values-secret-v2-base-k8s-backend.yaml b/ansible/tests/unit/v2/values-secret-v2-base-k8s-backend.yaml new file mode 100644 index 00000000..7194ebc3 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-base-k8s-backend.yaml @@ -0,0 +1,9 @@ +version: "2.0" + +backingStore: kubernetes + +secrets: + - name: config-demo + fields: + - name: secret + value: secret diff --git a/ansible/tests/unit/v2/values-secret-v2-base-none-backend.yaml b/ansible/tests/unit/v2/values-secret-v2-base-none-backend.yaml new file mode 100644 index 00000000..4e1e3cd2 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-base-none-backend.yaml @@ -0,0 +1,11 @@ +version: "2.0" + +backingStore: none + +secrets: + - name: config-demo + targetNamespaces: + - default + fields: + - name: secret + value: secret diff --git a/ansible/tests/unit/v2/values-secret-v2-base-unknown-backend.yaml b/ansible/tests/unit/v2/values-secret-v2-base-unknown-backend.yaml new file mode 100644 index 00000000..e1f4c6d5 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-base-unknown-backend.yaml @@ -0,0 +1,9 @@ +version: "2.0" + +backingStore: unknown + +secrets: + - name: config-demo + fields: + - name: secret + value: secret diff --git a/ansible/tests/unit/v2/values-secret-v2-block-yamlstring.yaml b/ansible/tests/unit/v2/values-secret-v2-block-yamlstring.yaml new file mode 100644 index 00000000..84165f69 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-block-yamlstring.yaml @@ -0,0 +1,16 @@ +version: "2.0" + +secrets: + - name: config-demo + fields: + - name: sshprivkey + onMissingValue: error + value: |- + ssh-rsa oNb/kAvwdQl+FKdwzzKo5rnGIB68UOxWoaKPnKdgF/ts67CDBslWGnpUZCpp8TdaxfHmpoyA6nutMwQw8OAMEUybxvilDn+ZVJ/5qgfRBdi8wLKRLTIj0v+ZW7erN9yuZG53xUQAaQjivM3cRyNLIZ9torShYaYwD1UTTDkV97RMfNDlWI5f5FGRvfy429ZfCwbUWUbijrcv/mWc/uO3x/+MBXwa4f8ubzEYlrt4yH/Vbpzs67kE9UJ9z1zurFUFJydy1ZDAdKSiBS91ImI3ccKnbz0lji2bgSYR0Wp1IQhzSpjyJU2rIu9HAEUh85Rwf2jakfLpMcg/hSBer3sG kilroy@example.com + - name: sshpubkey + onMissingValue: error + value: |- + -----BEGIN OPENSSH PRIVATE KEY----- + TtzxGgWrNerAr1hzUqPW2xphF/Aur1rQXSLv4J7frEJxNED6u/eScsNgwJMGXwRx7QYVohh0ARHVhJdUzJK7pEIphi4BGw== + wlo+oQsi828b47SKZB8/K9dbeLlLiXh9/hu47MGpeGHZsKbjAdauncuw+YUDDN2EADJjasNMZHjxYhXKtqDjXTIw1X1n0Q== + -----END OPENSSH PRIVATE KEY----- diff --git a/ansible/tests/unit/v2/values-secret-v2-default-annotations.yaml b/ansible/tests/unit/v2/values-secret-v2-default-annotations.yaml new file mode 100644 index 00000000..af3e2f9b --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-default-annotations.yaml @@ -0,0 +1,13 @@ +--- +version: "2.0" + +annotations: + test-annotation: 42 + +secrets: + - name: test-secret + fields: + - name: username + value: user + - name: password + value: testpass diff --git a/ansible/tests/unit/v2/values-secret-v2-default-labels.yaml b/ansible/tests/unit/v2/values-secret-v2-default-labels.yaml new file mode 100644 index 00000000..56af6586 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-default-labels.yaml @@ -0,0 +1,11 @@ +--- +version: "2.0" + +defaultLabels: + testlabel: 4 + +secrets: + - name: test-secret + fields: + - name: username + value: user diff --git a/ansible/tests/unit/v2/values-secret-v2-default-namespace.yaml b/ansible/tests/unit/v2/values-secret-v2-default-namespace.yaml new file mode 100644 index 00000000..a0f4db63 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-default-namespace.yaml @@ -0,0 +1,8 @@ +--- +version: "2.0" + +secrets: + test-secret: + fields: + - name: username + value: user diff --git a/ansible/tests/unit/v2/values-secret-v2-file-contents-b64.yaml b/ansible/tests/unit/v2/values-secret-v2-file-contents-b64.yaml new file mode 100644 index 00000000..47ed7219 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-file-contents-b64.yaml @@ -0,0 +1,9 @@ +--- +version: "2.0" + +secrets: + - name: test-secret + fields: + - name: username + path: ~/test-file-contents + base64: true diff --git a/ansible/tests/unit/v2/values-secret-v2-file-contents-double-b64.yaml b/ansible/tests/unit/v2/values-secret-v2-file-contents-double-b64.yaml new file mode 100644 index 00000000..3a968eca --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-file-contents-double-b64.yaml @@ -0,0 +1,9 @@ +--- +version: "2.0" + +secrets: + - name: test-secret + fields: + - name: username + path: ~/test-file-contents.b64 + base64: true diff --git a/ansible/tests/unit/v2/values-secret-v2-file-contents.yaml b/ansible/tests/unit/v2/values-secret-v2-file-contents.yaml new file mode 100644 index 00000000..e2da90c2 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-file-contents.yaml @@ -0,0 +1,8 @@ +--- +version: "2.0" + +secrets: + - name: test-secret + fields: + - name: username + path: ~/test-file-contents diff --git a/ansible/tests/unit/v2/values-secret-v2-generic-onlygenerate.yaml b/ansible/tests/unit/v2/values-secret-v2-generic-onlygenerate.yaml new file mode 100644 index 00000000..46992af1 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-generic-onlygenerate.yaml @@ -0,0 +1,33 @@ +version: "2.0" + +vaultPolicies: + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + + advancedPolicy: | + length=20 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + rule "charset" { charset = "!@#%^&*" min-chars = 1 } + +secrets: + - name: config-demo + targetNamespaces: + - default + vaultMount: foo + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: secret + onMissingValue: generate + override: true + vaultPolicy: basicPolicy + - name: secret2 + onMissingValue: generate + override: true + vaultPolicy: advancedPolicy diff --git a/ansible/tests/unit/v2/values-secret-v2-ini-file-b64.yaml b/ansible/tests/unit/v2/values-secret-v2-ini-file-b64.yaml new file mode 100644 index 00000000..ff08d20a --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-ini-file-b64.yaml @@ -0,0 +1,23 @@ +version: "2.0" +secrets: + - name: aws + fields: + - name: aws_access_key_id + ini_file: '~/aws-example.ini' + ini_section: default + ini_key: aws_access_key_id + - name: aws_secret_access_key + ini_file: '~/aws-example.ini' + ini_key: aws_secret_access_key + - name: awsb64 + fields: + - name: aws_access_key_id + ini_file: '~/aws-example.ini' + ini_section: default + ini_key: aws_access_key_id + base64: true + - name: aws_secret_access_key + ini_file: '~/aws-example.ini' + ini_section: default + ini_key: aws_secret_access_key + base64: true diff --git a/ansible/tests/unit/v2/values-secret-v2-more-namespaces.yaml b/ansible/tests/unit/v2/values-secret-v2-more-namespaces.yaml new file mode 100644 index 00000000..be409af7 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-more-namespaces.yaml @@ -0,0 +1,11 @@ +--- +version: "2.0" + +secrets: + - name: test-secret + targetNamespaces: + - default + - extra + fields: + - name: username + value: user diff --git a/ansible/tests/unit/v2/values-secret-v2-nondefault-namespace.yaml b/ansible/tests/unit/v2/values-secret-v2-nondefault-namespace.yaml new file mode 100644 index 00000000..a0f4db63 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-nondefault-namespace.yaml @@ -0,0 +1,8 @@ +--- +version: "2.0" + +secrets: + test-secret: + fields: + - name: username + value: user diff --git a/ansible/tests/unit/v2/values-secret-v2-none-no-targetnamespaces.yaml b/ansible/tests/unit/v2/values-secret-v2-none-no-targetnamespaces.yaml new file mode 100644 index 00000000..2a5ef0b6 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-none-no-targetnamespaces.yaml @@ -0,0 +1,33 @@ +version: "2.0" + +backingStore: vault + +vaultPolicies: + basicPolicy: | + length=10 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + + advancedPolicy: | + length=20 + rule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 } + rule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 } + rule "charset" { charset = "0123456789" min-chars = 1 } + rule "charset" { charset = "!@#%^&*" min-chars = 1 } + +secrets: + - name: config-demo + vaultMount: foo + vaultPrefixes: + - region-one + - snowflake.blueprints.rhecoeng.com + fields: + - name: secret + onMissingValue: generate + override: true + vaultPolicy: basicPolicy + - name: secret2 + onMissingValue: generate + override: true + vaultPolicy: advancedPolicy diff --git a/ansible/tests/unit/v2/values-secret-v2-override-labels.yaml b/ansible/tests/unit/v2/values-secret-v2-override-labels.yaml new file mode 100644 index 00000000..13a460be --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-override-labels.yaml @@ -0,0 +1,13 @@ +--- +version: "2.0" + +defaultLabels: + testlabel: 4 + +secrets: + - name: test-secret + labels: + overridelabel: 42 + fields: + - name: username + value: user diff --git a/ansible/tests/unit/v2/values-secret-v2-override-namespace.yaml b/ansible/tests/unit/v2/values-secret-v2-override-namespace.yaml new file mode 100644 index 00000000..ad53cf77 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-override-namespace.yaml @@ -0,0 +1,10 @@ +--- +version: "2.0" + +secretStoreNamespace: 'overridden-namespace' + +secrets: + - name: test-secret + fields: + - name: username + value: user diff --git a/ansible/tests/unit/v2/values-secret-v2-override-type-none.yaml b/ansible/tests/unit/v2/values-secret-v2-override-type-none.yaml new file mode 100644 index 00000000..1d110671 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-override-type-none.yaml @@ -0,0 +1,14 @@ +--- +version: "2.0" + +# This is the actual default +defaultNamespace: 'validated-patterns-secrets' + +secrets: + - name: test-secret + type: 'user-specified' + targetNamespaces: + - default + fields: + - name: username + value: user diff --git a/ansible/tests/unit/v2/values-secret-v2-override-type.yaml b/ansible/tests/unit/v2/values-secret-v2-override-type.yaml new file mode 100644 index 00000000..1bf8e369 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-override-type.yaml @@ -0,0 +1,12 @@ +--- +version: "2.0" + +# This is the actual default +defaultNamespace: 'validated-patterns-secrets' + +secrets: + - name: test-secret + type: 'user-specified' + fields: + - name: username + value: user diff --git a/ansible/tests/unit/v2/values-secret-v2-secret-binary-b64.yaml b/ansible/tests/unit/v2/values-secret-v2-secret-binary-b64.yaml new file mode 100644 index 00000000..579c7d6e --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-secret-binary-b64.yaml @@ -0,0 +1,10 @@ +version: "2.0" + +secrets: + - name: secret + fields: + - name: secret + # Should contain 8, 6, 7, 5, 3, 0, 9 in binary + path: '/tmp/testbinfile.bin' + onMissingValue: error + base64: true diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index d0dbc3c7..4db14be3 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -1,3 +1,4 @@ +{{- if eq .Values.global.secretStore.backend "vault" | default "vault" }} {{- if not (eq .Values.enabled "plumbing") }} {{- if $.Values.clusterGroup.isHubCluster }} --- @@ -56,3 +57,4 @@ spec: restartPolicy: Never {{- end }} {{- end }} +{{- end }} diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index bb3a6e27..c74db48c 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -1,6 +1,8 @@ global: extraValueFiles: [] pattern: common + secretStore: + backend: "vault" targetRevision: main options: useCSV: True diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml deleted file mode 100644 index fc0b410f..00000000 --- a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +++ /dev/null @@ -1,40 +0,0 @@ -apiVersion: external-secrets.io/v1beta1 -kind: ClusterSecretStore -metadata: - name: vault-backend - namespace: golang-external-secrets -spec: - provider: - vault: - server: https://vault-vault.{{ .Values.global.hubClusterDomain }} - path: secret - # Version of KV backend - version: v2 -{{- if .Values.golangExternalSecrets.caProvider.enabled }} -{{ if .Values.clusterGroup.isHubCluster }} - caProvider: - type: {{ .Values.golangExternalSecrets.caProvider.vaultHostCluster.type }} - name: {{ .Values.golangExternalSecrets.caProvider.vaultHostCluster.name }} - key: {{ .Values.golangExternalSecrets.caProvider.vaultHostCluster.key }} - namespace: {{ .Values.golangExternalSecrets.caProvider.vaultHostCluster.namespace }} -{{ else }} - caProvider: - type: {{ .Values.golangExternalSecrets.caProvider.vaultClientCluster.type }} - name: {{ .Values.golangExternalSecrets.caProvider.vaultClientCluster.name }} - key: {{ .Values.golangExternalSecrets.caProvider.vaultClientCluster.key }} - namespace: {{ .Values.golangExternalSecrets.caProvider.vaultClientCluster.namespace }} -{{ end }} -{{- end }} - auth: - kubernetes: -{{ if .Values.clusterGroup.isHubCluster }} - mountPath: {{ .Values.mountPath }} - role: {{ .Values.mountRole }} -{{ else }} - mountPath: {{ $.Values.global.clusterDomain }} - role: {{ $.Values.global.clusterDomain }}-role -{{ end }} - secretRef: - name: golang-external-secrets - namespace: golang-external-secrets - key: "token" diff --git a/golang-external-secrets/templates/kubernetes/golang-external-secrets-hub-role.yaml b/golang-external-secrets/templates/kubernetes/golang-external-secrets-hub-role.yaml new file mode 100644 index 00000000..05ce87a7 --- /dev/null +++ b/golang-external-secrets/templates/kubernetes/golang-external-secrets-hub-role.yaml @@ -0,0 +1,22 @@ +{{- if and (eq .Values.global.secretStore.backend "kubernetes") (eq .Values.clusterGroup.isHubCluster true) }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: {{ .Values.golangExternalSecrets.kubernetes.remoteNamespace }} + name: golang-external-secrets +rules: +- apiGroups: [""] + resources: + - secrets + verbs: + - get + - list + - watch +- apiGroups: + - authorization.k8s.io + resources: + - selfsubjectrulesreviews + verbs: + - create +{{- end }} diff --git a/golang-external-secrets/templates/kubernetes/golang-external-secrets-hub-secretstore.yaml b/golang-external-secrets/templates/kubernetes/golang-external-secrets-hub-secretstore.yaml new file mode 100644 index 00000000..62253f1f --- /dev/null +++ b/golang-external-secrets/templates/kubernetes/golang-external-secrets-hub-secretstore.yaml @@ -0,0 +1,34 @@ +{{- $backend := .Values.global.secretStore.backend | default "vault" }} +{{- if eq $backend "kubernetes" }} +--- +apiVersion: external-secrets.io/v1beta1 +kind: ClusterSecretStore +metadata: + name: {{ $backend }}-backend + namespace: golang-external-secrets +spec: + provider: + kubernetes: + remoteNamespace: {{ .Values.golangExternalSecrets.kubernetes.remoteNamespace }} + server: + url: {{ .Values.golangExternalSecrets.kubernetes.server.url }} +{{- if .Values.golangExternalSecrets.caProvider.enabled }} +{{- if .Values.clusterGroup.isHubCluster }} + caProvider: + type: {{ .Values.golangExternalSecrets.caProvider.hostCluster.type }} + name: {{ .Values.golangExternalSecrets.caProvider.hostCluster.name }} + key: {{ .Values.golangExternalSecrets.caProvider.hostCluster.key }} + namespace: {{ .Values.golangExternalSecrets.caProvider.hostCluster.namespace }} +{{- else }} + caProvider: + type: {{ .Values.golangExternalSecrets.caProvider.clientCluster.type }} + name: {{ .Values.golangExternalSecrets.caProvider.clientCluster.name }} + key: {{ .Values.golangExternalSecrets.caProvider.clientCluster.key }} + namespace: {{ .Values.golangExternalSecrets.caProvider.clientCluster.namespace }} +{{- end }} +{{- end }} + auth: + serviceAccount: + name: golang-external-secrets + namespace: golang-external-secrets +{{- end }} diff --git a/golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml b/golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml new file mode 100644 index 00000000..8fdd4ab0 --- /dev/null +++ b/golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml @@ -0,0 +1,44 @@ +{{- $backend := .Values.global.secretStore.backend | default "vault" }} +{{- if eq $backend "vault" }} +--- +apiVersion: external-secrets.io/v1beta1 +kind: ClusterSecretStore +metadata: + name: {{ $backend }}-backend + namespace: golang-external-secrets +spec: + provider: + vault: + server: https://vault-vault.{{ .Values.global.hubClusterDomain }} + path: secret + # Version of KV backend + version: v2 +{{- if .Values.golangExternalSecrets.caProvider.enabled }} +{{ if .Values.clusterGroup.isHubCluster }} + caProvider: + type: {{ .Values.golangExternalSecrets.caProvider.hostCluster.type }} + name: {{ .Values.golangExternalSecrets.caProvider.hostCluster.name }} + key: {{ .Values.golangExternalSecrets.caProvider.hostCluster.key }} + namespace: {{ .Values.golangExternalSecrets.caProvider.hostCluster.namespace }} +{{ else }} + caProvider: + type: {{ .Values.golangExternalSecrets.caProvider.clientCluster.type }} + name: {{ .Values.golangExternalSecrets.caProvider.clientCluster.name }} + key: {{ .Values.golangExternalSecrets.caProvider.clientCluster.key }} + namespace: {{ .Values.golangExternalSecrets.caProvider.clientCluster.namespace }} +{{ end }} +{{- end }} + auth: + kubernetes: +{{ if .Values.clusterGroup.isHubCluster }} + mountPath: {{ .Values.golangExternalSecrets.vault.mountPath }} + role: {{ .Values.golangExternalSecrets.rbac.rolename }} +{{ else }} + mountPath: {{ $.Values.global.clusterDomain }} + role: {{ $.Values.global.clusterDomain }}-role +{{ end }} + secretRef: + name: golang-external-secrets + namespace: golang-external-secrets + key: "token" +{{- end }} diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index 8a37f554..6ecd32f2 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -1,18 +1,25 @@ --- -# Eventually we should aim to move these two under the golangExternalSecrets key -mountPath: "hub" -mountRole: "hub-role" - golangExternalSecrets: - # This controls how ESO connects to vault + rbac: + rolename: "hub-role" + + kubernetes: + remoteNamespace: "validated-patterns-secrets" + server: + url: 'https://kubernetes.default' + + vault: + mountPath: "hub" + + # This controls how ESO connects to vault caProvider: enabled: true # If vault is exposed via a route that is signed by a non internal CA you might want to disable this - vaultHostCluster: + hostCluster: type: ConfigMap name: kube-root-ca.crt key: ca.crt namespace: golang-external-secrets - vaultClientCluster: + clientCluster: type: Secret name: hub-ca key: hub-kube-root-ca.crt @@ -22,6 +29,9 @@ global: hubClusterDomain: hub.example.com clusterDomain: foo.example.com + secretStore: + backend: "vault" + clusterGroup: isHubCluster: true diff --git a/scripts/determine-main-clustergroup.sh b/scripts/determine-main-clustergroup.sh new file mode 100755 index 00000000..6271dbad --- /dev/null +++ b/scripts/determine-main-clustergroup.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +PATTERN_DIR="$1" + +if [ -z "$PATTERN_DIR" ]; then + PATTERN_DIR="." +fi + +CGNAME=$(yq '.main.clusterGroupName' "$PATTERN_DIR/values-global.yaml") + +if [ -z "$CGNAME" ] || [ "$CGNAME" == "null" ]; then + echo "Error - cannot detrmine clusterGroupName" + exit 1 +fi + +echo "$CGNAME" diff --git a/scripts/determine-pattern-name.sh b/scripts/determine-pattern-name.sh new file mode 100755 index 00000000..fb503fe6 --- /dev/null +++ b/scripts/determine-pattern-name.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +PATTERN_DIR="$1" + +if [ -z "$PATTERN_DIR" ]; then + PATTERN_DIR="." +fi + +PATNAME=$(yq '.global.pattern' "$PATTERN_DIR/values-global.yaml" 2>/dev/null) + +if [ -z "$PATNAME" ] || [ "$PATNAME" == "null" ]; then + PATNAME="$(basename "$PWD")" +fi + +echo "$PATNAME" diff --git a/scripts/determine-secretstore-backend.sh b/scripts/determine-secretstore-backend.sh new file mode 100755 index 00000000..ef784790 --- /dev/null +++ b/scripts/determine-secretstore-backend.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +PATTERN_DIR="$1" + +if [ -z "$PATTERN_DIR" ]; then + PATTERN_DIR="." +fi + +BACKEND=$(yq '.global.secretStore.backend' "$PATTERN_DIR/values-global.yaml" 2>/dev/null) + +if [ -z "$BACKEND" -o "$BACKEND" == "null" ]; then + BACKEND="vault" +fi + +echo "$BACKEND" diff --git a/scripts/display-secrets-info.sh b/scripts/display-secrets-info.sh new file mode 100755 index 00000000..124a3454 --- /dev/null +++ b/scripts/display-secrets-info.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -eu + +get_abs_filename() { + # $1 : relative filename + echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" +} + +SCRIPT=$(get_abs_filename "$0") +SCRIPTPATH=$(dirname "${SCRIPT}") +COMMONPATH=$(dirname "${SCRIPTPATH}") +PATTERNPATH=$(dirname "${COMMONPATH}") +ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" +PLAYBOOKPATH="${ANSIBLEPATH}/playbooks" + +export ANSIBLE_CONFIG="${ANSIBLEPATH}/ansible.cfg" + +if [ "$#" -ge 1 ]; then + export VALUES_SECRET=$(get_abs_filename "${1}") +fi + +if [[ "$#" == 2 ]]; then + SECRETS_BACKING_STORE="$2" +else + SECRETS_BACKING_STORE="$($SCRIPTPATH/determine-secretstore-backend.sh)" +fi + +PATTERN_NAME=$(basename "`pwd`") + +ansible-playbook -e pattern_name="${PATTERN_NAME}" -e pattern_dir="${PATTERNPATH}" -e secrets_backing_store="${SECRETS_BACKING_STORE}" -e override_no_log=false "${PLAYBOOKPATH}/process_secrets/display_secrets_info.yml" diff --git a/scripts/load-k8s-secrets.sh b/scripts/load-k8s-secrets.sh new file mode 100755 index 00000000..33c2f9a5 --- /dev/null +++ b/scripts/load-k8s-secrets.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -eu + +get_abs_filename() { + # $1 : relative filename + echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" +} + +SCRIPT=$(get_abs_filename "$0") +SCRIPTPATH=$(dirname "${SCRIPT}") +COMMONPATH=$(dirname "${SCRIPTPATH}") +PATTERNPATH=$(dirname "${COMMONPATH}") +ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" +PLAYBOOKPATH="${ANSIBLEPATH}/playbooks" +export ANSIBLE_CONFIG="${ANSIBLEPATH}/ansible.cfg" + +PATTERN_NAME=${1:-$(basename "`pwd`")} + +ansible-playbook -e pattern_name="${PATTERN_NAME}" -e pattern_dir="${PATTERNPATH}" "${PLAYBOOKPATH}/k8s_secrets/k8s_secrets.yml" diff --git a/scripts/manage-secret-app.sh b/scripts/manage-secret-app.sh new file mode 100755 index 00000000..1ea0d0bb --- /dev/null +++ b/scripts/manage-secret-app.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +APP=$1 +STATE=$2 + +MAIN_CLUSTERGROUP_FILE="./values-$(common/scripts/determine-main-clustergroup.sh).yaml" +MAIN_CLUSTERGROUP_PROJECT="$(common/scripts/determine-main-clustergroup.sh)" + +case "$APP" in + "vault") + APP_NAME="vault" + NAMESPACE="vault" + PROJECT="$MAIN_CLUSTERGROUP_PROJECT" + CHART_LOCATION="common/hashicorp-vault" + ;; + "golang-external-secrets") + APP_NAME="golang-external-secrets" + NAMESPACE="golang-external-secrets" + PROJECT="$MAIN_CLUSTERGROUP_PROJECT" + CHART_LOCATION="common/golang-external-secrets" + ;; + *) + echo "Error - cannot manage $APP can only manage vault and golang-external-secrets" + exit 1 + ;; +esac + +case "$STATE" in + "present") + common/scripts/manage-secret-namespace.sh "$NAMESPACE" "$STATE" + + RES=$(yq ".clusterGroup.applications[] | select(.path == \"$CHART_LOCATION\")" "$MAIN_CLUSTERGROUP_FILE" 2>/dev/null) + if [ -z "$RES" ]; then + echo "Application with chart location $CHART_LOCATION not found, adding" + yq -i ".clusterGroup.applications.$APP_NAME = { \"name\": \"$APP_NAME\", \"namespace\": \"$NAMESPACE\", \"project\": \"$PROJECT\", \"path\": \"$CHART_LOCATION\" }" "$MAIN_CLUSTERGROUP_FILE" + fi + ;; + "absent") + common/scripts/manage-secret-namespace.sh "$NAMESPACE" "$STATE" + echo "Removing application wth chart location $CHART_LOCATION" + yq -i "del(.clusterGroup.applications[] | select(.path == \"$CHART_LOCATION\"))" "$MAIN_CLUSTERGROUP_FILE" + ;; + *) + echo "$STATE not supported" + exit 1 + ;; +esac + +exit 0 diff --git a/scripts/manage-secret-namespace.sh b/scripts/manage-secret-namespace.sh new file mode 100755 index 00000000..bcb06742 --- /dev/null +++ b/scripts/manage-secret-namespace.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +NAMESPACE=$1 +STATE=$2 + +MAIN_CLUSTERGROUP_FILE="./values-$(common/scripts/determine-main-clustergroup.sh).yaml" +MAIN_CLUSTERGROUP_PROJECT="$(common/scripts/determine-main-clustergroup.sh)" + +case "$STATE" in + "present") + + RES=$(yq ".clusterGroup.namespaces[] | select(. == \"$NAMESPACE\")" "$MAIN_CLUSTERGROUP_FILE" 2>/dev/null) + if [ -z "$RES" ]; then + echo "Namespace $NAMESPACE not found, adding" + yq -i ".clusterGroup.namespaces += [ \"$NAMESPACE\" ]" "$MAIN_CLUSTERGROUP_FILE" + fi + ;; + "absent") + echo "Removing namespace $NAMESPACE" + yq -i "del(.clusterGroup.namespaces[] | select(. == \"$NAMESPACE\"))" "$MAIN_CLUSTERGROUP_FILE" + ;; + *) + echo "$STATE not supported" + exit 1 + ;; +esac + +exit 0 diff --git a/scripts/process-secrets.sh b/scripts/process-secrets.sh new file mode 100755 index 00000000..509d6d71 --- /dev/null +++ b/scripts/process-secrets.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -eu + +get_abs_filename() { + # $1 : relative filename + echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" +} + +SCRIPT=$(get_abs_filename "$0") +SCRIPTPATH=$(dirname "${SCRIPT}") +COMMONPATH=$(dirname "${SCRIPTPATH}") +PATTERNPATH=$(dirname "${COMMONPATH}") +ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" +PLAYBOOKPATH="${ANSIBLEPATH}/playbooks" +export ANSIBLE_CONFIG="${ANSIBLEPATH}/ansible.cfg" + +PATTERN_NAME=${1:-$(basename "`pwd`")} +SECRETS_BACKING_STORE="$($SCRIPTPATH/determine-secretstore-backend.sh)" + +ansible-playbook -e pattern_name="${PATTERN_NAME}" -e pattern_dir="${PATTERNPATH}" -e secrets_backing_store="${SECRETS_BACKING_STORE}" "${PLAYBOOKPATH}/process_secrets/process_secrets.yml" diff --git a/scripts/set-secret-backend.sh b/scripts/set-secret-backend.sh new file mode 100755 index 00000000..e07b15bf --- /dev/null +++ b/scripts/set-secret-backend.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +BACKEND=$1 + +yq -i ".global.secretStore.backend = \"$BACKEND\"" values-global.yaml diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index aef52f65..948ec58b 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -186,6 +186,8 @@ data: useCSV: true pattern: mypattern repoURL: https://github.com/pattern-clone/mypattern + secretStore: + backend: vault targetRevision: main main: clusterGroupName: example diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 3fcca694..541d6128 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -347,6 +347,8 @@ data: useCSV: true pattern: mypattern repoURL: https://github.com/pattern-clone/mypattern + secretStore: + backend: vault targetRevision: main main: clusterGroupName: example diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 5678d8bc..e7c66202 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -306,6 +306,8 @@ data: useCSV: true pattern: mypattern repoURL: https://github.com/pattern-clone/mypattern + secretStore: + backend: vault targetRevision: main main: clusterGroupName: example diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index ec8099f3..de02651e 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -76,6 +76,8 @@ data: syncPolicy: Automatic useCSV: true pattern: common + secretStore: + backend: vault targetRevision: main secretStore: kind: ClusterSecretStore diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index a3dd7cd4..9bf39732 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -268,6 +268,8 @@ data: useCSV: false pattern: mypattern repoURL: https://github.com/pattern-clone/mypattern + secretStore: + backend: vault targetRevision: main main: clusterGroupName: example diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index d92ef427..e6b3d6fb 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -9225,7 +9225,7 @@ spec: secret: secretName: golang-external-secrets-webhook --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +# Source: golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index 43c5d3fc..3fca7728 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -9225,7 +9225,7 @@ spec: secret: secretName: golang-external-secrets-webhook --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +# Source: golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index 43c5d3fc..3fca7728 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -9225,7 +9225,7 @@ spec: secret: secretName: golang-external-secrets-webhook --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +# Source: golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 6b9d3030..fda09175 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -9225,7 +9225,7 @@ spec: secret: secretName: golang-external-secrets-webhook --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +# Source: golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 43c5d3fc..3fca7728 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -9225,7 +9225,7 @@ spec: secret: secretName: golang-external-secrets-webhook --- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +# Source: golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: diff --git a/values-global.yaml b/values-global.yaml index 24feccd5..684f89f2 100644 --- a/values-global.yaml +++ b/values-global.yaml @@ -12,6 +12,9 @@ global: email: someone@somewhere.com dev_revision: main + secretStore: + backend: vault + main: clusterGroupName: example From 672da04a64123630ad2ee8d004a9900a1e821b2e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 9 Feb 2024 17:02:49 +0100 Subject: [PATCH 1101/1263] Upgrade ESO to v0.9.12 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.9.11.tgz | Bin 86686 -> 0 bytes .../charts/external-secrets-0.9.12.tgz | Bin 0 -> 93006 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 3982 +++++++++++++---- ...-secrets-industrial-edge-hub.expected.yaml | 3982 +++++++++++++---- ...ecrets-medical-diagnosis-hub.expected.yaml | 3982 +++++++++++++---- ...olang-external-secrets-naked.expected.yaml | 3982 +++++++++++++---- ...lang-external-secrets-normal.expected.yaml | 3982 +++++++++++++---- 9 files changed, 15434 insertions(+), 4484 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.11.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.9.12.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index b60b499c..38549d5c 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.3 dependencies: - name: external-secrets - version: "0.9.11" + version: "0.9.12" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.9.11.tgz b/golang-external-secrets/charts/external-secrets-0.9.11.tgz deleted file mode 100644 index 0f813640598414f183461f5f42e9d774fd723a86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86686 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 z 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 I${0}p1t(C-`~Uy| literal 0 HcmV?d00001 diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index 6ecd32f2..04b65029 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -37,10 +37,10 @@ clusterGroup: external-secrets: image: - tag: v0.9.11-ubi + tag: v0.9.12-ubi webhook: image: - tag: v0.9.11-ubi + tag: v0.9.12-ubi certController: image: - tag: v0.9.11-ubi + tag: v0.9.12-ubi diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index e6b3d6fb..24dd79ae 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -85,18 +85,39 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: "ACRAccessToken returns a Azure Container Registry token that can be used for pushing/pulling images. Note: by default it will return an ACR Refresh Token with full access (depending on the identity). This can be scoped down to the repository level using .spec.scope. In case scope is defined it will return an ACR Access Token. \n See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md" + description: |- + ACRAccessToken returns a Azure Container Registry token + that can be used for pushing/pulling images. + Note: by default it will return an ACR Refresh Token with full access + (depending on the identity). + This can be scoped down to the repository level using .spec.scope. + In case scope is defined it will return an ACR Access Token. + + + See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md 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' + 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' + 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: 'ACRAccessTokenSpec defines how to generate the access token e.g. how to authenticate and which registry to use. see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview' + description: |- + ACRAccessTokenSpec defines how to generate the access token + e.g. how to authenticate and which registry to use. + see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview properties: auth: properties: @@ -111,32 +132,42 @@ spec: description: ServicePrincipal uses Azure Service Principal credentials to authenticate with Azure. properties: secretRef: - description: Configuration used to authenticate with Azure using static credentials stored in a Kind=Secret. + description: |- + Configuration used to authenticate with Azure using static + credentials stored in a Kind=Secret. properties: clientId: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -147,10 +178,15 @@ spec: description: WorkloadIdentity uses Azure Workload Identity to authenticate with Azure. properties: serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -158,7 +194,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -167,7 +205,11 @@ spec: type: object environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -175,10 +217,23 @@ spec: - GermanCloud type: string registry: - description: the domain name of the ACR registry e.g. foobarexample.azurecr.io + description: |- + the domain name of the ACR registry + e.g. foobarexample.azurecr.io type: string scope: - description: "Define the scope for the access token, e.g. pull/push access for a repository. if not provided it will return a refresh token that has full scope. Note: you need to pin it down to the repository level, there is no wildcard available. \n examples: repository:my-repository:pull,push repository:my-repository:pull \n see docs for details: https://docs.docker.com/registry/spec/auth/scope/" + description: |- + Define the scope for the access token, e.g. pull/push access for a repository. + if not provided it will return a refresh token that has full scope. + Note: you need to pin it down to the repository level, there is no wildcard available. + + + examples: + repository:my-repository:pull,push + repository:my-repository:pull + + + see docs for details: https://docs.docker.com/registry/spec/auth/scope/ type: string tenantId: description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. @@ -208,7 +263,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -239,10 +294,19 @@ spec: description: ClusterExternalSecret is the Schema for the clusterexternalsecrets 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' + 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' + 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 @@ -273,7 +337,9 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. + description: |- + RemoteRef points to the remote secret and defines + which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -311,14 +377,23 @@ spec: - key type: object secretKey: - description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret + description: |- + SecretKey defines the key in which the controller stores + the value. This is the key in the Kind=Secret type: string sourceRef: - description: SourceRef allows you to override the source from which the value will pulled from. + description: |- + SourceRef allows you to override the source + from which the value will pulled from. maxProperties: 1 properties: generatorRef: - description: "GeneratorRef points to a generator custom resource. \n Deprecated: The generatorRef is not implemented in .data[]. this will be removed with v1." + description: |- + GeneratorRef points to a generator custom resource. + + + Deprecated: The generatorRef is not implemented in .data[]. + this will be removed with v1. properties: apiVersion: default: generators.external-secrets.io/v1alpha1 @@ -338,7 +413,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -353,11 +430,15 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: properties: extract: - description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to extract multiple key/value pairs from one secret + Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -395,7 +476,9 @@ spec: - key type: object find: - description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to find secrets based on tags or regular expressions + Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -430,11 +513,15 @@ spec: type: object type: object rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + description: |- + Used to rewrite secret Keys after getting them from the secret Provider + Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) items: properties: regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + description: |- + Used to rewrite with regular expressions. + The resulting key will be the output of a regexp.ReplaceAll operation. properties: source: description: Used to define the regular expression of a re.Compiler. @@ -447,10 +534,14 @@ spec: - target type: object transform: - description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + description: |- + Used to apply string transformation on the secrets. + The resulting key will be the output of the template applied by the operation. properties: template: - description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + description: |- + Used to define the template to apply on the secret name. + `.value ` will specify the secret name in the template. type: string required: - template @@ -458,7 +549,13 @@ spec: type: object type: array sourceRef: - description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + description: |- + SourceRef points to a store or generator + which contains secret values ready to use. + Use this in combination with Extract or Find pull values out of + a specific SecretStore. + When sourceRef points to a generator Extract or Find is not supported. + The generator returns a static map of values maxProperties: 1 properties: generatorRef: @@ -482,7 +579,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -495,13 +594,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -513,11 +617,15 @@ spec: default: creationPolicy: Owner deletionPolicy: Retain - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Orphan @@ -526,7 +634,9 @@ spec: type: string deletionPolicy: default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + description: |- + DeletionPolicy defines rules on how to delete the resulting Secret + Defaults to 'Retain' enum: - Delete - Merge @@ -536,7 +646,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -547,7 +660,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -641,16 +757,24 @@ spec: 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. + 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. + 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. + 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 @@ -662,7 +786,10 @@ spec: 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. + 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 @@ -739,7 +866,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -768,10 +895,19 @@ spec: description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -779,7 +915,9 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -796,7 +934,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -805,23 +945,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -829,7 +984,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -839,51 +996,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -942,26 +1120,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -980,7 +1166,10 @@ spec: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -989,7 +1178,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -997,39 +1189,51 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1060,32 +1264,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -1095,10 +1311,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1106,7 +1327,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1155,13 +1378,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1177,7 +1404,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1185,7 +1415,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1212,13 +1444,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1246,13 +1482,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1277,29 +1517,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1310,7 +1562,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1318,7 +1573,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1328,16 +1585,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1387,7 +1650,10 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, instance principal is used. Optionally, the authenticating principal type + and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -1396,26 +1662,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1434,13 +1708,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -1451,10 +1732,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1462,7 +1748,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1481,26 +1769,40 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1509,55 +1811,83 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1565,7 +1895,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1575,55 +1907,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1631,7 +1994,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1641,27 +2006,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -1671,18 +2049,26 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -1708,23 +2094,40 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -1740,7 +2143,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -1781,7 +2188,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -1791,13 +2200,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1828,13 +2241,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1842,16 +2259,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1918,10 +2341,19 @@ spec: description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -1931,7 +2363,9 @@ spec: conditions: description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore items: - description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + description: |- + ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in + for a ClusterSecretStore instance. properties: namespaceSelector: description: Choose namespace using a labelSelector @@ -1939,16 +2373,24 @@ spec: 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. + 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. + 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. + 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 @@ -1960,7 +2402,10 @@ spec: 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. + 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 @@ -1972,7 +2417,9 @@ spec: type: object type: array controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -1989,7 +2436,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -1998,23 +2447,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2022,7 +2486,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2032,51 +2498,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -2089,7 +2576,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2135,26 +2624,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2178,7 +2675,10 @@ spec: type: string type: array auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -2187,7 +2687,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2195,52 +2698,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2258,10 +2780,20 @@ spec: description: SecretsManager defines how the provider behaves when interacting with AWS SecretsManager properties: forceDeleteWithoutRecovery: - description: 'Specifies whether to delete the secret without any recovery window. You can''t use both this parameter and RecoveryWindowInDays in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery' + description: |- + Specifies whether to delete the secret without any recovery window. You + can't use both this parameter and RecoveryWindowInDays in the same call. + If you don't use either, then by default Secrets Manager uses a 30 day + recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery type: boolean recoveryWindowInDays: - description: 'The number of days from 7 to 30 that Secrets Manager waits before permanently deleting the secret. You can''t use both this parameter and ForceDeleteWithoutRecovery in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays' + description: |- + The number of days from 7 to 30 that Secrets Manager waits before + permanently deleting the secret. You can't use both this parameter and + ForceDeleteWithoutRecovery in the same call. If you don't use either, + then by default Secrets Manager uses a 30 day recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays format: int64 type: integer type: object @@ -2303,32 +2835,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -2336,7 +2880,11 @@ spec: type: string environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -2347,10 +2895,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2358,7 +2911,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2382,29 +2937,41 @@ spec: account: type: string apiKeyRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2417,23 +2984,34 @@ spec: account: type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Conjur using the JWT authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountRef specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2441,7 +3019,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2457,7 +3037,10 @@ spec: caBundle: type: string caProvider: - description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + description: |- + Used to provide custom certificate authority (CA) certificates + for a secret store. The CAProvider points to a Secret or ConfigMap resource + that contains a PEM-encoded certificate. properties: key: description: The key where the CA certificate can be found in the Secret or ConfigMap. @@ -2466,7 +3049,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2485,7 +3070,9 @@ spec: - url type: object delinea: - description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + description: |- + Delinea DevOps Secrets Vault + https://docs.delinea.com/online-help/products/devops-secrets-vault/current properties: clientId: description: ClientID is the non-secret part of the credential. @@ -2494,13 +3081,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -2514,13 +3105,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -2531,10 +3126,14 @@ spec: description: Tenant is the chosen hostname / site name. type: string tld: - description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + description: |- + TLD is based on the server location that was chosen during provisioning. + If unset, defaults to "com". type: string urlTemplate: - description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + description: |- + URLTemplate + If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". type: string required: - clientId @@ -2550,16 +3149,23 @@ spec: secretRef: properties: dopplerToken: - description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + description: |- + The DopplerToken is used for authentication. + See https://docs.doppler.com/reference/api#authentication for auth token types. + The Key attribute defaults to dopplerToken if not specified. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2632,13 +3238,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2654,7 +3264,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2662,7 +3275,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2689,13 +3304,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2750,13 +3369,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2771,16 +3394,22 @@ spec: description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider properties: authRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object folderID: @@ -2801,29 +3430,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2831,7 +3472,10 @@ spec: description: points to a service account that should be used for authentication properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2839,7 +3483,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2848,16 +3494,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2883,7 +3535,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2916,13 +3570,17 @@ spec: description: The ConnectToken is used for authentication to a 1Password Connect Server. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2948,7 +3606,9 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, use the instance principal, otherwise the user credentials specified in Auth. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -2957,26 +3617,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2995,13 +3663,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -3012,10 +3687,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3023,7 +3703,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3045,13 +3727,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -3074,13 +3760,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -3102,16 +3792,22 @@ spec: clientId: type: string clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3140,39 +3836,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3180,37 +3898,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -3222,7 +3956,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3230,7 +3967,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3252,39 +3991,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3298,25 +4052,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3324,7 +4094,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3334,55 +4106,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3390,7 +4193,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3400,27 +4205,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -3430,13 +4248,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -3444,23 +4266,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -3468,7 +4301,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -3481,7 +4318,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -3494,23 +4333,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -3526,7 +4431,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -3567,7 +4476,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -3577,13 +4488,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3614,13 +4529,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3628,16 +4547,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3657,13 +4582,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3671,16 +4600,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3750,7 +4685,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3768,13 +4703,28 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an authorization token. The authorization token is valid for 12 hours. The authorizationToken returned is a base64 encoded string that can be decoded and used in a docker login command to authenticate to a registry. For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. + description: |- + ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an + authorization token. + The authorization token is valid for 12 hours. + The authorizationToken returned is a base64 encoded string that can be decoded + and used in a docker login command to authenticate to a registry. + For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. 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' + 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' + 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 @@ -3790,7 +4740,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3798,52 +4751,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3852,7 +4824,9 @@ spec: description: Region specifies the region to operate in. type: string role: - description: You can assume a role before making calls to the desired AWS service. + description: |- + You can assume a role before making calls to the + desired AWS service. type: string required: - region @@ -3878,7 +4852,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -3910,10 +4884,19 @@ spec: description: ExternalSecret is the Schema for the external-secrets 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' + 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' + 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 @@ -3955,7 +4938,9 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: description: ExternalSecretDataRemoteRef defines Provider data location. properties: @@ -3981,13 +4966,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -3996,11 +4986,15 @@ spec: - name type: object target: - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Merge @@ -4010,7 +5004,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -4021,7 +5018,10 @@ spec: type: object engineVersion: default: v1 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -4093,7 +5093,10 @@ spec: description: Binding represents a servicebinding.io Provisioned Service reference to the secret properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -4117,7 +5120,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -4149,10 +5154,19 @@ spec: description: ExternalSecret is the Schema for the external-secrets 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' + 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' + 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 @@ -4165,7 +5179,9 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. + description: |- + RemoteRef points to the remote secret and defines + which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -4203,14 +5219,23 @@ spec: - key type: object secretKey: - description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret + description: |- + SecretKey defines the key in which the controller stores + the value. This is the key in the Kind=Secret type: string sourceRef: - description: SourceRef allows you to override the source from which the value will pulled from. + description: |- + SourceRef allows you to override the source + from which the value will pulled from. maxProperties: 1 properties: generatorRef: - description: "GeneratorRef points to a generator custom resource. \n Deprecated: The generatorRef is not implemented in .data[]. this will be removed with v1." + description: |- + GeneratorRef points to a generator custom resource. + + + Deprecated: The generatorRef is not implemented in .data[]. + this will be removed with v1. properties: apiVersion: default: generators.external-secrets.io/v1alpha1 @@ -4230,7 +5255,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4245,11 +5272,15 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: properties: extract: - description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to extract multiple key/value pairs from one secret + Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -4287,7 +5318,9 @@ spec: - key type: object find: - description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to find secrets based on tags or regular expressions + Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -4322,11 +5355,15 @@ spec: type: object type: object rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + description: |- + Used to rewrite secret Keys after getting them from the secret Provider + Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) items: properties: regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + description: |- + Used to rewrite with regular expressions. + The resulting key will be the output of a regexp.ReplaceAll operation. properties: source: description: Used to define the regular expression of a re.Compiler. @@ -4339,10 +5376,14 @@ spec: - target type: object transform: - description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + description: |- + Used to apply string transformation on the secrets. + The resulting key will be the output of the template applied by the operation. properties: template: - description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + description: |- + Used to define the template to apply on the secret name. + `.value ` will specify the secret name in the template. type: string required: - template @@ -4350,7 +5391,13 @@ spec: type: object type: array sourceRef: - description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + description: |- + SourceRef points to a store or generator + which contains secret values ready to use. + Use this in combination with Extract or Find pull values out of + a specific SecretStore. + When sourceRef points to a generator Extract or Find is not supported. + The generator returns a static map of values maxProperties: 1 properties: generatorRef: @@ -4374,7 +5421,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4387,13 +5436,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4405,11 +5459,15 @@ spec: default: creationPolicy: Owner deletionPolicy: Retain - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Orphan @@ -4418,7 +5476,9 @@ spec: type: string deletionPolicy: default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + description: |- + DeletionPolicy defines rules on how to delete the resulting Secret + Defaults to 'Retain' enum: - Delete - Merge @@ -4428,7 +5488,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -4439,7 +5502,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -4533,7 +5599,10 @@ spec: description: Binding represents a servicebinding.io Provisioned Service reference to the secret properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -4557,7 +5626,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -4586,7 +5657,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4604,13 +5675,24 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Fake generator is used for testing. It lets you define a static set of credentials that is always returned. + description: |- + Fake generator is used for testing. It lets you define + a static set of credentials that is always returned. 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' + 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' + 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 @@ -4618,12 +5700,16 @@ spec: description: FakeSpec contains the static data. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters VDS based on this property type: string data: additionalProperties: type: string - description: Data defines the static data returned by this generator. + description: |- + Data defines the static data returned + by this generator. type: object type: object type: object @@ -4647,7 +5733,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4665,13 +5751,24 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: GCRAccessToken generates an GCP access token that can be used to authenticate with GCR. + description: |- + GCRAccessToken generates an GCP access token + that can be used to authenticate with GCR. 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' + 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' + 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 @@ -4686,13 +5783,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -4708,7 +5809,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -4716,7 +5820,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -4755,7 +5861,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4773,13 +5879,25 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Password generates a random password based on the configuration parameters in spec. You can specify the length, characterset and other attributes. + description: |- + Password generates a random password based on the + configuration parameters in spec. + You can specify the length, characterset and other attributes. 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' + 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' + 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 @@ -4791,21 +5909,29 @@ spec: description: set AllowRepeat to true to allow repeating characters. type: boolean digits: - description: Digits specifies the number of digits in the generated password. If omitted it defaults to 25% of the length of the password + description: |- + Digits specifies the number of digits in the generated + password. If omitted it defaults to 25% of the length of the password type: integer length: default: 24 - description: Length of the password to be generated. Defaults to 24 + description: |- + Length of the password to be generated. + Defaults to 24 type: integer noUpper: default: false description: Set NoUpper to disable uppercase characters type: boolean symbolCharacters: - description: SymbolCharacters specifies the special characters that should be used in the generated password. + description: |- + SymbolCharacters specifies the special characters that should be used + in the generated password. type: string symbols: - description: Symbols specifies the number of symbol characters in the generated password. If omitted it defaults to 25% of the length of the password + description: |- + Symbols specifies the number of symbol characters in the generated + password. If omitted it defaults to 25% of the length of the password type: integer required: - allowRepeat @@ -4833,7 +5959,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4858,10 +5984,19 @@ spec: openAPIV3Schema: 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' + 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' + 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 @@ -4894,7 +6029,9 @@ spec: - remoteRef type: object metadata: - description: Metadata is metadata attached to the secret. The structure of metadata is provider specific, please look it up in the provider documentation. + description: |- + Metadata is metadata attached to the secret. + The structure of metadata is provider specific, please look it up in the provider documentation. x-kubernetes-preserve-unknown-fields: true required: - match @@ -4915,7 +6052,9 @@ spec: properties: kind: default: SecretStore - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string labelSelector: description: Optionally, sync to secret stores with label selector @@ -4923,16 +6062,24 @@ spec: 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. + 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. + 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. + 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 @@ -4944,7 +6091,10 @@ spec: 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. + 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 @@ -4977,7 +6127,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -5092,7 +6245,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -5122,7 +6277,9 @@ spec: - remoteRef type: object metadata: - description: Metadata is metadata attached to the secret. The structure of metadata is provider specific, please look it up in the provider documentation. + description: |- + Metadata is metadata attached to the secret. + The structure of metadata is provider specific, please look it up in the provider documentation. x-kubernetes-preserve-unknown-fields: true required: - match @@ -5155,7 +6312,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -5184,10 +6341,19 @@ spec: description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -5195,7 +6361,9 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -5212,7 +6380,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -5221,23 +6391,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5245,7 +6430,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5255,51 +6442,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -5358,26 +6566,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5396,7 +6612,10 @@ spec: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -5405,7 +6624,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5413,39 +6635,51 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5476,32 +6710,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -5511,10 +6757,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5522,7 +6773,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5571,13 +6824,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5593,7 +6850,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5601,7 +6861,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5628,13 +6890,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5662,13 +6928,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5693,29 +6963,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5726,7 +7008,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5734,7 +7019,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5744,16 +7031,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5803,7 +7096,10 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, instance principal is used. Optionally, the authenticating principal type + and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -5812,26 +7108,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5850,13 +7154,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -5867,10 +7178,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5878,7 +7194,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5897,26 +7215,40 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5925,55 +7257,83 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5981,7 +7341,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5991,55 +7353,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6047,7 +7440,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6057,27 +7452,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -6087,18 +7495,26 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6124,23 +7540,40 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -6156,7 +7589,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6197,7 +7634,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -6207,13 +7646,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6244,13 +7687,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6258,16 +7705,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6334,10 +7787,19 @@ spec: description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -6347,7 +7809,9 @@ spec: conditions: description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore items: - description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + description: |- + ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in + for a ClusterSecretStore instance. properties: namespaceSelector: description: Choose namespace using a labelSelector @@ -6355,16 +7819,24 @@ spec: 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. + 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. + 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. + 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 @@ -6376,7 +7848,10 @@ spec: 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. + 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 @@ -6388,7 +7863,9 @@ spec: type: object type: array controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -6405,7 +7882,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -6414,23 +7893,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6438,7 +7932,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6448,51 +7944,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6505,7 +8022,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -6551,26 +8070,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6594,7 +8121,10 @@ spec: type: string type: array auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -6603,7 +8133,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6611,52 +8144,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6674,10 +8226,20 @@ spec: description: SecretsManager defines how the provider behaves when interacting with AWS SecretsManager properties: forceDeleteWithoutRecovery: - description: 'Specifies whether to delete the secret without any recovery window. You can''t use both this parameter and RecoveryWindowInDays in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery' + description: |- + Specifies whether to delete the secret without any recovery window. You + can't use both this parameter and RecoveryWindowInDays in the same call. + If you don't use either, then by default Secrets Manager uses a 30 day + recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery type: boolean recoveryWindowInDays: - description: 'The number of days from 7 to 30 that Secrets Manager waits before permanently deleting the secret. You can''t use both this parameter and ForceDeleteWithoutRecovery in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays' + description: |- + The number of days from 7 to 30 that Secrets Manager waits before + permanently deleting the secret. You can't use both this parameter and + ForceDeleteWithoutRecovery in the same call. If you don't use either, + then by default Secrets Manager uses a 30 day recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays format: int64 type: integer type: object @@ -6719,32 +8281,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -6752,7 +8326,11 @@ spec: type: string environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -6763,10 +8341,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6774,7 +8357,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6798,29 +8383,41 @@ spec: account: type: string apiKeyRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6833,23 +8430,34 @@ spec: account: type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Conjur using the JWT authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountRef specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6857,7 +8465,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6873,7 +8483,10 @@ spec: caBundle: type: string caProvider: - description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + description: |- + Used to provide custom certificate authority (CA) certificates + for a secret store. The CAProvider points to a Secret or ConfigMap resource + that contains a PEM-encoded certificate. properties: key: description: The key where the CA certificate can be found in the Secret or ConfigMap. @@ -6882,7 +8495,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -6901,7 +8516,9 @@ spec: - url type: object delinea: - description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + description: |- + Delinea DevOps Secrets Vault + https://docs.delinea.com/online-help/products/devops-secrets-vault/current properties: clientId: description: ClientID is the non-secret part of the credential. @@ -6910,13 +8527,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -6930,13 +8551,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -6947,10 +8572,14 @@ spec: description: Tenant is the chosen hostname / site name. type: string tld: - description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + description: |- + TLD is based on the server location that was chosen during provisioning. + If unset, defaults to "com". type: string urlTemplate: - description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + description: |- + URLTemplate + If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". type: string required: - clientId @@ -6966,16 +8595,23 @@ spec: secretRef: properties: dopplerToken: - description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + description: |- + The DopplerToken is used for authentication. + See https://docs.doppler.com/reference/api#authentication for auth token types. + The Key attribute defaults to dopplerToken if not specified. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7048,13 +8684,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7070,7 +8710,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7078,7 +8721,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7105,13 +8750,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7166,13 +8815,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7187,16 +8840,22 @@ spec: description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider properties: authRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object folderID: @@ -7217,29 +8876,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7247,7 +8918,10 @@ spec: description: points to a service account that should be used for authentication properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7255,7 +8929,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7264,16 +8940,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7299,7 +8981,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -7332,13 +9016,17 @@ spec: description: The ConnectToken is used for authentication to a 1Password Connect Server. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7364,7 +9052,9 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, use the instance principal, otherwise the user credentials specified in Auth. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -7373,26 +9063,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7411,13 +9109,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -7428,10 +9133,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7439,7 +9149,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7461,13 +9173,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -7490,13 +9206,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -7518,16 +9238,22 @@ spec: clientId: type: string clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7556,39 +9282,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7596,37 +9344,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -7638,7 +9402,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7646,7 +9413,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7668,39 +9437,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7714,25 +9498,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7740,7 +9540,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7750,55 +9552,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7806,7 +9639,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7816,27 +9651,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -7846,13 +9694,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -7860,23 +9712,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -7884,7 +9747,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -7897,7 +9764,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -7910,23 +9779,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -7942,7 +9877,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -7983,7 +9922,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -7993,13 +9934,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -8030,13 +9975,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8044,16 +9993,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8073,13 +10028,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8087,16 +10046,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8166,7 +10131,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -8186,17 +10151,28 @@ spec: openAPIV3Schema: 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' + 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' + 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: properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters VDS based on this property type: string method: description: Vault API method to use (GET/POST/other) @@ -8214,39 +10190,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -8254,37 +10252,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -8296,7 +10310,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8304,7 +10321,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8326,39 +10345,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8372,25 +10406,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8398,7 +10448,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8408,55 +10460,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8464,7 +10547,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8474,27 +10559,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -8504,13 +10602,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -8518,23 +10620,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -8542,7 +10655,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -8555,7 +10672,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -8568,23 +10687,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -8595,7 +10780,12 @@ spec: type: object resultType: default: Data - description: Result type defines which data is returned from the generator. By default it is the "data" section of the Vault API response. When using e.g. /auth/token/create the "data" section is empty but the "auth" section contains the generated token. Please refer to the vault docs regarding the result data structure. + description: |- + Result type defines which data is returned from the generator. + By default it is the "data" section of the Vault API response. + When using e.g. /auth/token/create the "data" section is empty but + the "auth" section contains the generated token. + Please refer to the vault docs regarding the result data structure. enum: - Data - Auth @@ -8626,10 +10816,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8693,10 +10883,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8802,10 +10992,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -8842,10 +11032,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8886,10 +11076,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8907,10 +11097,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8927,10 +11117,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8963,10 +11153,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -9002,10 +11192,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -9023,10 +11213,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -9047,10 +11237,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9062,10 +11252,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -9080,7 +11270,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -9110,10 +11300,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9125,10 +11315,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -9143,7 +11333,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -9160,10 +11350,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9175,10 +11365,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -9193,7 +11383,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index 3fca7728..ffcf6cb6 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -85,18 +85,39 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: "ACRAccessToken returns a Azure Container Registry token that can be used for pushing/pulling images. Note: by default it will return an ACR Refresh Token with full access (depending on the identity). This can be scoped down to the repository level using .spec.scope. In case scope is defined it will return an ACR Access Token. \n See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md" + description: |- + ACRAccessToken returns a Azure Container Registry token + that can be used for pushing/pulling images. + Note: by default it will return an ACR Refresh Token with full access + (depending on the identity). + This can be scoped down to the repository level using .spec.scope. + In case scope is defined it will return an ACR Access Token. + + + See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md 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' + 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' + 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: 'ACRAccessTokenSpec defines how to generate the access token e.g. how to authenticate and which registry to use. see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview' + description: |- + ACRAccessTokenSpec defines how to generate the access token + e.g. how to authenticate and which registry to use. + see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview properties: auth: properties: @@ -111,32 +132,42 @@ spec: description: ServicePrincipal uses Azure Service Principal credentials to authenticate with Azure. properties: secretRef: - description: Configuration used to authenticate with Azure using static credentials stored in a Kind=Secret. + description: |- + Configuration used to authenticate with Azure using static + credentials stored in a Kind=Secret. properties: clientId: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -147,10 +178,15 @@ spec: description: WorkloadIdentity uses Azure Workload Identity to authenticate with Azure. properties: serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -158,7 +194,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -167,7 +205,11 @@ spec: type: object environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -175,10 +217,23 @@ spec: - GermanCloud type: string registry: - description: the domain name of the ACR registry e.g. foobarexample.azurecr.io + description: |- + the domain name of the ACR registry + e.g. foobarexample.azurecr.io type: string scope: - description: "Define the scope for the access token, e.g. pull/push access for a repository. if not provided it will return a refresh token that has full scope. Note: you need to pin it down to the repository level, there is no wildcard available. \n examples: repository:my-repository:pull,push repository:my-repository:pull \n see docs for details: https://docs.docker.com/registry/spec/auth/scope/" + description: |- + Define the scope for the access token, e.g. pull/push access for a repository. + if not provided it will return a refresh token that has full scope. + Note: you need to pin it down to the repository level, there is no wildcard available. + + + examples: + repository:my-repository:pull,push + repository:my-repository:pull + + + see docs for details: https://docs.docker.com/registry/spec/auth/scope/ type: string tenantId: description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. @@ -208,7 +263,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -239,10 +294,19 @@ spec: description: ClusterExternalSecret is the Schema for the clusterexternalsecrets 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' + 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' + 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 @@ -273,7 +337,9 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. + description: |- + RemoteRef points to the remote secret and defines + which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -311,14 +377,23 @@ spec: - key type: object secretKey: - description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret + description: |- + SecretKey defines the key in which the controller stores + the value. This is the key in the Kind=Secret type: string sourceRef: - description: SourceRef allows you to override the source from which the value will pulled from. + description: |- + SourceRef allows you to override the source + from which the value will pulled from. maxProperties: 1 properties: generatorRef: - description: "GeneratorRef points to a generator custom resource. \n Deprecated: The generatorRef is not implemented in .data[]. this will be removed with v1." + description: |- + GeneratorRef points to a generator custom resource. + + + Deprecated: The generatorRef is not implemented in .data[]. + this will be removed with v1. properties: apiVersion: default: generators.external-secrets.io/v1alpha1 @@ -338,7 +413,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -353,11 +430,15 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: properties: extract: - description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to extract multiple key/value pairs from one secret + Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -395,7 +476,9 @@ spec: - key type: object find: - description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to find secrets based on tags or regular expressions + Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -430,11 +513,15 @@ spec: type: object type: object rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + description: |- + Used to rewrite secret Keys after getting them from the secret Provider + Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) items: properties: regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + description: |- + Used to rewrite with regular expressions. + The resulting key will be the output of a regexp.ReplaceAll operation. properties: source: description: Used to define the regular expression of a re.Compiler. @@ -447,10 +534,14 @@ spec: - target type: object transform: - description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + description: |- + Used to apply string transformation on the secrets. + The resulting key will be the output of the template applied by the operation. properties: template: - description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + description: |- + Used to define the template to apply on the secret name. + `.value ` will specify the secret name in the template. type: string required: - template @@ -458,7 +549,13 @@ spec: type: object type: array sourceRef: - description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + description: |- + SourceRef points to a store or generator + which contains secret values ready to use. + Use this in combination with Extract or Find pull values out of + a specific SecretStore. + When sourceRef points to a generator Extract or Find is not supported. + The generator returns a static map of values maxProperties: 1 properties: generatorRef: @@ -482,7 +579,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -495,13 +594,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -513,11 +617,15 @@ spec: default: creationPolicy: Owner deletionPolicy: Retain - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Orphan @@ -526,7 +634,9 @@ spec: type: string deletionPolicy: default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + description: |- + DeletionPolicy defines rules on how to delete the resulting Secret + Defaults to 'Retain' enum: - Delete - Merge @@ -536,7 +646,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -547,7 +660,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -641,16 +757,24 @@ spec: 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. + 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. + 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. + 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 @@ -662,7 +786,10 @@ spec: 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. + 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 @@ -739,7 +866,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -768,10 +895,19 @@ spec: description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -779,7 +915,9 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -796,7 +934,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -805,23 +945,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -829,7 +984,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -839,51 +996,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -942,26 +1120,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -980,7 +1166,10 @@ spec: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -989,7 +1178,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -997,39 +1189,51 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1060,32 +1264,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -1095,10 +1311,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1106,7 +1327,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1155,13 +1378,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1177,7 +1404,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1185,7 +1415,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1212,13 +1444,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1246,13 +1482,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1277,29 +1517,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1310,7 +1562,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1318,7 +1573,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1328,16 +1585,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1387,7 +1650,10 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, instance principal is used. Optionally, the authenticating principal type + and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -1396,26 +1662,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1434,13 +1708,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -1451,10 +1732,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1462,7 +1748,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1481,26 +1769,40 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1509,55 +1811,83 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1565,7 +1895,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1575,55 +1907,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1631,7 +1994,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1641,27 +2006,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -1671,18 +2049,26 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -1708,23 +2094,40 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -1740,7 +2143,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -1781,7 +2188,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -1791,13 +2200,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1828,13 +2241,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1842,16 +2259,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1918,10 +2341,19 @@ spec: description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -1931,7 +2363,9 @@ spec: conditions: description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore items: - description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + description: |- + ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in + for a ClusterSecretStore instance. properties: namespaceSelector: description: Choose namespace using a labelSelector @@ -1939,16 +2373,24 @@ spec: 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. + 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. + 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. + 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 @@ -1960,7 +2402,10 @@ spec: 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. + 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 @@ -1972,7 +2417,9 @@ spec: type: object type: array controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -1989,7 +2436,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -1998,23 +2447,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2022,7 +2486,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2032,51 +2498,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -2089,7 +2576,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2135,26 +2624,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2178,7 +2675,10 @@ spec: type: string type: array auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -2187,7 +2687,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2195,52 +2698,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2258,10 +2780,20 @@ spec: description: SecretsManager defines how the provider behaves when interacting with AWS SecretsManager properties: forceDeleteWithoutRecovery: - description: 'Specifies whether to delete the secret without any recovery window. You can''t use both this parameter and RecoveryWindowInDays in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery' + description: |- + Specifies whether to delete the secret without any recovery window. You + can't use both this parameter and RecoveryWindowInDays in the same call. + If you don't use either, then by default Secrets Manager uses a 30 day + recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery type: boolean recoveryWindowInDays: - description: 'The number of days from 7 to 30 that Secrets Manager waits before permanently deleting the secret. You can''t use both this parameter and ForceDeleteWithoutRecovery in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays' + description: |- + The number of days from 7 to 30 that Secrets Manager waits before + permanently deleting the secret. You can't use both this parameter and + ForceDeleteWithoutRecovery in the same call. If you don't use either, + then by default Secrets Manager uses a 30 day recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays format: int64 type: integer type: object @@ -2303,32 +2835,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -2336,7 +2880,11 @@ spec: type: string environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -2347,10 +2895,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2358,7 +2911,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2382,29 +2937,41 @@ spec: account: type: string apiKeyRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2417,23 +2984,34 @@ spec: account: type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Conjur using the JWT authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountRef specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2441,7 +3019,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2457,7 +3037,10 @@ spec: caBundle: type: string caProvider: - description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + description: |- + Used to provide custom certificate authority (CA) certificates + for a secret store. The CAProvider points to a Secret or ConfigMap resource + that contains a PEM-encoded certificate. properties: key: description: The key where the CA certificate can be found in the Secret or ConfigMap. @@ -2466,7 +3049,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2485,7 +3070,9 @@ spec: - url type: object delinea: - description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + description: |- + Delinea DevOps Secrets Vault + https://docs.delinea.com/online-help/products/devops-secrets-vault/current properties: clientId: description: ClientID is the non-secret part of the credential. @@ -2494,13 +3081,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -2514,13 +3105,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -2531,10 +3126,14 @@ spec: description: Tenant is the chosen hostname / site name. type: string tld: - description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + description: |- + TLD is based on the server location that was chosen during provisioning. + If unset, defaults to "com". type: string urlTemplate: - description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + description: |- + URLTemplate + If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". type: string required: - clientId @@ -2550,16 +3149,23 @@ spec: secretRef: properties: dopplerToken: - description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + description: |- + The DopplerToken is used for authentication. + See https://docs.doppler.com/reference/api#authentication for auth token types. + The Key attribute defaults to dopplerToken if not specified. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2632,13 +3238,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2654,7 +3264,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2662,7 +3275,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2689,13 +3304,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2750,13 +3369,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2771,16 +3394,22 @@ spec: description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider properties: authRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object folderID: @@ -2801,29 +3430,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2831,7 +3472,10 @@ spec: description: points to a service account that should be used for authentication properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2839,7 +3483,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2848,16 +3494,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2883,7 +3535,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2916,13 +3570,17 @@ spec: description: The ConnectToken is used for authentication to a 1Password Connect Server. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2948,7 +3606,9 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, use the instance principal, otherwise the user credentials specified in Auth. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -2957,26 +3617,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2995,13 +3663,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -3012,10 +3687,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3023,7 +3703,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3045,13 +3727,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -3074,13 +3760,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -3102,16 +3792,22 @@ spec: clientId: type: string clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3140,39 +3836,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3180,37 +3898,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -3222,7 +3956,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3230,7 +3967,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3252,39 +3991,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3298,25 +4052,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3324,7 +4094,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3334,55 +4106,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3390,7 +4193,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3400,27 +4205,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -3430,13 +4248,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -3444,23 +4266,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -3468,7 +4301,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -3481,7 +4318,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -3494,23 +4333,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -3526,7 +4431,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -3567,7 +4476,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -3577,13 +4488,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3614,13 +4529,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3628,16 +4547,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3657,13 +4582,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3671,16 +4600,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3750,7 +4685,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3768,13 +4703,28 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an authorization token. The authorization token is valid for 12 hours. The authorizationToken returned is a base64 encoded string that can be decoded and used in a docker login command to authenticate to a registry. For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. + description: |- + ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an + authorization token. + The authorization token is valid for 12 hours. + The authorizationToken returned is a base64 encoded string that can be decoded + and used in a docker login command to authenticate to a registry. + For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. 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' + 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' + 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 @@ -3790,7 +4740,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3798,52 +4751,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3852,7 +4824,9 @@ spec: description: Region specifies the region to operate in. type: string role: - description: You can assume a role before making calls to the desired AWS service. + description: |- + You can assume a role before making calls to the + desired AWS service. type: string required: - region @@ -3878,7 +4852,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -3910,10 +4884,19 @@ spec: description: ExternalSecret is the Schema for the external-secrets 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' + 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' + 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 @@ -3955,7 +4938,9 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: description: ExternalSecretDataRemoteRef defines Provider data location. properties: @@ -3981,13 +4966,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -3996,11 +4986,15 @@ spec: - name type: object target: - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Merge @@ -4010,7 +5004,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -4021,7 +5018,10 @@ spec: type: object engineVersion: default: v1 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -4093,7 +5093,10 @@ spec: description: Binding represents a servicebinding.io Provisioned Service reference to the secret properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -4117,7 +5120,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -4149,10 +5154,19 @@ spec: description: ExternalSecret is the Schema for the external-secrets 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' + 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' + 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 @@ -4165,7 +5179,9 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. + description: |- + RemoteRef points to the remote secret and defines + which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -4203,14 +5219,23 @@ spec: - key type: object secretKey: - description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret + description: |- + SecretKey defines the key in which the controller stores + the value. This is the key in the Kind=Secret type: string sourceRef: - description: SourceRef allows you to override the source from which the value will pulled from. + description: |- + SourceRef allows you to override the source + from which the value will pulled from. maxProperties: 1 properties: generatorRef: - description: "GeneratorRef points to a generator custom resource. \n Deprecated: The generatorRef is not implemented in .data[]. this will be removed with v1." + description: |- + GeneratorRef points to a generator custom resource. + + + Deprecated: The generatorRef is not implemented in .data[]. + this will be removed with v1. properties: apiVersion: default: generators.external-secrets.io/v1alpha1 @@ -4230,7 +5255,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4245,11 +5272,15 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: properties: extract: - description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to extract multiple key/value pairs from one secret + Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -4287,7 +5318,9 @@ spec: - key type: object find: - description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to find secrets based on tags or regular expressions + Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -4322,11 +5355,15 @@ spec: type: object type: object rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + description: |- + Used to rewrite secret Keys after getting them from the secret Provider + Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) items: properties: regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + description: |- + Used to rewrite with regular expressions. + The resulting key will be the output of a regexp.ReplaceAll operation. properties: source: description: Used to define the regular expression of a re.Compiler. @@ -4339,10 +5376,14 @@ spec: - target type: object transform: - description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + description: |- + Used to apply string transformation on the secrets. + The resulting key will be the output of the template applied by the operation. properties: template: - description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + description: |- + Used to define the template to apply on the secret name. + `.value ` will specify the secret name in the template. type: string required: - template @@ -4350,7 +5391,13 @@ spec: type: object type: array sourceRef: - description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + description: |- + SourceRef points to a store or generator + which contains secret values ready to use. + Use this in combination with Extract or Find pull values out of + a specific SecretStore. + When sourceRef points to a generator Extract or Find is not supported. + The generator returns a static map of values maxProperties: 1 properties: generatorRef: @@ -4374,7 +5421,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4387,13 +5436,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4405,11 +5459,15 @@ spec: default: creationPolicy: Owner deletionPolicy: Retain - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Orphan @@ -4418,7 +5476,9 @@ spec: type: string deletionPolicy: default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + description: |- + DeletionPolicy defines rules on how to delete the resulting Secret + Defaults to 'Retain' enum: - Delete - Merge @@ -4428,7 +5488,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -4439,7 +5502,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -4533,7 +5599,10 @@ spec: description: Binding represents a servicebinding.io Provisioned Service reference to the secret properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -4557,7 +5626,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -4586,7 +5657,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4604,13 +5675,24 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Fake generator is used for testing. It lets you define a static set of credentials that is always returned. + description: |- + Fake generator is used for testing. It lets you define + a static set of credentials that is always returned. 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' + 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' + 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 @@ -4618,12 +5700,16 @@ spec: description: FakeSpec contains the static data. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters VDS based on this property type: string data: additionalProperties: type: string - description: Data defines the static data returned by this generator. + description: |- + Data defines the static data returned + by this generator. type: object type: object type: object @@ -4647,7 +5733,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4665,13 +5751,24 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: GCRAccessToken generates an GCP access token that can be used to authenticate with GCR. + description: |- + GCRAccessToken generates an GCP access token + that can be used to authenticate with GCR. 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' + 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' + 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 @@ -4686,13 +5783,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -4708,7 +5809,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -4716,7 +5820,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -4755,7 +5861,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4773,13 +5879,25 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Password generates a random password based on the configuration parameters in spec. You can specify the length, characterset and other attributes. + description: |- + Password generates a random password based on the + configuration parameters in spec. + You can specify the length, characterset and other attributes. 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' + 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' + 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 @@ -4791,21 +5909,29 @@ spec: description: set AllowRepeat to true to allow repeating characters. type: boolean digits: - description: Digits specifies the number of digits in the generated password. If omitted it defaults to 25% of the length of the password + description: |- + Digits specifies the number of digits in the generated + password. If omitted it defaults to 25% of the length of the password type: integer length: default: 24 - description: Length of the password to be generated. Defaults to 24 + description: |- + Length of the password to be generated. + Defaults to 24 type: integer noUpper: default: false description: Set NoUpper to disable uppercase characters type: boolean symbolCharacters: - description: SymbolCharacters specifies the special characters that should be used in the generated password. + description: |- + SymbolCharacters specifies the special characters that should be used + in the generated password. type: string symbols: - description: Symbols specifies the number of symbol characters in the generated password. If omitted it defaults to 25% of the length of the password + description: |- + Symbols specifies the number of symbol characters in the generated + password. If omitted it defaults to 25% of the length of the password type: integer required: - allowRepeat @@ -4833,7 +5959,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4858,10 +5984,19 @@ spec: openAPIV3Schema: 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' + 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' + 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 @@ -4894,7 +6029,9 @@ spec: - remoteRef type: object metadata: - description: Metadata is metadata attached to the secret. The structure of metadata is provider specific, please look it up in the provider documentation. + description: |- + Metadata is metadata attached to the secret. + The structure of metadata is provider specific, please look it up in the provider documentation. x-kubernetes-preserve-unknown-fields: true required: - match @@ -4915,7 +6052,9 @@ spec: properties: kind: default: SecretStore - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string labelSelector: description: Optionally, sync to secret stores with label selector @@ -4923,16 +6062,24 @@ spec: 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. + 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. + 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. + 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 @@ -4944,7 +6091,10 @@ spec: 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. + 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 @@ -4977,7 +6127,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -5092,7 +6245,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -5122,7 +6277,9 @@ spec: - remoteRef type: object metadata: - description: Metadata is metadata attached to the secret. The structure of metadata is provider specific, please look it up in the provider documentation. + description: |- + Metadata is metadata attached to the secret. + The structure of metadata is provider specific, please look it up in the provider documentation. x-kubernetes-preserve-unknown-fields: true required: - match @@ -5155,7 +6312,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -5184,10 +6341,19 @@ spec: description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -5195,7 +6361,9 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -5212,7 +6380,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -5221,23 +6391,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5245,7 +6430,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5255,51 +6442,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -5358,26 +6566,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5396,7 +6612,10 @@ spec: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -5405,7 +6624,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5413,39 +6635,51 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5476,32 +6710,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -5511,10 +6757,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5522,7 +6773,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5571,13 +6824,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5593,7 +6850,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5601,7 +6861,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5628,13 +6890,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5662,13 +6928,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5693,29 +6963,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5726,7 +7008,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5734,7 +7019,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5744,16 +7031,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5803,7 +7096,10 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, instance principal is used. Optionally, the authenticating principal type + and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -5812,26 +7108,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5850,13 +7154,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -5867,10 +7178,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5878,7 +7194,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5897,26 +7215,40 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5925,55 +7257,83 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5981,7 +7341,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5991,55 +7353,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6047,7 +7440,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6057,27 +7452,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -6087,18 +7495,26 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6124,23 +7540,40 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -6156,7 +7589,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6197,7 +7634,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -6207,13 +7646,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6244,13 +7687,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6258,16 +7705,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6334,10 +7787,19 @@ spec: description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -6347,7 +7809,9 @@ spec: conditions: description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore items: - description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + description: |- + ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in + for a ClusterSecretStore instance. properties: namespaceSelector: description: Choose namespace using a labelSelector @@ -6355,16 +7819,24 @@ spec: 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. + 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. + 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. + 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 @@ -6376,7 +7848,10 @@ spec: 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. + 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 @@ -6388,7 +7863,9 @@ spec: type: object type: array controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -6405,7 +7882,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -6414,23 +7893,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6438,7 +7932,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6448,51 +7944,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6505,7 +8022,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -6551,26 +8070,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6594,7 +8121,10 @@ spec: type: string type: array auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -6603,7 +8133,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6611,52 +8144,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6674,10 +8226,20 @@ spec: description: SecretsManager defines how the provider behaves when interacting with AWS SecretsManager properties: forceDeleteWithoutRecovery: - description: 'Specifies whether to delete the secret without any recovery window. You can''t use both this parameter and RecoveryWindowInDays in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery' + description: |- + Specifies whether to delete the secret without any recovery window. You + can't use both this parameter and RecoveryWindowInDays in the same call. + If you don't use either, then by default Secrets Manager uses a 30 day + recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery type: boolean recoveryWindowInDays: - description: 'The number of days from 7 to 30 that Secrets Manager waits before permanently deleting the secret. You can''t use both this parameter and ForceDeleteWithoutRecovery in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays' + description: |- + The number of days from 7 to 30 that Secrets Manager waits before + permanently deleting the secret. You can't use both this parameter and + ForceDeleteWithoutRecovery in the same call. If you don't use either, + then by default Secrets Manager uses a 30 day recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays format: int64 type: integer type: object @@ -6719,32 +8281,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -6752,7 +8326,11 @@ spec: type: string environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -6763,10 +8341,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6774,7 +8357,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6798,29 +8383,41 @@ spec: account: type: string apiKeyRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6833,23 +8430,34 @@ spec: account: type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Conjur using the JWT authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountRef specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6857,7 +8465,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6873,7 +8483,10 @@ spec: caBundle: type: string caProvider: - description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + description: |- + Used to provide custom certificate authority (CA) certificates + for a secret store. The CAProvider points to a Secret or ConfigMap resource + that contains a PEM-encoded certificate. properties: key: description: The key where the CA certificate can be found in the Secret or ConfigMap. @@ -6882,7 +8495,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -6901,7 +8516,9 @@ spec: - url type: object delinea: - description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + description: |- + Delinea DevOps Secrets Vault + https://docs.delinea.com/online-help/products/devops-secrets-vault/current properties: clientId: description: ClientID is the non-secret part of the credential. @@ -6910,13 +8527,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -6930,13 +8551,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -6947,10 +8572,14 @@ spec: description: Tenant is the chosen hostname / site name. type: string tld: - description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + description: |- + TLD is based on the server location that was chosen during provisioning. + If unset, defaults to "com". type: string urlTemplate: - description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + description: |- + URLTemplate + If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". type: string required: - clientId @@ -6966,16 +8595,23 @@ spec: secretRef: properties: dopplerToken: - description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + description: |- + The DopplerToken is used for authentication. + See https://docs.doppler.com/reference/api#authentication for auth token types. + The Key attribute defaults to dopplerToken if not specified. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7048,13 +8684,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7070,7 +8710,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7078,7 +8721,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7105,13 +8750,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7166,13 +8815,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7187,16 +8840,22 @@ spec: description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider properties: authRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object folderID: @@ -7217,29 +8876,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7247,7 +8918,10 @@ spec: description: points to a service account that should be used for authentication properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7255,7 +8929,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7264,16 +8940,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7299,7 +8981,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -7332,13 +9016,17 @@ spec: description: The ConnectToken is used for authentication to a 1Password Connect Server. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7364,7 +9052,9 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, use the instance principal, otherwise the user credentials specified in Auth. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -7373,26 +9063,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7411,13 +9109,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -7428,10 +9133,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7439,7 +9149,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7461,13 +9173,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -7490,13 +9206,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -7518,16 +9238,22 @@ spec: clientId: type: string clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7556,39 +9282,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7596,37 +9344,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -7638,7 +9402,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7646,7 +9413,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7668,39 +9437,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7714,25 +9498,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7740,7 +9540,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7750,55 +9552,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7806,7 +9639,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7816,27 +9651,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -7846,13 +9694,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -7860,23 +9712,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -7884,7 +9747,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -7897,7 +9764,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -7910,23 +9779,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -7942,7 +9877,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -7983,7 +9922,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -7993,13 +9934,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -8030,13 +9975,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8044,16 +9993,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8073,13 +10028,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8087,16 +10046,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8166,7 +10131,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -8186,17 +10151,28 @@ spec: openAPIV3Schema: 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' + 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' + 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: properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters VDS based on this property type: string method: description: Vault API method to use (GET/POST/other) @@ -8214,39 +10190,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -8254,37 +10252,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -8296,7 +10310,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8304,7 +10321,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8326,39 +10345,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8372,25 +10406,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8398,7 +10448,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8408,55 +10460,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8464,7 +10547,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8474,27 +10559,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -8504,13 +10602,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -8518,23 +10620,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -8542,7 +10655,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -8555,7 +10672,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -8568,23 +10687,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -8595,7 +10780,12 @@ spec: type: object resultType: default: Data - description: Result type defines which data is returned from the generator. By default it is the "data" section of the Vault API response. When using e.g. /auth/token/create the "data" section is empty but the "auth" section contains the generated token. Please refer to the vault docs regarding the result data structure. + description: |- + Result type defines which data is returned from the generator. + By default it is the "data" section of the Vault API response. + When using e.g. /auth/token/create the "data" section is empty but + the "auth" section contains the generated token. + Please refer to the vault docs regarding the result data structure. enum: - Data - Auth @@ -8626,10 +10816,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8693,10 +10883,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8802,10 +10992,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -8842,10 +11032,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8886,10 +11076,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8907,10 +11097,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8927,10 +11117,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8963,10 +11153,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -9002,10 +11192,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -9023,10 +11213,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -9047,10 +11237,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9062,10 +11252,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -9080,7 +11270,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -9110,10 +11300,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9125,10 +11315,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -9143,7 +11333,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -9160,10 +11350,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9175,10 +11365,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -9193,7 +11383,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index 3fca7728..ffcf6cb6 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -85,18 +85,39 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: "ACRAccessToken returns a Azure Container Registry token that can be used for pushing/pulling images. Note: by default it will return an ACR Refresh Token with full access (depending on the identity). This can be scoped down to the repository level using .spec.scope. In case scope is defined it will return an ACR Access Token. \n See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md" + description: |- + ACRAccessToken returns a Azure Container Registry token + that can be used for pushing/pulling images. + Note: by default it will return an ACR Refresh Token with full access + (depending on the identity). + This can be scoped down to the repository level using .spec.scope. + In case scope is defined it will return an ACR Access Token. + + + See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md 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' + 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' + 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: 'ACRAccessTokenSpec defines how to generate the access token e.g. how to authenticate and which registry to use. see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview' + description: |- + ACRAccessTokenSpec defines how to generate the access token + e.g. how to authenticate and which registry to use. + see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview properties: auth: properties: @@ -111,32 +132,42 @@ spec: description: ServicePrincipal uses Azure Service Principal credentials to authenticate with Azure. properties: secretRef: - description: Configuration used to authenticate with Azure using static credentials stored in a Kind=Secret. + description: |- + Configuration used to authenticate with Azure using static + credentials stored in a Kind=Secret. properties: clientId: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -147,10 +178,15 @@ spec: description: WorkloadIdentity uses Azure Workload Identity to authenticate with Azure. properties: serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -158,7 +194,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -167,7 +205,11 @@ spec: type: object environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -175,10 +217,23 @@ spec: - GermanCloud type: string registry: - description: the domain name of the ACR registry e.g. foobarexample.azurecr.io + description: |- + the domain name of the ACR registry + e.g. foobarexample.azurecr.io type: string scope: - description: "Define the scope for the access token, e.g. pull/push access for a repository. if not provided it will return a refresh token that has full scope. Note: you need to pin it down to the repository level, there is no wildcard available. \n examples: repository:my-repository:pull,push repository:my-repository:pull \n see docs for details: https://docs.docker.com/registry/spec/auth/scope/" + description: |- + Define the scope for the access token, e.g. pull/push access for a repository. + if not provided it will return a refresh token that has full scope. + Note: you need to pin it down to the repository level, there is no wildcard available. + + + examples: + repository:my-repository:pull,push + repository:my-repository:pull + + + see docs for details: https://docs.docker.com/registry/spec/auth/scope/ type: string tenantId: description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. @@ -208,7 +263,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -239,10 +294,19 @@ spec: description: ClusterExternalSecret is the Schema for the clusterexternalsecrets 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' + 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' + 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 @@ -273,7 +337,9 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. + description: |- + RemoteRef points to the remote secret and defines + which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -311,14 +377,23 @@ spec: - key type: object secretKey: - description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret + description: |- + SecretKey defines the key in which the controller stores + the value. This is the key in the Kind=Secret type: string sourceRef: - description: SourceRef allows you to override the source from which the value will pulled from. + description: |- + SourceRef allows you to override the source + from which the value will pulled from. maxProperties: 1 properties: generatorRef: - description: "GeneratorRef points to a generator custom resource. \n Deprecated: The generatorRef is not implemented in .data[]. this will be removed with v1." + description: |- + GeneratorRef points to a generator custom resource. + + + Deprecated: The generatorRef is not implemented in .data[]. + this will be removed with v1. properties: apiVersion: default: generators.external-secrets.io/v1alpha1 @@ -338,7 +413,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -353,11 +430,15 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: properties: extract: - description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to extract multiple key/value pairs from one secret + Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -395,7 +476,9 @@ spec: - key type: object find: - description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to find secrets based on tags or regular expressions + Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -430,11 +513,15 @@ spec: type: object type: object rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + description: |- + Used to rewrite secret Keys after getting them from the secret Provider + Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) items: properties: regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + description: |- + Used to rewrite with regular expressions. + The resulting key will be the output of a regexp.ReplaceAll operation. properties: source: description: Used to define the regular expression of a re.Compiler. @@ -447,10 +534,14 @@ spec: - target type: object transform: - description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + description: |- + Used to apply string transformation on the secrets. + The resulting key will be the output of the template applied by the operation. properties: template: - description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + description: |- + Used to define the template to apply on the secret name. + `.value ` will specify the secret name in the template. type: string required: - template @@ -458,7 +549,13 @@ spec: type: object type: array sourceRef: - description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + description: |- + SourceRef points to a store or generator + which contains secret values ready to use. + Use this in combination with Extract or Find pull values out of + a specific SecretStore. + When sourceRef points to a generator Extract or Find is not supported. + The generator returns a static map of values maxProperties: 1 properties: generatorRef: @@ -482,7 +579,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -495,13 +594,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -513,11 +617,15 @@ spec: default: creationPolicy: Owner deletionPolicy: Retain - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Orphan @@ -526,7 +634,9 @@ spec: type: string deletionPolicy: default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + description: |- + DeletionPolicy defines rules on how to delete the resulting Secret + Defaults to 'Retain' enum: - Delete - Merge @@ -536,7 +646,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -547,7 +660,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -641,16 +757,24 @@ spec: 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. + 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. + 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. + 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 @@ -662,7 +786,10 @@ spec: 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. + 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 @@ -739,7 +866,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -768,10 +895,19 @@ spec: description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -779,7 +915,9 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -796,7 +934,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -805,23 +945,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -829,7 +984,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -839,51 +996,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -942,26 +1120,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -980,7 +1166,10 @@ spec: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -989,7 +1178,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -997,39 +1189,51 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1060,32 +1264,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -1095,10 +1311,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1106,7 +1327,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1155,13 +1378,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1177,7 +1404,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1185,7 +1415,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1212,13 +1444,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1246,13 +1482,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1277,29 +1517,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1310,7 +1562,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1318,7 +1573,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1328,16 +1585,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1387,7 +1650,10 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, instance principal is used. Optionally, the authenticating principal type + and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -1396,26 +1662,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1434,13 +1708,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -1451,10 +1732,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1462,7 +1748,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1481,26 +1769,40 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1509,55 +1811,83 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1565,7 +1895,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1575,55 +1907,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1631,7 +1994,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1641,27 +2006,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -1671,18 +2049,26 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -1708,23 +2094,40 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -1740,7 +2143,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -1781,7 +2188,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -1791,13 +2200,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1828,13 +2241,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1842,16 +2259,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1918,10 +2341,19 @@ spec: description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -1931,7 +2363,9 @@ spec: conditions: description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore items: - description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + description: |- + ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in + for a ClusterSecretStore instance. properties: namespaceSelector: description: Choose namespace using a labelSelector @@ -1939,16 +2373,24 @@ spec: 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. + 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. + 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. + 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 @@ -1960,7 +2402,10 @@ spec: 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. + 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 @@ -1972,7 +2417,9 @@ spec: type: object type: array controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -1989,7 +2436,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -1998,23 +2447,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2022,7 +2486,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2032,51 +2498,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -2089,7 +2576,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2135,26 +2624,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2178,7 +2675,10 @@ spec: type: string type: array auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -2187,7 +2687,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2195,52 +2698,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2258,10 +2780,20 @@ spec: description: SecretsManager defines how the provider behaves when interacting with AWS SecretsManager properties: forceDeleteWithoutRecovery: - description: 'Specifies whether to delete the secret without any recovery window. You can''t use both this parameter and RecoveryWindowInDays in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery' + description: |- + Specifies whether to delete the secret without any recovery window. You + can't use both this parameter and RecoveryWindowInDays in the same call. + If you don't use either, then by default Secrets Manager uses a 30 day + recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery type: boolean recoveryWindowInDays: - description: 'The number of days from 7 to 30 that Secrets Manager waits before permanently deleting the secret. You can''t use both this parameter and ForceDeleteWithoutRecovery in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays' + description: |- + The number of days from 7 to 30 that Secrets Manager waits before + permanently deleting the secret. You can't use both this parameter and + ForceDeleteWithoutRecovery in the same call. If you don't use either, + then by default Secrets Manager uses a 30 day recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays format: int64 type: integer type: object @@ -2303,32 +2835,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -2336,7 +2880,11 @@ spec: type: string environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -2347,10 +2895,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2358,7 +2911,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2382,29 +2937,41 @@ spec: account: type: string apiKeyRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2417,23 +2984,34 @@ spec: account: type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Conjur using the JWT authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountRef specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2441,7 +3019,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2457,7 +3037,10 @@ spec: caBundle: type: string caProvider: - description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + description: |- + Used to provide custom certificate authority (CA) certificates + for a secret store. The CAProvider points to a Secret or ConfigMap resource + that contains a PEM-encoded certificate. properties: key: description: The key where the CA certificate can be found in the Secret or ConfigMap. @@ -2466,7 +3049,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2485,7 +3070,9 @@ spec: - url type: object delinea: - description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + description: |- + Delinea DevOps Secrets Vault + https://docs.delinea.com/online-help/products/devops-secrets-vault/current properties: clientId: description: ClientID is the non-secret part of the credential. @@ -2494,13 +3081,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -2514,13 +3105,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -2531,10 +3126,14 @@ spec: description: Tenant is the chosen hostname / site name. type: string tld: - description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + description: |- + TLD is based on the server location that was chosen during provisioning. + If unset, defaults to "com". type: string urlTemplate: - description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + description: |- + URLTemplate + If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". type: string required: - clientId @@ -2550,16 +3149,23 @@ spec: secretRef: properties: dopplerToken: - description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + description: |- + The DopplerToken is used for authentication. + See https://docs.doppler.com/reference/api#authentication for auth token types. + The Key attribute defaults to dopplerToken if not specified. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2632,13 +3238,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2654,7 +3264,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2662,7 +3275,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2689,13 +3304,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2750,13 +3369,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2771,16 +3394,22 @@ spec: description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider properties: authRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object folderID: @@ -2801,29 +3430,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2831,7 +3472,10 @@ spec: description: points to a service account that should be used for authentication properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2839,7 +3483,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2848,16 +3494,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2883,7 +3535,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2916,13 +3570,17 @@ spec: description: The ConnectToken is used for authentication to a 1Password Connect Server. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2948,7 +3606,9 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, use the instance principal, otherwise the user credentials specified in Auth. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -2957,26 +3617,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2995,13 +3663,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -3012,10 +3687,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3023,7 +3703,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3045,13 +3727,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -3074,13 +3760,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -3102,16 +3792,22 @@ spec: clientId: type: string clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3140,39 +3836,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3180,37 +3898,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -3222,7 +3956,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3230,7 +3967,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3252,39 +3991,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3298,25 +4052,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3324,7 +4094,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3334,55 +4106,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3390,7 +4193,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3400,27 +4205,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -3430,13 +4248,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -3444,23 +4266,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -3468,7 +4301,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -3481,7 +4318,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -3494,23 +4333,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -3526,7 +4431,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -3567,7 +4476,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -3577,13 +4488,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3614,13 +4529,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3628,16 +4547,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3657,13 +4582,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3671,16 +4600,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3750,7 +4685,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3768,13 +4703,28 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an authorization token. The authorization token is valid for 12 hours. The authorizationToken returned is a base64 encoded string that can be decoded and used in a docker login command to authenticate to a registry. For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. + description: |- + ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an + authorization token. + The authorization token is valid for 12 hours. + The authorizationToken returned is a base64 encoded string that can be decoded + and used in a docker login command to authenticate to a registry. + For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. 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' + 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' + 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 @@ -3790,7 +4740,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3798,52 +4751,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3852,7 +4824,9 @@ spec: description: Region specifies the region to operate in. type: string role: - description: You can assume a role before making calls to the desired AWS service. + description: |- + You can assume a role before making calls to the + desired AWS service. type: string required: - region @@ -3878,7 +4852,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -3910,10 +4884,19 @@ spec: description: ExternalSecret is the Schema for the external-secrets 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' + 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' + 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 @@ -3955,7 +4938,9 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: description: ExternalSecretDataRemoteRef defines Provider data location. properties: @@ -3981,13 +4966,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -3996,11 +4986,15 @@ spec: - name type: object target: - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Merge @@ -4010,7 +5004,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -4021,7 +5018,10 @@ spec: type: object engineVersion: default: v1 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -4093,7 +5093,10 @@ spec: description: Binding represents a servicebinding.io Provisioned Service reference to the secret properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -4117,7 +5120,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -4149,10 +5154,19 @@ spec: description: ExternalSecret is the Schema for the external-secrets 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' + 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' + 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 @@ -4165,7 +5179,9 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. + description: |- + RemoteRef points to the remote secret and defines + which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -4203,14 +5219,23 @@ spec: - key type: object secretKey: - description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret + description: |- + SecretKey defines the key in which the controller stores + the value. This is the key in the Kind=Secret type: string sourceRef: - description: SourceRef allows you to override the source from which the value will pulled from. + description: |- + SourceRef allows you to override the source + from which the value will pulled from. maxProperties: 1 properties: generatorRef: - description: "GeneratorRef points to a generator custom resource. \n Deprecated: The generatorRef is not implemented in .data[]. this will be removed with v1." + description: |- + GeneratorRef points to a generator custom resource. + + + Deprecated: The generatorRef is not implemented in .data[]. + this will be removed with v1. properties: apiVersion: default: generators.external-secrets.io/v1alpha1 @@ -4230,7 +5255,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4245,11 +5272,15 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: properties: extract: - description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to extract multiple key/value pairs from one secret + Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -4287,7 +5318,9 @@ spec: - key type: object find: - description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to find secrets based on tags or regular expressions + Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -4322,11 +5355,15 @@ spec: type: object type: object rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + description: |- + Used to rewrite secret Keys after getting them from the secret Provider + Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) items: properties: regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + description: |- + Used to rewrite with regular expressions. + The resulting key will be the output of a regexp.ReplaceAll operation. properties: source: description: Used to define the regular expression of a re.Compiler. @@ -4339,10 +5376,14 @@ spec: - target type: object transform: - description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + description: |- + Used to apply string transformation on the secrets. + The resulting key will be the output of the template applied by the operation. properties: template: - description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + description: |- + Used to define the template to apply on the secret name. + `.value ` will specify the secret name in the template. type: string required: - template @@ -4350,7 +5391,13 @@ spec: type: object type: array sourceRef: - description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + description: |- + SourceRef points to a store or generator + which contains secret values ready to use. + Use this in combination with Extract or Find pull values out of + a specific SecretStore. + When sourceRef points to a generator Extract or Find is not supported. + The generator returns a static map of values maxProperties: 1 properties: generatorRef: @@ -4374,7 +5421,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4387,13 +5436,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4405,11 +5459,15 @@ spec: default: creationPolicy: Owner deletionPolicy: Retain - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Orphan @@ -4418,7 +5476,9 @@ spec: type: string deletionPolicy: default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + description: |- + DeletionPolicy defines rules on how to delete the resulting Secret + Defaults to 'Retain' enum: - Delete - Merge @@ -4428,7 +5488,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -4439,7 +5502,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -4533,7 +5599,10 @@ spec: description: Binding represents a servicebinding.io Provisioned Service reference to the secret properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -4557,7 +5626,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -4586,7 +5657,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4604,13 +5675,24 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Fake generator is used for testing. It lets you define a static set of credentials that is always returned. + description: |- + Fake generator is used for testing. It lets you define + a static set of credentials that is always returned. 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' + 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' + 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 @@ -4618,12 +5700,16 @@ spec: description: FakeSpec contains the static data. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters VDS based on this property type: string data: additionalProperties: type: string - description: Data defines the static data returned by this generator. + description: |- + Data defines the static data returned + by this generator. type: object type: object type: object @@ -4647,7 +5733,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4665,13 +5751,24 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: GCRAccessToken generates an GCP access token that can be used to authenticate with GCR. + description: |- + GCRAccessToken generates an GCP access token + that can be used to authenticate with GCR. 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' + 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' + 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 @@ -4686,13 +5783,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -4708,7 +5809,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -4716,7 +5820,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -4755,7 +5861,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4773,13 +5879,25 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Password generates a random password based on the configuration parameters in spec. You can specify the length, characterset and other attributes. + description: |- + Password generates a random password based on the + configuration parameters in spec. + You can specify the length, characterset and other attributes. 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' + 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' + 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 @@ -4791,21 +5909,29 @@ spec: description: set AllowRepeat to true to allow repeating characters. type: boolean digits: - description: Digits specifies the number of digits in the generated password. If omitted it defaults to 25% of the length of the password + description: |- + Digits specifies the number of digits in the generated + password. If omitted it defaults to 25% of the length of the password type: integer length: default: 24 - description: Length of the password to be generated. Defaults to 24 + description: |- + Length of the password to be generated. + Defaults to 24 type: integer noUpper: default: false description: Set NoUpper to disable uppercase characters type: boolean symbolCharacters: - description: SymbolCharacters specifies the special characters that should be used in the generated password. + description: |- + SymbolCharacters specifies the special characters that should be used + in the generated password. type: string symbols: - description: Symbols specifies the number of symbol characters in the generated password. If omitted it defaults to 25% of the length of the password + description: |- + Symbols specifies the number of symbol characters in the generated + password. If omitted it defaults to 25% of the length of the password type: integer required: - allowRepeat @@ -4833,7 +5959,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4858,10 +5984,19 @@ spec: openAPIV3Schema: 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' + 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' + 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 @@ -4894,7 +6029,9 @@ spec: - remoteRef type: object metadata: - description: Metadata is metadata attached to the secret. The structure of metadata is provider specific, please look it up in the provider documentation. + description: |- + Metadata is metadata attached to the secret. + The structure of metadata is provider specific, please look it up in the provider documentation. x-kubernetes-preserve-unknown-fields: true required: - match @@ -4915,7 +6052,9 @@ spec: properties: kind: default: SecretStore - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string labelSelector: description: Optionally, sync to secret stores with label selector @@ -4923,16 +6062,24 @@ spec: 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. + 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. + 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. + 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 @@ -4944,7 +6091,10 @@ spec: 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. + 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 @@ -4977,7 +6127,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -5092,7 +6245,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -5122,7 +6277,9 @@ spec: - remoteRef type: object metadata: - description: Metadata is metadata attached to the secret. The structure of metadata is provider specific, please look it up in the provider documentation. + description: |- + Metadata is metadata attached to the secret. + The structure of metadata is provider specific, please look it up in the provider documentation. x-kubernetes-preserve-unknown-fields: true required: - match @@ -5155,7 +6312,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -5184,10 +6341,19 @@ spec: description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -5195,7 +6361,9 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -5212,7 +6380,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -5221,23 +6391,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5245,7 +6430,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5255,51 +6442,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -5358,26 +6566,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5396,7 +6612,10 @@ spec: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -5405,7 +6624,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5413,39 +6635,51 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5476,32 +6710,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -5511,10 +6757,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5522,7 +6773,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5571,13 +6824,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5593,7 +6850,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5601,7 +6861,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5628,13 +6890,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5662,13 +6928,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5693,29 +6963,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5726,7 +7008,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5734,7 +7019,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5744,16 +7031,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5803,7 +7096,10 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, instance principal is used. Optionally, the authenticating principal type + and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -5812,26 +7108,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5850,13 +7154,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -5867,10 +7178,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5878,7 +7194,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5897,26 +7215,40 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5925,55 +7257,83 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5981,7 +7341,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5991,55 +7353,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6047,7 +7440,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6057,27 +7452,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -6087,18 +7495,26 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6124,23 +7540,40 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -6156,7 +7589,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6197,7 +7634,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -6207,13 +7646,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6244,13 +7687,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6258,16 +7705,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6334,10 +7787,19 @@ spec: description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -6347,7 +7809,9 @@ spec: conditions: description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore items: - description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + description: |- + ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in + for a ClusterSecretStore instance. properties: namespaceSelector: description: Choose namespace using a labelSelector @@ -6355,16 +7819,24 @@ spec: 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. + 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. + 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. + 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 @@ -6376,7 +7848,10 @@ spec: 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. + 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 @@ -6388,7 +7863,9 @@ spec: type: object type: array controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -6405,7 +7882,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -6414,23 +7893,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6438,7 +7932,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6448,51 +7944,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6505,7 +8022,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -6551,26 +8070,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6594,7 +8121,10 @@ spec: type: string type: array auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -6603,7 +8133,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6611,52 +8144,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6674,10 +8226,20 @@ spec: description: SecretsManager defines how the provider behaves when interacting with AWS SecretsManager properties: forceDeleteWithoutRecovery: - description: 'Specifies whether to delete the secret without any recovery window. You can''t use both this parameter and RecoveryWindowInDays in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery' + description: |- + Specifies whether to delete the secret without any recovery window. You + can't use both this parameter and RecoveryWindowInDays in the same call. + If you don't use either, then by default Secrets Manager uses a 30 day + recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery type: boolean recoveryWindowInDays: - description: 'The number of days from 7 to 30 that Secrets Manager waits before permanently deleting the secret. You can''t use both this parameter and ForceDeleteWithoutRecovery in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays' + description: |- + The number of days from 7 to 30 that Secrets Manager waits before + permanently deleting the secret. You can't use both this parameter and + ForceDeleteWithoutRecovery in the same call. If you don't use either, + then by default Secrets Manager uses a 30 day recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays format: int64 type: integer type: object @@ -6719,32 +8281,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -6752,7 +8326,11 @@ spec: type: string environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -6763,10 +8341,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6774,7 +8357,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6798,29 +8383,41 @@ spec: account: type: string apiKeyRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6833,23 +8430,34 @@ spec: account: type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Conjur using the JWT authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountRef specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6857,7 +8465,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6873,7 +8483,10 @@ spec: caBundle: type: string caProvider: - description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + description: |- + Used to provide custom certificate authority (CA) certificates + for a secret store. The CAProvider points to a Secret or ConfigMap resource + that contains a PEM-encoded certificate. properties: key: description: The key where the CA certificate can be found in the Secret or ConfigMap. @@ -6882,7 +8495,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -6901,7 +8516,9 @@ spec: - url type: object delinea: - description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + description: |- + Delinea DevOps Secrets Vault + https://docs.delinea.com/online-help/products/devops-secrets-vault/current properties: clientId: description: ClientID is the non-secret part of the credential. @@ -6910,13 +8527,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -6930,13 +8551,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -6947,10 +8572,14 @@ spec: description: Tenant is the chosen hostname / site name. type: string tld: - description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + description: |- + TLD is based on the server location that was chosen during provisioning. + If unset, defaults to "com". type: string urlTemplate: - description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + description: |- + URLTemplate + If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". type: string required: - clientId @@ -6966,16 +8595,23 @@ spec: secretRef: properties: dopplerToken: - description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + description: |- + The DopplerToken is used for authentication. + See https://docs.doppler.com/reference/api#authentication for auth token types. + The Key attribute defaults to dopplerToken if not specified. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7048,13 +8684,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7070,7 +8710,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7078,7 +8721,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7105,13 +8750,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7166,13 +8815,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7187,16 +8840,22 @@ spec: description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider properties: authRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object folderID: @@ -7217,29 +8876,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7247,7 +8918,10 @@ spec: description: points to a service account that should be used for authentication properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7255,7 +8929,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7264,16 +8940,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7299,7 +8981,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -7332,13 +9016,17 @@ spec: description: The ConnectToken is used for authentication to a 1Password Connect Server. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7364,7 +9052,9 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, use the instance principal, otherwise the user credentials specified in Auth. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -7373,26 +9063,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7411,13 +9109,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -7428,10 +9133,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7439,7 +9149,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7461,13 +9173,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -7490,13 +9206,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -7518,16 +9238,22 @@ spec: clientId: type: string clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7556,39 +9282,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7596,37 +9344,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -7638,7 +9402,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7646,7 +9413,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7668,39 +9437,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7714,25 +9498,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7740,7 +9540,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7750,55 +9552,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7806,7 +9639,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7816,27 +9651,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -7846,13 +9694,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -7860,23 +9712,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -7884,7 +9747,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -7897,7 +9764,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -7910,23 +9779,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -7942,7 +9877,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -7983,7 +9922,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -7993,13 +9934,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -8030,13 +9975,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8044,16 +9993,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8073,13 +10028,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8087,16 +10046,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8166,7 +10131,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -8186,17 +10151,28 @@ spec: openAPIV3Schema: 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' + 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' + 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: properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters VDS based on this property type: string method: description: Vault API method to use (GET/POST/other) @@ -8214,39 +10190,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -8254,37 +10252,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -8296,7 +10310,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8304,7 +10321,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8326,39 +10345,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8372,25 +10406,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8398,7 +10448,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8408,55 +10460,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8464,7 +10547,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8474,27 +10559,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -8504,13 +10602,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -8518,23 +10620,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -8542,7 +10655,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -8555,7 +10672,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -8568,23 +10687,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -8595,7 +10780,12 @@ spec: type: object resultType: default: Data - description: Result type defines which data is returned from the generator. By default it is the "data" section of the Vault API response. When using e.g. /auth/token/create the "data" section is empty but the "auth" section contains the generated token. Please refer to the vault docs regarding the result data structure. + description: |- + Result type defines which data is returned from the generator. + By default it is the "data" section of the Vault API response. + When using e.g. /auth/token/create the "data" section is empty but + the "auth" section contains the generated token. + Please refer to the vault docs regarding the result data structure. enum: - Data - Auth @@ -8626,10 +10816,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8693,10 +10883,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8802,10 +10992,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -8842,10 +11032,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8886,10 +11076,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8907,10 +11097,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8927,10 +11117,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8963,10 +11153,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -9002,10 +11192,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -9023,10 +11213,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -9047,10 +11237,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9062,10 +11252,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -9080,7 +11270,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -9110,10 +11300,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9125,10 +11315,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -9143,7 +11333,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -9160,10 +11350,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9175,10 +11365,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -9193,7 +11383,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index fda09175..312ed5fa 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -85,18 +85,39 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: "ACRAccessToken returns a Azure Container Registry token that can be used for pushing/pulling images. Note: by default it will return an ACR Refresh Token with full access (depending on the identity). This can be scoped down to the repository level using .spec.scope. In case scope is defined it will return an ACR Access Token. \n See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md" + description: |- + ACRAccessToken returns a Azure Container Registry token + that can be used for pushing/pulling images. + Note: by default it will return an ACR Refresh Token with full access + (depending on the identity). + This can be scoped down to the repository level using .spec.scope. + In case scope is defined it will return an ACR Access Token. + + + See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md 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' + 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' + 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: 'ACRAccessTokenSpec defines how to generate the access token e.g. how to authenticate and which registry to use. see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview' + description: |- + ACRAccessTokenSpec defines how to generate the access token + e.g. how to authenticate and which registry to use. + see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview properties: auth: properties: @@ -111,32 +132,42 @@ spec: description: ServicePrincipal uses Azure Service Principal credentials to authenticate with Azure. properties: secretRef: - description: Configuration used to authenticate with Azure using static credentials stored in a Kind=Secret. + description: |- + Configuration used to authenticate with Azure using static + credentials stored in a Kind=Secret. properties: clientId: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -147,10 +178,15 @@ spec: description: WorkloadIdentity uses Azure Workload Identity to authenticate with Azure. properties: serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -158,7 +194,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -167,7 +205,11 @@ spec: type: object environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -175,10 +217,23 @@ spec: - GermanCloud type: string registry: - description: the domain name of the ACR registry e.g. foobarexample.azurecr.io + description: |- + the domain name of the ACR registry + e.g. foobarexample.azurecr.io type: string scope: - description: "Define the scope for the access token, e.g. pull/push access for a repository. if not provided it will return a refresh token that has full scope. Note: you need to pin it down to the repository level, there is no wildcard available. \n examples: repository:my-repository:pull,push repository:my-repository:pull \n see docs for details: https://docs.docker.com/registry/spec/auth/scope/" + description: |- + Define the scope for the access token, e.g. pull/push access for a repository. + if not provided it will return a refresh token that has full scope. + Note: you need to pin it down to the repository level, there is no wildcard available. + + + examples: + repository:my-repository:pull,push + repository:my-repository:pull + + + see docs for details: https://docs.docker.com/registry/spec/auth/scope/ type: string tenantId: description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. @@ -208,7 +263,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -239,10 +294,19 @@ spec: description: ClusterExternalSecret is the Schema for the clusterexternalsecrets 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' + 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' + 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 @@ -273,7 +337,9 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. + description: |- + RemoteRef points to the remote secret and defines + which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -311,14 +377,23 @@ spec: - key type: object secretKey: - description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret + description: |- + SecretKey defines the key in which the controller stores + the value. This is the key in the Kind=Secret type: string sourceRef: - description: SourceRef allows you to override the source from which the value will pulled from. + description: |- + SourceRef allows you to override the source + from which the value will pulled from. maxProperties: 1 properties: generatorRef: - description: "GeneratorRef points to a generator custom resource. \n Deprecated: The generatorRef is not implemented in .data[]. this will be removed with v1." + description: |- + GeneratorRef points to a generator custom resource. + + + Deprecated: The generatorRef is not implemented in .data[]. + this will be removed with v1. properties: apiVersion: default: generators.external-secrets.io/v1alpha1 @@ -338,7 +413,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -353,11 +430,15 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: properties: extract: - description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to extract multiple key/value pairs from one secret + Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -395,7 +476,9 @@ spec: - key type: object find: - description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to find secrets based on tags or regular expressions + Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -430,11 +513,15 @@ spec: type: object type: object rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + description: |- + Used to rewrite secret Keys after getting them from the secret Provider + Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) items: properties: regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + description: |- + Used to rewrite with regular expressions. + The resulting key will be the output of a regexp.ReplaceAll operation. properties: source: description: Used to define the regular expression of a re.Compiler. @@ -447,10 +534,14 @@ spec: - target type: object transform: - description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + description: |- + Used to apply string transformation on the secrets. + The resulting key will be the output of the template applied by the operation. properties: template: - description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + description: |- + Used to define the template to apply on the secret name. + `.value ` will specify the secret name in the template. type: string required: - template @@ -458,7 +549,13 @@ spec: type: object type: array sourceRef: - description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + description: |- + SourceRef points to a store or generator + which contains secret values ready to use. + Use this in combination with Extract or Find pull values out of + a specific SecretStore. + When sourceRef points to a generator Extract or Find is not supported. + The generator returns a static map of values maxProperties: 1 properties: generatorRef: @@ -482,7 +579,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -495,13 +594,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -513,11 +617,15 @@ spec: default: creationPolicy: Owner deletionPolicy: Retain - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Orphan @@ -526,7 +634,9 @@ spec: type: string deletionPolicy: default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + description: |- + DeletionPolicy defines rules on how to delete the resulting Secret + Defaults to 'Retain' enum: - Delete - Merge @@ -536,7 +646,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -547,7 +660,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -641,16 +757,24 @@ spec: 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. + 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. + 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. + 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 @@ -662,7 +786,10 @@ spec: 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. + 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 @@ -739,7 +866,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -768,10 +895,19 @@ spec: description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -779,7 +915,9 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -796,7 +934,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -805,23 +945,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -829,7 +984,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -839,51 +996,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -942,26 +1120,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -980,7 +1166,10 @@ spec: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -989,7 +1178,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -997,39 +1189,51 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1060,32 +1264,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -1095,10 +1311,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1106,7 +1327,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1155,13 +1378,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1177,7 +1404,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1185,7 +1415,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1212,13 +1444,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1246,13 +1482,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1277,29 +1517,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1310,7 +1562,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1318,7 +1573,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1328,16 +1585,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1387,7 +1650,10 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, instance principal is used. Optionally, the authenticating principal type + and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -1396,26 +1662,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1434,13 +1708,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -1451,10 +1732,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1462,7 +1748,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1481,26 +1769,40 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1509,55 +1811,83 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1565,7 +1895,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1575,55 +1907,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1631,7 +1994,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1641,27 +2006,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -1671,18 +2049,26 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -1708,23 +2094,40 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -1740,7 +2143,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -1781,7 +2188,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -1791,13 +2200,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1828,13 +2241,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1842,16 +2259,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1918,10 +2341,19 @@ spec: description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -1931,7 +2363,9 @@ spec: conditions: description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore items: - description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + description: |- + ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in + for a ClusterSecretStore instance. properties: namespaceSelector: description: Choose namespace using a labelSelector @@ -1939,16 +2373,24 @@ spec: 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. + 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. + 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. + 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 @@ -1960,7 +2402,10 @@ spec: 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. + 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 @@ -1972,7 +2417,9 @@ spec: type: object type: array controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -1989,7 +2436,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -1998,23 +2447,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2022,7 +2486,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2032,51 +2498,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -2089,7 +2576,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2135,26 +2624,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2178,7 +2675,10 @@ spec: type: string type: array auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -2187,7 +2687,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2195,52 +2698,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2258,10 +2780,20 @@ spec: description: SecretsManager defines how the provider behaves when interacting with AWS SecretsManager properties: forceDeleteWithoutRecovery: - description: 'Specifies whether to delete the secret without any recovery window. You can''t use both this parameter and RecoveryWindowInDays in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery' + description: |- + Specifies whether to delete the secret without any recovery window. You + can't use both this parameter and RecoveryWindowInDays in the same call. + If you don't use either, then by default Secrets Manager uses a 30 day + recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery type: boolean recoveryWindowInDays: - description: 'The number of days from 7 to 30 that Secrets Manager waits before permanently deleting the secret. You can''t use both this parameter and ForceDeleteWithoutRecovery in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays' + description: |- + The number of days from 7 to 30 that Secrets Manager waits before + permanently deleting the secret. You can't use both this parameter and + ForceDeleteWithoutRecovery in the same call. If you don't use either, + then by default Secrets Manager uses a 30 day recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays format: int64 type: integer type: object @@ -2303,32 +2835,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -2336,7 +2880,11 @@ spec: type: string environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -2347,10 +2895,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2358,7 +2911,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2382,29 +2937,41 @@ spec: account: type: string apiKeyRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2417,23 +2984,34 @@ spec: account: type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Conjur using the JWT authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountRef specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2441,7 +3019,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2457,7 +3037,10 @@ spec: caBundle: type: string caProvider: - description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + description: |- + Used to provide custom certificate authority (CA) certificates + for a secret store. The CAProvider points to a Secret or ConfigMap resource + that contains a PEM-encoded certificate. properties: key: description: The key where the CA certificate can be found in the Secret or ConfigMap. @@ -2466,7 +3049,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2485,7 +3070,9 @@ spec: - url type: object delinea: - description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + description: |- + Delinea DevOps Secrets Vault + https://docs.delinea.com/online-help/products/devops-secrets-vault/current properties: clientId: description: ClientID is the non-secret part of the credential. @@ -2494,13 +3081,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -2514,13 +3105,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -2531,10 +3126,14 @@ spec: description: Tenant is the chosen hostname / site name. type: string tld: - description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + description: |- + TLD is based on the server location that was chosen during provisioning. + If unset, defaults to "com". type: string urlTemplate: - description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + description: |- + URLTemplate + If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". type: string required: - clientId @@ -2550,16 +3149,23 @@ spec: secretRef: properties: dopplerToken: - description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + description: |- + The DopplerToken is used for authentication. + See https://docs.doppler.com/reference/api#authentication for auth token types. + The Key attribute defaults to dopplerToken if not specified. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2632,13 +3238,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2654,7 +3264,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2662,7 +3275,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2689,13 +3304,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2750,13 +3369,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2771,16 +3394,22 @@ spec: description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider properties: authRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object folderID: @@ -2801,29 +3430,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2831,7 +3472,10 @@ spec: description: points to a service account that should be used for authentication properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2839,7 +3483,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2848,16 +3494,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2883,7 +3535,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2916,13 +3570,17 @@ spec: description: The ConnectToken is used for authentication to a 1Password Connect Server. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2948,7 +3606,9 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, use the instance principal, otherwise the user credentials specified in Auth. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -2957,26 +3617,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2995,13 +3663,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -3012,10 +3687,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3023,7 +3703,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3045,13 +3727,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -3074,13 +3760,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -3102,16 +3792,22 @@ spec: clientId: type: string clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3140,39 +3836,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3180,37 +3898,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -3222,7 +3956,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3230,7 +3967,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3252,39 +3991,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3298,25 +4052,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3324,7 +4094,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3334,55 +4106,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3390,7 +4193,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3400,27 +4205,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -3430,13 +4248,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -3444,23 +4266,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -3468,7 +4301,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -3481,7 +4318,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -3494,23 +4333,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -3526,7 +4431,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -3567,7 +4476,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -3577,13 +4488,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3614,13 +4529,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3628,16 +4547,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3657,13 +4582,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3671,16 +4600,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3750,7 +4685,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3768,13 +4703,28 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an authorization token. The authorization token is valid for 12 hours. The authorizationToken returned is a base64 encoded string that can be decoded and used in a docker login command to authenticate to a registry. For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. + description: |- + ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an + authorization token. + The authorization token is valid for 12 hours. + The authorizationToken returned is a base64 encoded string that can be decoded + and used in a docker login command to authenticate to a registry. + For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. 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' + 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' + 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 @@ -3790,7 +4740,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3798,52 +4751,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3852,7 +4824,9 @@ spec: description: Region specifies the region to operate in. type: string role: - description: You can assume a role before making calls to the desired AWS service. + description: |- + You can assume a role before making calls to the + desired AWS service. type: string required: - region @@ -3878,7 +4852,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -3910,10 +4884,19 @@ spec: description: ExternalSecret is the Schema for the external-secrets 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' + 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' + 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 @@ -3955,7 +4938,9 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: description: ExternalSecretDataRemoteRef defines Provider data location. properties: @@ -3981,13 +4966,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -3996,11 +4986,15 @@ spec: - name type: object target: - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Merge @@ -4010,7 +5004,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -4021,7 +5018,10 @@ spec: type: object engineVersion: default: v1 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -4093,7 +5093,10 @@ spec: description: Binding represents a servicebinding.io Provisioned Service reference to the secret properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -4117,7 +5120,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -4149,10 +5154,19 @@ spec: description: ExternalSecret is the Schema for the external-secrets 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' + 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' + 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 @@ -4165,7 +5179,9 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. + description: |- + RemoteRef points to the remote secret and defines + which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -4203,14 +5219,23 @@ spec: - key type: object secretKey: - description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret + description: |- + SecretKey defines the key in which the controller stores + the value. This is the key in the Kind=Secret type: string sourceRef: - description: SourceRef allows you to override the source from which the value will pulled from. + description: |- + SourceRef allows you to override the source + from which the value will pulled from. maxProperties: 1 properties: generatorRef: - description: "GeneratorRef points to a generator custom resource. \n Deprecated: The generatorRef is not implemented in .data[]. this will be removed with v1." + description: |- + GeneratorRef points to a generator custom resource. + + + Deprecated: The generatorRef is not implemented in .data[]. + this will be removed with v1. properties: apiVersion: default: generators.external-secrets.io/v1alpha1 @@ -4230,7 +5255,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4245,11 +5272,15 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: properties: extract: - description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to extract multiple key/value pairs from one secret + Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -4287,7 +5318,9 @@ spec: - key type: object find: - description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to find secrets based on tags or regular expressions + Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -4322,11 +5355,15 @@ spec: type: object type: object rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + description: |- + Used to rewrite secret Keys after getting them from the secret Provider + Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) items: properties: regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + description: |- + Used to rewrite with regular expressions. + The resulting key will be the output of a regexp.ReplaceAll operation. properties: source: description: Used to define the regular expression of a re.Compiler. @@ -4339,10 +5376,14 @@ spec: - target type: object transform: - description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + description: |- + Used to apply string transformation on the secrets. + The resulting key will be the output of the template applied by the operation. properties: template: - description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + description: |- + Used to define the template to apply on the secret name. + `.value ` will specify the secret name in the template. type: string required: - template @@ -4350,7 +5391,13 @@ spec: type: object type: array sourceRef: - description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + description: |- + SourceRef points to a store or generator + which contains secret values ready to use. + Use this in combination with Extract or Find pull values out of + a specific SecretStore. + When sourceRef points to a generator Extract or Find is not supported. + The generator returns a static map of values maxProperties: 1 properties: generatorRef: @@ -4374,7 +5421,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4387,13 +5436,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4405,11 +5459,15 @@ spec: default: creationPolicy: Owner deletionPolicy: Retain - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Orphan @@ -4418,7 +5476,9 @@ spec: type: string deletionPolicy: default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + description: |- + DeletionPolicy defines rules on how to delete the resulting Secret + Defaults to 'Retain' enum: - Delete - Merge @@ -4428,7 +5488,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -4439,7 +5502,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -4533,7 +5599,10 @@ spec: description: Binding represents a servicebinding.io Provisioned Service reference to the secret properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -4557,7 +5626,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -4586,7 +5657,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4604,13 +5675,24 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Fake generator is used for testing. It lets you define a static set of credentials that is always returned. + description: |- + Fake generator is used for testing. It lets you define + a static set of credentials that is always returned. 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' + 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' + 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 @@ -4618,12 +5700,16 @@ spec: description: FakeSpec contains the static data. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters VDS based on this property type: string data: additionalProperties: type: string - description: Data defines the static data returned by this generator. + description: |- + Data defines the static data returned + by this generator. type: object type: object type: object @@ -4647,7 +5733,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4665,13 +5751,24 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: GCRAccessToken generates an GCP access token that can be used to authenticate with GCR. + description: |- + GCRAccessToken generates an GCP access token + that can be used to authenticate with GCR. 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' + 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' + 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 @@ -4686,13 +5783,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -4708,7 +5809,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -4716,7 +5820,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -4755,7 +5861,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4773,13 +5879,25 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Password generates a random password based on the configuration parameters in spec. You can specify the length, characterset and other attributes. + description: |- + Password generates a random password based on the + configuration parameters in spec. + You can specify the length, characterset and other attributes. 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' + 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' + 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 @@ -4791,21 +5909,29 @@ spec: description: set AllowRepeat to true to allow repeating characters. type: boolean digits: - description: Digits specifies the number of digits in the generated password. If omitted it defaults to 25% of the length of the password + description: |- + Digits specifies the number of digits in the generated + password. If omitted it defaults to 25% of the length of the password type: integer length: default: 24 - description: Length of the password to be generated. Defaults to 24 + description: |- + Length of the password to be generated. + Defaults to 24 type: integer noUpper: default: false description: Set NoUpper to disable uppercase characters type: boolean symbolCharacters: - description: SymbolCharacters specifies the special characters that should be used in the generated password. + description: |- + SymbolCharacters specifies the special characters that should be used + in the generated password. type: string symbols: - description: Symbols specifies the number of symbol characters in the generated password. If omitted it defaults to 25% of the length of the password + description: |- + Symbols specifies the number of symbol characters in the generated + password. If omitted it defaults to 25% of the length of the password type: integer required: - allowRepeat @@ -4833,7 +5959,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4858,10 +5984,19 @@ spec: openAPIV3Schema: 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' + 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' + 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 @@ -4894,7 +6029,9 @@ spec: - remoteRef type: object metadata: - description: Metadata is metadata attached to the secret. The structure of metadata is provider specific, please look it up in the provider documentation. + description: |- + Metadata is metadata attached to the secret. + The structure of metadata is provider specific, please look it up in the provider documentation. x-kubernetes-preserve-unknown-fields: true required: - match @@ -4915,7 +6052,9 @@ spec: properties: kind: default: SecretStore - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string labelSelector: description: Optionally, sync to secret stores with label selector @@ -4923,16 +6062,24 @@ spec: 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. + 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. + 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. + 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 @@ -4944,7 +6091,10 @@ spec: 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. + 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 @@ -4977,7 +6127,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -5092,7 +6245,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -5122,7 +6277,9 @@ spec: - remoteRef type: object metadata: - description: Metadata is metadata attached to the secret. The structure of metadata is provider specific, please look it up in the provider documentation. + description: |- + Metadata is metadata attached to the secret. + The structure of metadata is provider specific, please look it up in the provider documentation. x-kubernetes-preserve-unknown-fields: true required: - match @@ -5155,7 +6312,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -5184,10 +6341,19 @@ spec: description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -5195,7 +6361,9 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -5212,7 +6380,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -5221,23 +6391,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5245,7 +6430,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5255,51 +6442,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -5358,26 +6566,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5396,7 +6612,10 @@ spec: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -5405,7 +6624,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5413,39 +6635,51 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5476,32 +6710,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -5511,10 +6757,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5522,7 +6773,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5571,13 +6824,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5593,7 +6850,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5601,7 +6861,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5628,13 +6890,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5662,13 +6928,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5693,29 +6963,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5726,7 +7008,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5734,7 +7019,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5744,16 +7031,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5803,7 +7096,10 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, instance principal is used. Optionally, the authenticating principal type + and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -5812,26 +7108,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5850,13 +7154,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -5867,10 +7178,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5878,7 +7194,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5897,26 +7215,40 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5925,55 +7257,83 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5981,7 +7341,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5991,55 +7353,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6047,7 +7440,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6057,27 +7452,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -6087,18 +7495,26 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6124,23 +7540,40 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -6156,7 +7589,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6197,7 +7634,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -6207,13 +7646,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6244,13 +7687,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6258,16 +7705,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6334,10 +7787,19 @@ spec: description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -6347,7 +7809,9 @@ spec: conditions: description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore items: - description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + description: |- + ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in + for a ClusterSecretStore instance. properties: namespaceSelector: description: Choose namespace using a labelSelector @@ -6355,16 +7819,24 @@ spec: 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. + 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. + 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. + 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 @@ -6376,7 +7848,10 @@ spec: 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. + 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 @@ -6388,7 +7863,9 @@ spec: type: object type: array controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -6405,7 +7882,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -6414,23 +7893,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6438,7 +7932,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6448,51 +7944,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6505,7 +8022,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -6551,26 +8070,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6594,7 +8121,10 @@ spec: type: string type: array auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -6603,7 +8133,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6611,52 +8144,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6674,10 +8226,20 @@ spec: description: SecretsManager defines how the provider behaves when interacting with AWS SecretsManager properties: forceDeleteWithoutRecovery: - description: 'Specifies whether to delete the secret without any recovery window. You can''t use both this parameter and RecoveryWindowInDays in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery' + description: |- + Specifies whether to delete the secret without any recovery window. You + can't use both this parameter and RecoveryWindowInDays in the same call. + If you don't use either, then by default Secrets Manager uses a 30 day + recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery type: boolean recoveryWindowInDays: - description: 'The number of days from 7 to 30 that Secrets Manager waits before permanently deleting the secret. You can''t use both this parameter and ForceDeleteWithoutRecovery in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays' + description: |- + The number of days from 7 to 30 that Secrets Manager waits before + permanently deleting the secret. You can't use both this parameter and + ForceDeleteWithoutRecovery in the same call. If you don't use either, + then by default Secrets Manager uses a 30 day recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays format: int64 type: integer type: object @@ -6719,32 +8281,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -6752,7 +8326,11 @@ spec: type: string environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -6763,10 +8341,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6774,7 +8357,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6798,29 +8383,41 @@ spec: account: type: string apiKeyRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6833,23 +8430,34 @@ spec: account: type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Conjur using the JWT authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountRef specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6857,7 +8465,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6873,7 +8483,10 @@ spec: caBundle: type: string caProvider: - description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + description: |- + Used to provide custom certificate authority (CA) certificates + for a secret store. The CAProvider points to a Secret or ConfigMap resource + that contains a PEM-encoded certificate. properties: key: description: The key where the CA certificate can be found in the Secret or ConfigMap. @@ -6882,7 +8495,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -6901,7 +8516,9 @@ spec: - url type: object delinea: - description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + description: |- + Delinea DevOps Secrets Vault + https://docs.delinea.com/online-help/products/devops-secrets-vault/current properties: clientId: description: ClientID is the non-secret part of the credential. @@ -6910,13 +8527,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -6930,13 +8551,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -6947,10 +8572,14 @@ spec: description: Tenant is the chosen hostname / site name. type: string tld: - description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + description: |- + TLD is based on the server location that was chosen during provisioning. + If unset, defaults to "com". type: string urlTemplate: - description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + description: |- + URLTemplate + If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". type: string required: - clientId @@ -6966,16 +8595,23 @@ spec: secretRef: properties: dopplerToken: - description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + description: |- + The DopplerToken is used for authentication. + See https://docs.doppler.com/reference/api#authentication for auth token types. + The Key attribute defaults to dopplerToken if not specified. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7048,13 +8684,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7070,7 +8710,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7078,7 +8721,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7105,13 +8750,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7166,13 +8815,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7187,16 +8840,22 @@ spec: description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider properties: authRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object folderID: @@ -7217,29 +8876,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7247,7 +8918,10 @@ spec: description: points to a service account that should be used for authentication properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7255,7 +8929,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7264,16 +8940,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7299,7 +8981,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -7332,13 +9016,17 @@ spec: description: The ConnectToken is used for authentication to a 1Password Connect Server. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7364,7 +9052,9 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, use the instance principal, otherwise the user credentials specified in Auth. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -7373,26 +9063,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7411,13 +9109,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -7428,10 +9133,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7439,7 +9149,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7461,13 +9173,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -7490,13 +9206,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -7518,16 +9238,22 @@ spec: clientId: type: string clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7556,39 +9282,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7596,37 +9344,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -7638,7 +9402,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7646,7 +9413,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7668,39 +9437,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7714,25 +9498,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7740,7 +9540,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7750,55 +9552,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7806,7 +9639,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7816,27 +9651,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -7846,13 +9694,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -7860,23 +9712,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -7884,7 +9747,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -7897,7 +9764,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -7910,23 +9779,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -7942,7 +9877,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -7983,7 +9922,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -7993,13 +9934,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -8030,13 +9975,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8044,16 +9993,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8073,13 +10028,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8087,16 +10046,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8166,7 +10131,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -8186,17 +10151,28 @@ spec: openAPIV3Schema: 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' + 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' + 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: properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters VDS based on this property type: string method: description: Vault API method to use (GET/POST/other) @@ -8214,39 +10190,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -8254,37 +10252,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -8296,7 +10310,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8304,7 +10321,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8326,39 +10345,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8372,25 +10406,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8398,7 +10448,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8408,55 +10460,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8464,7 +10547,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8474,27 +10559,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -8504,13 +10602,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -8518,23 +10620,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -8542,7 +10655,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -8555,7 +10672,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -8568,23 +10687,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -8595,7 +10780,12 @@ spec: type: object resultType: default: Data - description: Result type defines which data is returned from the generator. By default it is the "data" section of the Vault API response. When using e.g. /auth/token/create the "data" section is empty but the "auth" section contains the generated token. Please refer to the vault docs regarding the result data structure. + description: |- + Result type defines which data is returned from the generator. + By default it is the "data" section of the Vault API response. + When using e.g. /auth/token/create the "data" section is empty but + the "auth" section contains the generated token. + Please refer to the vault docs regarding the result data structure. enum: - Data - Auth @@ -8626,10 +10816,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8693,10 +10883,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8802,10 +10992,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -8842,10 +11032,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8886,10 +11076,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8907,10 +11097,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8927,10 +11117,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8963,10 +11153,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -9002,10 +11192,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -9023,10 +11213,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -9047,10 +11237,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9062,10 +11252,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -9080,7 +11270,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -9110,10 +11300,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9125,10 +11315,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -9143,7 +11333,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -9160,10 +11350,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9175,10 +11365,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -9193,7 +11383,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 3fca7728..ffcf6cb6 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -85,18 +85,39 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: "ACRAccessToken returns a Azure Container Registry token that can be used for pushing/pulling images. Note: by default it will return an ACR Refresh Token with full access (depending on the identity). This can be scoped down to the repository level using .spec.scope. In case scope is defined it will return an ACR Access Token. \n See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md" + description: |- + ACRAccessToken returns a Azure Container Registry token + that can be used for pushing/pulling images. + Note: by default it will return an ACR Refresh Token with full access + (depending on the identity). + This can be scoped down to the repository level using .spec.scope. + In case scope is defined it will return an ACR Access Token. + + + See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md 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' + 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' + 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: 'ACRAccessTokenSpec defines how to generate the access token e.g. how to authenticate and which registry to use. see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview' + description: |- + ACRAccessTokenSpec defines how to generate the access token + e.g. how to authenticate and which registry to use. + see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview properties: auth: properties: @@ -111,32 +132,42 @@ spec: description: ServicePrincipal uses Azure Service Principal credentials to authenticate with Azure. properties: secretRef: - description: Configuration used to authenticate with Azure using static credentials stored in a Kind=Secret. + description: |- + Configuration used to authenticate with Azure using static + credentials stored in a Kind=Secret. properties: clientId: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -147,10 +178,15 @@ spec: description: WorkloadIdentity uses Azure Workload Identity to authenticate with Azure. properties: serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -158,7 +194,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -167,7 +205,11 @@ spec: type: object environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -175,10 +217,23 @@ spec: - GermanCloud type: string registry: - description: the domain name of the ACR registry e.g. foobarexample.azurecr.io + description: |- + the domain name of the ACR registry + e.g. foobarexample.azurecr.io type: string scope: - description: "Define the scope for the access token, e.g. pull/push access for a repository. if not provided it will return a refresh token that has full scope. Note: you need to pin it down to the repository level, there is no wildcard available. \n examples: repository:my-repository:pull,push repository:my-repository:pull \n see docs for details: https://docs.docker.com/registry/spec/auth/scope/" + description: |- + Define the scope for the access token, e.g. pull/push access for a repository. + if not provided it will return a refresh token that has full scope. + Note: you need to pin it down to the repository level, there is no wildcard available. + + + examples: + repository:my-repository:pull,push + repository:my-repository:pull + + + see docs for details: https://docs.docker.com/registry/spec/auth/scope/ type: string tenantId: description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. @@ -208,7 +263,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -239,10 +294,19 @@ spec: description: ClusterExternalSecret is the Schema for the clusterexternalsecrets 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' + 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' + 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 @@ -273,7 +337,9 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. + description: |- + RemoteRef points to the remote secret and defines + which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -311,14 +377,23 @@ spec: - key type: object secretKey: - description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret + description: |- + SecretKey defines the key in which the controller stores + the value. This is the key in the Kind=Secret type: string sourceRef: - description: SourceRef allows you to override the source from which the value will pulled from. + description: |- + SourceRef allows you to override the source + from which the value will pulled from. maxProperties: 1 properties: generatorRef: - description: "GeneratorRef points to a generator custom resource. \n Deprecated: The generatorRef is not implemented in .data[]. this will be removed with v1." + description: |- + GeneratorRef points to a generator custom resource. + + + Deprecated: The generatorRef is not implemented in .data[]. + this will be removed with v1. properties: apiVersion: default: generators.external-secrets.io/v1alpha1 @@ -338,7 +413,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -353,11 +430,15 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: properties: extract: - description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to extract multiple key/value pairs from one secret + Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -395,7 +476,9 @@ spec: - key type: object find: - description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to find secrets based on tags or regular expressions + Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -430,11 +513,15 @@ spec: type: object type: object rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + description: |- + Used to rewrite secret Keys after getting them from the secret Provider + Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) items: properties: regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + description: |- + Used to rewrite with regular expressions. + The resulting key will be the output of a regexp.ReplaceAll operation. properties: source: description: Used to define the regular expression of a re.Compiler. @@ -447,10 +534,14 @@ spec: - target type: object transform: - description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + description: |- + Used to apply string transformation on the secrets. + The resulting key will be the output of the template applied by the operation. properties: template: - description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + description: |- + Used to define the template to apply on the secret name. + `.value ` will specify the secret name in the template. type: string required: - template @@ -458,7 +549,13 @@ spec: type: object type: array sourceRef: - description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + description: |- + SourceRef points to a store or generator + which contains secret values ready to use. + Use this in combination with Extract or Find pull values out of + a specific SecretStore. + When sourceRef points to a generator Extract or Find is not supported. + The generator returns a static map of values maxProperties: 1 properties: generatorRef: @@ -482,7 +579,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -495,13 +594,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -513,11 +617,15 @@ spec: default: creationPolicy: Owner deletionPolicy: Retain - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Orphan @@ -526,7 +634,9 @@ spec: type: string deletionPolicy: default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + description: |- + DeletionPolicy defines rules on how to delete the resulting Secret + Defaults to 'Retain' enum: - Delete - Merge @@ -536,7 +646,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -547,7 +660,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -641,16 +757,24 @@ spec: 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. + 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. + 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. + 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 @@ -662,7 +786,10 @@ spec: 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. + 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 @@ -739,7 +866,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -768,10 +895,19 @@ spec: description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -779,7 +915,9 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -796,7 +934,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -805,23 +945,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -829,7 +984,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -839,51 +996,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -942,26 +1120,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -980,7 +1166,10 @@ spec: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -989,7 +1178,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -997,39 +1189,51 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1060,32 +1264,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -1095,10 +1311,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1106,7 +1327,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1155,13 +1378,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1177,7 +1404,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1185,7 +1415,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1212,13 +1444,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1246,13 +1482,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1277,29 +1517,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1310,7 +1562,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1318,7 +1573,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1328,16 +1585,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1387,7 +1650,10 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, instance principal is used. Optionally, the authenticating principal type + and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -1396,26 +1662,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1434,13 +1708,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -1451,10 +1732,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1462,7 +1748,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1481,26 +1769,40 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1509,55 +1811,83 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1565,7 +1895,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1575,55 +1907,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -1631,7 +1994,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -1641,27 +2006,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -1671,18 +2049,26 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -1708,23 +2094,40 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -1740,7 +2143,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -1781,7 +2188,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -1791,13 +2200,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -1828,13 +2241,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1842,16 +2259,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -1918,10 +2341,19 @@ spec: description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -1931,7 +2363,9 @@ spec: conditions: description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore items: - description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + description: |- + ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in + for a ClusterSecretStore instance. properties: namespaceSelector: description: Choose namespace using a labelSelector @@ -1939,16 +2373,24 @@ spec: 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. + 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. + 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. + 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 @@ -1960,7 +2402,10 @@ spec: 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. + 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 @@ -1972,7 +2417,9 @@ spec: type: object type: array controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -1989,7 +2436,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -1998,23 +2447,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2022,7 +2486,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2032,51 +2498,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -2089,7 +2576,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2135,26 +2624,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2178,7 +2675,10 @@ spec: type: string type: array auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -2187,7 +2687,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2195,52 +2698,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2258,10 +2780,20 @@ spec: description: SecretsManager defines how the provider behaves when interacting with AWS SecretsManager properties: forceDeleteWithoutRecovery: - description: 'Specifies whether to delete the secret without any recovery window. You can''t use both this parameter and RecoveryWindowInDays in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery' + description: |- + Specifies whether to delete the secret without any recovery window. You + can't use both this parameter and RecoveryWindowInDays in the same call. + If you don't use either, then by default Secrets Manager uses a 30 day + recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery type: boolean recoveryWindowInDays: - description: 'The number of days from 7 to 30 that Secrets Manager waits before permanently deleting the secret. You can''t use both this parameter and ForceDeleteWithoutRecovery in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays' + description: |- + The number of days from 7 to 30 that Secrets Manager waits before + permanently deleting the secret. You can't use both this parameter and + ForceDeleteWithoutRecovery in the same call. If you don't use either, + then by default Secrets Manager uses a 30 day recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays format: int64 type: integer type: object @@ -2303,32 +2835,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -2336,7 +2880,11 @@ spec: type: string environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -2347,10 +2895,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2358,7 +2911,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2382,29 +2937,41 @@ spec: account: type: string apiKeyRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2417,23 +2984,34 @@ spec: account: type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Conjur using the JWT authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountRef specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2441,7 +3019,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2457,7 +3037,10 @@ spec: caBundle: type: string caProvider: - description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + description: |- + Used to provide custom certificate authority (CA) certificates + for a secret store. The CAProvider points to a Secret or ConfigMap resource + that contains a PEM-encoded certificate. properties: key: description: The key where the CA certificate can be found in the Secret or ConfigMap. @@ -2466,7 +3049,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2485,7 +3070,9 @@ spec: - url type: object delinea: - description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + description: |- + Delinea DevOps Secrets Vault + https://docs.delinea.com/online-help/products/devops-secrets-vault/current properties: clientId: description: ClientID is the non-secret part of the credential. @@ -2494,13 +3081,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -2514,13 +3105,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -2531,10 +3126,14 @@ spec: description: Tenant is the chosen hostname / site name. type: string tld: - description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + description: |- + TLD is based on the server location that was chosen during provisioning. + If unset, defaults to "com". type: string urlTemplate: - description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + description: |- + URLTemplate + If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". type: string required: - clientId @@ -2550,16 +3149,23 @@ spec: secretRef: properties: dopplerToken: - description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + description: |- + The DopplerToken is used for authentication. + See https://docs.doppler.com/reference/api#authentication for auth token types. + The Key attribute defaults to dopplerToken if not specified. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2632,13 +3238,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2654,7 +3264,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2662,7 +3275,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2689,13 +3304,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2750,13 +3369,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2771,16 +3394,22 @@ spec: description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider properties: authRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object folderID: @@ -2801,29 +3430,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2831,7 +3472,10 @@ spec: description: points to a service account that should be used for authentication properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -2839,7 +3483,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -2848,16 +3494,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -2883,7 +3535,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2916,13 +3570,17 @@ spec: description: The ConnectToken is used for authentication to a 1Password Connect Server. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2948,7 +3606,9 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, use the instance principal, otherwise the user credentials specified in Auth. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -2957,26 +3617,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -2995,13 +3663,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -3012,10 +3687,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3023,7 +3703,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3045,13 +3727,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -3074,13 +3760,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -3102,16 +3792,22 @@ spec: clientId: type: string clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3140,39 +3836,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3180,37 +3898,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -3222,7 +3956,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3230,7 +3967,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3252,39 +3991,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3298,25 +4052,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3324,7 +4094,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3334,55 +4106,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3390,7 +4193,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -3400,27 +4205,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -3430,13 +4248,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -3444,23 +4266,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -3468,7 +4301,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -3481,7 +4318,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -3494,23 +4333,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -3526,7 +4431,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -3567,7 +4476,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -3577,13 +4488,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -3614,13 +4529,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3628,16 +4547,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3657,13 +4582,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3671,16 +4600,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3750,7 +4685,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -3768,13 +4703,28 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an authorization token. The authorization token is valid for 12 hours. The authorizationToken returned is a base64 encoded string that can be decoded and used in a docker login command to authenticate to a registry. For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. + description: |- + ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an + authorization token. + The authorization token is valid for 12 hours. + The authorizationToken returned is a base64 encoded string that can be decoded + and used in a docker login command to authenticate to a registry. + For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. 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' + 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' + 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 @@ -3790,7 +4740,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -3798,52 +4751,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -3852,7 +4824,9 @@ spec: description: Region specifies the region to operate in. type: string role: - description: You can assume a role before making calls to the desired AWS service. + description: |- + You can assume a role before making calls to the + desired AWS service. type: string required: - region @@ -3878,7 +4852,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -3910,10 +4884,19 @@ spec: description: ExternalSecret is the Schema for the external-secrets 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' + 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' + 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 @@ -3955,7 +4938,9 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: description: ExternalSecretDataRemoteRef defines Provider data location. properties: @@ -3981,13 +4966,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -3996,11 +4986,15 @@ spec: - name type: object target: - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Merge @@ -4010,7 +5004,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -4021,7 +5018,10 @@ spec: type: object engineVersion: default: v1 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -4093,7 +5093,10 @@ spec: description: Binding represents a servicebinding.io Provisioned Service reference to the secret properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -4117,7 +5120,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -4149,10 +5154,19 @@ spec: description: ExternalSecret is the Schema for the external-secrets 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' + 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' + 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 @@ -4165,7 +5179,9 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. + description: |- + RemoteRef points to the remote secret and defines + which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -4203,14 +5219,23 @@ spec: - key type: object secretKey: - description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret + description: |- + SecretKey defines the key in which the controller stores + the value. This is the key in the Kind=Secret type: string sourceRef: - description: SourceRef allows you to override the source from which the value will pulled from. + description: |- + SourceRef allows you to override the source + from which the value will pulled from. maxProperties: 1 properties: generatorRef: - description: "GeneratorRef points to a generator custom resource. \n Deprecated: The generatorRef is not implemented in .data[]. this will be removed with v1." + description: |- + GeneratorRef points to a generator custom resource. + + + Deprecated: The generatorRef is not implemented in .data[]. + this will be removed with v1. properties: apiVersion: default: generators.external-secrets.io/v1alpha1 @@ -4230,7 +5255,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4245,11 +5272,15 @@ spec: type: object type: array dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order + description: |- + DataFrom is used to fetch all properties from a specific Provider data + If multiple entries are specified, the Secret keys are merged in the specified order items: properties: extract: - description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to extract multiple key/value pairs from one secret + Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -4287,7 +5318,9 @@ spec: - key type: object find: - description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' + description: |- + Used to find secrets based on tags or regular expressions + Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef. properties: conversionStrategy: default: Default @@ -4322,11 +5355,15 @@ spec: type: object type: object rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + description: |- + Used to rewrite secret Keys after getting them from the secret Provider + Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) items: properties: regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + description: |- + Used to rewrite with regular expressions. + The resulting key will be the output of a regexp.ReplaceAll operation. properties: source: description: Used to define the regular expression of a re.Compiler. @@ -4339,10 +5376,14 @@ spec: - target type: object transform: - description: Used to apply string transformation on the secrets. The resulting key will be the output of the template applied by the operation. + description: |- + Used to apply string transformation on the secrets. + The resulting key will be the output of the template applied by the operation. properties: template: - description: Used to define the template to apply on the secret name. `.value ` will specify the secret name in the template. + description: |- + Used to define the template to apply on the secret name. + `.value ` will specify the secret name in the template. type: string required: - template @@ -4350,7 +5391,13 @@ spec: type: object type: array sourceRef: - description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + description: |- + SourceRef points to a store or generator + which contains secret values ready to use. + Use this in combination with Extract or Find pull values out of + a specific SecretStore. + When sourceRef points to a generator Extract or Find is not supported. + The generator returns a static map of values maxProperties: 1 properties: generatorRef: @@ -4374,7 +5421,9 @@ spec: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4387,13 +5436,18 @@ spec: type: array refreshInterval: default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. + description: |- + RefreshInterval is the amount of time before the values are read again from the SecretStore provider + Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + May be set to zero to fetch and create it once. Defaults to 1h. type: string secretStoreRef: description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. properties: kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string name: description: Name of the SecretStore resource @@ -4405,11 +5459,15 @@ spec: default: creationPolicy: Owner deletionPolicy: Retain - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. + description: |- + ExternalSecretTarget defines the Kubernetes Secret to be created + There can be only one target per ExternalSecret. properties: creationPolicy: default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' + description: |- + CreationPolicy defines rules on how to create the resulting Secret + Defaults to 'Owner' enum: - Owner - Orphan @@ -4418,7 +5476,9 @@ spec: type: string deletionPolicy: default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' + description: |- + DeletionPolicy defines rules on how to delete the resulting Secret + Defaults to 'Retain' enum: - Delete - Merge @@ -4428,7 +5488,10 @@ spec: description: Immutable defines if the final secret will be immutable type: boolean name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource + description: |- + Name defines the name of the Secret resource to be managed + This field is immutable + Defaults to the .metadata.name of the ExternalSecret resource type: string template: description: Template defines a blueprint for the created Secret resource. @@ -4439,7 +5502,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -4533,7 +5599,10 @@ spec: description: Binding represents a servicebinding.io Provisioned Service reference to the secret properties: name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object x-kubernetes-map-type: atomic @@ -4557,7 +5626,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -4586,7 +5657,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4604,13 +5675,24 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Fake generator is used for testing. It lets you define a static set of credentials that is always returned. + description: |- + Fake generator is used for testing. It lets you define + a static set of credentials that is always returned. 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' + 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' + 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 @@ -4618,12 +5700,16 @@ spec: description: FakeSpec contains the static data. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters VDS based on this property type: string data: additionalProperties: type: string - description: Data defines the static data returned by this generator. + description: |- + Data defines the static data returned + by this generator. type: object type: object type: object @@ -4647,7 +5733,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4665,13 +5751,24 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: GCRAccessToken generates an GCP access token that can be used to authenticate with GCR. + description: |- + GCRAccessToken generates an GCP access token + that can be used to authenticate with GCR. 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' + 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' + 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 @@ -4686,13 +5783,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -4708,7 +5809,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -4716,7 +5820,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -4755,7 +5861,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -4773,13 +5879,25 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Password generates a random password based on the configuration parameters in spec. You can specify the length, characterset and other attributes. + description: |- + Password generates a random password based on the + configuration parameters in spec. + You can specify the length, characterset and other attributes. 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' + 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' + 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 @@ -4791,21 +5909,29 @@ spec: description: set AllowRepeat to true to allow repeating characters. type: boolean digits: - description: Digits specifies the number of digits in the generated password. If omitted it defaults to 25% of the length of the password + description: |- + Digits specifies the number of digits in the generated + password. If omitted it defaults to 25% of the length of the password type: integer length: default: 24 - description: Length of the password to be generated. Defaults to 24 + description: |- + Length of the password to be generated. + Defaults to 24 type: integer noUpper: default: false description: Set NoUpper to disable uppercase characters type: boolean symbolCharacters: - description: SymbolCharacters specifies the special characters that should be used in the generated password. + description: |- + SymbolCharacters specifies the special characters that should be used + in the generated password. type: string symbols: - description: Symbols specifies the number of symbol characters in the generated password. If omitted it defaults to 25% of the length of the password + description: |- + Symbols specifies the number of symbol characters in the generated + password. If omitted it defaults to 25% of the length of the password type: integer required: - allowRepeat @@ -4833,7 +5959,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -4858,10 +5984,19 @@ spec: openAPIV3Schema: 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' + 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' + 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 @@ -4894,7 +6029,9 @@ spec: - remoteRef type: object metadata: - description: Metadata is metadata attached to the secret. The structure of metadata is provider specific, please look it up in the provider documentation. + description: |- + Metadata is metadata attached to the secret. + The structure of metadata is provider specific, please look it up in the provider documentation. x-kubernetes-preserve-unknown-fields: true required: - match @@ -4915,7 +6052,9 @@ spec: properties: kind: default: SecretStore - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + description: |- + Kind of the SecretStore resource (SecretStore or ClusterSecretStore) + Defaults to `SecretStore` type: string labelSelector: description: Optionally, sync to secret stores with label selector @@ -4923,16 +6062,24 @@ spec: 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. + 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. + 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. + 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 @@ -4944,7 +6091,10 @@ spec: 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. + 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 @@ -4977,7 +6127,10 @@ spec: type: object engineVersion: default: v2 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. + description: |- + EngineVersion specifies the template engine version + that should be used to compile/execute the + template specified in .data and .templateFrom[]. enum: - v1 - v2 @@ -5092,7 +6245,9 @@ spec: type: object type: array refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated + description: |- + refreshTime is the time and date the external secret was fetched and + the target secret updated format: date-time nullable: true type: string @@ -5122,7 +6277,9 @@ spec: - remoteRef type: object metadata: - description: Metadata is metadata attached to the secret. The structure of metadata is provider specific, please look it up in the provider documentation. + description: |- + Metadata is metadata attached to the secret. + The structure of metadata is provider specific, please look it up in the provider documentation. x-kubernetes-preserve-unknown-fields: true required: - match @@ -5155,7 +6312,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -5184,10 +6341,19 @@ spec: description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -5195,7 +6361,9 @@ spec: description: SecretStoreSpec defines the desired state of SecretStore. properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -5212,7 +6380,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -5221,23 +6391,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5245,7 +6430,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5255,51 +6442,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -5358,26 +6566,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5396,7 +6612,10 @@ spec: description: AWS configures this store to sync secrets using AWS Secret Manager provider properties: auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -5405,7 +6624,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5413,39 +6635,51 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5476,32 +6710,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -5511,10 +6757,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5522,7 +6773,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5571,13 +6824,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5593,7 +6850,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5601,7 +6861,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5628,13 +6890,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5662,13 +6928,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5693,29 +6963,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5726,7 +7008,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5734,7 +7019,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5744,16 +7031,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -5803,7 +7096,10 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, instance principal is used. Optionally, the authenticating principal type and/or user data may be supplied for the use of workload identity and user principal. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, instance principal is used. Optionally, the authenticating principal type + and/or user data may be supplied for the use of workload identity and user principal. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -5812,26 +7108,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5850,13 +7154,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -5867,10 +7178,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5878,7 +7194,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5897,26 +7215,40 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -5925,55 +7257,83 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. items: type: string type: array expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -5981,7 +7341,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -5991,55 +7353,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6047,7 +7440,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6057,27 +7452,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -6087,18 +7495,26 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6124,23 +7540,40 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -6156,7 +7589,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6197,7 +7634,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -6207,13 +7646,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6244,13 +7687,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6258,16 +7705,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6334,10 +7787,19 @@ spec: description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. 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' + 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' + 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 @@ -6347,7 +7809,9 @@ spec: conditions: description: Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore items: - description: ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in for a ClusterSecretStore instance. + description: |- + ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in + for a ClusterSecretStore instance. properties: namespaceSelector: description: Choose namespace using a labelSelector @@ -6355,16 +7819,24 @@ spec: 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. + 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. + 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. + 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 @@ -6376,7 +7848,10 @@ spec: 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. + 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 @@ -6388,7 +7863,9 @@ spec: type: object type: array controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters ES based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters ES based on this property type: string provider: description: Used to configure the provider. Only one provider may be set @@ -6405,7 +7882,9 @@ spec: description: Auth configures how the operator authenticates with Akeyless. properties: kubernetesAuth: - description: Kubernetes authenticates with Akeyless by passing the ServiceAccount token stored in the named Secret resource. + description: |- + Kubernetes authenticates with Akeyless by passing the ServiceAccount + token stored in the named Secret resource. properties: accessID: description: the Akeyless Kubernetes auth-method access-id @@ -6414,23 +7893,38 @@ spec: description: Kubernetes-auth configuration name in Akeyless-Gateway type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Akeyless. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Akeyless. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Akeyless. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Akeyless. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6438,7 +7932,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6448,51 +7944,72 @@ spec: - k8sConfName type: object secretRef: - description: Reference to a Secret that contains the details to authenticate with Akeyless. + description: |- + Reference to a Secret that contains the details + to authenticate with Akeyless. properties: accessID: description: The SecretAccessID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object type: object caBundle: - description: PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM/base64 encoded CA bundle used to validate Akeyless Gateway certificate. Only used + if the AkeylessGWApiURL URL is using HTTPS protocol. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -6505,7 +8022,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -6551,26 +8070,34 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object accessKeySecretSecretRef: description: The AccessKeySecret is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6594,7 +8121,10 @@ spec: type: string type: array auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + description: |- + Auth defines the information necessary to authenticate against AWS + if not set aws sdk will infer credentials from your environment + see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: jwt: description: Authenticate against AWS using service account tokens. @@ -6603,7 +8133,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6611,52 +8144,71 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name type: object type: object secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + description: |- + AWSAuthSecretRef holds secret references for AWS credentials + both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. properties: accessKeyIDSecretRef: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -6674,10 +8226,20 @@ spec: description: SecretsManager defines how the provider behaves when interacting with AWS SecretsManager properties: forceDeleteWithoutRecovery: - description: 'Specifies whether to delete the secret without any recovery window. You can''t use both this parameter and RecoveryWindowInDays in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery' + description: |- + Specifies whether to delete the secret without any recovery window. You + can't use both this parameter and RecoveryWindowInDays in the same call. + If you don't use either, then by default Secrets Manager uses a 30 day + recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-ForceDeleteWithoutRecovery type: boolean recoveryWindowInDays: - description: 'The number of days from 7 to 30 that Secrets Manager waits before permanently deleting the secret. You can''t use both this parameter and ForceDeleteWithoutRecovery in the same call. If you don''t use either, then by default Secrets Manager uses a 30 day recovery window. see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays' + description: |- + The number of days from 7 to 30 that Secrets Manager waits before + permanently deleting the secret. You can't use both this parameter and + ForceDeleteWithoutRecovery in the same call. If you don't use either, + then by default Secrets Manager uses a 30 day recovery window. + see: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html#SecretsManager-DeleteSecret-request-RecoveryWindowInDays format: int64 type: integer type: object @@ -6719,32 +8281,44 @@ spec: description: The Azure clientId of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientSecret: description: The Azure ClientSecret of the service principle used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object authType: default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' + description: |- + Auth type defines how to authenticate to the keyvault service. + Valid values are: + - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) + - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity) enum: - ServicePrincipal - ManagedIdentity @@ -6752,7 +8326,11 @@ spec: type: string environmentType: default: PublicCloud - description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + description: |- + EnvironmentType specifies the Azure cloud environment endpoints to use for + connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. + The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 + PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud enum: - PublicCloud - USGovernmentCloud @@ -6763,10 +8341,15 @@ spec: description: If multiple Managed Identity is assigned to the pod, you can select the one to be used type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6774,7 +8357,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6798,29 +8383,41 @@ spec: account: type: string apiKeyRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -6833,23 +8430,34 @@ spec: account: type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Conjur using the JWT authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Conjur using the JWT authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional ServiceAccountRef specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountRef specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -6857,7 +8465,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -6873,7 +8483,10 @@ spec: caBundle: type: string caProvider: - description: Used to provide custom certificate authority (CA) certificates for a secret store. The CAProvider points to a Secret or ConfigMap resource that contains a PEM-encoded certificate. + description: |- + Used to provide custom certificate authority (CA) certificates + for a secret store. The CAProvider points to a Secret or ConfigMap resource + that contains a PEM-encoded certificate. properties: key: description: The key where the CA certificate can be found in the Secret or ConfigMap. @@ -6882,7 +8495,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -6901,7 +8516,9 @@ spec: - url type: object delinea: - description: Delinea DevOps Secrets Vault https://docs.delinea.com/online-help/products/devops-secrets-vault/current + description: |- + Delinea DevOps Secrets Vault + https://docs.delinea.com/online-help/products/devops-secrets-vault/current properties: clientId: description: ClientID is the non-secret part of the credential. @@ -6910,13 +8527,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -6930,13 +8551,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -6947,10 +8572,14 @@ spec: description: Tenant is the chosen hostname / site name. type: string tld: - description: TLD is based on the server location that was chosen during provisioning. If unset, defaults to "com". + description: |- + TLD is based on the server location that was chosen during provisioning. + If unset, defaults to "com". type: string urlTemplate: - description: URLTemplate If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". + description: |- + URLTemplate + If unset, defaults to "https://%s.secretsvaultcloud.%s/v1/%s%s". type: string required: - clientId @@ -6966,16 +8595,23 @@ spec: secretRef: properties: dopplerToken: - description: The DopplerToken is used for authentication. See https://docs.doppler.com/reference/api#authentication for auth token types. The Key attribute defaults to dopplerToken if not specified. + description: |- + The DopplerToken is used for authentication. + See https://docs.doppler.com/reference/api#authentication for auth token types. + The Key attribute defaults to dopplerToken if not specified. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7048,13 +8684,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7070,7 +8710,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7078,7 +8721,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7105,13 +8750,17 @@ spec: description: AccessToken is used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7166,13 +8815,17 @@ spec: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7187,16 +8840,22 @@ spec: description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider properties: authRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object folderID: @@ -7217,29 +8876,41 @@ spec: description: has both clientCert and clientKey as secretKeySelector properties: clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7247,7 +8918,10 @@ spec: description: points to a service account that should be used for authentication properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7255,7 +8929,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7264,16 +8940,22 @@ spec: description: use static token to authenticate with properties: bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7299,7 +8981,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -7332,13 +9016,17 @@ spec: description: The ConnectToken is used for authentication to a 1Password Connect Server. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7364,7 +9052,9 @@ spec: description: Oracle configures this store to sync secrets using Oracle Vault provider properties: auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. + description: |- + Auth configures how secret-manager authenticates with the Oracle Vault. + If empty, use the instance principal, otherwise the user credentials specified in Auth. properties: secretRef: description: SecretRef to pass through sensitive information. @@ -7373,26 +9063,34 @@ spec: description: Fingerprint is the fingerprint of the API private key. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object privatekey: description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7411,13 +9109,20 @@ spec: - user type: object compartment: - description: Compartment is the vault compartment OCID. Required for PushSecret + description: |- + Compartment is the vault compartment OCID. + Required for PushSecret type: string encryptionKey: - description: EncryptionKey is the OCID of the encryption key within the vault. Required for PushSecret + description: |- + EncryptionKey is the OCID of the encryption key within the vault. + Required for PushSecret type: string principalType: - description: The type of principal to use for authentication. If left blank, the Auth struct will determine the principal type. This optional field must be specified if using workload identity. + description: |- + The type of principal to use for authentication. If left blank, the Auth struct will + determine the principal type. This optional field must be specified if using + workload identity. enum: - "" - UserPrincipal @@ -7428,10 +9133,15 @@ spec: description: Region is the region where vault is located. type: string serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + description: |- + ServiceAccountRef specified the service account + that should be used when authenticating with WorkloadIdentity. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7439,7 +9149,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7461,13 +9173,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -7490,13 +9206,17 @@ spec: description: SecretRef references a key in a secret that will be used as value. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object value: @@ -7518,16 +9238,22 @@ spec: clientId: type: string clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7556,39 +9282,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -7596,37 +9344,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -7638,7 +9402,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7646,7 +9413,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7668,39 +9437,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -7714,25 +9498,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7740,7 +9540,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7750,55 +9552,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -7806,7 +9639,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -7816,27 +9651,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -7846,13 +9694,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -7860,23 +9712,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -7884,7 +9747,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -7897,7 +9764,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -7910,23 +9779,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -7942,7 +9877,11 @@ spec: description: Body type: string caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate webhook server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -7983,7 +9922,9 @@ spec: type: string type: object secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name + description: |- + Secrets to fill in templates + These secrets will be passed to the templating function as key value pairs under the given name items: properties: name: @@ -7993,13 +9934,17 @@ spec: description: Secret ref to fill in credentials properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -8030,13 +9975,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8044,16 +9993,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8073,13 +10028,17 @@ spec: description: The authorized key used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8087,16 +10046,22 @@ spec: description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. properties: certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. + description: |- + A reference to a specific 'key' within a Secret resource, + In some instances, `key` is a required field. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8166,7 +10131,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -8186,17 +10151,28 @@ spec: openAPIV3Schema: 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' + 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' + 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: properties: controller: - description: 'Used to select the correct ESO controller (think: ingress.ingressClassName) The ESO controller is instantiated with a specific controller name and filters VDS based on this property' + description: |- + Used to select the correct ESO controller (think: ingress.ingressClassName) + The ESO controller is instantiated with a specific controller name and filters VDS based on this property type: string method: description: Vault API method to use (GET/POST/other) @@ -8214,39 +10190,61 @@ spec: description: Auth configures how secret-manager authenticates with the Vault server. properties: appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. + description: |- + AppRole authenticates with Vault using the App Role auth mechanism, + with the role and secret stored in a Kubernetes Secret resource. properties: path: default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' + description: |- + Path where the App Role authentication backend is mounted + in Vault, e.g: "approle" type: string roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. + description: |- + RoleID configured in the App Role authentication backend when setting + up the authentication backend in Vault. type: string roleRef: - description: Reference to a key in a Secret that contains the App Role ID used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role id. + description: |- + Reference to a key in a Secret that contains the App Role ID used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role id. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. + description: |- + Reference to a key in a Secret that contains the App Role secret used + to authenticate with Vault. + The `key` field must be specified and denotes which entry within the Secret + resource is used as the app role secret. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: @@ -8254,37 +10252,53 @@ spec: - secretRef type: object cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method + description: |- + Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate + Cert authentication method properties: clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method + description: |- + ClientCert is a certificate to authenticate using the Cert Vault + authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method + description: |- + SecretRef to a key in a Secret resource containing client private key to + authenticate with Vault using the Cert authentication method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object iam: - description: Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials AWS IAM authentication method + description: |- + Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials + AWS IAM authentication method properties: externalID: description: AWS External ID set on assumed IAM roles @@ -8296,7 +10310,10 @@ spec: description: A reference to a ServiceAccount resource. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8304,7 +10321,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8326,39 +10345,54 @@ spec: description: The AccessKeyID is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object secretAccessKeySecretRef: description: The SecretAccessKey is used for authentication properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object sessionTokenSecretRef: - description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + description: |- + The SessionToken used for authentication + This must be defined if AccessKeyID and SecretAccessKey are temporary credentials + see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object type: object @@ -8372,25 +10406,41 @@ spec: - vaultRole type: object jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method + description: |- + Jwt authenticates with Vault by passing role and JWT token using the + JWT/OIDC authentication method properties: kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. + description: |- + Optional ServiceAccountToken specifies the Kubernetes service account for which to request + a token for with the `TokenRequest` API. properties: audiences: - description: 'Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. Deprecated: use serviceAccountRef.Audiences instead' + description: |- + Optional audiences field that will be used to request a temporary Kubernetes service + account token for the service account referenced by `serviceAccountRef`. + Defaults to a single audience `vault` it not specified. + Deprecated: use serviceAccountRef.Audiences instead items: type: string type: array expirationSeconds: - description: 'Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Deprecated: this will be removed in the future. Defaults to 10 minutes.' + description: |- + Optional expiration time in seconds that will be used to request a temporary + Kubernetes service account token for the service account referenced by + `serviceAccountRef`. + Deprecated: this will be removed in the future. + Defaults to 10 minutes. format: int64 type: integer serviceAccountRef: description: Service account field containing the name of a kubernetes ServiceAccount. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8398,7 +10448,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8408,55 +10460,86 @@ spec: type: object path: default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' + description: |- + Path where the JWT authentication backend is mounted + in Vault, e.g: "jwt" type: string role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method + description: |- + Role is a JWT role to authenticate using the JWT/OIDC Vault + authentication method type: string secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. + description: |- + Optional SecretRef that refers to a key in a Secret resource containing JWT token to + authenticate with Vault using the JWT/OIDC authentication method. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object required: - path type: object kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. + description: |- + Kubernetes authenticates with Vault by passing the ServiceAccount + token stored in the named Secret resource to the Vault server. properties: mountPath: default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' + description: |- + Path where the Kubernetes authentication backend is mounted in Vault, e.g: + "kubernetes" type: string role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. + description: |- + A required field containing the Vault Role to assume. A Role binds a + Kubernetes ServiceAccount with a set of Vault policies. type: string secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. + description: |- + Optional secret field containing a Kubernetes ServiceAccount JWT used + for authenticating with Vault. If a name is specified without a key, + `token` is the default. If one is not specified, the one bound to + the controller will be used. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. + description: |- + Optional service account field containing the name of a kubernetes ServiceAccount. + If the service account is specified, the service account secret token JWT will be used + for authenticating with Vault. If the service account selector is not supplied, + the secretRef will be used instead. properties: audiences: - description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + description: |- + Audience specifies the `aud` claim for the service account token + If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity + then this audiences will be appended to the list items: type: string type: array @@ -8464,7 +10547,9 @@ spec: description: The name of the ServiceAccount resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string required: - name @@ -8474,27 +10559,40 @@ spec: - role type: object ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method + description: |- + Ldap authenticates with Vault by passing username/password pair using + the LDAP authentication method properties: path: default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' + description: |- + Path where the LDAP authentication backend is mounted + in Vault, e.g: "ldap" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the LDAP + user used to authenticate with Vault using the LDAP authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method + description: |- + Username is a LDAP user name used to authenticate using the LDAP Vault + authentication method type: string required: - path @@ -8504,13 +10602,17 @@ spec: description: TokenSecretRef authenticates with Vault by presenting a token. properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object userPass: @@ -8518,23 +10620,34 @@ spec: properties: path: default: user - description: 'Path where the UserPassword authentication backend is mounted in Vault, e.g: "user"' + description: |- + Path where the UserPassword authentication backend is mounted + in Vault, e.g: "user" type: string secretRef: - description: SecretRef to a key in a Secret resource containing password for the user used to authenticate with Vault using the UserPass authentication method + description: |- + SecretRef to a key in a Secret resource containing password for the + user used to authenticate with Vault using the UserPass authentication + method properties: key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. type: string name: description: The name of the Secret resource being referred to. type: string namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. type: string type: object username: - description: Username is a user name used to authenticate using the UserPass Vault authentication method + description: |- + Username is a user name used to authenticate using the UserPass Vault + authentication method type: string required: - path @@ -8542,7 +10655,11 @@ spec: type: object type: object caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. + description: |- + PEM encoded CA bundle used to validate Vault server certificate. Only used + if the Server URL is using HTTPS protocol. This parameter is ignored for + plain HTTP protocol connection. If not set the system root certificates + are used to validate the TLS connection. format: byte type: string caProvider: @@ -8555,7 +10672,9 @@ spec: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. + description: |- + The namespace the Provider type is in. + Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -8568,23 +10687,89 @@ spec: - type type: object forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header + description: |- + ForwardInconsistent tells Vault to forward read-after-write requests to the Vault + leader instead of simply retrying within a loop. This can increase performance if + the option is enabled serverside. + https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header type: boolean namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' + description: |- + Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces type: string path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' + description: |- + Path is the mount path of the Vault KV backend endpoint, e.g: + "secret". The v2 KV secret engine version specific "/data" path suffix + for fetching secrets from Vault is optional and will be appended + if not present in specified path. type: string readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency + description: |- + ReadYourWrites ensures isolated read-after-write semantics by + providing discovered cluster replication states in each request. + More information about eventual consistency in Vault can be found here + https://www.vaultproject.io/docs/enterprise/consistency type: boolean server: description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' type: string + tls: + description: |- + The configuration used for client side related TLS communication, when the Vault server + requires mutual authentication. Only used if the Server URL is using HTTPS protocol. + This parameter is ignored for plain HTTP protocol connection. + It's worth noting this configuration is different from the "TLS certificates auth method", + which is available under the `auth.cert` section. + properties: + certSecretRef: + description: |- + CertSecretRef is a certificate added to the transport layer + when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.crt'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + keySecretRef: + description: |- + KeySecretRef to a key in a Secret resource containing client private key + added to the transport layer when communicating with the Vault server. + If no key for the Secret is specified, external-secret will default to 'tls.key'. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object version: default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". + description: |- + Version is the Vault KV secret engine version. This can be either "v1" or + "v2". Version defaults to "v2". enum: - v1 - v2 @@ -8595,7 +10780,12 @@ spec: type: object resultType: default: Data - description: Result type defines which data is returned from the generator. By default it is the "data" section of the Vault API response. When using e.g. /auth/token/create the "data" section is empty but the "auth" section contains the generated token. Please refer to the vault docs regarding the result data structure. + description: |- + Result type defines which data is returned from the generator. + By default it is the "data" section of the Vault API response. + When using e.g. /auth/token/create the "data" section is empty but + the "auth" section contains the generated token. + Please refer to the vault docs regarding the result data structure. enum: - Data - Auth @@ -8626,10 +10816,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8693,10 +10883,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8802,10 +10992,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -8842,10 +11032,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8886,10 +11076,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8907,10 +11097,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8927,10 +11117,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8963,10 +11153,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -9002,10 +11192,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -9023,10 +11213,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -9047,10 +11237,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9062,10 +11252,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -9080,7 +11270,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -9110,10 +11300,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9125,10 +11315,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -9143,7 +11333,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -9160,10 +11350,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -9175,10 +11365,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.11 + helm.sh/chart: external-secrets-0.9.12 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.11" + app.kubernetes.io/version: "v0.9.12" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -9193,7 +11383,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: "ghcr.io/external-secrets/external-secrets:v0.9.11-ubi" + image: ghcr.io/external-secrets/external-secrets:v0.9.12-ubi imagePullPolicy: IfNotPresent args: - webhook From 81574849d0c549aa95b396d95f1a0fce695073f4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 12 Feb 2024 12:15:45 +0100 Subject: [PATCH 1102/1263] Update vault image to 1.15.5-ubi This fixes a few CVEs. Tested on MCG. --- hashicorp-vault/values.yaml | 2 +- tests/hashicorp-vault-industrial-edge-factory.expected.yaml | 4 ++-- tests/hashicorp-vault-industrial-edge-hub.expected.yaml | 4 ++-- tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml | 4 ++-- tests/hashicorp-vault-naked.expected.yaml | 4 ++-- tests/hashicorp-vault-normal.expected.yaml | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index 25740d98..aca2fdba 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -48,4 +48,4 @@ vault: termination: "reencrypt" image: repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.15.2-ubi" + tag: "1.15.5-ubi" diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index 0c68e832..76454ee6 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index 0c68e832..76454ee6 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index 0c68e832..76454ee6 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index e09ac08f..fc05bc43 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index 0c68e832..76454ee6 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR From dde1055d6f250be6320cd0dae0ead70e2ec12f5c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 15 Feb 2024 09:21:36 +0100 Subject: [PATCH 1103/1263] Use gitops-1.11 in acm as well This is mainly for consistency reasons as the value is taken from main.gitops anyways. --- acm/templates/policies/ocp-gitops-policy.yaml | 2 +- acm/values.yaml | 2 +- tests/acm-industrial-edge-factory.expected.yaml | 2 +- tests/acm-industrial-edge-hub.expected.yaml | 2 +- tests/acm-medical-diagnosis-hub.expected.yaml | 2 +- tests/acm-naked.expected.yaml | 2 +- tests/acm-normal.expected.yaml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index 4691c18d..a0ed611f 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -35,7 +35,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: {{ default "gitops-1.8" .Values.main.gitops.channel }} + channel: {{ default "gitops-1.11" .Values.main.gitops.channel }} installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/acm/values.yaml b/acm/values.yaml index 1100bafd..fb7cb03a 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -1,6 +1,6 @@ main: gitops: - channel: "gitops-1.8" + channel: "gitops-1.11" global: extraValueFiles: [] diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index 2210b4cf..66c0c0b9 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -100,7 +100,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.8 + channel: gitops-1.11 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index f9627771..afcd6ab3 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -307,7 +307,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.8 + channel: gitops-1.11 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index cea5a1dc..6e2e98b0 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -298,7 +298,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.8 + channel: gitops-1.11 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index 5ba9bd60..880ef747 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -101,7 +101,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.8 + channel: gitops-1.11 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 55553a79..03b0eae5 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -797,7 +797,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.8 + channel: gitops-1.11 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators From 947dd22d9878964a9e11d67ebe4b1b76186f345d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 20 Feb 2024 17:46:27 +0100 Subject: [PATCH 1104/1263] Small gitops channel cleanups Mainly for consistency reasons. gitops-1.11 is already the default --- operator-install/templates/pattern.yaml | 2 +- reference-output.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index 3dc1948a..3615d18d 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -13,7 +13,7 @@ spec: tokenSecretNamespace: {{ .Values.main.tokenSecretNamespace }} {{- end }} {{/* if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace */}} gitOpsSpec: - operatorChannel: {{ default "gitops-1.8" .Values.main.gitops.channel }} + operatorChannel: {{ default "gitops-1.11" .Values.main.gitops.channel }} operatorSource: {{ default "redhat-operators" .Values.main.gitops.operatorSource }} multiSourceConfig: enabled: {{ .Values.main.multiSourceConfig.enabled }} diff --git a/reference-output.yaml b/reference-output.yaml index dbb4c6dc..cdc1a303 100644 --- a/reference-output.yaml +++ b/reference-output.yaml @@ -112,7 +112,7 @@ metadata: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: "" spec: - channel: gitops-1.8 + channel: gitops-1.11 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators From 9614757f6e012f595d37b611e666a02d1483e78d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 21 Feb 2024 19:37:23 +0100 Subject: [PATCH 1105/1263] Upgrade namespaced argocd version to v1beta1 --- clustergroup/templates/plumbing/argocd.yaml | 2 +- tests/clustergroup-industrial-edge-factory.expected.yaml | 2 +- tests/clustergroup-industrial-edge-hub.expected.yaml | 2 +- tests/clustergroup-medical-diagnosis-hub.expected.yaml | 2 +- tests/clustergroup-naked.expected.yaml | 2 +- tests/clustergroup-normal.expected.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index b0977e9c..12e362aa 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -1,6 +1,6 @@ {{- if (eq .Values.enabled "all") }} {{- $namespace := print $.Values.global.pattern "-" $.Values.clusterGroup.name }} -apiVersion: argoproj.io/v1alpha1 +apiVersion: argoproj.io/v1beta1 kind: ArgoCD metadata: finalizers: diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 948ec58b..851119a8 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -533,7 +533,7 @@ spec: limit: 20 --- # Source: clustergroup/templates/plumbing/argocd.yaml -apiVersion: argoproj.io/v1alpha1 +apiVersion: argoproj.io/v1beta1 kind: ArgoCD metadata: finalizers: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 541d6128..611ecbb7 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -1235,7 +1235,7 @@ spec: limit: 20 --- # Source: clustergroup/templates/plumbing/argocd.yaml -apiVersion: argoproj.io/v1alpha1 +apiVersion: argoproj.io/v1beta1 kind: ArgoCD metadata: finalizers: diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index e7c66202..9855500c 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -1396,7 +1396,7 @@ spec: limit: 20 --- # Source: clustergroup/templates/plumbing/argocd.yaml -apiVersion: argoproj.io/v1alpha1 +apiVersion: argoproj.io/v1beta1 kind: ArgoCD metadata: finalizers: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index de02651e..194bfa8f 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -283,7 +283,7 @@ spec: restartPolicy: Never --- # Source: clustergroup/templates/plumbing/argocd.yaml -apiVersion: argoproj.io/v1alpha1 +apiVersion: argoproj.io/v1beta1 kind: ArgoCD metadata: finalizers: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 9bf39732..f038446d 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -982,7 +982,7 @@ spec: - /status --- # Source: clustergroup/templates/plumbing/argocd.yaml -apiVersion: argoproj.io/v1alpha1 +apiVersion: argoproj.io/v1beta1 kind: ArgoCD metadata: finalizers: From f0b3d01ccdb6bc0b11c86bb48f38546ec48acc5b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 22 Feb 2024 19:52:01 +0100 Subject: [PATCH 1106/1263] Stop using OpenShiftControllerManager lookups When applying the policy to install the cluster-wide argo on regional clusters, we do some lookups() on the regional cluster in order to pass the version values, domain names, etc. To get the cluster version we were using the OpenShiftControllerManager which is problematic because it does not exist on hyper-shift clusters. Let's switch to use the ClusterVersion.status.history[0].version entry. The only smaller caveat is that due to limitations in go templates + sprig functions, we cannot really take the last version only when the state is "Completed", but we simply take the last version. This means that during a cluster upgrade on a regional cluster, we will include values of the version the cluster is upgrading to, which is less than ideal, but it should eventually converge in any case. For reference the function that guarantees that the ordering of the history status in ClusterVersion is preserved is here: https://pkg.go.dev/github.com/openshift/api/config/v1#ClusterVersionStatus Tested on Lester's cluster and on a local cluster of mine. Co-Authored-By: Lester Claudio --- acm/templates/_helpers.tpl | 4 ++-- acm/templates/policies/application-policies.yaml | 2 +- tests/acm-industrial-edge-hub.expected.yaml | 6 +++--- tests/acm-medical-diagnosis-hub.expected.yaml | 6 +++--- tests/acm-normal.expected.yaml | 12 ++++++------ 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/acm/templates/_helpers.tpl b/acm/templates/_helpers.tpl index fdd91273..910b3970 100644 --- a/acm/templates/_helpers.tpl +++ b/acm/templates/_helpers.tpl @@ -5,9 +5,9 @@ Default always defined valueFiles to be included when pushing the cluster wide a - "/values-global.yaml" - "/values-{{ .name }}.yaml" - '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}.yaml' -- '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}-{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}.yaml' +- '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}-{{ `{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}` }}.yaml' - '/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}-{{ .name }}.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version -- '/values-{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}-{{ .name }}.yaml' +- '/values-{{ `{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}` }}.yaml' {{- end }} {{- /*acm.app.policies.valuefiles */}} diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index e2e717ca..01082e54 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -68,7 +68,7 @@ spec: value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}` }}' # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion - value: '{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}' + value: '{{ `{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}` }}' - name: global.localClusterName value: '{{ `{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}` }}' - name: global.clusterPlatform diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index afcd6ab3..07593017 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -218,11 +218,11 @@ spec: - "/values-global.yaml" - "/values-factory.yaml" - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-factory.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-factory.yaml' + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -241,7 +241,7 @@ spec: value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion - value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + value: '{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}' - name: global.localClusterName value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 6e2e98b0..959fbe30 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -209,11 +209,11 @@ spec: - "/values-global.yaml" - "/values-region-one.yaml" - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-region-one.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-region-one.yaml' + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -232,7 +232,7 @@ spec: value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion - value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + value: '{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}' - name: global.localClusterName value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 03b0eae5..87071cc4 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -612,11 +612,11 @@ spec: - "/values-global.yaml" - "/values-acm-edge.yaml" - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-edge.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-edge.yaml' + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -635,7 +635,7 @@ spec: value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion - value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + value: '{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}' - name: global.localClusterName value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform @@ -708,11 +708,11 @@ spec: - "/values-global.yaml" - "/values-acm-provision-edge.yaml" - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-edge.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-provision-edge.yaml' + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -731,7 +731,7 @@ spec: value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion - value: '{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}' + value: '{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}' - name: global.localClusterName value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform From bd9efba00c0822b8a95b211e7ad44a57e5936453 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 19:41:53 +0000 Subject: [PATCH 1107/1263] Bump azure/setup-helm from 3 to 4 Bumps [azure/setup-helm](https://github.com/azure/setup-helm) from 3 to 4. - [Release notes](https://github.com/azure/setup-helm/releases) - [Changelog](https://github.com/Azure/setup-helm/blob/main/CHANGELOG.md) - [Commits](https://github.com/azure/setup-helm/compare/v3...v4) --- updated-dependencies: - dependency-name: azure/setup-helm dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 39aa63cb..39843f26 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -34,7 +34,7 @@ jobs: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 - name: Setup helm - uses: azure/setup-helm@v3 + uses: azure/setup-helm@v4 with: version: 'v3.13.2' From 48b2e11002ebdf293cb7fc396f9fc4e9ebc34e9f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 21 Mar 2024 09:47:41 +0100 Subject: [PATCH 1108/1263] Upgrade ESO to v0.9.13 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.9.12.tgz | Bin 93006 -> 0 bytes .../charts/external-secrets-0.9.13.tgz | Bin 0 -> 96126 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 409 ++++++++++++++++-- ...-secrets-industrial-edge-hub.expected.yaml | 409 ++++++++++++++++-- ...ecrets-medical-diagnosis-hub.expected.yaml | 409 ++++++++++++++++-- ...olang-external-secrets-naked.expected.yaml | 409 ++++++++++++++++-- ...lang-external-secrets-normal.expected.yaml | 409 ++++++++++++++++-- 9 files changed, 1834 insertions(+), 219 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.12.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.9.13.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 38549d5c..a2a2b061 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.3 dependencies: - name: external-secrets - version: "0.9.12" + version: "0.9.13" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.9.12.tgz b/golang-external-secrets/charts/external-secrets-0.9.12.tgz deleted file mode 100644 index 368cabd9034a0f823297e5036e4191438ebf7800..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93006 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 z 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 I${0}p1t(C-`~Uy| diff --git a/golang-external-secrets/charts/external-secrets-0.9.13.tgz b/golang-external-secrets/charts/external-secrets-0.9.13.tgz new file mode 100644 index 0000000000000000000000000000000000000000..464befd970d337e51451d4871cd1a9aef6458da2 GIT binary patch literal 96126 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 Date: Thu, 21 Mar 2024 10:21:54 +0100 Subject: [PATCH 1109/1263] Upgrade vault to 1.15.6 --- hashicorp-vault/values.yaml | 2 +- tests/hashicorp-vault-industrial-edge-factory.expected.yaml | 4 ++-- tests/hashicorp-vault-industrial-edge-hub.expected.yaml | 4 ++-- tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml | 4 ++-- tests/hashicorp-vault-naked.expected.yaml | 4 ++-- tests/hashicorp-vault-normal.expected.yaml | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index aca2fdba..cb1b769f 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -48,4 +48,4 @@ vault: termination: "reencrypt" image: repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.15.5-ubi" + tag: "1.15.6-ubi" diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index 76454ee6..0f2218b0 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index 76454ee6..0f2218b0 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index 76454ee6..0f2218b0 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index fc05bc43..68b5a897 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index 76454ee6..0f2218b0 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -204,7 +204,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -373,7 +373,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.5-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR From 399370f0d222157ca5ea00819a556af8de218afe Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 25 Mar 2024 16:07:58 +0100 Subject: [PATCH 1110/1263] Bind mount /etc/pki in the wrapper This is useful whenever a custom CA is installed on the system and is needed to connect to a remote cluster. --- scripts/pattern-util.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 9cec19fa..ea02b7ef 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -56,6 +56,7 @@ podman run -it --rm --pull=newer \ --security-opt label=disable \ -e EXTRA_HELM_OPTS \ -e KUBECONFIG \ + -v /etc/pki:/etc/pki:ro \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ ${PODMAN_ARGS} \ From a88f040f1a0a4880ed90b1adf3ffec8ea57b6d6b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 25 Mar 2024 17:27:09 +0100 Subject: [PATCH 1111/1263] Properly error out in preview-all when we cannot connect to the cluster Before: $ ./pattern.sh make preview-all make -f common/Makefile preview-all make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' error: Missing or incomplete configuration info. Please point to an existing, complete config file: 1. Via the command-line flag --kubeconfig 2. Via the KUBECONFIG environment variable 3. In your home directory as ~/.kube/config To view or setup config directly use the 'config' command. error: Missing or incomplete configuration info. Please point to an existing, complete config file: 1. Via the command-line flag --kubeconfig 2. Via the KUBECONFIG environment variable 3. In your home directory as ~/.kube/config To view or setup config directly use the 'config' command. ...This goes on for many more iterations... After: $ ./pattern.sh make preview-all make -f common/Makefile preview-all make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Could not access the cluster: error: Missing or incomplete configuration info. Please point to an existing, complete config file: 1. Via the command-line flag --kubeconfig 2. Via the KUBECONFIG environment variable 3. In your home directory as ~/.kube/config To view or setup config directly use the 'config' command. make[1]: *** [common/Makefile:59: preview-all] Error 1 make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' make: *** [Makefile:12: preview-all] Error 2 --- scripts/preview-all.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/preview-all.sh b/scripts/preview-all.sh index cc7775bf..296e7dc8 100755 --- a/scripts/preview-all.sh +++ b/scripts/preview-all.sh @@ -7,6 +7,14 @@ HUB=$( yq ".main.clusterGroupName" values-global.yaml ) MANAGED_CLUSTERS=$( yq ".clusterGroup.managedClusterGroups.[].name" values-$HUB.yaml ) ALL_CLUSTERS=( $HUB $MANAGED_CLUSTERS ) +CLUSTER_INFO_OUT=$(oc cluster-info 2>&1) +CLUSTER_INFO_RET=$? +if [ $CLUSTER_INFO_RET -ne 0 ]; then + echo "Could not access the cluster:" + echo "${CLUSTER_INFO_OUT}" + exit 1 +fi + for cluster in ${ALL_CLUSTERS[@]}; do APPS=$( yq ".clusterGroup.applications.[].name" values-$cluster.yaml ) for app in $APPS; do From ffd421b5117c0867677cc19f75aa67af6b40687e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 25 Mar 2024 17:44:32 +0100 Subject: [PATCH 1112/1263] Only include values files if they do exist in preview.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is because in helm we use "ignoreMissingValueFiles: true". I.e. we just ignore non existing value files. Let's do the same for the preview.sh script. Before: ❯ make preview-all make -f common/Makefile preview-all make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Error: open /home/michele/Engineering/cloud-patterns/multicloud-gitops/overrides/values-None.yaml: no such file or directory Error: open /home/michele/Engineering/cloud-patterns/multicloud-gitops/overrides/values-None.yaml: no such file or directory Error: open /home/michele/Engineering/cloud-patterns/multicloud-gitops/overrides/values-None.yaml: no such file or directory Error: open /home/michele/Engineering/cloud-patterns/multicloud-gitops/overrides/values-None.yaml: no such file or directory Error: open /home/michele/Engineering/cloud-patterns/multicloud-gitops/overrides/values-None.yaml: no such file or directory common/scripts/preview.sh: eval: line 79: unexpected EOF while looking for matching `"' common/scripts/preview.sh: eval: line 79: unexpected EOF while looking for matching `"' common/scripts/preview.sh: eval: line 79: unexpected EOF while looking for matching `"' make[1]: *** [common/Makefile:59: preview-all] Error 2 make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' make: *** [Makefile:12: preview-all] Error 2 After: ❯ make preview-all > /dev/null ; echo $? 0 --- scripts/preview.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/preview.sh b/scripts/preview.sh index 379f240d..2d8079cf 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -62,16 +62,16 @@ OVERRIDES=$( getOverrides ) VALUE_FILES="" IFS=$'\n' for line in $sharedValueFiles; do - if [ $line != "null" ]; then - file=$(replaceGlobals $line) - VALUE_FILES="$VALUE_FILES -f $PWD$file" + if [ $line != "null" ] && [ -f $line ]; then + file=$(replaceGlobals $line) + VALUE_FILES="$VALUE_FILES -f $PWD$file" fi done for line in $appValueFiles; do - if [ $line != "null" ]; then - file=$(replaceGlobals $line) - VALUE_FILES="$VALUE_FILES -f $PWD$file" + if [ $line != "null" ] && [ -f $line ]; then + file=$(replaceGlobals $line) + VALUE_FILES="$VALUE_FILES -f $PWD$file" fi done From e6241d9b163d147837b2fa409a4ed776a8b695a1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 25 Mar 2024 18:46:21 +0100 Subject: [PATCH 1113/1263] Do not error out in preview when kustomize: true When `kustomize: true` simply take the path and call `kustomize build `. In any other case keep using helm for templating. Before: ... + common/scripts/preview.sh hub compliance-operator https://github.com/mbaldessari/multicloud-gitops.git preview-fixes Error: Chart.yaml file is missing After: ... + common/scripts/preview.sh hub compliance-operator https://github.com/mbaldessari/multicloud-gitops.git preview-fixes apiVersion: console.openshift.io/v1 kind: ConsoleNotification metadata: name: purpose-banner spec: backgroundColor: '#ff0000' color: '#fff' location: BannerTop text: HUBOPS --- scripts/preview.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/preview.sh b/scripts/preview.sh index 2d8079cf..762a8ec5 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -57,6 +57,7 @@ CLUSTER_OPTS="$CLUSTER_OPTS --set global.clusterPlatform=$platform" sharedValueFiles=$(yq ".clusterGroup.sharedValueFiles" values-$SITE.yaml) appValueFiles=$(yq ".clusterGroup.applications.$APP.extraValueFiles" values-$SITE.yaml) +isKustomize=$(yq ".clusterGroup.applications.$APP.kustomize" values-$SITE.yaml) OVERRIDES=$( getOverrides ) VALUE_FILES="" @@ -75,5 +76,11 @@ for line in $appValueFiles; do fi done -cmd="helm template $chart --name-template ${APP} -n ${namespace} ${VALUE_FILES} ${OVERRIDES} ${CLUSTER_OPTS}" -eval "$cmd" +if [ $isKustomize == "true" ]; then + kustomizePath=$(yq ".clusterGroup.applications.$APP.path" values-$SITE.yaml) + cmd="kustomize build ${kustomizePath}" + eval "$cmd" +else + cmd="helm template $chart --name-template ${APP} -n ${namespace} ${VALUE_FILES} ${OVERRIDES} ${CLUSTER_OPTS}" + eval "$cmd" +fi From 8dee997ae865373ed7fcc395868001890281acfc Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Mon, 25 Mar 2024 14:45:47 -0500 Subject: [PATCH 1114/1263] Also pass EXTRA_PLAYBOOK_OPTS environment setting --- scripts/pattern-util.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index ea02b7ef..0d1b8fa4 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -55,6 +55,7 @@ fi podman run -it --rm --pull=newer \ --security-opt label=disable \ -e EXTRA_HELM_OPTS \ + -e EXTRA_PLAYBOOK_OPTS \ -e KUBECONFIG \ -v /etc/pki:/etc/pki:ro \ -v "${HOME}":"${HOME}" \ From dafd44b812ad2ea9fd2ab414198079abdfbb8602 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 26 Mar 2024 09:49:20 +0100 Subject: [PATCH 1115/1263] Use oc kustomize That is what we have inside the utility container, so let's just rely on that --- scripts/preview.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/preview.sh b/scripts/preview.sh index 762a8ec5..154e7bda 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -78,7 +78,7 @@ done if [ $isKustomize == "true" ]; then kustomizePath=$(yq ".clusterGroup.applications.$APP.path" values-$SITE.yaml) - cmd="kustomize build ${kustomizePath}" + cmd="oc kustomize ${kustomizePath}" eval "$cmd" else cmd="helm template $chart --name-template ${APP} -n ${namespace} ${VALUE_FILES} ${OVERRIDES} ${CLUSTER_OPTS}" From a2c3a2e6e75ab5e3c769752b98c0f3a255b52e95 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 26 Mar 2024 10:09:51 +0100 Subject: [PATCH 1116/1263] Print out application being parsed for easier debugging --- scripts/preview-all.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/preview-all.sh b/scripts/preview-all.sh index 296e7dc8..9fdcd955 100755 --- a/scripts/preview-all.sh +++ b/scripts/preview-all.sh @@ -18,6 +18,7 @@ fi for cluster in ${ALL_CLUSTERS[@]}; do APPS=$( yq ".clusterGroup.applications.[].name" values-$cluster.yaml ) for app in $APPS; do + printf "# Parsing application $app from cluster $cluster\n" common/scripts/preview.sh $cluster $app $REPO $TARGET_BRANCH done done From c634054d9f19487ad9943a0f6d2328e579756482 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 27 Mar 2024 11:57:28 +0100 Subject: [PATCH 1117/1263] Add .global.secretStore.backend in the clustergroup schema It is currently not there even though we mention it in the values files. --- clustergroup/values.schema.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index e88fc5a9..0ab0fc37 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -153,6 +153,9 @@ }, "options": { "$ref": "#/definitions/Options" + }, + "secretStore": { + "$ref": "#/definitions/GlobalSecretStore" } }, "required": [ @@ -160,6 +163,18 @@ ], "title": "Global" }, + "GlobalSecretStore": { + "type": "object", + "additionalProperties": false, + "properties": { + "backend": { + "type": "string", + "description": "Name of the secrets backend", + "default": "vault" + } + }, + "title": "GlobalSecretsStore" + }, "GlobalGit": { "type": "object", "additionalProperties": true, From 5280f2ca9f6253245b7215bc2e724a778e96f183 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 28 Mar 2024 15:24:40 +0100 Subject: [PATCH 1118/1263] Fix check for secretStore.backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default function in helm is somewhat unintuitive: ❯ cat templates/test.yaml metadata: name: foo {{- if eq .Values.global.secretStore.backend "vault" | default "vault" }} label: vault_is_here {{- else }} labe: not_here {{- end }} ❯ helm template --set global.secretStore.backend=foo . metadata: name: foo label: vault_is_here No matter the value of .Values.global.secretStore.backend, the default branch takes over. So let's change this to something that is correct albeit somewhat less readable Tested as follows: # global.secretStore.backend unset ❯ helm template --set global.secretStore.backend=null common/clustergroup |grep unsealjob.yaml |wc -l 1 # global.secretStore.backend set to 'vault' ❯ helm template --set global.secretStore.backend=vault common/clustergroup |grep unsealjob.yaml |wc -l 1 # global.secretStore.backend set to 'kubernetes' ❯ helm template --set global.secretStore.backend=kubernetes common/clustergroup |grep unsealjob.yaml |wc -l 0 ❯ helm template --set global.secretStore.backend=vault golang-external-secrets |grep -- -backend name: vault-backend ❯ helm template --set global.secretStore.backend=null golang-external-secrets |grep -- -backend name: vault-backend ❯ helm template --set global.secretStore.backend=kubernetes golang-external-secrets |grep -- -backend name: kubernetes-backend --- clustergroup/templates/imperative/unsealjob.yaml | 4 +++- .../kubernetes/golang-external-secrets-hub-secretstore.yaml | 5 ++--- .../vault/golang-external-secrets-hub-secretstore.yaml | 5 ++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index 4db14be3..b251c5da 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -1,4 +1,6 @@ -{{- if eq .Values.global.secretStore.backend "vault" | default "vault" }} +{{/* If the backend is not set at all we default to "vault". See https://www.github.com/helm/helm/issues/3308 + why we avoid using the default function */}} +{{- if or (eq .Values.global.secretStore.backend "vault") (not (hasKey .Values.global.secretStore "backend")) }} {{- if not (eq .Values.enabled "plumbing") }} {{- if $.Values.clusterGroup.isHubCluster }} --- diff --git a/golang-external-secrets/templates/kubernetes/golang-external-secrets-hub-secretstore.yaml b/golang-external-secrets/templates/kubernetes/golang-external-secrets-hub-secretstore.yaml index 62253f1f..02128e9a 100644 --- a/golang-external-secrets/templates/kubernetes/golang-external-secrets-hub-secretstore.yaml +++ b/golang-external-secrets/templates/kubernetes/golang-external-secrets-hub-secretstore.yaml @@ -1,10 +1,9 @@ -{{- $backend := .Values.global.secretStore.backend | default "vault" }} -{{- if eq $backend "kubernetes" }} +{{- if eq .Values.global.secretStore.backend "kubernetes" }} --- apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: - name: {{ $backend }}-backend + name: kubernetes-backend namespace: golang-external-secrets spec: provider: diff --git a/golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml b/golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml index 8fdd4ab0..59f55086 100644 --- a/golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml +++ b/golang-external-secrets/templates/vault/golang-external-secrets-hub-secretstore.yaml @@ -1,10 +1,9 @@ -{{- $backend := .Values.global.secretStore.backend | default "vault" }} -{{- if eq $backend "vault" }} +{{- if or (eq .Values.global.secretStore.backend "vault") (not (hasKey .Values.global.secretStore "backend")) }} --- apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: - name: {{ $backend }}-backend + name: vault-backend namespace: golang-external-secrets spec: provider: From 8869f11989320d07700203b6d6d50a31ec064639 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 28 Mar 2024 18:45:13 +0100 Subject: [PATCH 1119/1263] Introduce a generic parameter override via the EXTRA_ARGS env variable This way a user can decide to modify the podman command line. For example to inject additional useful extra variables in the container. For example: export EXTRA_ARGS="-e OCP_DOMAIN" ./pattern-util.sh make preview-all.sh --- scripts/pattern-util.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 0d1b8fa4..0f865e0a 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -61,6 +61,7 @@ podman run -it --rm --pull=newer \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ ${PODMAN_ARGS} \ + ${EXTRA_ARGS} \ -w "$(pwd)" \ "$PATTERN_UTILITY_CONTAINER" \ $@ From ba583f569aa571827c82e498bcca15c98af7a3d3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 28 Mar 2024 18:51:11 +0100 Subject: [PATCH 1120/1263] Allow overriding the oc lookups in preview.sh This allows us to inject values to ease testing. Tested as follows: $ unset OCP_DOMAIN OCP_PLATFORM OCP_VERSION $ export EXTRA_ARGS="-e OCP_DOMAIN -e OCP_PLATFORM -e OCP_VERSION" $ ./pattern.sh make preview-all &> /tmp/1 $ export OCP_DOMAIN=adifferentdomain.foo $ ./pattern.sh make preview-all &> /tmp/2 # Templates have effectively changed the domain $ diff -u /tmp/1 /tmp/2 | wc -l 73 $ unset OCP_DOMAIN # Without the domain change the templates are unchanged $ ./pattern.sh make preview-all &> /tmp/3 $ diff -u /tmp/1 /tmp/3 | wc -l 0 Note: When using pattern.sh you will need to inject the env variables in the container via `export EXTRA_ARGS="-e OCP_PLATFORM -e OCP_VERSION -e OCP_DOMAIN"` --- scripts/preview.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/preview.sh b/scripts/preview.sh index 154e7bda..347a0cdd 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -17,9 +17,13 @@ chart=$(yq ".clusterGroup.applications.$APP.path" values-$SITE.yaml) namespace=$(yq ".clusterGroup.applications.$APP.namespace" values-$SITE.yaml) pattern=$(yq ".global.pattern" values-global.yaml) -platform=$(oc get Infrastructure.config.openshift.io/cluster -o jsonpath='{.spec.platformSpec.type}') -ocpversion=$(oc get clusterversion/version -o jsonpath='{.status.desired.version}' | awk -F. '{print $1"."$2}') -domain=$(oc get Ingress.config.openshift.io/cluster -o jsonpath='{.spec.domain}' | sed 's/^apps.//') +# You can override the default lookups by using OCP_{PLATFORM,VERSION,DOMAIN} +# Note that when using the utility container you need to pass in the above variables +# by export EXTRA_ARGS="-e OCP_PLATFORM -e OCP_VERSION -e OCP_DOMAIN" before +# invoking pattern-util.sh +platform=${OCP_PLATFORM:-$(oc get Infrastructure.config.openshift.io/cluster -o jsonpath='{.spec.platformSpec.type}')} +ocpversion=${OCP_VERSION:-$(oc get clusterversion/version -o jsonpath='{.status.desired.version}' | awk -F. '{print $1"."$2}')} +domain=${OCP_DOMAIN:-$(oc get Ingress.config.openshift.io/cluster -o jsonpath='{.spec.domain}' | sed 's/^apps.//')} function replaceGlobals() { output=$( echo $1 | sed -e 's/ //g' -e 's/\$//g' -e s@^-@@g -e s@\'@@g ) From 66d426dfb904a3099d747df9629ddaaed1d360a4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 29 Mar 2024 11:25:05 +0100 Subject: [PATCH 1121/1263] Always include values-global.yaml and values-.yaml Those are the starting points for setting the values. Without this, for example, the rendering of common/acm on the hub is basically empty because clusterGroup.isHubCluster won't be true. --- scripts/preview.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/preview.sh b/scripts/preview.sh index 347a0cdd..a39b1046 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -64,7 +64,7 @@ appValueFiles=$(yq ".clusterGroup.applications.$APP.extraValueFiles" values-$SIT isKustomize=$(yq ".clusterGroup.applications.$APP.kustomize" values-$SITE.yaml) OVERRIDES=$( getOverrides ) -VALUE_FILES="" +VALUE_FILES="-f values-global.yaml -f values-$SITE.yaml" IFS=$'\n' for line in $sharedValueFiles; do if [ $line != "null" ] && [ -f $line ]; then From 4de8385661f99f88dc4757fae72d798de7dace4e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 29 Mar 2024 11:41:59 +0100 Subject: [PATCH 1122/1263] Add help and message clarifying that preview has certain limits --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0d5d0a36..c6c7539c 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,8 @@ help: ## This help message show: ## show the starting template without installing it helm template common/operator-install/ --name-template $(NAME) $(HELM_OPTS) -preview-all: +preview-all: ## (EXPERIMENTAL) Previews all applications on hub and managed clusters + @echo "NOTE: This is just a tentative approximation of rendering all hub and managed clusters templates" @common/scripts/preview-all.sh $(TARGET_REPO) $(TARGET_BRANCH) preview-%: From d7e913bca9bb6fc5313d20e1d56be64f894ebe97 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 29 Mar 2024 12:07:40 +0100 Subject: [PATCH 1123/1263] Add clustergroup support to preview target With this the preview all should be a lot more complete and useful. Closes: https://github.com/validatedpatterns/common/issues/452 --- scripts/preview-all.sh | 3 ++- scripts/preview.sh | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/preview-all.sh b/scripts/preview-all.sh index 9fdcd955..4bf59322 100755 --- a/scripts/preview-all.sh +++ b/scripts/preview-all.sh @@ -16,7 +16,8 @@ if [ $CLUSTER_INFO_RET -ne 0 ]; then fi for cluster in ${ALL_CLUSTERS[@]}; do - APPS=$( yq ".clusterGroup.applications.[].name" values-$cluster.yaml ) + # We always add clustergroup as it is the entry point and it gets special cased in preview.sh. + APPS="clustergroup $( yq ".clusterGroup.applications.[].name" values-$cluster.yaml )" for app in $APPS; do printf "# Parsing application $app from cluster $cluster\n" common/scripts/preview.sh $cluster $app $REPO $TARGET_BRANCH diff --git a/scripts/preview.sh b/scripts/preview.sh index a39b1046..d5e695e4 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -13,8 +13,13 @@ APP=$1; shift GIT_REPO=$1; shift GIT_BRANCH=$1; shift -chart=$(yq ".clusterGroup.applications.$APP.path" values-$SITE.yaml) -namespace=$(yq ".clusterGroup.applications.$APP.namespace" values-$SITE.yaml) +if [ "${APP}" != "clustergroup" ]; then + chart=$(yq ".clusterGroup.applications.$APP.path" values-$SITE.yaml) + namespace=$(yq ".clusterGroup.applications.$APP.namespace" values-$SITE.yaml) +else + chart="common/clustergroup" + namespace="openshift-operators" +fi pattern=$(yq ".global.pattern" values-global.yaml) # You can override the default lookups by using OCP_{PLATFORM,VERSION,DOMAIN} From dd25da002c36c035407c7f70485961fdbe5488c3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 29 Mar 2024 13:22:02 +0100 Subject: [PATCH 1124/1263] Fix preview when the application's index name is not the same as the name attribute This covers the following case: foobar: name: foo namespace: foo project: foo path: charts/all/foo The preview.sh script is passed the name attribute of the application `foo`. So now we first find the key which corresponds to the attribute name `foo` and then use that when looking up the other attributes like path, etc. Closes: https://github.com/validatedpatterns/multicloud-gitops/issues/351 --- scripts/preview.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/preview.sh b/scripts/preview.sh index d5e695e4..ac2cd5c8 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -9,14 +9,23 @@ # - Make output can be included in the YAML. SITE=$1; shift -APP=$1; shift +APPNAME=$1; shift GIT_REPO=$1; shift GIT_BRANCH=$1; shift -if [ "${APP}" != "clustergroup" ]; then +if [ "${APPNAME}" != "clustergroup" ]; then + # This covers the following case: + # foobar: + # name: foo + # namespace: foo + # project: foo + # path: charts/all/foo + # So we retrieve the actual index ("foobar") given the name attribute of the application + APP=$(yq ".clusterGroup.applications | with_entries(select(.value.name == \"$APPNAME\")) | keys | .[0]" values-$SITE.yaml) chart=$(yq ".clusterGroup.applications.$APP.path" values-$SITE.yaml) namespace=$(yq ".clusterGroup.applications.$APP.namespace" values-$SITE.yaml) else + APP=$APPNAME chart="common/clustergroup" namespace="openshift-operators" fi From 4ec9d3e3f3808ef4a17e66c7827b8a6a6c2f5b08 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 8 Apr 2024 21:21:36 +0200 Subject: [PATCH 1125/1263] Force strings in extraParametersNested Otherwise if we pass a boolean in the extraParametersNested we will get: spec.source.helm.parameters[10].value: Invalid value: "boolean": spec.source.helm.parameters[10].value in body must be of type string: "boolean" --- clustergroup/templates/plumbing/applications.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 3706d839..dbfefa8f 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -105,7 +105,7 @@ spec: {{- end }} {{- range $k, $v := $.Values.extraParametersNested }} - name: {{ $k }} - value: {{ $v }} + value: {{ printf "%s" $v | quote }} {{- end }} {{- range .overrides }} - name: {{ .name }} @@ -184,7 +184,7 @@ spec: {{- end }} {{- range $k, $v := $.Values.extraParametersNested }} - name: {{ $k }} - value: {{ $v }} + value: {{ printf "%s" $v | quote }} {{- end }} {{- range .overrides }} - name: {{ .name }} @@ -251,7 +251,7 @@ spec: {{- end }} {{- range $k, $v := $.Values.extraParametersNested }} - name: {{ $k }} - value: {{ $v }} + value: {{ printf "%s" $v | quote }} {{- end }} {{- range .overrides }} - name: {{ .name }} From cb77e121b9fbb2c152d21552678253cb1bf1794b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 10 Apr 2024 13:50:28 +0200 Subject: [PATCH 1126/1263] In ACM policies do not use $ARGOCD_APP_SOURCE_* variables The reason for this is the following: When changing the repo on the hub (by editing the pattern), the expectation is that the repo change will replicate from the hub to the spokes managed by ACM. Today this is very unlikely to happen because changing the repo on the hub will not change the policy and so ACM will not reapply it on the spokes. (I believe there is like a daily repush that happens even when the policy has not changed, but that is way too slow to be relied upon). By using the actual variable the policy will actually change, ACM will notice this and push the change on the spokes. Found while testing disconnected mode. I am not replacing them everywhere because I am not sure yet if there are additional semantics in common/clustergroup that I am unaware of. --- acm/templates/policies/application-policies.yaml | 4 ++-- tests/acm-industrial-edge-hub.expected.yaml | 4 ++-- tests/acm-medical-diagnosis-hub.expected.yaml | 4 ++-- tests/acm-normal.expected.yaml | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 01082e54..68548893 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -52,9 +52,9 @@ spec: {{- end }} parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: {{ $.Values.global.repoURL }} - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: {{ $.Values.global.targetRevision }} - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 07593017..59c7e60f 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -225,9 +225,9 @@ spec: - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 959fbe30..3a65a6be 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -216,9 +216,9 @@ spec: - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 87071cc4..af97f532 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -619,9 +619,9 @@ spec: - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -715,9 +715,9 @@ spec: - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern From 8dbb5ff7809f39d6b80874f762c4bad22cd09730 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 20 Feb 2024 21:20:42 +0100 Subject: [PATCH 1127/1263] Add support for custom CAs This feature relies on the VP operator version >= 0.0.44 in order to work. The way to enable this is to add a feature flag called 'initcontainers' in the VP operator. Once this is enabled, we will detect this and take over the all ArgoCD instances' definition and add initContainers which will inject the CAs contained in the trusted-bundle-ca configmap and also the openshift internal CA. Testing protocol: 1. (Operator 0.0.44) MCG deployment with experimentalCapabilities set to '' and using a github main upstream (i.e. without this PR) 2. (Operator 0.0.44) MCG deployment with experimentalCapabilities set to 'initcontainers' and using a github diconnected common upstream (requiring a custom CA) (i.e. with this PR) 3. (Operator 0.0.44) MCG deployment with experimentalCapabilities set to '' and using a github diconnected common upstream. (same as 1.2) and then set the initcontainer capability on the hub. Checked that the .global.experimentalCapabilities property replicated from hub to spoke and the initcontainers have been generated correctly 3.1 (Operator 0.0.44) Change the repo from github to an internal one that does need the custom ca to work 4. (Operator 0.0.43) Test an old operator with a newer common that contains this very branch Note: Once we will make initcontainers a default feature of the operator we will remove the ifs added in this PR and just make it the defaut behaviour. --- .../policies/application-policies.yaml | 3 + acm/templates/policies/ocp-gitops-policy.yaml | 167 ++++++++++++++++++ clustergroup/templates/_helpers.tpl | 2 + .../templates/imperative/_helpers.tpl | 103 ++++++++++- .../templates/imperative/configmap.yaml | 9 + clustergroup/templates/imperative/job.yaml | 21 ++- .../templates/imperative/unsealjob.yaml | 22 ++- clustergroup/templates/plumbing/argocd.yaml | 43 +++++ .../plumbing/trusted-bundle-ca-configmap.yaml | 7 + 9 files changed, 366 insertions(+), 11 deletions(-) create mode 100644 clustergroup/templates/plumbing/trusted-bundle-ca-configmap.yaml diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 68548893..925915c8 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -1,4 +1,5 @@ # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io +{{- $hasInitContainerCapability := and (.Values.global.experimentalCapabilities) (has "initcontainers" (splitList "," .Values.global.experimentalCapabilities)) }} {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} {{- if not .hostedArgoSites }} @@ -75,6 +76,8 @@ spec: value: {{ $.Values.global.clusterPlatform }} - name: clusterGroup.name value: {{ $group.name }} + - name: global.experimentalCapabilities + value: {{ $.Values.global.experimentalCapabilities }} {{- range .helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index a0ed611f..f46b30c6 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -1,3 +1,4 @@ +{{- $hasInitContainerCapability := and (.Values.global.experimentalCapabilities) (has "initcontainers" (splitList "," .Values.global.experimentalCapabilities)) }} apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: @@ -24,6 +25,15 @@ spec: include: - default object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-ca-bundle + namespace: openshift-gitops + labels: + config.openshift.io/inject-trusted-cabundle: 'true' - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT @@ -44,6 +54,163 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" +{{- if $hasInitContainerCapability }} + - complianceType: mustonlyhave + objectDefinition: + apiVersion: argoproj.io/v1beta1 + kind: ArgoCD + metadata: + name: openshift-gitops + namespace: openshift-gitops + spec: + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + webhookServer: + ingress: + enabled: false + route: + enabled: false + controller: + processors: {} + resources: + limits: + cpu: "2" + memory: 2Gi + requests: + cpu: 250m + memory: 1Gi + sharding: {} + grafana: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + route: + enabled: false + ha: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + monitoring: + enabled: false + notifications: + enabled: false + prometheus: + enabled: false + ingress: + enabled: false + route: + enabled: false + rbac: + defaultPolicy: "" + policy: |- + g, system:cluster-admins, role:admin + g, cluster-admins, role:admin + scopes: '[groups]' + redis: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + repo: + initContainers: + - command: + - bash + - -c + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt + || true + image: registry.access.redhat.com/ubi9/ubi-minimal:latest + name: fetch-ca + resources: {} + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - mountPath: /etc/pki/tls/certs + name: ca-bundles + volumes: + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - emptyDir: {} + name: ca-bundles + resourceExclusions: |- + - apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + service: + type: "" + sso: + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + provider: dex + tls: + ca: {} +{{- end }}{{/* if hasInitContainerCapability */}} --- apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding diff --git a/clustergroup/templates/_helpers.tpl b/clustergroup/templates/_helpers.tpl index 83b06a04..cd0f5e8f 100644 --- a/clustergroup/templates/_helpers.tpl +++ b/clustergroup/templates/_helpers.tpl @@ -22,6 +22,8 @@ Default always defined top-level variables for helm charts value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} - name: global.privateRepo value: {{ $.Values.global.privateRepo | quote }} +- name: global.experimentalCapabilities + value: {{ $.Values.global.experimentalCapabilities | default "" }} {{- end }} {{/* clustergroup.globalvaluesparameters */}} diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl index f75e781e..64786acf 100644 --- a/clustergroup/templates/imperative/_helpers.tpl +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -4,6 +4,29 @@ # 3. If it is an http secret, generate the correct URL # 4. If it is an ssh secret, create the private ssh key and make sure the git clone works +{{/* fetch-ca InitContainer */}} +{{- define "imperative.initcontainers.fetch-ca" }} +- name: fetch-ca + image: {{ $.Values.clusterGroup.imperative.image }} + imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - >- + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + ls -l /tmp/ca-bundles/ + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles +{{- end }} + {{/* git-init InitContainer */}} {{- define "imperative.initcontainers.gitinit" }} - name: git-init @@ -12,6 +35,9 @@ env: - name: HOME value: /git/home + volumeMounts: + - name: git + mountPath: "/git" command: - 'sh' - '-c' @@ -37,11 +63,47 @@ mkdir /git/{repo,home}; git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; +{{- end }} + +{{/* git-init-ca InitContainer */}} +{{- define "imperative.initcontainers.gitinit-ca" }} +- name: git-init + image: {{ $.Values.clusterGroup.imperative.image }} + imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} + env: + - name: HOME + value: /git/home volumeMounts: - name: git mountPath: "/git" + - name: ca-bundles + mountPath: /etc/pki/tls/certs + command: + - 'sh' + - '-c' + - >- + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials &> /dev/null; then + URL="{{ $.Values.global.repoURL }}"; + else + if ! oc get secrets -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.sshPrivateKey | base64decode}}` }}' &>/dev/null; then + U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.username | base64decode }}` }}')"; + P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.password | base64decode }}` }}')"; + URL=$(echo {{ $.Values.global.repoURL }} | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); + echo "USER/PASS: ${URL}"; + else + S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.sshPrivateKey | base64decode }}` }}')"; + mkdir -p --mode 0700 "${HOME}/.ssh"; + echo "${S}" > "${HOME}/.ssh/id_rsa"; + chmod 0600 "${HOME}/.ssh/id_rsa"; + URL=$(echo {{ $.Values.global.repoURL }} | sed -E "s/(https?:\/\/)/\1git@/"); + git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; + echo "SSH: ${URL}"; + fi; + fi; + mkdir /git/{repo,home}; + git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- "${URL}" /git/repo; + chmod 0770 /git/{repo,home}; {{- end }} - {{/* Final done container */}} {{- define "imperative.containers.done" }} - name: "done" @@ -56,6 +118,19 @@ {{- end }} {{/* volume-mounts for all containers */}} +{{- define "imperative.volumemounts_ca" }} +- name: git + mountPath: "/git" +- name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml +- mountPath: /var/run/kube-root-ca + name: kube-root-ca +- mountPath: /var/run/trusted-ca + name: trusted-ca-bundle +- mountPath: /tmp/ca-bundles + name: ca-bundles +{{- end }} {{- define "imperative.volumemounts" }} - name: git mountPath: "/git" @@ -63,3 +138,29 @@ mountPath: /values/values.yaml subPath: values.yaml {{- end }} + +{{/* volumes for all containers */}} +{{- define "imperative.volumes" }} +- name: git + emptyDir: {} +- name: values-volume + configMap: + name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} +{{- end }} + +{{- define "imperative.volumes_ca" }} +- name: git + emptyDir: {} +- name: values-volume + configMap: + name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} +- configMap: + name: kube-root-ca.crt + name: kube-root-ca +- configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle +- name: ca-bundles + emptyDir: {} +{{- end }} diff --git a/clustergroup/templates/imperative/configmap.yaml b/clustergroup/templates/imperative/configmap.yaml index 8ca5a176..9f2d6155 100644 --- a/clustergroup/templates/imperative/configmap.yaml +++ b/clustergroup/templates/imperative/configmap.yaml @@ -9,4 +9,13 @@ metadata: data: values.yaml: | {{ tpl $valuesyaml . | indent 4 }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: trusted-ca-bundle + namespace: {{ $.Values.clusterGroup.imperative.namespace}} + annotations: + labels: + config.openshift.io/inject-trusted-cabundle: 'true' {{- end }} diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index cb092649..0b82d47c 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -1,3 +1,5 @@ +{{- $hasInitContainerCapability := and (.Values.global.experimentalCapabilities) (has "initcontainers" (splitList "," .Values.global.experimentalCapabilities)) }} + {{- if not (eq .Values.enabled "plumbing") }} {{/* Define this if needed (jobs defined */}} {{- if (and $.Values.clusterGroup.imperative (gt (len $.Values.clusterGroup.imperative.jobs) 0)) -}} @@ -22,7 +24,12 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there +{{- if $hasInitContainerCapability }} + {{- include "imperative.initcontainers.fetch-ca" . | indent 12 }} + {{- include "imperative.initcontainers.gitinit-ca" . | indent 12 }} +{{- else }} {{- include "imperative.initcontainers.gitinit" . | indent 12 }} +{{- end }} {{- range $.Values.clusterGroup.imperative.jobs }} {{- if ne (.disabled | default "false" | toString | lower ) "true" }} - name: {{ .name }} @@ -53,17 +60,21 @@ spec: {{- end }} - {{ .playbook }} volumeMounts: +{{- if $hasInitContainerCapability }} + {{- include "imperative.volumemounts_ca" . | indent 16 }} +{{- else }} {{- include "imperative.volumemounts" . | indent 16 }} +{{- end }} {{- end }} {{- end }} containers: {{- include "imperative.containers.done" . | indent 12 }} volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} +{{- if $hasInitContainerCapability }} + {{- include "imperative.volumes_ca" . | indent 12 }} +{{- else }} + {{- include "imperative.volumes" . | indent 12 }} +{{- end }} restartPolicy: Never {{- end }} {{- end }} diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index b251c5da..e0ff2c78 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -1,3 +1,5 @@ +{{- $hasInitContainerCapability := and (.Values.global.experimentalCapabilities) (has "initcontainers" (splitList "," .Values.global.experimentalCapabilities)) }} + {{/* If the backend is not set at all we default to "vault". See https://www.github.com/helm/helm/issues/3308 why we avoid using the default function */}} {{- if or (eq .Values.global.secretStore.backend "vault") (not (hasKey .Values.global.secretStore "backend")) }} @@ -24,7 +26,13 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there +{{- if $hasInitContainerCapability }} + + {{- include "imperative.initcontainers.fetch-ca" . | indent 12 }} + {{- include "imperative.initcontainers.gitinit-ca" . | indent 12 }} +{{- else }} {{- include "imperative.initcontainers.gitinit" . | indent 12 }} +{{- end }} - name: unseal-playbook image: {{ $.Values.clusterGroup.imperative.image }} imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} @@ -47,15 +55,19 @@ spec: - 'vault_init,vault_unseal,vault_secrets_init,vault_spokes_init' - "common/ansible/playbooks/vault/vault.yaml" volumeMounts: +{{- if $hasInitContainerCapability }} + {{- include "imperative.volumemounts_ca" . | indent 16 }} +{{- else }} {{- include "imperative.volumemounts" . | indent 16 }} +{{- end }} containers: {{- include "imperative.containers.done" . | indent 12 }} volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} +{{- if $hasInitContainerCapability }} + {{- include "imperative.volumes_ca" . | indent 12 }} +{{- else }} + {{- include "imperative.volumes" . | indent 12 }} +{{- end }} restartPolicy: Never {{- end }} {{- end }} diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 12e362aa..dcce1b4b 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -1,3 +1,5 @@ +{{- $hasInitContainerCapability := and (.Values.global.experimentalCapabilities) (has "initcontainers" (splitList "," .Values.global.experimentalCapabilities)) }} + {{- if (eq .Values.enabled "all") }} {{- $namespace := print $.Values.global.pattern "-" $.Values.clusterGroup.name }} apiVersion: argoproj.io/v1beta1 @@ -68,9 +70,50 @@ spec: rbac: defaultPolicy: role:admin repo: +{{- if $hasInitContainerCapability }} + initContainers: + - command: + - bash + - -c + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true + image: {{ $.Values.clusterGroup.imperative.image }} + name: fetch-ca + resources: {} + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - mountPath: /etc/pki/tls/certs + name: ca-bundles + volumes: + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - emptyDir: {} + name: ca-bundles +{{- if len $.Values.clusterGroup.argoCD.initContainers }} +{{ $.Values.clusterGroup.argoCD.initContainers | toPrettyJson }} +{{- end }} +{{- else }} {{- if len $.Values.clusterGroup.argoCD.initContainers }} initContainers: {{ $.Values.clusterGroup.argoCD.initContainers | toPrettyJson }} {{- end }} +{{- end }}{{/* if $hasInitContainerCapability */}} {{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} sidecarContainers: {{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} diff --git a/clustergroup/templates/plumbing/trusted-bundle-ca-configmap.yaml b/clustergroup/templates/plumbing/trusted-bundle-ca-configmap.yaml new file mode 100644 index 00000000..8b2a9cde --- /dev/null +++ b/clustergroup/templates/plumbing/trusted-bundle-ca-configmap.yaml @@ -0,0 +1,7 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: trusted-ca-bundle + namespace: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} + labels: + config.openshift.io/inject-trusted-cabundle: 'true' From 4e75d71f061dc2c9ba2826fc74549fcb83149a48 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 11 Apr 2024 18:12:05 +0200 Subject: [PATCH 1128/1263] Update tests --- .../acm-industrial-edge-factory.expected.yaml | 9 +++ tests/acm-industrial-edge-hub.expected.yaml | 11 +++ tests/acm-medical-diagnosis-hub.expected.yaml | 11 +++ tests/acm-naked.expected.yaml | 9 +++ tests/acm-normal.expected.yaml | 13 +++ ...roup-industrial-edge-factory.expected.yaml | 39 ++++++--- ...tergroup-industrial-edge-hub.expected.yaml | 69 +++++++++++----- ...rgroup-medical-diagnosis-hub.expected.yaml | 81 ++++++++++++++----- tests/clustergroup-naked.expected.yaml | 37 ++++++--- tests/clustergroup-normal.expected.yaml | 59 +++++++++----- 10 files changed, 266 insertions(+), 72 deletions(-) diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index 66c0c0b9..0291231a 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -89,6 +89,15 @@ spec: include: - default object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-ca-bundle + namespace: openshift-gitops + labels: + config.openshift.io/inject-trusted-cabundle: 'true' - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 59c7e60f..a912f0cd 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -248,6 +248,8 @@ spec: value: aws - name: clusterGroup.name value: factory + - name: global.experimentalCapabilities + value: - name: clusterGroup.isHubCluster value: "false" destination: @@ -296,6 +298,15 @@ spec: include: - default object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-ca-bundle + namespace: openshift-gitops + labels: + config.openshift.io/inject-trusted-cabundle: 'true' - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 3a65a6be..3ac5fe17 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -239,6 +239,8 @@ spec: value: aws - name: clusterGroup.name value: region-one + - name: global.experimentalCapabilities + value: - name: clusterGroup.isHubCluster value: "false" destination: @@ -287,6 +289,15 @@ spec: include: - default object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-ca-bundle + namespace: openshift-gitops + labels: + config.openshift.io/inject-trusted-cabundle: 'true' - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index 880ef747..4f0ac751 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -90,6 +90,15 @@ spec: include: - default object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-ca-bundle + namespace: openshift-gitops + labels: + config.openshift.io/inject-trusted-cabundle: 'true' - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index af97f532..1a3f6e72 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -642,6 +642,8 @@ spec: value: aws - name: clusterGroup.name value: acm-edge + - name: global.experimentalCapabilities + value: - name: clusterGroup.isHubCluster value: "false" destination: @@ -738,6 +740,8 @@ spec: value: aws - name: clusterGroup.name value: acm-provision-edge + - name: global.experimentalCapabilities + value: - name: clusterGroup.isHubCluster value: "false" destination: @@ -786,6 +790,15 @@ spec: include: - default object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-ca-bundle + namespace: openshift-gitops + labels: + config.openshift.io/inject-trusted-cabundle: 'true' - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 851119a8..39ec60c7 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -198,6 +198,16 @@ data: kind: ClusterSecretStore name: vault-backend --- +# Source: clustergroup/templates/imperative/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: trusted-ca-bundle + namespace: imperative + annotations: + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- # Source: clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml kind: ConfigMap apiVersion: v1 @@ -230,6 +240,15 @@ data: --set clusterGroup.name=factory --post-renderer ./kustomize"] --- +# Source: clustergroup/templates/plumbing/trusted-bundle-ca-configmap.yaml +kind: ConfigMap +apiVersion: v1 +metadata: + name: trusted-ca-bundle + namespace: mypattern-factory + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- # Source: clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -359,6 +378,9 @@ spec: env: - name: HOME value: /git/home + volumeMounts: + - name: git + mountPath: "/git" command: - 'sh' - '-c' @@ -384,9 +406,6 @@ spec: mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - volumeMounts: - - name: git - mountPath: "/git" - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -419,12 +438,12 @@ spec: - 'echo' - 'done' - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap-factory + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-factory restartPolicy: Never --- # Source: clustergroup/templates/core/subscriptions.yaml @@ -527,6 +546,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 611ecbb7..8355852c 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -359,6 +359,16 @@ data: kind: ClusterSecretStore name: vault-backend --- +# Source: clustergroup/templates/imperative/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: trusted-ca-bundle + namespace: imperative + annotations: + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- # Source: clustergroup/templates/plumbing/argocd-cmp-plugin-cms.yaml kind: ConfigMap apiVersion: v1 @@ -391,6 +401,15 @@ data: --set clusterGroup.name=datacenter --post-renderer ./kustomize"] --- +# Source: clustergroup/templates/plumbing/trusted-bundle-ca-configmap.yaml +kind: ConfigMap +apiVersion: v1 +metadata: + name: trusted-ca-bundle + namespace: mypattern-datacenter + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- # Source: clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -520,6 +539,9 @@ spec: env: - name: HOME value: /git/home + volumeMounts: + - name: git + mountPath: "/git" command: - 'sh' - '-c' @@ -545,9 +567,6 @@ spec: mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - volumeMounts: - - name: git - mountPath: "/git" - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -580,12 +599,12 @@ spec: - 'echo' - 'done' - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap-datacenter + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-datacenter restartPolicy: Never --- # Source: clustergroup/templates/imperative/unsealjob.yaml @@ -615,6 +634,9 @@ spec: env: - name: HOME value: /git/home + volumeMounts: + - name: git + mountPath: "/git" command: - 'sh' - '-c' @@ -640,9 +662,6 @@ spec: mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - volumeMounts: - - name: git - mountPath: "/git" - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -677,12 +696,12 @@ spec: - 'echo' - 'done' - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap-datacenter + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-datacenter restartPolicy: Never --- # Source: clustergroup/templates/core/subscriptions.yaml @@ -821,6 +840,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: ignoreDifferences: [ { "group": "internal.open-cluster-management.io", @@ -884,6 +905,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -938,6 +961,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -992,6 +1017,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: ignoreDifferences: [ { "group": "apps", @@ -1076,6 +1103,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -1130,6 +1159,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -1211,6 +1242,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: - name: global.openshift value: "true" - name: injector.enabled diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 9855500c..819e2ef1 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -318,6 +318,25 @@ data: kind: ClusterSecretStore name: vault-backend --- +# Source: clustergroup/templates/imperative/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: trusted-ca-bundle + namespace: imperative + annotations: + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- +# Source: clustergroup/templates/plumbing/trusted-bundle-ca-configmap.yaml +kind: ConfigMap +apiVersion: v1 +metadata: + name: trusted-ca-bundle + namespace: mypattern-hub + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- # Source: clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -447,6 +466,9 @@ spec: env: - name: HOME value: /git/home + volumeMounts: + - name: git + mountPath: "/git" command: - 'sh' - '-c' @@ -472,9 +494,6 @@ spec: mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - volumeMounts: - - name: git - mountPath: "/git" - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -507,12 +526,12 @@ spec: - 'echo' - 'done' - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap-hub + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-hub restartPolicy: Never --- # Source: clustergroup/templates/imperative/unsealjob.yaml @@ -542,6 +561,9 @@ spec: env: - name: HOME value: /git/home + volumeMounts: + - name: git + mountPath: "/git" command: - 'sh' - '-c' @@ -567,9 +589,6 @@ spec: mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - volumeMounts: - - name: git - mountPath: "/git" - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -604,12 +623,12 @@ spec: - 'echo' - 'done' - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap-hub + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-hub restartPolicy: Never --- # Source: clustergroup/templates/core/subscriptions.yaml @@ -706,6 +725,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -760,6 +781,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -814,6 +837,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -868,6 +893,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -922,6 +949,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -976,6 +1005,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -1030,6 +1061,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -1084,6 +1117,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: - name: global.openshift value: "true" - name: injector.enabled @@ -1156,6 +1191,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -1210,6 +1247,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: @@ -1264,6 +1303,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: ignoreDifferences: [ { "group": "apps.openshift.io", @@ -1327,6 +1368,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: ignoreDifferences: [ { "group": "apps.openshift.io", @@ -1390,6 +1433,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 194bfa8f..b8e329d6 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -83,6 +83,25 @@ data: kind: ClusterSecretStore name: vault-backend --- +# Source: clustergroup/templates/imperative/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: trusted-ca-bundle + namespace: imperative + annotations: + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- +# Source: clustergroup/templates/plumbing/trusted-bundle-ca-configmap.yaml +kind: ConfigMap +apiVersion: v1 +metadata: + name: trusted-ca-bundle + namespace: common-example + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- # Source: clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -212,6 +231,9 @@ spec: env: - name: HOME value: /git/home + volumeMounts: + - name: git + mountPath: "/git" command: - 'sh' - '-c' @@ -237,9 +259,6 @@ spec: mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - volumeMounts: - - name: git - mountPath: "/git" - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -274,12 +293,12 @@ spec: - 'echo' - 'done' - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap-example + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-example restartPolicy: Never --- # Source: clustergroup/templates/plumbing/argocd.yaml diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index f038446d..b3387af2 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -280,6 +280,25 @@ data: kind: ClusterSecretStore name: vault-backend --- +# Source: clustergroup/templates/imperative/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: trusted-ca-bundle + namespace: imperative + annotations: + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- +# Source: clustergroup/templates/plumbing/trusted-bundle-ca-configmap.yaml +kind: ConfigMap +apiVersion: v1 +metadata: + name: trusted-ca-bundle + namespace: mypattern-example + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- # Source: clustergroup/templates/imperative/clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -409,6 +428,9 @@ spec: env: - name: HOME value: /git/home + volumeMounts: + - name: git + mountPath: "/git" command: - 'sh' - '-c' @@ -434,9 +456,6 @@ spec: mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - volumeMounts: - - name: git - mountPath: "/git" - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -469,12 +488,12 @@ spec: - 'echo' - 'done' - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap-example + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-example restartPolicy: Never --- # Source: clustergroup/templates/imperative/unsealjob.yaml @@ -504,6 +523,9 @@ spec: env: - name: HOME value: /git/home + volumeMounts: + - name: git + mountPath: "/git" command: - 'sh' - '-c' @@ -529,9 +551,6 @@ spec: mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - volumeMounts: - - name: git - mountPath: "/git" - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -566,12 +585,12 @@ spec: - 'echo' - 'done' - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap-example + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-example restartPolicy: Never --- # Source: clustergroup/templates/core/subscriptions.yaml @@ -670,6 +689,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: ignoreDifferences: [ { "group": "internal.open-cluster-management.io", @@ -736,6 +757,8 @@ spec: value: apps.region.example.com - name: global.privateRepo value: + - name: global.experimentalCapabilities + value: syncPolicy: automated: {} retry: From 9b965559c08931f5625aea50772f8d3a97f12b94 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Thu, 11 Apr 2024 14:58:13 -0600 Subject: [PATCH 1129/1263] Support for issue #459 The changes here support the "Support for merging of namespaces, projects, subscriptions and application in overrides/values-common.yaml #459" issue that was opened by Northrop Grumman Files that were changed are: clustergroup/templates/_helpers.tpl clustergroup/templates/core/namespaces.yaml clustergroup/templates/core/operatorgroup.yaml clustergroup/templates/plumbing/projects.yaml clustergroup/values.schema.json examples/values-example.yaml The idea is that if you define the projects section, or the namespaces section, in two different values files using a map construct we will be able to merge both definition of projects into the final rendering of the manifests. The new structure for projects is as follows: ``` clusterGroup: ... projects: project1: ``` The new structure for namespaces is as follows: ``` clusterGroup: ... namespaces: namespace1: open-cluster-management: labels: openshift.io/node-selector: "" kubernetes.io/os: linux annotations: openshift.io/cluster-monitoring: "true" owner: "namespace owner" ``` The user would need to choose to use a list or a hashmap object. The user would not be able to use a mix of hashes and list to describe projects or namespaces. --- clustergroup/templates/_helpers.tpl | 152 ++++++++++++++++++ clustergroup/templates/core/namespaces.yaml | 10 ++ .../templates/core/operatorgroup.yaml | 10 ++ clustergroup/templates/plumbing/projects.yaml | 14 +- clustergroup/values.schema.json | 18 ++- examples/values-example.yaml | 18 +++ 6 files changed, 218 insertions(+), 4 deletions(-) diff --git a/clustergroup/templates/_helpers.tpl b/clustergroup/templates/_helpers.tpl index 83b06a04..7e764655 100644 --- a/clustergroup/templates/_helpers.tpl +++ b/clustergroup/templates/_helpers.tpl @@ -70,3 +70,155 @@ Default always defined valueFiles to be included in Applications but with a pref {{- end }} {{/* range $.Values.global.extraValueFiles */}} {{- end }} {{/* if $.Values.global.extraValueFiles */}} {{- end }} {{/* clustergroup.app.globalvalues.prefixedvaluefiles */}} + +{{/* +Helper function to generate AppProject from a map object +Called from common/clustergroup/templates/plumbing/projects.yaml +*/}} +{{- define "clustergroup.template.plumbing.projects.map" -}} +{{- $projects := index . 0 }} +{{- $namespace := index . 1 }} +{{- $enabled := index . 2 }} +{{- range $k, $v := $projects}} +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: {{ $k }} +{{- if (eq $enabled "plumbing") }} + namespace: openshift-gitops +{{- else }} + namespace: {{ $namespace }} +{{- end }} +spec: + description: "Pattern {{ . }}" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +--- +{{- end }} +{{- end }} + +{{/* + Helper function to generate AppProject from a list object. + Called from common/clustergroup/templates/plumbing/projects.yaml +*/}} +{{- define "clustergroup.template.plumbing.projects.list" -}} +{{- $projects := index . 0 }} +{{- $namespace := index . 1 }} +{{- $enabled := index . 2 }} +{{- range $projects}} +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: {{ . }} +{{- if (eq $enabled "plumbing") }} + namespace: openshift-gitops +{{- else }} + namespace: {{ $namespace }} +{{- end }} +spec: + description: "Pattern {{ . }}" + destinations: + - namespace: '*' + server: '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + namespaceResourceWhitelist: + - group: '*' + kind: '*' + sourceRepos: + - '*' +status: {} +{{- end }} +{{- end }} + +{{/* + Helper function to generate Namespaces from a map object. + Arguments passed as a list object are: + 0 - The namespace hash keys + 1 - Pattern name from .Values.global.pattern + 2 - Cluster group name from .Values.clusterGroup.name + Called from common/clustergroup/templates/core/namespaces.yaml +*/}} +{{- define "clustergroup.template.core.namespaces.map" -}} +{{- $ns := index . 0 }} +{{- $patternName := index . 1 }} +{{- $clusterGroupName := index . 2 }} + +{{- range $k, $v := $ns }}{{- /* We loop here even though the map has always just one key */}} +apiVersion: v1 +kind: Namespace +metadata: + name: {{ $k }} + {{- if ne $v nil }} + labels: + argocd.argoproj.io/managed-by: {{ $patternName }}-{{ .clusterGroupName }} + {{- if $v.labels }} + {{- range $key, $value := $v.labels }} {{- /* We loop here even though the map has always just one key */}} + {{ $key }}: {{ $value | default "" | quote }} + {{- end }} + {{- end }} + {{- if $v.annotations }} + annotations: + {{- range $key, $value := $v.annotations }} {{- /* We loop through the map to get key/value pairs */}} + {{ $key }}: {{ $value | default "" | quote }} + {{- end }} + {{- end }}{{- /* if $v.annotations */}} + {{- end }} +spec: +--- +{{- end }}{{- /* range $k, $v := $ns */}} +{{- end }} + +{{- /* + Helper function to generate OperatorGroup from a map object. + Arguments passed as a list object are: + 0 - The namespace hash keys + 1 - The operatorExcludes section from .Values.clusterGroup.operatorgroupExcludes + Called from common/clustergroup/templates/core/operatorgroup.yaml +*/ -}} +{{- define "clustergroup.template.core.operatorgroup.map" -}} +{{- $ns := index . 0 }} +{{- $operatorgroupExcludes := index . 1 }} +{{- if or (empty $operatorgroupExcludes) (not (has . $operatorgroupExcludes)) }} + {{- range $k, $v := $ns }}{{- /* We loop here even though the map has always just one key */}} + {{- if $v }} + {{- if $v.operatorGroup }}{{- /* Checks if the user sets operatorGroup: false */}} +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: {{ $k }}-operator-group + namespace: {{ $k }} +spec: + targetNamespaces: + {{- if (hasKey $v "targetNamespaces") }} + {{- range $v.targetNamespaces }}{{- /* We loop through the list of tergetnamespaces */}} + - {{ . }} + {{- end }}{{- /* End range targetNamespaces */}} + {{- else }} + - {{ $k }} + {{- end }}{{- /* End of if hasKey $v "targetNamespaces" */}} + {{- end }}{{- /* End if $v.operatorGroup */}} + {{- else }}{{- /* else if $v == nil */}} +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: {{ $k }}-operator-group + namespace: {{ $k }} +spec: + targetNamespaces: + - {{ $k }} + {{- end }}{{- /* end if $v */}} + {{- end }}{{- /* End range $k, $v = $ns */}} +{{- end }}{{- /* End of if operatorGroupExcludes */}} +{{- end }} {{- /* End define "clustergroup.template.core.operatorgroup.map" */}} diff --git a/clustergroup/templates/core/namespaces.yaml b/clustergroup/templates/core/namespaces.yaml index dfa6ae1a..c9a26afb 100644 --- a/clustergroup/templates/core/namespaces.yaml +++ b/clustergroup/templates/core/namespaces.yaml @@ -1,4 +1,13 @@ {{- if not (eq .Values.enabled "plumbing") }} +{{- /* + We first check if namespaces are defined as a map. If it is we call + our helper function in _helpers.tpl to process the namespaces + described in the values file. This is to support issue + https://github.com/validatedpatterns/common/issues/459 created by our customer. +*/ -}} +{{- if kindIs "map" .Values.clusterGroup.namespaces }} +{{- template "clustergroup.template.core.namespaces.map" (list .Values.clusterGroup.namespaces $.Values.global.pattern $.Values.clusterGroup.name) }} +{{- else }} {{- range $ns := .Values.clusterGroup.namespaces }} apiVersion: v1 kind: Namespace @@ -30,3 +39,4 @@ spec: --- {{- end }} {{- end }} +{{- end }} diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index cd679bd5..17aa966b 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -1,4 +1,13 @@ {{- if not (eq .Values.enabled "plumbing") }} +{{- /* + We first check if namespaces are defined as a map. If it is we call + our helper function in _helpers.tpl to process the projects + described in the values file. This is to support issue + https://github.com/validatedpatterns/common/issues/459 created by our customer. +*/ -}} +{{- if kindIs "map" .Values.clusterGroup.namespaces }} +{{- template "clustergroup.template.core.operatorgroup.map" (list .Values.clusterGroup.namespaces .Values.clusterGroup.operatorgroupExcludes) }} +{{- else }} {{- range $ns := .Values.clusterGroup.namespaces }} {{- if or (empty $.Values.clusterGroup.operatorgroupExcludes) (not (has . $.Values.clusterGroup.operatorgroupExcludes)) }} @@ -35,4 +44,5 @@ spec: --- {{- end }} {{- /* if or (empty $.Values.clusterGroup.operatorgroupExcludes) (not (has . $.Values.clusterGroup.operatorgroupExcludes)) */}} {{- end }} {{- /* range $ns := .Values.clusterGroup.namespaces */}} +{{- end }} {{- /* if kindIs "map" $ns */}} {{- end }} {{- /* if not (eq .Values.enabled "plumbing") */}} diff --git a/clustergroup/templates/plumbing/projects.yaml b/clustergroup/templates/plumbing/projects.yaml index 7f3b8c22..1050f2ee 100644 --- a/clustergroup/templates/plumbing/projects.yaml +++ b/clustergroup/templates/plumbing/projects.yaml @@ -1,5 +1,14 @@ {{- if not (eq .Values.enabled "core") }} {{- $namespace := print $.Values.global.pattern "-" $.Values.clusterGroup.name }} +{{- /* + We first check if projects are defined as a map. If it is we call + our helper function in _helpers.tpl to process the projects + described in the values file. This is to support issue + https://github.com/validatedpatterns/common/issues/459 created by our customer. +*/ -}} +{{- if kindIs "map" .Values.clusterGroup.projects }} +{{- template "clustergroup.template.plumbing.projects.map" (list .Values.clusterGroup.projects $namespace $.Values.enabled) }} +{{- else }} {{- range .Values.clusterGroup.projects }} apiVersion: argoproj.io/v1alpha1 kind: AppProject @@ -25,5 +34,6 @@ spec: - '*' status: {} --- -{{- end }} -{{- end }} +{{- end }} {{- /* end range */ -}} +{{- end }} {{- /* end if map */ -}} +{{- end }} {{- /* end if not "core" */ -}} diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 0ab0fc37..275a80a3 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -254,7 +254,14 @@ "description": "Templated value file paths." }, "namespaces": { - "type": "array", + "anyOf": [ + { + "type": "array" + }, + { + "type": "object" + } + ], "description": "This is the array of namespaces that the VP framework will create. In addition, operator groups will also be created for each namespace.", "items": { "$ref": "#/definitions/Namespaces" @@ -312,7 +319,14 @@ } }, "projects": { - "type": "array", + "anyOf": [ + { + "type": "array" + }, + { + "type": "object" + } + ], "description": "The list of projects that will be created in the ArgoCD instances.", "items": { "type": "string" diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 6c006b00..84682e20 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -15,6 +15,18 @@ clusterGroup: - /values/{{ .Values.global.clusterPlatform }}.yaml - /values/{{ .Values.global.clusterVersion }}.yaml + # + # You can define namespaces using hashes and not as a list like so: + # namespaces: + # open-cluster-management: + # labels: + # openshift.io/node-selector: "" + # kubernetes.io/os: linux + # annotations: + # openshift.io/cluster-monitoring: "true" + # owner: "namespace owner" + # application-ci: + # You cannot mix list and hashes to define namespaces namespaces: - open-cluster-management: labels: @@ -58,6 +70,12 @@ clusterGroup: name: openshift-pipelines-operator-rh csv: redhat-openshift-pipelines.v1.5.2 + # + # You can define projects using hashes like so: + # projects: + # hub: + # datacenter: + # You cannot mix list and hashes to define projects. projects: - datacenter From 7128a3f3e583504c3127b96482a8b2c567247ae1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 12 Apr 2024 12:01:48 +0200 Subject: [PATCH 1130/1263] Drop unused piece of schema json Not entirely sure how this slipped in. Reported-by: Martin Jackson --- clustergroup/values.schema.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 0ab0fc37..ac45b8a5 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -351,9 +351,6 @@ "items": { "$ref": "#/definitions/ManagedClusterGroup" } - }, - "externalClusters": { - "type": "array" } }, "required": [ From 36b2cb658ccfe848c6d3c2a07593366953eedccd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 14 Apr 2024 13:34:26 +0200 Subject: [PATCH 1131/1263] Small cleanup to remove unneeded log messages --- clustergroup/templates/imperative/_helpers.tpl | 4 ---- tests/clustergroup-industrial-edge-factory.expected.yaml | 2 -- tests/clustergroup-industrial-edge-hub.expected.yaml | 4 ---- tests/clustergroup-medical-diagnosis-hub.expected.yaml | 4 ---- tests/clustergroup-naked.expected.yaml | 2 -- tests/clustergroup-normal.expected.yaml | 4 ---- 6 files changed, 20 deletions(-) diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl index 64786acf..1800b5fd 100644 --- a/clustergroup/templates/imperative/_helpers.tpl +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -49,7 +49,6 @@ U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.username | base64decode }}` }}')"; P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.password | base64decode }}` }}')"; URL=$(echo {{ $.Values.global.repoURL }} | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); - echo "USER/PASS: ${URL}"; else S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.sshPrivateKey | base64decode }}` }}')"; mkdir -p --mode 0700 "${HOME}/.ssh"; @@ -57,7 +56,6 @@ chmod 0600 "${HOME}/.ssh/id_rsa"; URL=$(echo {{ $.Values.global.repoURL }} | sed -E "s/(https?:\/\/)/\1git@/"); git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; - echo "SSH: ${URL}"; fi; fi; mkdir /git/{repo,home}; @@ -89,7 +87,6 @@ U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.username | base64decode }}` }}')"; P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.password | base64decode }}` }}')"; URL=$(echo {{ $.Values.global.repoURL }} | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); - echo "USER/PASS: ${URL}"; else S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.sshPrivateKey | base64decode }}` }}')"; mkdir -p --mode 0700 "${HOME}/.ssh"; @@ -97,7 +94,6 @@ chmod 0600 "${HOME}/.ssh/id_rsa"; URL=$(echo {{ $.Values.global.repoURL }} | sed -E "s/(https?:\/\/)/\1git@/"); git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; - echo "SSH: ${URL}"; fi; fi; mkdir /git/{repo,home}; diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 39ec60c7..3acb8a92 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -392,7 +392,6 @@ spec: U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); - echo "USER/PASS: ${URL}"; else S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; mkdir -p --mode 0700 "${HOME}/.ssh"; @@ -400,7 +399,6 @@ spec: chmod 0600 "${HOME}/.ssh/id_rsa"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; - echo "SSH: ${URL}"; fi; fi; mkdir /git/{repo,home}; diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 8355852c..c022acf5 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -553,7 +553,6 @@ spec: U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); - echo "USER/PASS: ${URL}"; else S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; mkdir -p --mode 0700 "${HOME}/.ssh"; @@ -561,7 +560,6 @@ spec: chmod 0600 "${HOME}/.ssh/id_rsa"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; - echo "SSH: ${URL}"; fi; fi; mkdir /git/{repo,home}; @@ -648,7 +646,6 @@ spec: U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); - echo "USER/PASS: ${URL}"; else S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; mkdir -p --mode 0700 "${HOME}/.ssh"; @@ -656,7 +653,6 @@ spec: chmod 0600 "${HOME}/.ssh/id_rsa"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; - echo "SSH: ${URL}"; fi; fi; mkdir /git/{repo,home}; diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 819e2ef1..8d74bcb1 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -480,7 +480,6 @@ spec: U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); - echo "USER/PASS: ${URL}"; else S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; mkdir -p --mode 0700 "${HOME}/.ssh"; @@ -488,7 +487,6 @@ spec: chmod 0600 "${HOME}/.ssh/id_rsa"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; - echo "SSH: ${URL}"; fi; fi; mkdir /git/{repo,home}; @@ -575,7 +573,6 @@ spec: U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); - echo "USER/PASS: ${URL}"; else S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; mkdir -p --mode 0700 "${HOME}/.ssh"; @@ -583,7 +580,6 @@ spec: chmod 0600 "${HOME}/.ssh/id_rsa"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; - echo "SSH: ${URL}"; fi; fi; mkdir /git/{repo,home}; diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index b8e329d6..842bf58b 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -245,7 +245,6 @@ spec: U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; URL=$(echo | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); - echo "USER/PASS: ${URL}"; else S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; mkdir -p --mode 0700 "${HOME}/.ssh"; @@ -253,7 +252,6 @@ spec: chmod 0600 "${HOME}/.ssh/id_rsa"; URL=$(echo | sed -E "s/(https?:\/\/)/\1git@/"); git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; - echo "SSH: ${URL}"; fi; fi; mkdir /git/{repo,home}; diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index b3387af2..1e5719d4 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -442,7 +442,6 @@ spec: U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); - echo "USER/PASS: ${URL}"; else S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; mkdir -p --mode 0700 "${HOME}/.ssh"; @@ -450,7 +449,6 @@ spec: chmod 0600 "${HOME}/.ssh/id_rsa"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; - echo "SSH: ${URL}"; fi; fi; mkdir /git/{repo,home}; @@ -537,7 +535,6 @@ spec: U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.username | base64decode }}')"; P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.password | base64decode }}')"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); - echo "USER/PASS: ${URL}"; else S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{index .data.sshPrivateKey | base64decode }}')"; mkdir -p --mode 0700 "${HOME}/.ssh"; @@ -545,7 +542,6 @@ spec: chmod 0600 "${HOME}/.ssh/id_rsa"; URL=$(echo https://github.com/pattern-clone/mypattern | sed -E "s/(https?:\/\/)/\1git@/"); git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; - echo "SSH: ${URL}"; fi; fi; mkdir /git/{repo,home}; From 247ee651da63e3ecd2faaf56335a46270b2a77eb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 14 Apr 2024 13:46:50 +0200 Subject: [PATCH 1132/1263] Support for cluster-wide proxy If the clusterwide proxy object is configured, let's support it when we clone the git repos for the imperative framework. --- clustergroup/templates/imperative/_helpers.tpl | 12 ++++++++++++ ...lustergroup-industrial-edge-factory.expected.yaml | 6 ++++++ tests/clustergroup-industrial-edge-hub.expected.yaml | 12 ++++++++++++ .../clustergroup-medical-diagnosis-hub.expected.yaml | 12 ++++++++++++ tests/clustergroup-naked.expected.yaml | 6 ++++++ tests/clustergroup-normal.expected.yaml | 12 ++++++++++++ 6 files changed, 60 insertions(+) diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl index 1800b5fd..f28b5668 100644 --- a/clustergroup/templates/imperative/_helpers.tpl +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -58,6 +58,12 @@ git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; fi; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTP_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpsProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; @@ -96,6 +102,12 @@ git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; fi; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTP_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpsProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 3acb8a92..da521518 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -401,6 +401,12 @@ spec: git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; fi; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTP_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpsProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index c022acf5..f84bbebb 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -562,6 +562,12 @@ spec: git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; fi; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTP_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpsProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; @@ -655,6 +661,12 @@ spec: git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; fi; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTP_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpsProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 8d74bcb1..9effcbab 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -489,6 +489,12 @@ spec: git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; fi; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTP_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpsProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; @@ -582,6 +588,12 @@ spec: git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; fi; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTP_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpsProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 842bf58b..13e66fc8 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -254,6 +254,12 @@ spec: git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; fi; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTP_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpsProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 1e5719d4..8d3d4d51 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -451,6 +451,12 @@ spec: git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; fi; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTP_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpsProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; @@ -544,6 +550,12 @@ spec: git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; fi; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTP_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpsProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; + OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; + if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; From 86164c6a0da9860382ba54325565e4657e2903fb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 14 Apr 2024 14:09:15 +0200 Subject: [PATCH 1133/1263] Update ESO to 0.9.14 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.9.13.tgz | Bin 96126 -> 0 bytes .../charts/external-secrets-0.9.14.tgz | Bin 0 -> 99176 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 507 +++++++++++++++--- ...-secrets-industrial-edge-hub.expected.yaml | 507 +++++++++++++++--- ...ecrets-medical-diagnosis-hub.expected.yaml | 507 +++++++++++++++--- ...olang-external-secrets-naked.expected.yaml | 507 +++++++++++++++--- ...lang-external-secrets-normal.expected.yaml | 507 +++++++++++++++--- 9 files changed, 2239 insertions(+), 304 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.13.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.9.14.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index a2a2b061..6e2f5b00 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.3 dependencies: - name: external-secrets - version: "0.9.13" + version: "0.9.14" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.9.13.tgz b/golang-external-secrets/charts/external-secrets-0.9.13.tgz deleted file mode 100644 index 464befd970d337e51451d4871cd1a9aef6458da2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96126 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⁡Ve)YG||}Hk5Q1YGH8~ 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 J1K>vh_&-eMZIl22 literal 0 HcmV?d00001 diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index 42418202..bda2ce6c 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -37,10 +37,10 @@ clusterGroup: external-secrets: image: - tag: v0.9.13-ubi + tag: v0.9.14-ubi webhook: image: - tag: v0.9.13-ubi + tag: v0.9.14-ubi certController: image: - tag: v0.9.13-ubi + tag: v0.9.14-ubi diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index 788f0ce4..0e5156a7 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -4,12 +4,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: external-secrets-cert-controller - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -17,12 +17,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: golang-external-secrets - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -30,12 +30,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -43,12 +43,12 @@ apiVersion: v1 kind: Secret metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -1762,6 +1762,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object vault: description: Vault configures this store to sync secrets using Hashi provider properties: @@ -3026,6 +3066,11 @@ spec: properties: account: type: string + hostId: + description: |- + Optional HostID for JWT authentication. This may be used depending + on how the Conjur JWT authenticator policy is configured. + type: string secretRef: description: |- Optional SecretRef that refers to a key in a Secret resource containing JWT token to @@ -3269,6 +3314,34 @@ spec: required: - data type: object + fortanix: + description: Fortanix configures this store to sync secrets using the Fortanix provider + properties: + apiKey: + description: APIKey is the API token to access SDKMS Applications. + properties: + secretRef: + description: SecretRef is a reference to a secret containing the SDKMS API Key. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + apiUrl: + description: APIURL is the URL of SDKMS API. Defaults to `sdkms.fortanix.com`. + type: string + type: object gcpsm: description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider properties: @@ -3600,6 +3673,70 @@ spec: required: - auth type: object + onboardbase: + description: Onboardbase configures this store to sync secrets using the Onboardbase provider + properties: + apiHost: + default: https://public.onboardbase.com/api/v1/ + description: APIHost use this to configure the host url for the API for selfhosted installation, default is https://public.onboardbase.com/api/v1/ + type: string + auth: + description: Auth configures how the Operator authenticates with the Onboardbase API + properties: + apiKeyRef: + description: |- + OnboardbaseAPIKey is the APIKey generated by an admin account. + It is used to recognize and authorize access to a project and environment within onboardbase + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + passcodeRef: + description: OnboardbasePasscode is the passcode attached to the API Key + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + required: + - apiKeyRef + - passcodeRef + type: object + environment: + default: development + description: Environment is the name of an environmnent within a project to pull the secrets from + type: string + project: + default: development + description: Project is an onboardbase project that the secrets should be pulled from + type: string + required: + - apiHost + - auth + - environment + - project + type: object onepassword: description: OnePassword configures this store to sync secrets using the 1Password Cloud provider properties: @@ -3760,6 +3897,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object pulumi: description: Pulumi configures this store to sync secrets using the Pulumi provider properties: @@ -4332,6 +4509,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -6304,6 +6489,13 @@ spec: type: type: string type: object + updatePolicy: + default: Replace + description: 'UpdatePolicy to handle Secrets in the provider. Possible Values: "Replace/IfNotExists". Defaults to "Replace".' + enum: + - Replace + - IfNotExists + type: string required: - secretStoreRefs - selector @@ -6373,7 +6565,9 @@ spec: - match type: object type: object - description: Synced Push Secrets for later deletion. Matches Secret Stores to PushSecretData that was stored to that secretStore. + description: |- + Synced PushSecrets, including secrets that already exist in provider. + Matches secret stores to PushSecretData that was stored to that secret store. type: object syncedResourceVersion: description: SyncedResourceVersion keeps track of the last synced version. @@ -7296,6 +7490,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object vault: description: Vault configures this store to sync secrets using Hashi provider properties: @@ -8560,6 +8794,11 @@ spec: properties: account: type: string + hostId: + description: |- + Optional HostID for JWT authentication. This may be used depending + on how the Conjur JWT authenticator policy is configured. + type: string secretRef: description: |- Optional SecretRef that refers to a key in a Secret resource containing JWT token to @@ -8803,6 +9042,34 @@ spec: required: - data type: object + fortanix: + description: Fortanix configures this store to sync secrets using the Fortanix provider + properties: + apiKey: + description: APIKey is the API token to access SDKMS Applications. + properties: + secretRef: + description: SecretRef is a reference to a secret containing the SDKMS API Key. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + apiUrl: + description: APIURL is the URL of SDKMS API. Defaults to `sdkms.fortanix.com`. + type: string + type: object gcpsm: description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider properties: @@ -9134,6 +9401,70 @@ spec: required: - auth type: object + onboardbase: + description: Onboardbase configures this store to sync secrets using the Onboardbase provider + properties: + apiHost: + default: https://public.onboardbase.com/api/v1/ + description: APIHost use this to configure the host url for the API for selfhosted installation, default is https://public.onboardbase.com/api/v1/ + type: string + auth: + description: Auth configures how the Operator authenticates with the Onboardbase API + properties: + apiKeyRef: + description: |- + OnboardbaseAPIKey is the APIKey generated by an admin account. + It is used to recognize and authorize access to a project and environment within onboardbase + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + passcodeRef: + description: OnboardbasePasscode is the passcode attached to the API Key + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + required: + - apiKeyRef + - passcodeRef + type: object + environment: + default: development + description: Environment is the name of an environmnent within a project to pull the secrets from + type: string + project: + default: development + description: Project is an onboardbase project that the secrets should be pulled from + type: string + required: + - apiHost + - auth + - environment + - project + type: object onepassword: description: OnePassword configures this store to sync secrets using the 1Password Cloud provider properties: @@ -9294,6 +9625,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object pulumi: description: Pulumi configures this store to sync secrets using the Pulumi provider properties: @@ -9866,6 +10237,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -10774,6 +11153,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -11139,10 +11526,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11206,10 +11593,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11315,10 +11702,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -11355,10 +11742,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -11399,10 +11786,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11420,10 +11807,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11431,7 +11818,7 @@ roleRef: name: golang-external-secrets-cert-controller subjects: - name: external-secrets-cert-controller - namespace: "default" + namespace: default kind: ServiceAccount --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml @@ -11440,10 +11827,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11451,7 +11838,7 @@ roleRef: name: golang-external-secrets-controller subjects: - name: golang-external-secrets - namespace: "default" + namespace: default kind: ServiceAccount --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml @@ -11474,12 +11861,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: golang-external-secrets-leaderelection - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11513,12 +11900,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: golang-external-secrets-leaderelection - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11527,19 +11914,19 @@ roleRef: subjects: - kind: ServiceAccount name: golang-external-secrets - namespace: "default" + namespace: default --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -11558,12 +11945,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets-cert-controller - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11575,10 +11962,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -11593,7 +11980,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -11621,12 +12008,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11638,10 +12025,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -11656,7 +12043,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -11671,12 +12058,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11688,10 +12075,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -11706,7 +12093,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - webhook @@ -11786,7 +12173,7 @@ webhooks: scope: "Namespaced" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-secretstore admissionReviewVersions: ["v1", "v1beta1"] @@ -11802,7 +12189,7 @@ webhooks: scope: "Cluster" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-clustersecretstore admissionReviewVersions: ["v1", "v1beta1"] @@ -11826,7 +12213,7 @@ webhooks: scope: "Namespaced" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-externalsecret admissionReviewVersions: ["v1", "v1beta1"] diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index 735d99f4..847a025e 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -4,12 +4,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: external-secrets-cert-controller - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -17,12 +17,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: golang-external-secrets - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -30,12 +30,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -43,12 +43,12 @@ apiVersion: v1 kind: Secret metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -1762,6 +1762,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object vault: description: Vault configures this store to sync secrets using Hashi provider properties: @@ -3026,6 +3066,11 @@ spec: properties: account: type: string + hostId: + description: |- + Optional HostID for JWT authentication. This may be used depending + on how the Conjur JWT authenticator policy is configured. + type: string secretRef: description: |- Optional SecretRef that refers to a key in a Secret resource containing JWT token to @@ -3269,6 +3314,34 @@ spec: required: - data type: object + fortanix: + description: Fortanix configures this store to sync secrets using the Fortanix provider + properties: + apiKey: + description: APIKey is the API token to access SDKMS Applications. + properties: + secretRef: + description: SecretRef is a reference to a secret containing the SDKMS API Key. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + apiUrl: + description: APIURL is the URL of SDKMS API. Defaults to `sdkms.fortanix.com`. + type: string + type: object gcpsm: description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider properties: @@ -3600,6 +3673,70 @@ spec: required: - auth type: object + onboardbase: + description: Onboardbase configures this store to sync secrets using the Onboardbase provider + properties: + apiHost: + default: https://public.onboardbase.com/api/v1/ + description: APIHost use this to configure the host url for the API for selfhosted installation, default is https://public.onboardbase.com/api/v1/ + type: string + auth: + description: Auth configures how the Operator authenticates with the Onboardbase API + properties: + apiKeyRef: + description: |- + OnboardbaseAPIKey is the APIKey generated by an admin account. + It is used to recognize and authorize access to a project and environment within onboardbase + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + passcodeRef: + description: OnboardbasePasscode is the passcode attached to the API Key + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + required: + - apiKeyRef + - passcodeRef + type: object + environment: + default: development + description: Environment is the name of an environmnent within a project to pull the secrets from + type: string + project: + default: development + description: Project is an onboardbase project that the secrets should be pulled from + type: string + required: + - apiHost + - auth + - environment + - project + type: object onepassword: description: OnePassword configures this store to sync secrets using the 1Password Cloud provider properties: @@ -3760,6 +3897,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object pulumi: description: Pulumi configures this store to sync secrets using the Pulumi provider properties: @@ -4332,6 +4509,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -6304,6 +6489,13 @@ spec: type: type: string type: object + updatePolicy: + default: Replace + description: 'UpdatePolicy to handle Secrets in the provider. Possible Values: "Replace/IfNotExists". Defaults to "Replace".' + enum: + - Replace + - IfNotExists + type: string required: - secretStoreRefs - selector @@ -6373,7 +6565,9 @@ spec: - match type: object type: object - description: Synced Push Secrets for later deletion. Matches Secret Stores to PushSecretData that was stored to that secretStore. + description: |- + Synced PushSecrets, including secrets that already exist in provider. + Matches secret stores to PushSecretData that was stored to that secret store. type: object syncedResourceVersion: description: SyncedResourceVersion keeps track of the last synced version. @@ -7296,6 +7490,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object vault: description: Vault configures this store to sync secrets using Hashi provider properties: @@ -8560,6 +8794,11 @@ spec: properties: account: type: string + hostId: + description: |- + Optional HostID for JWT authentication. This may be used depending + on how the Conjur JWT authenticator policy is configured. + type: string secretRef: description: |- Optional SecretRef that refers to a key in a Secret resource containing JWT token to @@ -8803,6 +9042,34 @@ spec: required: - data type: object + fortanix: + description: Fortanix configures this store to sync secrets using the Fortanix provider + properties: + apiKey: + description: APIKey is the API token to access SDKMS Applications. + properties: + secretRef: + description: SecretRef is a reference to a secret containing the SDKMS API Key. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + apiUrl: + description: APIURL is the URL of SDKMS API. Defaults to `sdkms.fortanix.com`. + type: string + type: object gcpsm: description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider properties: @@ -9134,6 +9401,70 @@ spec: required: - auth type: object + onboardbase: + description: Onboardbase configures this store to sync secrets using the Onboardbase provider + properties: + apiHost: + default: https://public.onboardbase.com/api/v1/ + description: APIHost use this to configure the host url for the API for selfhosted installation, default is https://public.onboardbase.com/api/v1/ + type: string + auth: + description: Auth configures how the Operator authenticates with the Onboardbase API + properties: + apiKeyRef: + description: |- + OnboardbaseAPIKey is the APIKey generated by an admin account. + It is used to recognize and authorize access to a project and environment within onboardbase + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + passcodeRef: + description: OnboardbasePasscode is the passcode attached to the API Key + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + required: + - apiKeyRef + - passcodeRef + type: object + environment: + default: development + description: Environment is the name of an environmnent within a project to pull the secrets from + type: string + project: + default: development + description: Project is an onboardbase project that the secrets should be pulled from + type: string + required: + - apiHost + - auth + - environment + - project + type: object onepassword: description: OnePassword configures this store to sync secrets using the 1Password Cloud provider properties: @@ -9294,6 +9625,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object pulumi: description: Pulumi configures this store to sync secrets using the Pulumi provider properties: @@ -9866,6 +10237,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -10774,6 +11153,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -11139,10 +11526,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11206,10 +11593,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11315,10 +11702,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -11355,10 +11742,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -11399,10 +11786,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11420,10 +11807,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11431,7 +11818,7 @@ roleRef: name: golang-external-secrets-cert-controller subjects: - name: external-secrets-cert-controller - namespace: "default" + namespace: default kind: ServiceAccount --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml @@ -11440,10 +11827,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11451,7 +11838,7 @@ roleRef: name: golang-external-secrets-controller subjects: - name: golang-external-secrets - namespace: "default" + namespace: default kind: ServiceAccount --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml @@ -11474,12 +11861,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: golang-external-secrets-leaderelection - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11513,12 +11900,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: golang-external-secrets-leaderelection - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11527,19 +11914,19 @@ roleRef: subjects: - kind: ServiceAccount name: golang-external-secrets - namespace: "default" + namespace: default --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -11558,12 +11945,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets-cert-controller - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11575,10 +11962,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -11593,7 +11980,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -11621,12 +12008,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11638,10 +12025,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -11656,7 +12043,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -11671,12 +12058,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11688,10 +12075,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -11706,7 +12093,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - webhook @@ -11786,7 +12173,7 @@ webhooks: scope: "Namespaced" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-secretstore admissionReviewVersions: ["v1", "v1beta1"] @@ -11802,7 +12189,7 @@ webhooks: scope: "Cluster" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-clustersecretstore admissionReviewVersions: ["v1", "v1beta1"] @@ -11826,7 +12213,7 @@ webhooks: scope: "Namespaced" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-externalsecret admissionReviewVersions: ["v1", "v1beta1"] diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index 735d99f4..847a025e 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -4,12 +4,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: external-secrets-cert-controller - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -17,12 +17,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: golang-external-secrets - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -30,12 +30,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -43,12 +43,12 @@ apiVersion: v1 kind: Secret metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -1762,6 +1762,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object vault: description: Vault configures this store to sync secrets using Hashi provider properties: @@ -3026,6 +3066,11 @@ spec: properties: account: type: string + hostId: + description: |- + Optional HostID for JWT authentication. This may be used depending + on how the Conjur JWT authenticator policy is configured. + type: string secretRef: description: |- Optional SecretRef that refers to a key in a Secret resource containing JWT token to @@ -3269,6 +3314,34 @@ spec: required: - data type: object + fortanix: + description: Fortanix configures this store to sync secrets using the Fortanix provider + properties: + apiKey: + description: APIKey is the API token to access SDKMS Applications. + properties: + secretRef: + description: SecretRef is a reference to a secret containing the SDKMS API Key. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + apiUrl: + description: APIURL is the URL of SDKMS API. Defaults to `sdkms.fortanix.com`. + type: string + type: object gcpsm: description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider properties: @@ -3600,6 +3673,70 @@ spec: required: - auth type: object + onboardbase: + description: Onboardbase configures this store to sync secrets using the Onboardbase provider + properties: + apiHost: + default: https://public.onboardbase.com/api/v1/ + description: APIHost use this to configure the host url for the API for selfhosted installation, default is https://public.onboardbase.com/api/v1/ + type: string + auth: + description: Auth configures how the Operator authenticates with the Onboardbase API + properties: + apiKeyRef: + description: |- + OnboardbaseAPIKey is the APIKey generated by an admin account. + It is used to recognize and authorize access to a project and environment within onboardbase + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + passcodeRef: + description: OnboardbasePasscode is the passcode attached to the API Key + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + required: + - apiKeyRef + - passcodeRef + type: object + environment: + default: development + description: Environment is the name of an environmnent within a project to pull the secrets from + type: string + project: + default: development + description: Project is an onboardbase project that the secrets should be pulled from + type: string + required: + - apiHost + - auth + - environment + - project + type: object onepassword: description: OnePassword configures this store to sync secrets using the 1Password Cloud provider properties: @@ -3760,6 +3897,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object pulumi: description: Pulumi configures this store to sync secrets using the Pulumi provider properties: @@ -4332,6 +4509,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -6304,6 +6489,13 @@ spec: type: type: string type: object + updatePolicy: + default: Replace + description: 'UpdatePolicy to handle Secrets in the provider. Possible Values: "Replace/IfNotExists". Defaults to "Replace".' + enum: + - Replace + - IfNotExists + type: string required: - secretStoreRefs - selector @@ -6373,7 +6565,9 @@ spec: - match type: object type: object - description: Synced Push Secrets for later deletion. Matches Secret Stores to PushSecretData that was stored to that secretStore. + description: |- + Synced PushSecrets, including secrets that already exist in provider. + Matches secret stores to PushSecretData that was stored to that secret store. type: object syncedResourceVersion: description: SyncedResourceVersion keeps track of the last synced version. @@ -7296,6 +7490,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object vault: description: Vault configures this store to sync secrets using Hashi provider properties: @@ -8560,6 +8794,11 @@ spec: properties: account: type: string + hostId: + description: |- + Optional HostID for JWT authentication. This may be used depending + on how the Conjur JWT authenticator policy is configured. + type: string secretRef: description: |- Optional SecretRef that refers to a key in a Secret resource containing JWT token to @@ -8803,6 +9042,34 @@ spec: required: - data type: object + fortanix: + description: Fortanix configures this store to sync secrets using the Fortanix provider + properties: + apiKey: + description: APIKey is the API token to access SDKMS Applications. + properties: + secretRef: + description: SecretRef is a reference to a secret containing the SDKMS API Key. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + apiUrl: + description: APIURL is the URL of SDKMS API. Defaults to `sdkms.fortanix.com`. + type: string + type: object gcpsm: description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider properties: @@ -9134,6 +9401,70 @@ spec: required: - auth type: object + onboardbase: + description: Onboardbase configures this store to sync secrets using the Onboardbase provider + properties: + apiHost: + default: https://public.onboardbase.com/api/v1/ + description: APIHost use this to configure the host url for the API for selfhosted installation, default is https://public.onboardbase.com/api/v1/ + type: string + auth: + description: Auth configures how the Operator authenticates with the Onboardbase API + properties: + apiKeyRef: + description: |- + OnboardbaseAPIKey is the APIKey generated by an admin account. + It is used to recognize and authorize access to a project and environment within onboardbase + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + passcodeRef: + description: OnboardbasePasscode is the passcode attached to the API Key + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + required: + - apiKeyRef + - passcodeRef + type: object + environment: + default: development + description: Environment is the name of an environmnent within a project to pull the secrets from + type: string + project: + default: development + description: Project is an onboardbase project that the secrets should be pulled from + type: string + required: + - apiHost + - auth + - environment + - project + type: object onepassword: description: OnePassword configures this store to sync secrets using the 1Password Cloud provider properties: @@ -9294,6 +9625,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object pulumi: description: Pulumi configures this store to sync secrets using the Pulumi provider properties: @@ -9866,6 +10237,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -10774,6 +11153,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -11139,10 +11526,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11206,10 +11593,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11315,10 +11702,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -11355,10 +11742,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -11399,10 +11786,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11420,10 +11807,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11431,7 +11818,7 @@ roleRef: name: golang-external-secrets-cert-controller subjects: - name: external-secrets-cert-controller - namespace: "default" + namespace: default kind: ServiceAccount --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml @@ -11440,10 +11827,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11451,7 +11838,7 @@ roleRef: name: golang-external-secrets-controller subjects: - name: golang-external-secrets - namespace: "default" + namespace: default kind: ServiceAccount --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml @@ -11474,12 +11861,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: golang-external-secrets-leaderelection - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11513,12 +11900,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: golang-external-secrets-leaderelection - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11527,19 +11914,19 @@ roleRef: subjects: - kind: ServiceAccount name: golang-external-secrets - namespace: "default" + namespace: default --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -11558,12 +11945,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets-cert-controller - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11575,10 +11962,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -11593,7 +11980,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -11621,12 +12008,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11638,10 +12025,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -11656,7 +12043,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -11671,12 +12058,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11688,10 +12075,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -11706,7 +12093,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - webhook @@ -11786,7 +12173,7 @@ webhooks: scope: "Namespaced" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-secretstore admissionReviewVersions: ["v1", "v1beta1"] @@ -11802,7 +12189,7 @@ webhooks: scope: "Cluster" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-clustersecretstore admissionReviewVersions: ["v1", "v1beta1"] @@ -11826,7 +12213,7 @@ webhooks: scope: "Namespaced" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-externalsecret admissionReviewVersions: ["v1", "v1beta1"] diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index e09b409d..081ba464 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -4,12 +4,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: external-secrets-cert-controller - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -17,12 +17,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: golang-external-secrets - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -30,12 +30,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -43,12 +43,12 @@ apiVersion: v1 kind: Secret metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -1762,6 +1762,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object vault: description: Vault configures this store to sync secrets using Hashi provider properties: @@ -3026,6 +3066,11 @@ spec: properties: account: type: string + hostId: + description: |- + Optional HostID for JWT authentication. This may be used depending + on how the Conjur JWT authenticator policy is configured. + type: string secretRef: description: |- Optional SecretRef that refers to a key in a Secret resource containing JWT token to @@ -3269,6 +3314,34 @@ spec: required: - data type: object + fortanix: + description: Fortanix configures this store to sync secrets using the Fortanix provider + properties: + apiKey: + description: APIKey is the API token to access SDKMS Applications. + properties: + secretRef: + description: SecretRef is a reference to a secret containing the SDKMS API Key. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + apiUrl: + description: APIURL is the URL of SDKMS API. Defaults to `sdkms.fortanix.com`. + type: string + type: object gcpsm: description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider properties: @@ -3600,6 +3673,70 @@ spec: required: - auth type: object + onboardbase: + description: Onboardbase configures this store to sync secrets using the Onboardbase provider + properties: + apiHost: + default: https://public.onboardbase.com/api/v1/ + description: APIHost use this to configure the host url for the API for selfhosted installation, default is https://public.onboardbase.com/api/v1/ + type: string + auth: + description: Auth configures how the Operator authenticates with the Onboardbase API + properties: + apiKeyRef: + description: |- + OnboardbaseAPIKey is the APIKey generated by an admin account. + It is used to recognize and authorize access to a project and environment within onboardbase + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + passcodeRef: + description: OnboardbasePasscode is the passcode attached to the API Key + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + required: + - apiKeyRef + - passcodeRef + type: object + environment: + default: development + description: Environment is the name of an environmnent within a project to pull the secrets from + type: string + project: + default: development + description: Project is an onboardbase project that the secrets should be pulled from + type: string + required: + - apiHost + - auth + - environment + - project + type: object onepassword: description: OnePassword configures this store to sync secrets using the 1Password Cloud provider properties: @@ -3760,6 +3897,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object pulumi: description: Pulumi configures this store to sync secrets using the Pulumi provider properties: @@ -4332,6 +4509,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -6304,6 +6489,13 @@ spec: type: type: string type: object + updatePolicy: + default: Replace + description: 'UpdatePolicy to handle Secrets in the provider. Possible Values: "Replace/IfNotExists". Defaults to "Replace".' + enum: + - Replace + - IfNotExists + type: string required: - secretStoreRefs - selector @@ -6373,7 +6565,9 @@ spec: - match type: object type: object - description: Synced Push Secrets for later deletion. Matches Secret Stores to PushSecretData that was stored to that secretStore. + description: |- + Synced PushSecrets, including secrets that already exist in provider. + Matches secret stores to PushSecretData that was stored to that secret store. type: object syncedResourceVersion: description: SyncedResourceVersion keeps track of the last synced version. @@ -7296,6 +7490,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object vault: description: Vault configures this store to sync secrets using Hashi provider properties: @@ -8560,6 +8794,11 @@ spec: properties: account: type: string + hostId: + description: |- + Optional HostID for JWT authentication. This may be used depending + on how the Conjur JWT authenticator policy is configured. + type: string secretRef: description: |- Optional SecretRef that refers to a key in a Secret resource containing JWT token to @@ -8803,6 +9042,34 @@ spec: required: - data type: object + fortanix: + description: Fortanix configures this store to sync secrets using the Fortanix provider + properties: + apiKey: + description: APIKey is the API token to access SDKMS Applications. + properties: + secretRef: + description: SecretRef is a reference to a secret containing the SDKMS API Key. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + apiUrl: + description: APIURL is the URL of SDKMS API. Defaults to `sdkms.fortanix.com`. + type: string + type: object gcpsm: description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider properties: @@ -9134,6 +9401,70 @@ spec: required: - auth type: object + onboardbase: + description: Onboardbase configures this store to sync secrets using the Onboardbase provider + properties: + apiHost: + default: https://public.onboardbase.com/api/v1/ + description: APIHost use this to configure the host url for the API for selfhosted installation, default is https://public.onboardbase.com/api/v1/ + type: string + auth: + description: Auth configures how the Operator authenticates with the Onboardbase API + properties: + apiKeyRef: + description: |- + OnboardbaseAPIKey is the APIKey generated by an admin account. + It is used to recognize and authorize access to a project and environment within onboardbase + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + passcodeRef: + description: OnboardbasePasscode is the passcode attached to the API Key + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + required: + - apiKeyRef + - passcodeRef + type: object + environment: + default: development + description: Environment is the name of an environmnent within a project to pull the secrets from + type: string + project: + default: development + description: Project is an onboardbase project that the secrets should be pulled from + type: string + required: + - apiHost + - auth + - environment + - project + type: object onepassword: description: OnePassword configures this store to sync secrets using the 1Password Cloud provider properties: @@ -9294,6 +9625,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object pulumi: description: Pulumi configures this store to sync secrets using the Pulumi provider properties: @@ -9866,6 +10237,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -10774,6 +11153,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -11139,10 +11526,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11206,10 +11593,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11315,10 +11702,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -11355,10 +11742,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -11399,10 +11786,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11420,10 +11807,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11431,7 +11818,7 @@ roleRef: name: golang-external-secrets-cert-controller subjects: - name: external-secrets-cert-controller - namespace: "default" + namespace: default kind: ServiceAccount --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml @@ -11440,10 +11827,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11451,7 +11838,7 @@ roleRef: name: golang-external-secrets-controller subjects: - name: golang-external-secrets - namespace: "default" + namespace: default kind: ServiceAccount --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml @@ -11474,12 +11861,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: golang-external-secrets-leaderelection - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11513,12 +11900,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: golang-external-secrets-leaderelection - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11527,19 +11914,19 @@ roleRef: subjects: - kind: ServiceAccount name: golang-external-secrets - namespace: "default" + namespace: default --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -11558,12 +11945,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets-cert-controller - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11575,10 +11962,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -11593,7 +11980,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -11621,12 +12008,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11638,10 +12025,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -11656,7 +12043,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -11671,12 +12058,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11688,10 +12075,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -11706,7 +12093,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - webhook @@ -11786,7 +12173,7 @@ webhooks: scope: "Namespaced" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-secretstore admissionReviewVersions: ["v1", "v1beta1"] @@ -11802,7 +12189,7 @@ webhooks: scope: "Cluster" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-clustersecretstore admissionReviewVersions: ["v1", "v1beta1"] @@ -11826,7 +12213,7 @@ webhooks: scope: "Namespaced" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-externalsecret admissionReviewVersions: ["v1", "v1beta1"] diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 735d99f4..847a025e 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -4,12 +4,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: external-secrets-cert-controller - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -17,12 +17,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: golang-external-secrets - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -30,12 +30,12 @@ apiVersion: v1 kind: ServiceAccount metadata: name: external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -43,12 +43,12 @@ apiVersion: v1 kind: Secret metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -1762,6 +1762,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object vault: description: Vault configures this store to sync secrets using Hashi provider properties: @@ -3026,6 +3066,11 @@ spec: properties: account: type: string + hostId: + description: |- + Optional HostID for JWT authentication. This may be used depending + on how the Conjur JWT authenticator policy is configured. + type: string secretRef: description: |- Optional SecretRef that refers to a key in a Secret resource containing JWT token to @@ -3269,6 +3314,34 @@ spec: required: - data type: object + fortanix: + description: Fortanix configures this store to sync secrets using the Fortanix provider + properties: + apiKey: + description: APIKey is the API token to access SDKMS Applications. + properties: + secretRef: + description: SecretRef is a reference to a secret containing the SDKMS API Key. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + apiUrl: + description: APIURL is the URL of SDKMS API. Defaults to `sdkms.fortanix.com`. + type: string + type: object gcpsm: description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider properties: @@ -3600,6 +3673,70 @@ spec: required: - auth type: object + onboardbase: + description: Onboardbase configures this store to sync secrets using the Onboardbase provider + properties: + apiHost: + default: https://public.onboardbase.com/api/v1/ + description: APIHost use this to configure the host url for the API for selfhosted installation, default is https://public.onboardbase.com/api/v1/ + type: string + auth: + description: Auth configures how the Operator authenticates with the Onboardbase API + properties: + apiKeyRef: + description: |- + OnboardbaseAPIKey is the APIKey generated by an admin account. + It is used to recognize and authorize access to a project and environment within onboardbase + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + passcodeRef: + description: OnboardbasePasscode is the passcode attached to the API Key + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + required: + - apiKeyRef + - passcodeRef + type: object + environment: + default: development + description: Environment is the name of an environmnent within a project to pull the secrets from + type: string + project: + default: development + description: Project is an onboardbase project that the secrets should be pulled from + type: string + required: + - apiHost + - auth + - environment + - project + type: object onepassword: description: OnePassword configures this store to sync secrets using the 1Password Cloud provider properties: @@ -3760,6 +3897,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object pulumi: description: Pulumi configures this store to sync secrets using the Pulumi provider properties: @@ -4332,6 +4509,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -6304,6 +6489,13 @@ spec: type: type: string type: object + updatePolicy: + default: Replace + description: 'UpdatePolicy to handle Secrets in the provider. Possible Values: "Replace/IfNotExists". Defaults to "Replace".' + enum: + - Replace + - IfNotExists + type: string required: - secretStoreRefs - selector @@ -6373,7 +6565,9 @@ spec: - match type: object type: object - description: Synced Push Secrets for later deletion. Matches Secret Stores to PushSecretData that was stored to that secretStore. + description: |- + Synced PushSecrets, including secrets that already exist in provider. + Matches secret stores to PushSecretData that was stored to that secret store. type: object syncedResourceVersion: description: SyncedResourceVersion keeps track of the last synced version. @@ -7296,6 +7490,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object vault: description: Vault configures this store to sync secrets using Hashi provider properties: @@ -8560,6 +8794,11 @@ spec: properties: account: type: string + hostId: + description: |- + Optional HostID for JWT authentication. This may be used depending + on how the Conjur JWT authenticator policy is configured. + type: string secretRef: description: |- Optional SecretRef that refers to a key in a Secret resource containing JWT token to @@ -8803,6 +9042,34 @@ spec: required: - data type: object + fortanix: + description: Fortanix configures this store to sync secrets using the Fortanix provider + properties: + apiKey: + description: APIKey is the API token to access SDKMS Applications. + properties: + secretRef: + description: SecretRef is a reference to a secret containing the SDKMS API Key. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + apiUrl: + description: APIURL is the URL of SDKMS API. Defaults to `sdkms.fortanix.com`. + type: string + type: object gcpsm: description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider properties: @@ -9134,6 +9401,70 @@ spec: required: - auth type: object + onboardbase: + description: Onboardbase configures this store to sync secrets using the Onboardbase provider + properties: + apiHost: + default: https://public.onboardbase.com/api/v1/ + description: APIHost use this to configure the host url for the API for selfhosted installation, default is https://public.onboardbase.com/api/v1/ + type: string + auth: + description: Auth configures how the Operator authenticates with the Onboardbase API + properties: + apiKeyRef: + description: |- + OnboardbaseAPIKey is the APIKey generated by an admin account. + It is used to recognize and authorize access to a project and environment within onboardbase + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + passcodeRef: + description: OnboardbasePasscode is the passcode attached to the API Key + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + required: + - apiKeyRef + - passcodeRef + type: object + environment: + default: development + description: Environment is the name of an environmnent within a project to pull the secrets from + type: string + project: + default: development + description: Project is an onboardbase project that the secrets should be pulled from + type: string + required: + - apiHost + - auth + - environment + - project + type: object onepassword: description: OnePassword configures this store to sync secrets using the 1Password Cloud provider properties: @@ -9294,6 +9625,46 @@ spec: - region - vault type: object + passworddepot: + description: Configures a store to sync secrets with a Password Depot instance. + properties: + auth: + description: Auth configures how secret-manager authenticates with a Password Depot instance. + properties: + secretRef: + properties: + credentials: + description: Username / Password is used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + database: + description: Database to use as source + type: string + host: + description: URL configures the Password Depot instance URL. + type: string + required: + - auth + - database + - host + type: object pulumi: description: Pulumi configures this store to sync secrets using the Pulumi provider properties: @@ -9866,6 +10237,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -10774,6 +11153,14 @@ spec: - path - username type: object + namespace: + description: |- + Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in. + Namespaces is a set of features within Vault Enterprise that allows + Vault environments to support Secure Multi-tenancy. e.g: "ns1". + More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces + This will default to Vault.Namespace field if set, or empty otherwise + type: string tokenSecretRef: description: TokenSecretRef authenticates with Vault by presenting a token. properties: @@ -11139,10 +11526,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11206,10 +11593,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11315,10 +11702,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -11355,10 +11742,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -11399,10 +11786,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11420,10 +11807,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11431,7 +11818,7 @@ roleRef: name: golang-external-secrets-cert-controller subjects: - name: external-secrets-cert-controller - namespace: "default" + namespace: default kind: ServiceAccount --- # Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml @@ -11440,10 +11827,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11451,7 +11838,7 @@ roleRef: name: golang-external-secrets-controller subjects: - name: golang-external-secrets - namespace: "default" + namespace: default kind: ServiceAccount --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml @@ -11474,12 +11861,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: golang-external-secrets-leaderelection - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11513,12 +11900,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: golang-external-secrets-leaderelection - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -11527,19 +11914,19 @@ roleRef: subjects: - kind: ServiceAccount name: golang-external-secrets - namespace: "default" + namespace: default --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml apiVersion: v1 kind: Service metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -11558,12 +11945,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets-cert-controller - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11575,10 +11962,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -11593,7 +11980,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -11621,12 +12008,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11638,10 +12025,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -11656,7 +12043,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -11671,12 +12058,12 @@ apiVersion: apps/v1 kind: Deployment metadata: name: golang-external-secrets-webhook - namespace: "default" + namespace: default labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -11688,10 +12075,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.13 + helm.sh/chart: external-secrets-0.9.14 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.13" + app.kubernetes.io/version: "v0.9.14" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -11706,7 +12093,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.13-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.14-ubi imagePullPolicy: IfNotPresent args: - webhook @@ -11786,7 +12173,7 @@ webhooks: scope: "Namespaced" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-secretstore admissionReviewVersions: ["v1", "v1beta1"] @@ -11802,7 +12189,7 @@ webhooks: scope: "Cluster" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-clustersecretstore admissionReviewVersions: ["v1", "v1beta1"] @@ -11826,7 +12213,7 @@ webhooks: scope: "Namespaced" clientConfig: service: - namespace: "default" + namespace: default name: golang-external-secrets-webhook path: /validate-external-secrets-io-v1beta1-externalsecret admissionReviewVersions: ["v1", "v1beta1"] From f4bed3a35f9ddd3ed49ea59dc4a3d8a394ebe55a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 Apr 2024 07:04:01 +0200 Subject: [PATCH 1134/1263] Update CRD from operator v0.0.44 --- ...ops.hybrid-cloud-patterns.io_patterns.yaml | 55 +++++++++++-------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml index 68be225f..b3d769bb 100644 --- a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml +++ b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.14.0 name: patterns.gitops.hybrid-cloud-patterns.io spec: group: gitops.hybrid-cloud-patterns.io @@ -31,14 +31,19 @@ spec: description: Pattern is the Schema for the patterns 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' + 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' + 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 @@ -51,9 +56,14 @@ spec: type: string clusterGroupName: type: string + experimentalCapabilities: + description: Comma separated capabilities to enable certain experimental + features + type: string extraParameters: - description: '.Name is dot separated per the helm --set syntax, such - as: global.something.field' + description: |- + .Name is dot separated per the helm --set syntax, such as: + global.something.field items: properties: name: @@ -105,9 +115,9 @@ spec: short-sha''s. Default: HEAD' type: string tokenSecret: - description: Optional. K8s secret name where the info for connecting - to git can be found. The supported secrets are modeled after - the private repositories in argo (https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#repositories) + description: |- + Optional. K8s secret name where the info for connecting to git can be found. The supported secrets are modeled after the + private repositories in argo (https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#repositories) currently ssh and username+password are supported type: string tokenSecretNamespace: @@ -121,18 +131,18 @@ spec: properties: clusterGroupChartGitRevision: default: main - description: The git reference when deploying the clustergroup - helm chart directly from a git repo Defaults to 'main'. (Only - used when developing the clustergroup helm chart) + description: |- + The git reference when deploying the clustergroup helm chart directly from a git repo + Defaults to 'main'. (Only used when developing the clustergroup helm chart) type: string clusterGroupChartVersion: description: Which chart version for the clustergroup helm chart. Defaults to "0.8.*" type: string clusterGroupGitRepoUrl: - description: The url when deploying the clustergroup helm chart - directly from a git repo Defaults to '' which means not used - (Only used when developing the clustergroup helm chart) + description: |- + The url when deploying the clustergroup helm chart directly from a git repo + Defaults to '' which means not used (Only used when developing the clustergroup helm chart) type: string enabled: default: true @@ -160,10 +170,11 @@ spec: type: string applications: items: - description: PatternApplicationInfo defines the Applications Status - for the Pattern. This structure is part of the PatternStatus as - an array The Application Status will be included as part of the - Observed state of Pattern + description: |- + PatternApplicationInfo defines the Applications + Status for the Pattern. + This structure is part of the PatternStatus as an array + The Application Status will be included as part of the Observed state of Pattern properties: healthMessage: type: string From 2319eea438e5931cf42b591b9501919dc8fd6227 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 Apr 2024 17:32:35 +0200 Subject: [PATCH 1135/1263] Expose main.experimentalCapabilities in operator-install --- operator-install/templates/pattern.yaml | 3 +++ operator-install/values.yaml | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index 3615d18d..8bf7761a 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -20,6 +20,9 @@ spec: {{- if .Values.main.analyticsUUID }} analyticsUUID: {{ .Values.main.analyticsUUID }} {{- end }} {{/* if .Values.main.analyticsUUID */}} +{{- if .Values.main.experimentalCapabilities }} + experimentalCapabilities: {{ .Values.main.experimentalCapabilities }} +{{- end }} {{/* if .Values.main.experimentalCapabilities */}} {{- if .Values.main.extraParameters }} extraParameters: {{- range .Values.main.extraParameters }} diff --git a/operator-install/values.yaml b/operator-install/values.yaml index 9c2f7386..28bc3d4b 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -13,6 +13,10 @@ main: multiSourceConfig: enabled: false + # String to enable certain experimental capabilities in the operator and the + # framework. Not needed unless you know exactly what you're doing. + experimentalCapabilities: "" + patternsOperator: channel: fast source: community-operators From 2ffb55fec01e945d805fbcdd00fdf07743daed3b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 16 Apr 2024 19:50:34 +0200 Subject: [PATCH 1136/1263] Release clustergroup v0.8.3 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 345b8175..367cea35 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.8.2 +version: 0.8.3 From f2215f746338b1c38f5fdfc2db096059935e8a4b Mon Sep 17 00:00:00 2001 From: Tomer Figenblat Date: Tue, 16 Apr 2024 17:41:21 -0400 Subject: [PATCH 1137/1263] feat: add support for hive clusterdeployments creating spokes Co-authored-by: Alejandro Villegas Signed-off-by: Tomer Figenblat --- .../provision/clusterdeployment.yaml | 71 +++++++++++++++++++ acm/templates/provision/clusterpool.yaml | 15 +--- .../provision/managedclusterset.yaml | 16 +++++ acm/templates/provision/secrets-aws.yaml | 65 +++++++++++------ acm/templates/provision/secrets-azure.yaml | 65 +++++++++++------ acm/templates/provision/secrets-common.yaml | 59 ++++++++++----- acm/values.yaml | 21 +++++- clustergroup/values.schema.json | 32 +++++++++ 8 files changed, 267 insertions(+), 77 deletions(-) create mode 100644 acm/templates/provision/clusterdeployment.yaml create mode 100644 acm/templates/provision/managedclusterset.yaml diff --git a/acm/templates/provision/clusterdeployment.yaml b/acm/templates/provision/clusterdeployment.yaml new file mode 100644 index 00000000..cc37c161 --- /dev/null +++ b/acm/templates/provision/clusterdeployment.yaml @@ -0,0 +1,71 @@ +{{- range .Values.clusterGroup.managedClusterGroups }} +{{- $group := . }} + +{{- range $group.clusterDeployments}} +{{ $cluster := . }} +{{- $deploymentName := print $cluster.name "-" $group.name }} + +{{- $cloud := "None" }} +{{- $region := "None" }} + +{{- if $cluster.platform.aws }} +{{- $cloud = "aws" }} +{{- $region = $cluster.platform.aws.region }} +{{- else if $cluster.platform.azure }} +{{- $cloud = "azure" }} +{{- $region = $cluster.platform.azure.region }} +{{- end }} + +--- +apiVersion: v1 +kind: Namespace +metadata: + name: {{ $deploymentName }} + +--- +apiVersion: hive.openshift.io/v1 +kind: ClusterDeployment +metadata: + name: {{ $deploymentName }} + namespace: {{ $deploymentName }} + labels: + vendor: OpenShift +spec: + baseDomain: {{ $cluster.baseDomain }} + clusterName: {{ $deploymentName }} + installAttemptsLimit: 1 + platform: + {{ $cloud }}: + credentialsSecretRef: + name: {{ $deploymentName }}-creds + region: {{ $region }} + provisioning: + installConfigSecretRef: + name: {{ $deploymentName }}-install-config + sshPrivateKeySecretRef: + name: {{ $deploymentName }}-ssh-private-key + imageSetRef: + name: img{{ $cluster.openshiftVersion }}-multi-appsub + pullSecretRef: + name: {{ $deploymentName }}-pull-secret + +--- +apiVersion: cluster.open-cluster-management.io/v1 +kind: ManagedCluster +metadata: + labels: + cluster.open-cluster-management.io/clusterset: {{ $group.name }} + {{- if (not $group.acmlabels) }} + clusterGroup: {{ $group.name }} + {{- else if eq (len $group.acmlabels) 0 }} + clusterGroup: {{ $group.name }} + {{- else }} + {{- range $group.acmlabels }} + {{ .name }}: {{ .value }} + {{- end }} + {{- end }} + name: {{ $deploymentName }} +spec: + hubAcceptsClient: true +{{- end }}{{- /* range $group.clusterDeployments */}} +{{- end }}{{- /* range .Values.clusterGroup.managedClusterGroups */}} diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index e2f9d3d1..d95905f7 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -1,17 +1,5 @@ {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} -{{- if .clusterPools }}{{- /* We only create ManagedClusterSets if there are clusterPools defined */}} -apiVersion: cluster.open-cluster-management.io/v1beta1 -kind: ManagedClusterSet -metadata: - annotations: - cluster.open-cluster-management.io/submariner-broker-ns: {{ .name }}-broker - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - name: {{ .name }} -spec: - clusterSelector: - selectorType: LegacyClusterSetLabel ---- {{- range .clusterPools }} {{- $pool := . }} @@ -54,7 +42,7 @@ spec: runningCount: {{ $numClusters }} baseDomain: {{ .baseDomain }} installConfigSecretTemplateRef: - name: {{ $poolName }}-install-config + name: {{ $poolName }}-install-config imageSetRef: name: img{{ .openshiftVersion }}-multi-appsub pullSecretRef: @@ -91,5 +79,4 @@ spec: --- {{- end }}{{- /* range .range clusters */}} {{- end }}{{- /* range .clusterPools */}} -{{- end }}{{- /* if .clusterPools) */}} {{- end }}{{- /* range .Values.clusterGroup.managedClusterGroups */}} diff --git a/acm/templates/provision/managedclusterset.yaml b/acm/templates/provision/managedclusterset.yaml new file mode 100644 index 00000000..dce01f73 --- /dev/null +++ b/acm/templates/provision/managedclusterset.yaml @@ -0,0 +1,16 @@ +{{- range .Values.clusterGroup.managedClusterGroups }} +{{- if or .clusterPools .clusterDeployments }}{{- /* We only create ManagedClusterSets if there are clusterPools or clusterDeployments defined */}} +--- +apiVersion: cluster.open-cluster-management.io/v1beta2 +kind: ManagedClusterSet +metadata: + annotations: + cluster.open-cluster-management.io/submariner-broker-ns: {{ .name }}-broker + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + name: {{ .name }} +spec: + clusterSelector: + selectorType: LegacyClusterSetLabel + +{{- end }}{{- /* if .clusterPools) */}} +{{- end }}{{- /* range .Values.clusterGroup.managedClusterGroups */}} diff --git a/acm/templates/provision/secrets-aws.yaml b/acm/templates/provision/secrets-aws.yaml index 002c9247..a671638d 100644 --- a/acm/templates/provision/secrets-aws.yaml +++ b/acm/templates/provision/secrets-aws.yaml @@ -3,58 +3,82 @@ {{- range .clusterPools }} {{- $poolName := print .name "-" $group.name }} {{- if .platform.aws }} +--- +{{- template "externalsecret.aws.creds" (dict "name" $poolName "context" . "secretStore" $.Values.secretStore) }} +--- +{{- template "externalsecret.aws.infra-creds" (dict "name" $poolName "context" . "secretStore" $.Values.secretStore) }} + +{{- end }}{{- /* if .platform.aws */}} +{{- end }}{{- /* range .clusterPools */}} + +{{- range .clusterDeployments }} +{{- $deploymentName := print .name "-" $group.name }} +{{- if .platform.aws }} +--- +{{- template "externalsecret.aws.creds" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore) }} +--- +{{- template "externalsecret.aws.infra-creds" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore) }} + +{{- end }}{{- /* if .platform.aws */}} +{{- end }}{{- /* range .clusterDeployments */}} + +{{- end }}{{- /* range .Values.clusterGroup.managedClusterGroups */}} + +{{- define "externalsecret.aws.creds" }} apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: - name: {{ $poolName }}-creds + name: {{ .name }}-creds spec: dataFrom: - extract: # Expects entries called: aws_access_key_id and aws_secret_access_key - key: {{ default "secret/data/hub/aws" .awsKeyPath }} + key: {{ default "secret/data/hub/aws" .context.awsKeyPath }} refreshInterval: 24h0m0s secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} + name: {{ .secretStore.name }} + kind: {{ .secretStore.kind }} target: - name: {{ $poolName }}-creds + name: {{ .name }}-creds creationPolicy: Owner template: type: Opaque ---- +{{- end}} + +{{- define "externalsecret.aws.infra-creds"}} # For use when manually creating clusters with ACM apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: - name: {{ $poolName }}-infra-creds -spec: + name: {{ .name }}-infra-creds +spec: data: - secretKey: openshiftPullSecret remoteRef: - key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} + key: {{ default "secret/data/hub/openshiftPullSecret" .context.pullSecretKeyPath }} property: content - secretKey: awsKeyId remoteRef: - key: {{ default "secret/data/hub/aws" .awsKeyPath }} + key: {{ default "secret/data/hub/aws" .context.awsKeyPath }} property: aws_access_key_id - secretKey: awsAccessKey remoteRef: - key: {{ default "secret/data/hub/aws" .awsKeyPath }} + key: {{ default "secret/data/hub/aws" .context.awsKeyPath }} property: aws_secret_access_key - secretKey: sshPublicKey remoteRef: - key: {{ default "secret/data/hub/publickey" .sshPublicKeyPath }} + key: {{ default "secret/data/hub/publickey" .context.sshPublicKeyPath }} property: content - secretKey: sshPrivateKey remoteRef: - key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} + key: {{ default "secret/data/hub/privatekey" .context.sshPrivateKeyPath }} property: content refreshInterval: 24h0m0s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} + secretStoreRef: + name: {{ .secretStore.name }} + kind: {{ .secretStore.kind }} target: - name: {{ $poolName }}-infra-creds + name: {{ .name }}-infra-creds creationPolicy: Owner template: type: Opaque @@ -63,7 +87,7 @@ spec: cluster.open-cluster-management.io/credentials: "" cluster.open-cluster-management.io/type: aws data: - baseDomain: "{{ .baseDomain }}" + baseDomain: "{{ .context.baseDomain }}" pullSecret: |- {{ "{{ .openshiftPullSecret | toString }}" }} aws_access_key_id: |- @@ -78,7 +102,4 @@ spec: httpsProxy: "" noProxy: "" additionalTrustBundle: "" ---- -{{- end }} -{{- end }} -{{- end }} \ No newline at end of file +{{- end}} diff --git a/acm/templates/provision/secrets-azure.yaml b/acm/templates/provision/secrets-azure.yaml index 7fe6271b..21c9d482 100644 --- a/acm/templates/provision/secrets-azure.yaml +++ b/acm/templates/provision/secrets-azure.yaml @@ -3,58 +3,84 @@ {{- range .clusterPools }} {{- $poolName := print .name "-" $group.name }} {{- if .platform.azure }} +--- +{{- template "externalsecret.azure.creds" (dict "name" $poolName "context" . "secretStore" $.Values.secretStore) }} +--- +{{- template "externalsecret.azure.infra-creds" (dict "name" $poolName "context" . "secretStore" $.Values.secretStore) }} + +--- +{{- end }}{{- /* if .platform.azure */}} +{{- end }}{{- /* range .clusterPools */}} + +{{- range .clusterDeployments }} +{{- $deploymentName := print .name "-" $group.name }} +{{- if .platform.azure }} +--- +{{- template "externalsecret.azure.creds" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore) }} +--- +{{- template "externalsecret.azure.infra-creds" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore) }} + + +{{- end }}{{- /* if .platform.azure */}} +{{- end }}{{- /* range .clusterPools */}} + +{{- end }}{{- /* range .Values.clusterGroup.managedClusterGroups */}} + +{{- define "externalsecret.azure.creds" }} apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: - name: {{ $poolName }}-creds + name: {{ .name }}-creds spec: data: - secretKey: azureOsServicePrincipal remoteRef: - key: {{ default "secret/data/hub/azureOsServicePrincipal" .azureKeyPath }} + key: {{ default "secret/data/hub/azureOsServicePrincipal" .context.azureKeyPath }} property: content refreshInterval: 24h0m0s secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} + name: {{ .secretStore.name }} + kind: {{ .secretStore.kind }} target: - name: {{ $poolName }}-creds + name: {{ .name }}-creds creationPolicy: Owner template: type: Opaque data: osServicePrincipal.json: |- {{ "{{ .azureOsServicePrincipal | toString }}" }} ---- +{{- end }} + +{{- define "externalsecret.azure.infra-creds"}} # For use when manually creating clusters with ACM apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: - name: {{ $poolName }}-infra-creds -spec: + name: {{ .name }}-infra-creds +spec: data: - secretKey: openshiftPullSecret remoteRef: - key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} + key: {{ default "secret/data/hub/openshiftPullSecret" .context.pullSecretKeyPath }} property: content - secretKey: sshPublicKey remoteRef: - key: {{ default "secret/data/hub/publickey" .sshPublicKeyPath }} + key: {{ default "secret/data/hub/publickey" .context.sshPublicKeyPath }} property: content - secretKey: sshPrivateKey remoteRef: - key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} + key: {{ default "secret/data/hub/privatekey" .context.sshPrivateKeyPath }} property: content - secretKey: azureOsServicePrincipal remoteRef: - key: {{ default "secret/data/hub/azureOsServicePrincipal" .azureKeyPath }} + key: {{ default "secret/data/hub/azureOsServicePrincipal" .context.azureKeyPath }} property: content refreshInterval: 24h0m0s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} + secretStoreRef: + name: {{ .secretStore.name }} + kind: {{ .secretStore.kind }} target: - name: {{ $poolName }}-infra-creds + name: {{ .name }}-infra-creds creationPolicy: Owner template: type: Opaque @@ -66,8 +92,8 @@ spec: cloudName: AzurePublicCloud osServicePrincipal.json: |- {{ "{{ .azureOsServicePrincipal | toString }}" }} - baseDomain: "{{ .baseDomain }}" - baseDomainResourceGroupName: "{{ .platform.azure.baseDomainResourceGroupName | toString }}" + baseDomain: "{{ .context.baseDomain }}" + baseDomainResourceGroupName: "{{ .context.platform.azure.baseDomainResourceGroupName | toString }}" pullSecret: |- {{ "{{ .openshiftPullSecret | toString }}" }} ssh-privatekey: |- @@ -78,7 +104,4 @@ spec: httpsProxy: "" noProxy: "" additionalTrustBundle: "" ---- -{{- end }} -{{- end }} {{- end }} diff --git a/acm/templates/provision/secrets-common.yaml b/acm/templates/provision/secrets-common.yaml index 21a03b73..474347c6 100644 --- a/acm/templates/provision/secrets-common.yaml +++ b/acm/templates/provision/secrets-common.yaml @@ -1,61 +1,86 @@ {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} + {{- range .clusterPools }} {{- $poolName := print .name "-" $group.name }} +--- +{{- template "secret.install-config" (dict "name" $poolName "context" .) }} +--- +{{- template "externalsecret.pull-secret" (dict "name" $poolName "context" . "secretStore" $.Values.secretStore) }} +--- +{{- template "externalsecret.ssh.private.key" (dict "name" $poolName "context" . "secretStore" $.Values.secretStore) }} +{{- end }}{{- /* range .clusterPools */}} + +{{- range .clusterDeployments }} +{{- $deploymentName := print .name "-" $group.name }} +--- +{{- template "secret.install-config" (dict "name" $deploymentName "context" .) }} +--- +{{- template "externalsecret.pull-secret" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore) }} +--- +{{- template "externalsecret.ssh.private.key" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore) }} +{{- end }}{{- /* range .clusterDeplyments */}} + +{{- end }}{{- /* range .Values.clusterGroup.managedClusterGroups */}} + +{{- define "secret.install-config"}} apiVersion: v1 kind: Secret metadata: - name: {{ $poolName }}-install-config + name: {{ .name }}-install-config data: # Base64 encoding of install-config yaml - install-config.yaml: {{ include "cluster.install-config" . | b64enc }} + install-config.yaml: {{ include "cluster.install-config" .context | b64enc }} type: Opaque ---- +{{- end }} + +{{- define "externalsecret.pull-secret" }} apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: - name: {{ $poolName }}-pull-secret -spec: + name: {{ .name }}-pull-secret +spec: data: - secretKey: openshiftPullSecret remoteRef: - key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} + key: {{ default "secret/data/hub/openshiftPullSecret" .context.pullSecretKeyPath }} property: content refreshInterval: 24h0m0s secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} + name: {{ .secretStore.name }} + kind: {{ .secretStore.kind }} target: - name: {{ $poolName }}-pull-secret + name: {{ .name }}-pull-secret creationPolicy: Owner template: type: kubernetes.io/dockerconfigjson data: .dockerconfigjson: |- {{ "{{ .openshiftPullSecret | toString }}" }} ---- +{{- end }} + + +{{- define "externalsecret.ssh.private.key" }} apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: - name: {{ $poolName }}-ssh-private-key + name: {{ .name }}-ssh-private-key spec: data: - secretKey: sshPrivateKey remoteRef: - key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} + key: {{ default "secret/data/hub/privatekey" .context.sshPrivateKeyPath }} property: content refreshInterval: 24h0m0s secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} + name: {{ .secretStore.name }} + kind: {{ .secretStore.kind }} target: - name: {{ $poolName }}-ssh-private-key + name: {{ .name }}-ssh-private-key creationPolicy: Owner template: type: Opaque data: ssh-privatekey: |- {{ "{{ .sshPrivateKey | toString }}" }} ---- -{{- end }} {{- end }} diff --git a/acm/values.yaml b/acm/values.yaml index fb7cb03a..54c84a2e 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -21,14 +21,29 @@ clusterGroup: # testPool: # name: spoke # openshiftVersion: 4.10.18 -# provider: -# region: ap-southeast-2 -# baseDomain: blueprints.rhecoeng.com +# baseDomain: blueprints.rhecoeng.com +# platform: +# aws: +# region: ap-southeast-2 # clusters: # - spoke1 # labels: # - name: clusterGroup # value: region-one +# testRegionTwo: +# name: region-two +# clusterDeployments: +# myFirstCluster: +# name: mcluster1 +# openshiftVersion: 4.10.18 +# baseDomain: blueprints.rhecoeng.com +# platform: +# azure: +# baseDomainResourceGroupName: dojo-dns-zones +# region: eastus +# labels: +# - name: clusterGroup +# value: region-two secretStore: name: vault-backend diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index cf14bf26..4b6190ae 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -741,6 +741,12 @@ "$ref": "#/definitions/ClusterPools" } }, + "clusterDeployments": { + "type": "object", + "items": { + "$ref": "#/definitions/ClusterDeployments" + } + }, "clusterSelector": { "type": "object", "additionalProperties": true @@ -788,6 +794,32 @@ ], "title": "ClusterPools" }, + "ClusterDeployments": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "openshiftVersion": { + "type": "string" + }, + "baseDomain": { + "type": "string" + }, + "platform": { + "type": "object", + "$ref": "#/definitions/ClusterPoolsPlatform" + } + }, + "required": [ + "name", + "openshiftVersion", + "baseDomain", + "platform" + ], + "title": "ClusterDeployments" + }, "ClusterPoolsPlatform": { "type": "object", "additionalProperties": false, From f9bf1f7842343181ee95c68d1126aa41087f6ee6 Mon Sep 17 00:00:00 2001 From: Tomer Figenblat Date: Tue, 16 Apr 2024 18:46:24 -0400 Subject: [PATCH 1138/1263] test: regenerated tests after clusterdeployment commit Co-authored-by: Alejandro Villegas Signed-off-by: Tomer Figenblat --- tests/acm-normal.expected.yaml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 1a3f6e72..e83a83a6 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -6,7 +6,7 @@ metadata: name: aws-ap-acm-provision-edge-install-config data: # Base64 encoding of install-config yaml - install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXdzLWFwJyAKYmFzZURvbWFpbjogYmx1ZXByaW50cy5yaGVjb2VuZy5jb20KY29udHJvbFBsYW5lOgogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIG5hbWU6IGNvbnRyb2xQbGFuZQogIHJlcGxpY2FzOiAxCiAgcGxhdGZvcm06CiAgICBhd3M6CiAgICAgIHR5cGU6IG01LnhsYXJnZQpjb21wdXRlOgotIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIG5hbWU6ICd3b3JrZXInCiAgcmVwbGljYXM6IDAKbmV0d29ya2luZzoKICBjbHVzdGVyTmV0d29yazoKICAtIGNpZHI6IDEwLjEyOC4wLjAvMTQKICAgIGhvc3RQcmVmaXg6IDIzCiAgbWFjaGluZU5ldHdvcms6CiAgLSBjaWRyOiAxMC4wLjAuMC8xNgogIG5ldHdvcmtUeXBlOiBPVk5LdWJlcm5ldGVzCiAgc2VydmljZU5ldHdvcms6CiAgLSAxNzIuMzAuMC4wLzE2CnBsYXRmb3JtOgogIGF3czoKICAgIHJlZ2lvbjogYXAtc291dGhlYXN0LTIKcHVsbFNlY3JldDogIiIgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cwpzc2hLZXk6ICIiICAgICAjIHNraXAsIGhpdmUgd2lsbCBpbmplY3QgYmFzZWQgb24gaXQncyBzZWNyZXRz + install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXdzLWFwJyAKYmFzZURvbWFpbjogYmx1ZXByaW50cy5yaGVjb2VuZy5jb20KY29udHJvbFBsYW5lOgogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIG5hbWU6IGNvbnRyb2xQbGFuZQogIHJlcGxpY2FzOiAxCiAgcGxhdGZvcm06CiAgICBhd3M6CiAgICAgIHR5cGU6IG01LnhsYXJnZQpjb21wdXRlOgotIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIG5hbWU6ICd3b3JrZXInCiAgcmVwbGljYXM6IDAKbmV0d29ya2luZzoKICBjbHVzdGVyTmV0d29yazoKICAtIGNpZHI6IDEwLjEyOC4wLjAvMTQKICAgIGhvc3RQcmVmaXg6IDIzCiAgbWFjaGluZU5ldHdvcms6CiAgLSBjaWRyOiAxMC4wLjAuMC8xNgogIG5ldHdvcmtUeXBlOiBPVk5LdWJlcm5ldGVzCiAgc2VydmljZU5ldHdvcms6CiAgLSAxNzIuMzAuMC4wLzE2CnBsYXRmb3JtOgogIGF3czoKICAgIHJlZ2lvbjogYXAtc291dGhlYXN0LTIKcHVsbFNlY3JldDogIiIgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cwpzc2hLZXk6ICIiICAgICAjIHNraXAsIGhpdmUgd2lsbCBpbmplY3QgYmFzZWQgb24gaXQncyBzZWNyZXRz type: Opaque --- # Source: acm/templates/provision/secrets-common.yaml @@ -16,7 +16,7 @@ metadata: name: azure-us-acm-provision-edge-install-config data: # Base64 encoding of install-config yaml - install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXp1cmUtdXMnIApiYXNlRG9tYWluOiBibHVlcHJpbnRzLnJoZWNvZW5nLmNvbQpjb250cm9sUGxhbmU6CiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgbmFtZTogY29udHJvbFBsYW5lCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF6dXJlOgogICAgICB0eXBlOiBTdGFuZGFyZF9EOHNfdjMKY29tcHV0ZToKLSBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBuYW1lOiAnd29ya2VyJwogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhenVyZToKICAgICAgdHlwZTogU3RhbmRhcmRfRDhzX3YzCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT1ZOS3ViZXJuZXRlcwogIHNlcnZpY2VOZXR3b3JrOgogIC0gMTcyLjMwLjAuMC8xNgpwbGF0Zm9ybToKICBhenVyZToKICAgIGJhc2VEb21haW5SZXNvdXJjZUdyb3VwTmFtZTogZG9qby1kbnMtem9uZXMKICAgIHJlZ2lvbjogZWFzdHVzCnB1bGxTZWNyZXQ6ICIiICMgc2tpcCwgaGl2ZSB3aWxsIGluamVjdCBiYXNlZCBvbiBpdCdzIHNlY3JldHMKc3NoS2V5OiAiIiAgICAgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cw== + install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXp1cmUtdXMnIApiYXNlRG9tYWluOiBibHVlcHJpbnRzLnJoZWNvZW5nLmNvbQpjb250cm9sUGxhbmU6CiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgbmFtZTogY29udHJvbFBsYW5lCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF6dXJlOgogICAgICB0eXBlOiBTdGFuZGFyZF9EOHNfdjMKY29tcHV0ZToKLSBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBuYW1lOiAnd29ya2VyJwogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhenVyZToKICAgICAgdHlwZTogU3RhbmRhcmRfRDhzX3YzCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT1ZOS3ViZXJuZXRlcwogIHNlcnZpY2VOZXR3b3JrOgogIC0gMTcyLjMwLjAuMC8xNgpwbGF0Zm9ybToKICBhenVyZToKICAgIGJhc2VEb21haW5SZXNvdXJjZUdyb3VwTmFtZTogZG9qby1kbnMtem9uZXMKICAgIHJlZ2lvbjogZWFzdHVzCnB1bGxTZWNyZXQ6ICIiICMgc2tpcCwgaGl2ZSB3aWxsIGluamVjdCBiYXNlZCBvbiBpdCdzIHNlY3JldHMKc3NoS2V5OiAiIiAgICAgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cw== type: Opaque --- # Source: acm/templates/policies/acm-hub-ca-policy.yaml @@ -79,7 +79,7 @@ spec: runningCount: 0 baseDomain: blueprints.rhecoeng.com installConfigSecretTemplateRef: - name: aws-ap-acm-provision-edge-install-config + name: aws-ap-acm-provision-edge-install-config imageSetRef: name: img4.10.18-multi-appsub pullSecretRef: @@ -109,7 +109,7 @@ spec: runningCount: 2 baseDomain: blueprints.rhecoeng.com installConfigSecretTemplateRef: - name: azure-us-acm-provision-edge-install-config + name: azure-us-acm-provision-edge-install-config imageSetRef: name: img4.10.18-multi-appsub pullSecretRef: @@ -147,7 +147,7 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: aws-ap-acm-provision-edge-infra-creds -spec: +spec: data: - secretKey: openshiftPullSecret remoteRef: @@ -170,7 +170,7 @@ spec: key: secret/data/hub/privatekey property: content refreshInterval: 24h0m0s - secretStoreRef: + secretStoreRef: name: vault-backend kind: ClusterSecretStore target: @@ -229,7 +229,7 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: azure-us-acm-provision-edge-infra-creds -spec: +spec: data: - secretKey: openshiftPullSecret remoteRef: @@ -248,7 +248,7 @@ spec: key: secret/data/hub/azureOsServicePrincipal property: content refreshInterval: 24h0m0s - secretStoreRef: + secretStoreRef: name: vault-backend kind: ClusterSecretStore target: @@ -282,7 +282,7 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: aws-ap-acm-provision-edge-pull-secret -spec: +spec: data: - secretKey: openshiftPullSecret remoteRef: @@ -330,7 +330,7 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: azure-us-acm-provision-edge-pull-secret -spec: +spec: data: - secretKey: openshiftPullSecret remoteRef: @@ -373,8 +373,8 @@ spec: ssh-privatekey: |- {{ .sshPrivateKey | toString }} --- -# Source: acm/templates/provision/clusterpool.yaml -apiVersion: cluster.open-cluster-management.io/v1beta1 +# Source: acm/templates/provision/managedclusterset.yaml +apiVersion: cluster.open-cluster-management.io/v1beta2 kind: ManagedClusterSet metadata: annotations: From aee51586f7a02366fada0032ebe74b89e15ce4c6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 17 Apr 2024 16:17:57 +0200 Subject: [PATCH 1139/1263] Support remote repoURL when previewing templates This allows us to have a remote repoURL + path kustomize combo and show the resulting templates in `make preview`. Tested with: web-terminal: name: web-terminal namespace: hello-world project: hub kustomize: true targetRevision: main repoURL: https://github.com/redhat-cop/gitops-catalog path: web-terminal/aggregate/overlays/default Closes: https://github.com/validatedpatterns/multicloud-gitops/issues/356 --- scripts/preview.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/preview.sh b/scripts/preview.sh index ac2cd5c8..b9839c51 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -96,6 +96,10 @@ done if [ $isKustomize == "true" ]; then kustomizePath=$(yq ".clusterGroup.applications.$APP.path" values-$SITE.yaml) + repoURL=$(yq ".clusterGroup.applications.$APP.repoURL" values-$SITE.yaml) + if [[ $repoURL == http* ]] || [[ $repoURL == git@ ]]; then + kustomizePath="${repoURL}/${kustomizePath}" + fi cmd="oc kustomize ${kustomizePath}" eval "$cmd" else From 5b4e9038b1e9dca267137cccf5648b25c10c2665 Mon Sep 17 00:00:00 2001 From: Tomer Figenblat Date: Tue, 16 Apr 2024 20:03:57 -0400 Subject: [PATCH 1140/1263] test: updated test-cased and regeneated expectations Co-authored-by: Alejandro Villegas Signed-off-by: Tomer Figenblat --- examples/values-example.yaml | 23 +- tests/acm-normal.expected.yaml | 499 ++++++++++++++++++++++++ tests/clustergroup-normal.expected.yaml | 17 + 3 files changed, 536 insertions(+), 3 deletions(-) diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 84682e20..b8b1dde9 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -15,7 +15,7 @@ clusterGroup: - /values/{{ .Values.global.clusterPlatform }}.yaml - /values/{{ .Values.global.clusterVersion }}.yaml - # + # # You can define namespaces using hashes and not as a list like so: # namespaces: # open-cluster-management: @@ -25,7 +25,7 @@ clusterGroup: # annotations: # openshift.io/cluster-monitoring: "true" # owner: "namespace owner" - # application-ci: + # application-ci: # You cannot mix list and hashes to define namespaces namespaces: - open-cluster-management: @@ -70,7 +70,7 @@ clusterGroup: name: openshift-pipelines-operator-rh csv: redhat-openshift-pipelines.v1.5.2 - # + # # You can define projects using hashes like so: # projects: # hub: @@ -159,9 +159,26 @@ clusterGroup: clusters: - Two - three + clusterDeployments: + myFirstCluster: + name: aws-cd-one-w-pool + openshiftVersion: 4.10.18 + baseDomain: blueprints.rhecoeng.com + platform: + aws: + region: ap-southeast-1 acmlabels: - name: clusterGroup value: region + - name: acm-provision-on-deploy + clusterDeployments: + mySecondCluster: + name: aws-cd-two-wo-pool + openshiftVersion: 4.10.18 + baseDomain: blueprints.rhecoeng.com + platform: + aws: + region: ap-southeast-3 - name: argo-edge hostedArgoSites: - name: perth diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index e83a83a6..1e63d2e7 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -1,4 +1,16 @@ --- +# Source: acm/templates/provision/clusterdeployment.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: aws-cd-one-w-pool-acm-provision-edge +--- +# Source: acm/templates/provision/clusterdeployment.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: aws-cd-two-wo-pool-acm-provision-on-deploy +--- # Source: acm/templates/provision/secrets-common.yaml apiVersion: v1 kind: Secret @@ -19,6 +31,26 @@ data: install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXp1cmUtdXMnIApiYXNlRG9tYWluOiBibHVlcHJpbnRzLnJoZWNvZW5nLmNvbQpjb250cm9sUGxhbmU6CiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgbmFtZTogY29udHJvbFBsYW5lCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF6dXJlOgogICAgICB0eXBlOiBTdGFuZGFyZF9EOHNfdjMKY29tcHV0ZToKLSBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBuYW1lOiAnd29ya2VyJwogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhenVyZToKICAgICAgdHlwZTogU3RhbmRhcmRfRDhzX3YzCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT1ZOS3ViZXJuZXRlcwogIHNlcnZpY2VOZXR3b3JrOgogIC0gMTcyLjMwLjAuMC8xNgpwbGF0Zm9ybToKICBhenVyZToKICAgIGJhc2VEb21haW5SZXNvdXJjZUdyb3VwTmFtZTogZG9qby1kbnMtem9uZXMKICAgIHJlZ2lvbjogZWFzdHVzCnB1bGxTZWNyZXQ6ICIiICMgc2tpcCwgaGl2ZSB3aWxsIGluamVjdCBiYXNlZCBvbiBpdCdzIHNlY3JldHMKc3NoS2V5OiAiIiAgICAgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cw== type: Opaque --- +# Source: acm/templates/provision/secrets-common.yaml +apiVersion: v1 +kind: Secret +metadata: + name: aws-cd-one-w-pool-acm-provision-edge-install-config +data: + # Base64 encoding of install-config yaml + install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXdzLWNkLW9uZS13LXBvb2wnIApiYXNlRG9tYWluOiBibHVlcHJpbnRzLnJoZWNvZW5nLmNvbQpjb250cm9sUGxhbmU6CiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgbmFtZTogY29udHJvbFBsYW5lCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF3czoKICAgICAgdHlwZTogbTUueGxhcmdlCmNvbXB1dGU6Ci0gaHlwZXJ0aHJlYWRpbmc6IEVuYWJsZWQKICBhcmNoaXRlY3R1cmU6IGFtZDY0CiAgbmFtZTogJ3dvcmtlcicKICByZXBsaWNhczogMwogIHBsYXRmb3JtOgogICAgYXdzOgogICAgICB0eXBlOiBtNS54bGFyZ2UKbmV0d29ya2luZzoKICBjbHVzdGVyTmV0d29yazoKICAtIGNpZHI6IDEwLjEyOC4wLjAvMTQKICAgIGhvc3RQcmVmaXg6IDIzCiAgbWFjaGluZU5ldHdvcms6CiAgLSBjaWRyOiAxMC4wLjAuMC8xNgogIG5ldHdvcmtUeXBlOiBPVk5LdWJlcm5ldGVzCiAgc2VydmljZU5ldHdvcms6CiAgLSAxNzIuMzAuMC4wLzE2CnBsYXRmb3JtOgogIGF3czoKICAgIHJlZ2lvbjogYXAtc291dGhlYXN0LTEKcHVsbFNlY3JldDogIiIgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cwpzc2hLZXk6ICIiICAgICAjIHNraXAsIGhpdmUgd2lsbCBpbmplY3QgYmFzZWQgb24gaXQncyBzZWNyZXRz +type: Opaque +--- +# Source: acm/templates/provision/secrets-common.yaml +apiVersion: v1 +kind: Secret +metadata: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-install-config +data: + # Base64 encoding of install-config yaml + install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXdzLWNkLXR3by13by1wb29sJyAKYmFzZURvbWFpbjogYmx1ZXByaW50cy5yaGVjb2VuZy5jb20KY29udHJvbFBsYW5lOgogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIG5hbWU6IGNvbnRyb2xQbGFuZQogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhd3M6CiAgICAgIHR5cGU6IG01LnhsYXJnZQpjb21wdXRlOgotIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIG5hbWU6ICd3b3JrZXInCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF3czoKICAgICAgdHlwZTogbTUueGxhcmdlCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT1ZOS3ViZXJuZXRlcwogIHNlcnZpY2VOZXR3b3JrOgogIC0gMTcyLjMwLjAuMC8xNgpwbGF0Zm9ybToKICBhd3M6CiAgICByZWdpb246IGFwLXNvdXRoZWFzdC0zCnB1bGxTZWNyZXQ6ICIiICMgc2tpcCwgaGl2ZSB3aWxsIGluamVjdCBiYXNlZCBvbiBpdCdzIHNlY3JldHMKc3NoS2V5OiAiIiAgICAgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cw== +type: Opaque +--- # Source: acm/templates/policies/acm-hub-ca-policy.yaml # This pushes out the HUB's Certificate Authorities on to the imported clusters --- @@ -61,6 +93,60 @@ metadata: spec: clusterPoolName: azure-us-acm-provision-edge --- +# Source: acm/templates/provision/clusterdeployment.yaml +apiVersion: hive.openshift.io/v1 +kind: ClusterDeployment +metadata: + name: aws-cd-one-w-pool-acm-provision-edge + namespace: aws-cd-one-w-pool-acm-provision-edge + labels: + vendor: OpenShift +spec: + baseDomain: blueprints.rhecoeng.com + clusterName: aws-cd-one-w-pool-acm-provision-edge + installAttemptsLimit: 1 + platform: + aws: + credentialsSecretRef: + name: aws-cd-one-w-pool-acm-provision-edge-creds + region: ap-southeast-1 + provisioning: + installConfigSecretRef: + name: aws-cd-one-w-pool-acm-provision-edge-install-config + sshPrivateKeySecretRef: + name: aws-cd-one-w-pool-acm-provision-edge-ssh-private-key + imageSetRef: + name: img4.10.18-multi-appsub + pullSecretRef: + name: aws-cd-one-w-pool-acm-provision-edge-pull-secret +--- +# Source: acm/templates/provision/clusterdeployment.yaml +apiVersion: hive.openshift.io/v1 +kind: ClusterDeployment +metadata: + name: aws-cd-two-wo-pool-acm-provision-on-deploy + namespace: aws-cd-two-wo-pool-acm-provision-on-deploy + labels: + vendor: OpenShift +spec: + baseDomain: blueprints.rhecoeng.com + clusterName: aws-cd-two-wo-pool-acm-provision-on-deploy + installAttemptsLimit: 1 + platform: + aws: + credentialsSecretRef: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-creds + region: ap-southeast-3 + provisioning: + installConfigSecretRef: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-install-config + sshPrivateKeySecretRef: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-ssh-private-key + imageSetRef: + name: img4.10.18-multi-appsub + pullSecretRef: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-pull-secret +--- # Source: acm/templates/provision/clusterpool.yaml apiVersion: hive.openshift.io/v1 kind: ClusterPool @@ -199,6 +285,162 @@ spec: noProxy: "" additionalTrustBundle: "" --- +# Source: acm/templates/provision/secrets-aws.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: aws-cd-one-w-pool-acm-provision-edge-creds +spec: + dataFrom: + - extract: + # Expects entries called: aws_access_key_id and aws_secret_access_key + key: secret/data/hub/aws + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-cd-one-w-pool-acm-provision-edge-creds + creationPolicy: Owner + template: + type: Opaque +--- +# Source: acm/templates/provision/secrets-aws.yaml +# For use when manually creating clusters with ACM +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: aws-cd-one-w-pool-acm-provision-edge-infra-creds +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: secret/data/hub/openshiftPullSecret + property: content + - secretKey: awsKeyId + remoteRef: + key: secret/data/hub/aws + property: aws_access_key_id + - secretKey: awsAccessKey + remoteRef: + key: secret/data/hub/aws + property: aws_secret_access_key + - secretKey: sshPublicKey + remoteRef: + key: secret/data/hub/publickey + property: content + - secretKey: sshPrivateKey + remoteRef: + key: secret/data/hub/privatekey + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-cd-one-w-pool-acm-provision-edge-infra-creds + creationPolicy: Owner + template: + type: Opaque + metadata: + labels: + cluster.open-cluster-management.io/credentials: "" + cluster.open-cluster-management.io/type: aws + data: + baseDomain: "blueprints.rhecoeng.com" + pullSecret: |- + {{ .openshiftPullSecret | toString }} + aws_access_key_id: |- + {{ .awsKeyId | toString }} + aws_secret_access_key: |- + {{ .awsAccessKey | toString }} + ssh-privatekey: |- + {{ .sshPrivateKey | toString }} + ssh-publickey: |- + {{ .sshPublicKey | toString }} + httpProxy: "" + httpsProxy: "" + noProxy: "" + additionalTrustBundle: "" +--- +# Source: acm/templates/provision/secrets-aws.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-creds +spec: + dataFrom: + - extract: + # Expects entries called: aws_access_key_id and aws_secret_access_key + key: secret/data/hub/aws + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-creds + creationPolicy: Owner + template: + type: Opaque +--- +# Source: acm/templates/provision/secrets-aws.yaml +# For use when manually creating clusters with ACM +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-infra-creds +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: secret/data/hub/openshiftPullSecret + property: content + - secretKey: awsKeyId + remoteRef: + key: secret/data/hub/aws + property: aws_access_key_id + - secretKey: awsAccessKey + remoteRef: + key: secret/data/hub/aws + property: aws_secret_access_key + - secretKey: sshPublicKey + remoteRef: + key: secret/data/hub/publickey + property: content + - secretKey: sshPrivateKey + remoteRef: + key: secret/data/hub/privatekey + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-infra-creds + creationPolicy: Owner + template: + type: Opaque + metadata: + labels: + cluster.open-cluster-management.io/credentials: "" + cluster.open-cluster-management.io/type: aws + data: + baseDomain: "blueprints.rhecoeng.com" + pullSecret: |- + {{ .openshiftPullSecret | toString }} + aws_access_key_id: |- + {{ .awsKeyId | toString }} + aws_secret_access_key: |- + {{ .awsAccessKey | toString }} + ssh-privatekey: |- + {{ .sshPrivateKey | toString }} + ssh-publickey: |- + {{ .sshPublicKey | toString }} + httpProxy: "" + httpsProxy: "" + noProxy: "" + additionalTrustBundle: "" +--- # Source: acm/templates/provision/secrets-azure.yaml apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret @@ -373,6 +615,124 @@ spec: ssh-privatekey: |- {{ .sshPrivateKey | toString }} --- +# Source: acm/templates/provision/secrets-common.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: aws-cd-one-w-pool-acm-provision-edge-pull-secret +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: secret/data/hub/openshiftPullSecret + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-cd-one-w-pool-acm-provision-edge-pull-secret + creationPolicy: Owner + template: + type: kubernetes.io/dockerconfigjson + data: + .dockerconfigjson: |- + {{ .openshiftPullSecret | toString }} +--- +# Source: acm/templates/provision/secrets-common.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: aws-cd-one-w-pool-acm-provision-edge-ssh-private-key +spec: + data: + - secretKey: sshPrivateKey + remoteRef: + key: secret/data/hub/privatekey + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-cd-one-w-pool-acm-provision-edge-ssh-private-key + creationPolicy: Owner + template: + type: Opaque + data: + ssh-privatekey: |- + {{ .sshPrivateKey | toString }} +--- +# Source: acm/templates/provision/secrets-common.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-pull-secret +spec: + data: + - secretKey: openshiftPullSecret + remoteRef: + key: secret/data/hub/openshiftPullSecret + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-pull-secret + creationPolicy: Owner + template: + type: kubernetes.io/dockerconfigjson + data: + .dockerconfigjson: |- + {{ .openshiftPullSecret | toString }} +--- +# Source: acm/templates/provision/secrets-common.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-ssh-private-key +spec: + data: + - secretKey: sshPrivateKey + remoteRef: + key: secret/data/hub/privatekey + property: content + refreshInterval: 24h0m0s + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: aws-cd-two-wo-pool-acm-provision-on-deploy-ssh-private-key + creationPolicy: Owner + template: + type: Opaque + data: + ssh-privatekey: |- + {{ .sshPrivateKey | toString }} +--- +# Source: acm/templates/provision/clusterdeployment.yaml +apiVersion: cluster.open-cluster-management.io/v1 +kind: ManagedCluster +metadata: + labels: + cluster.open-cluster-management.io/clusterset: acm-provision-edge + clusterGroup: region + name: aws-cd-one-w-pool-acm-provision-edge +spec: + hubAcceptsClient: true +--- +# Source: acm/templates/provision/clusterdeployment.yaml +apiVersion: cluster.open-cluster-management.io/v1 +kind: ManagedCluster +metadata: + labels: + cluster.open-cluster-management.io/clusterset: acm-provision-on-deploy + clusterGroup: acm-provision-on-deploy + name: aws-cd-two-wo-pool-acm-provision-on-deploy +spec: + hubAcceptsClient: true +--- # Source: acm/templates/provision/managedclusterset.yaml apiVersion: cluster.open-cluster-management.io/v1beta2 kind: ManagedClusterSet @@ -385,6 +745,18 @@ spec: clusterSelector: selectorType: LegacyClusterSetLabel --- +# Source: acm/templates/provision/managedclusterset.yaml +apiVersion: cluster.open-cluster-management.io/v1beta2 +kind: ManagedClusterSet +metadata: + annotations: + cluster.open-cluster-management.io/submariner-broker-ns: acm-provision-on-deploy-broker + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + name: acm-provision-on-deploy +spec: + clusterSelector: + selectorType: LegacyClusterSetLabel +--- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub @@ -444,6 +816,22 @@ subjects: kind: Policy apiGroup: policy.open-cluster-management.io --- +# Source: acm/templates/policies/application-policies.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: acm-provision-on-deploy-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: acm-provision-on-deploy-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: acm-provision-on-deploy-clustergroup-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- # Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding @@ -508,6 +896,21 @@ spec: matchLabels: clusterGroup: region --- +# Source: acm/templates/policies/application-policies.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: acm-provision-on-deploy-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchLabels: + clusterGroup: acm-provision-on-deploy +--- # Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule @@ -763,6 +1166,102 @@ spec: jsonPointers: - /status --- +# Source: acm/templates/policies/application-policies.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: acm-provision-on-deploy-clustergroup-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: acm-provision-on-deploy-clustergroup-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + apiVersion: argoproj.io/v1alpha1 + kind: Application + metadata: + name: mypattern-acm-provision-on-deploy + namespace: openshift-gitops + finalizers: + - resources-finalizer.argocd.argoproj.io/foreground + spec: + project: default + source: + repoURL: https://github.com/pattern-clone/mypattern + targetRevision: main + path: common/clustergroup + helm: + ignoreMissingValueFiles: true + valueFiles: + - "/values-global.yaml" + - "/values-acm-provision-on-deploy.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-on-deploy.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + parameters: + - name: global.repoURL + value: https://github.com/pattern-clone/mypattern + - name: global.targetRevision + value: main + - name: global.namespace + value: $ARGOCD_APP_NAMESPACE + - name: global.pattern + value: mypattern + - name: global.hubClusterDomain + value: apps.hub.example.com + - name: global.localClusterDomain + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' + # Requires ACM 2.6 or higher + - name: global.clusterDomain + value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' + # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) + - name: global.clusterVersion + value: '{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}' + - name: global.localClusterName + value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' + - name: global.clusterPlatform + value: aws + - name: clusterGroup.name + value: acm-provision-on-deploy + - name: global.experimentalCapabilities + value: + destination: + server: https://kubernetes.default.svc + namespace: mypattern-acm-provision-on-deploy + syncPolicy: + automated: + prune: false + selfHeal: true + retry: + limit: 20 + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + - group: route.openshift.io + kind: Route + jsonPointers: + - /status +--- # Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: policy.open-cluster-management.io/v1 kind: Policy diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 8d3d4d51..e449dd9c 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -160,6 +160,14 @@ data: - acmlabels: - name: clusterGroup value: region + clusterDeployments: + myFirstCluster: + baseDomain: blueprints.rhecoeng.com + name: aws-cd-one-w-pool + openshiftVersion: 4.10.18 + platform: + aws: + region: ap-southeast-1 clusterPools: exampleAWSPool: baseDomain: blueprints.rhecoeng.com @@ -192,6 +200,15 @@ data: value: "false" name: acm-provision-edge targetRevision: main + - clusterDeployments: + mySecondCluster: + baseDomain: blueprints.rhecoeng.com + name: aws-cd-two-wo-pool + openshiftVersion: 4.10.18 + platform: + aws: + region: ap-southeast-3 + name: acm-provision-on-deploy - helmOverrides: - name: clusterGroup.isHubCluster value: "false" From 890ac2e46e952446d9737105d97e52766603d638 Mon Sep 17 00:00:00 2001 From: Wade Bee Date: Wed, 17 Apr 2024 14:25:16 -0400 Subject: [PATCH 1141/1263] Moved CLUSTERGROUP declaration to restore make preview-% functionality --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c6c7539c..b15f4beb 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ NAME ?= $(shell basename "`pwd`") +CLUSTERGROUP ?= $(shell yq ".main.clusterGroupName" values-global.yaml) + ifneq ($(origin TARGET_SITE), undefined) TARGET_SITE_OPT=--set main.clusterGroupName=$(TARGET_SITE) endif @@ -60,7 +62,6 @@ preview-all: ## (EXPERIMENTAL) Previews all applications on hub and managed clus @common/scripts/preview-all.sh $(TARGET_REPO) $(TARGET_BRANCH) preview-%: - CLUSTERGROUP?=$(shell yq ".main.clusterGroupName" values-global.yaml) @common/scripts/preview.sh $(CLUSTERGROUP) $* $(TARGET_REPO) $(TARGET_BRANCH) .PHONY: operator-deploy From e27624cbf1f5e0664350d37bc90176d3de63fa59 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 17 Apr 2024 15:43:48 -0600 Subject: [PATCH 1142/1263] Namespace argocd.argoproj.io/managed-by label issue - Fixed issue in common/clustergroup/templates/_helpers.tpl to render correct label --- clustergroup/templates/_helpers.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/templates/_helpers.tpl b/clustergroup/templates/_helpers.tpl index 44b2d073..9054f3bd 100644 --- a/clustergroup/templates/_helpers.tpl +++ b/clustergroup/templates/_helpers.tpl @@ -164,7 +164,7 @@ metadata: name: {{ $k }} {{- if ne $v nil }} labels: - argocd.argoproj.io/managed-by: {{ $patternName }}-{{ .clusterGroupName }} + argocd.argoproj.io/managed-by: {{ $patternName }}-{{ $clusterGroupName }} {{- if $v.labels }} {{- range $key, $value := $v.labels }} {{- /* We loop here even though the map has always just one key */}} {{ $key }}: {{ $value | default "" | quote }} From 13ee338d128fde8a7026e4c09de3eee13fe77ffa Mon Sep 17 00:00:00 2001 From: Wade Bee Date: Wed, 17 Apr 2024 17:59:12 -0400 Subject: [PATCH 1143/1263] Fixed indenting and duplicate entries in application-policies.yaml This corrects Argo error: Failed to load target state: failed to generate manifest for source 1 of 1: rpc error: code = Unknown desc = Manifest generation error (cached): `helm template . --name-template acm --namespace open-cluster-management --kube-version 1.25 --set global.privateRepo=false --set global.experimentalCapabilities=initcontainers --set global.repoURL=https://github.myrepo.com/EnterpriseKubernetes/multicloud-gitops.git --set global.clusterDomain=mydomain.azure.us --set global.clusterPlatform=Azure --set global.hubClusterDomain=mydomain.azure.us --set global.localClusterDomain=mydomain.azure.us --set global.targetRevision=prod --set global.namespace=open-cluster-management --set global.pattern=ekho --set global.clusterVersion=4.12 --values /values-global.yaml --values /values-hub.yaml --include-crds` failed exit status 1: Error: YAML parse error on acm/templates/policies/application-policies.yaml: error converting YAML to JSON: yaml: line 50: did not find expected key Use --debug flag to render out invalid YAML Also corrects mapping error warning on make preview-acm --- acm/templates/policies/application-policies.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 925915c8..94c624b4 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -44,13 +44,10 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - {{- include "acm.app.policies.valuefiles" . | nindent 24 }} + {{- include "acm.app.policies.valuefiles" . | nindent 22 }} {{- range $valueFile := $.Values.global.extraValueFiles }} - {{ $valueFile | quote }} {{- end }} - {{- range $valueFile := .extraValueFiles }} - - {{ $valueFile | quote }} - {{- end }} parameters: - name: global.repoURL value: {{ $.Values.global.repoURL }} From 50b2f5554d7bf503f4e9e008935f9664c0ebf915 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 Apr 2024 08:17:27 +0200 Subject: [PATCH 1144/1263] Add main.experimentalCapabilities to values.schema.json This way it can be set straight from a values-*.yaml file Tested on MCG. --- clustergroup/values.schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index cf14bf26..071805fe 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -65,6 +65,10 @@ "clusterGroupName": { "type": "string" }, + "experimentalCapabilities": { + "type": "string", + "description": "String to enable certain experimental capabilities in the operator and the framework." + }, "git": { "type": "object", "additionalProperties": false, From 866cd995c6e801e29a97bf77ed63b1b16d7c4dff Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 Apr 2024 08:22:02 +0200 Subject: [PATCH 1145/1263] Release clustergroup v0.8.4 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 367cea35..c8ba873f 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.8.3 +version: 0.8.4 From 739a63b1ac0f1efadbe6db25632c8b710879664c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 Apr 2024 12:41:55 +0200 Subject: [PATCH 1146/1263] Move the CLUSTERGROUP env variable for previews inside its section This way the code is a bit less confusing and it's more obvious when/where CLUSTERGROUP is used. make preview-% still keeps working as usual and you can override things via `make CLUSTERGROUP=group-one preview-hello-world` Gotta love Makefile's idiosyncrasies around per-target variables --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b15f4beb..7882a9ce 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,4 @@ NAME ?= $(shell basename "`pwd`") -CLUSTERGROUP ?= $(shell yq ".main.clusterGroupName" values-global.yaml) ifneq ($(origin TARGET_SITE), undefined) TARGET_SITE_OPT=--set main.clusterGroupName=$(TARGET_SITE) @@ -62,6 +61,7 @@ preview-all: ## (EXPERIMENTAL) Previews all applications on hub and managed clus @common/scripts/preview-all.sh $(TARGET_REPO) $(TARGET_BRANCH) preview-%: + $(eval CLUSTERGROUP ?= $(shell yq ".main.clusterGroupName" values-global.yaml)) @common/scripts/preview.sh $(CLUSTERGROUP) $* $(TARGET_REPO) $(TARGET_BRANCH) .PHONY: operator-deploy From 65eab76b505dc46e21746e11d97ecd4207c597fa Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 Apr 2024 15:31:52 +0200 Subject: [PATCH 1147/1263] Fix up tests after upstream PR merge --- tests/acm-industrial-edge-hub.expected.yaml | 16 +++++----- tests/acm-medical-diagnosis-hub.expected.yaml | 16 +++++----- tests/acm-normal.expected.yaml | 32 +++++++++---------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index a912f0cd..b44e051d 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -215,14 +215,14 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-factory.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-factory.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - "/values-global.yaml" + - "/values-factory.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-factory.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: https://github.com/pattern-clone/mypattern diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 3ac5fe17..b98682af 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -206,14 +206,14 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-region-one.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-region-one.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - "/values-global.yaml" + - "/values-region-one.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-region-one.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: https://github.com/pattern-clone/mypattern diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 1a3f6e72..ee3f05f6 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -609,14 +609,14 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-acm-edge.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-edge.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - "/values-global.yaml" + - "/values-acm-edge.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-edge.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: https://github.com/pattern-clone/mypattern @@ -707,14 +707,14 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-acm-provision-edge.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-edge.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - "/values-global.yaml" + - "/values-acm-provision-edge.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-edge.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: https://github.com/pattern-clone/mypattern From 444016b931b25915ea892d52dad7c6851ae114e1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 Apr 2024 15:33:09 +0200 Subject: [PATCH 1148/1263] Drop old comments --- acm/templates/policies/application-policies.yaml | 2 -- tests/acm-industrial-edge-hub.expected.yaml | 2 -- tests/acm-medical-diagnosis-hub.expected.yaml | 2 -- tests/acm-normal.expected.yaml | 4 ---- 4 files changed, 10 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 94c624b4..131f4f3e 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -61,10 +61,8 @@ spec: value: {{ $.Values.global.hubClusterDomain }} - name: global.localClusterDomain value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' - # Requires ACM 2.6 or higher - name: global.clusterDomain value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}` }}' - # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ `{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}` }}' - name: global.localClusterName diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index b44e051d..21a1c30d 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -236,10 +236,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - # Requires ACM 2.6 or higher - name: global.clusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' - # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}' - name: global.localClusterName diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index b98682af..40df35e2 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -227,10 +227,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - # Requires ACM 2.6 or higher - name: global.clusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' - # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}' - name: global.localClusterName diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index ee3f05f6..20a38d52 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -630,10 +630,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - # Requires ACM 2.6 or higher - name: global.clusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' - # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}' - name: global.localClusterName @@ -728,10 +726,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - # Requires ACM 2.6 or higher - name: global.clusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' - # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}' - name: global.localClusterName From 0283fc2fba0c9c97cbd358b44482f33db06057fd Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Thu, 18 Apr 2024 07:57:29 -0600 Subject: [PATCH 1149/1263] bug: Fix to generate OperatorGroup definition when namespaces definition has labels - Added condition to check if operatorGroup key exists {{- if or $v.operatorGroup (not (hasKey $v "operatorGroup")) }} - Default behavior is that we generate an OperatorGroup for a Namespace definition. --- clustergroup/templates/_helpers.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/templates/_helpers.tpl b/clustergroup/templates/_helpers.tpl index 9054f3bd..04da642e 100644 --- a/clustergroup/templates/_helpers.tpl +++ b/clustergroup/templates/_helpers.tpl @@ -195,7 +195,7 @@ spec: {{- if or (empty $operatorgroupExcludes) (not (has . $operatorgroupExcludes)) }} {{- range $k, $v := $ns }}{{- /* We loop here even though the map has always just one key */}} {{- if $v }} - {{- if $v.operatorGroup }}{{- /* Checks if the user sets operatorGroup: false */}} + {{- if or $v.operatorGroup (not (hasKey $v "operatorGroup")) }}{{- /* Checks if the user sets operatorGroup: false */}} apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: From c35d1244b92245600c11743f29015c9b4bdef6e7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 18 Apr 2024 21:09:07 +0200 Subject: [PATCH 1150/1263] Try fallbacks for /etc/pki when it does not exist --- scripts/pattern-util.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 0f865e0a..f7be58c2 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -48,6 +48,16 @@ if [ -n "$KUBECONFIG" ]; then exit 1 fi fi + +# Use /etc/pki by default and try a couple of fallbacks if it does not exist +if [ -d /etc/pki ]; then + PKI_HOST_MOUNT="/etc/pki" +elif [ -d /etc/ssl ]; then + PKI_HOST_MOUNT="/etc/ssl" +else + PKI_HOST_MOUNT="/usr/share/ca-certificates" +fi + # Copy Kubeconfig from current environment. The utilities will pick up ~/.kube/config if set so it's not mandatory # $HOME is mounted as itself for any files that are referenced with absolute paths # $HOME is mounted to /root because the UID in the container is 0 and that's where SSH looks for credentials @@ -57,7 +67,7 @@ podman run -it --rm --pull=newer \ -e EXTRA_HELM_OPTS \ -e EXTRA_PLAYBOOK_OPTS \ -e KUBECONFIG \ - -v /etc/pki:/etc/pki:ro \ + -v "${PKI_HOST_MOUNT}":/etc/pki:ro \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ ${PODMAN_ARGS} \ From ae1227458bb3b3414bea68abb030c6d5a2178c18 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 19 Apr 2024 09:08:29 -0600 Subject: [PATCH 1151/1263] Fix for multiple OperatorGroup rendering - Fixed issue with rendering multiple OperatorGroup. Added the beginning (---) to signal document start. - Updated the applications.yaml to include beginning (---) to signal document start. --- clustergroup/templates/_helpers.tpl | 2 ++ clustergroup/templates/core/operatorgroup.yaml | 2 ++ clustergroup/templates/plumbing/applications.yaml | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/clustergroup/templates/_helpers.tpl b/clustergroup/templates/_helpers.tpl index 04da642e..5001a06e 100644 --- a/clustergroup/templates/_helpers.tpl +++ b/clustergroup/templates/_helpers.tpl @@ -196,6 +196,7 @@ spec: {{- range $k, $v := $ns }}{{- /* We loop here even though the map has always just one key */}} {{- if $v }} {{- if or $v.operatorGroup (not (hasKey $v "operatorGroup")) }}{{- /* Checks if the user sets operatorGroup: false */}} +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -212,6 +213,7 @@ spec: {{- end }}{{- /* End of if hasKey $v "targetNamespaces" */}} {{- end }}{{- /* End if $v.operatorGroup */}} {{- else }}{{- /* else if $v == nil */}} +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index 17aa966b..4d8c3014 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -15,6 +15,7 @@ {{- if kindIs "map" $ns }} {{- range $k, $v := $ns }}{{- /* We loop here even though the map has always just one key */}} {{- if $v.operatorGroup }}{{- /* Checks if the user sets operatorGroup: false */}} +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -32,6 +33,7 @@ spec: {{- end }}{{- /* range $k, $v := $ns */}} {{- end }}{{- /* End of if operatorGroup */}} {{- else if kindIs "string" $ns }} +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index dbfefa8f..29db6f39 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -40,7 +40,7 @@ spec: limit: {{ default 20 $.Values.global.options.applicationRetryLimit }} {{- end }} {{- if .ignoreDifferences }} - ignoreDifferences: {{ .ignoreDifferences | toPrettyJson }} + ignoreDifferences: {{ .ignoreDifferences | toPrettyJson | nindent 2 }} {{- end }} source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} @@ -119,6 +119,7 @@ spec: server: {{ coalesce .destinationServer "https://kubernetes.default.svc" }} namespace: {{ coalesce .destinationNamespace .namespace $namespace }} {{- else }} +--- apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -280,7 +281,6 @@ spec: retry: limit: {{ default 20 $.Values.global.applicationRetryLimit }} {{- end }}{{- /* .syncPolicy */}} ---- {{- end }}{{- /* if or (.generators) (.generatorFile) (.useGeneratorValues) (.destinationServer) (.destinationNamespace) */}} {{- end }}{{- /* range .Values.clusterGroup.applications */}} {{- end }}{{- /* if not (eq .Values.enabled "core") */}} From 6ed1a054c4455a7da3470b290368fb1df354740c Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 19 Apr 2024 09:17:03 -0600 Subject: [PATCH 1152/1263] Updated tests for CI --- tests/acm-industrial-edge-hub.expected.yaml | 16 +++++----- tests/acm-medical-diagnosis-hub.expected.yaml | 16 +++++----- tests/acm-normal.expected.yaml | 32 +++++++++---------- ...roup-industrial-edge-factory.expected.yaml | 2 ++ ...tergroup-industrial-edge-hub.expected.yaml | 7 ++++ ...rgroup-medical-diagnosis-hub.expected.yaml | 8 +++++ tests/clustergroup-normal.expected.yaml | 7 +++- 7 files changed, 55 insertions(+), 33 deletions(-) diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index a912f0cd..b44e051d 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -215,14 +215,14 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-factory.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-factory.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - "/values-global.yaml" + - "/values-factory.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-factory.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: https://github.com/pattern-clone/mypattern diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 3ac5fe17..b98682af 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -206,14 +206,14 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-region-one.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-region-one.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - "/values-global.yaml" + - "/values-region-one.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-region-one.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: https://github.com/pattern-clone/mypattern diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 1a3f6e72..ee3f05f6 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -609,14 +609,14 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-acm-edge.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-edge.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - "/values-global.yaml" + - "/values-acm-edge.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-edge.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: https://github.com/pattern-clone/mypattern @@ -707,14 +707,14 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - "/values-global.yaml" - - "/values-acm-provision-edge.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-edge.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - "/values-global.yaml" + - "/values-acm-provision-edge.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-edge.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: https://github.com/pattern-clone/mypattern diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index da521518..84d4eaa6 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -716,6 +716,7 @@ spec: - manuela-stormshift-line-dashboard --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -726,6 +727,7 @@ spec: - manuela-stormshift-machine-sensor --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index f84bbebb..d8158428 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -1434,6 +1434,7 @@ spec: - golang-external-secrets --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1444,6 +1445,7 @@ spec: - external-secrets --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1454,6 +1456,7 @@ spec: - open-cluster-management --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1464,6 +1467,7 @@ spec: - manuela-tst-all --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1474,6 +1478,7 @@ spec: - manuela-ci --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1484,6 +1489,7 @@ spec: - manuela-data-lake --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1494,6 +1500,7 @@ spec: - staging --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 9effcbab..4449986d 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -1581,6 +1581,7 @@ spec: - open-cluster-management --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1591,6 +1592,7 @@ spec: - openshift-serverless --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1601,6 +1603,7 @@ spec: - opendatahub --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1611,6 +1614,7 @@ spec: - openshift-storage --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1621,6 +1625,7 @@ spec: - xraylab-1 --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1631,6 +1636,7 @@ spec: - knative-serving --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1641,6 +1647,7 @@ spec: - staging --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1651,6 +1658,7 @@ spec: - vault --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 8d3d4d51..0cb1cc33 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -601,6 +601,9 @@ spec: name: helm-values-configmap-example restartPolicy: Never --- +# Source: clustergroup/templates/core/operatorgroup.yaml +--- +--- # Source: clustergroup/templates/core/subscriptions.yaml --- --- @@ -1135,6 +1138,7 @@ spec: text: 'Example ArgoCD' --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1146,6 +1150,7 @@ spec: - other-namespace --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1155,6 +1160,7 @@ spec: targetNamespaces: --- # Source: clustergroup/templates/core/operatorgroup.yaml +--- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: @@ -1165,7 +1171,6 @@ spec: - include-ci --- # Source: clustergroup/templates/core/operatorgroup.yaml ---- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: From 74a1f10bdd28ca042bfa15b1a73d973d22faa9f1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 22 Apr 2024 12:18:36 +0200 Subject: [PATCH 1153/1263] Update ESO to 0.9.16 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.9.14.tgz | Bin 99176 -> 0 bytes .../charts/external-secrets-0.9.16.tgz | Bin 0 -> 102068 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 414 +++++++++++++++--- ...-secrets-industrial-edge-hub.expected.yaml | 414 +++++++++++++++--- ...ecrets-medical-diagnosis-hub.expected.yaml | 414 +++++++++++++++--- ...olang-external-secrets-naked.expected.yaml | 414 +++++++++++++++--- ...lang-external-secrets-normal.expected.yaml | 414 +++++++++++++++--- 9 files changed, 1824 insertions(+), 254 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.14.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.9.16.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 6e2f5b00..961f3b29 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.3 dependencies: - name: external-secrets - version: "0.9.14" + version: "0.9.16" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.9.14.tgz b/golang-external-secrets/charts/external-secrets-0.9.14.tgz deleted file mode 100644 index b75ffc1039b538b531ed211a0d76dc056de4502a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99176 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⁡Ve)YG||}Hk5Q1YGH8~ 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 J1K>vh_&-eMZIl22 diff --git a/golang-external-secrets/charts/external-secrets-0.9.16.tgz b/golang-external-secrets/charts/external-secrets-0.9.16.tgz new file mode 100644 index 0000000000000000000000000000000000000000..ca268d8459d75ca2b82392618d1bda98327bb264 GIT binary patch literal 102068 zcmV)UK(N0biwFP!000001ML0lb{j{QCSYChB#~w!6P=kLMZ5i2^8)i^-`+be8F`NY5+GEHSm$(EL}XlcMC|*;|D5?r z#KK|fPZK{&$A9U+J%#qe_uu3H9{%wCQ~iHs`^(|;7l((>zkB}u;j_OyJ$!!n{QJLn z-)~_pm9|PVmU!M@7QuAxhkj$fuHV1V=9ka^c+Qe+bk7!HZ*$-Qzj*$<@%(>ydPwL0 z^ci%6yYTez#nTs0|KdIEZ3QjuD?fj>3_kct8pP2{@Alavwp`}_d^-BSMu#sR&HQwl z1j~&6_)6RtkKY$>!6J6;FZ?L;uHwXdvzoxqe&(l-=JCRR>CLljnZ6v4uY+v9nvACL zVqCd$+$YxqAbx%NlP==#x( ze}5Mzv-IVop?9N}KCCQg=*j=WUJ62w|M%a#!_ndYHF`RH`uGvfmBIgqUvWGi{vE#b zZu~F|qQ#Wm#`phmjeowVeO^pD$lsjIz(!rgL=R{}+cZO8EbW?_d1j@c%Aa z`TV^(OTCHjN8Zd|hVi{W^U~FH>Zj?|Dh%(vAR@$-F4@%g{`kX_*Tox@M;fVq18?O0 z`RD)f=+SB90k-uMFNz17lV9`ACI z#fkq2=*@8m=jSKdU%dqI{3?OfJwbxxe`c^i|G=Bvd(a1PD9p#a$G2=1W{>sZz8yV! z^bQ9IBKUAY*w%ypm(6`|7F=EV2@*L($f*}!>0hQEi)P+DzQdIQeYy&+S1=BIxOg;W z5o{G`;|jXKhGA*mYa(VZ5qu-gZv7o-C4gvCWZuL1Yw!P8xbI7#Hlu90886uW{t@jz zEx!L}PoIDP+`a!@v_JkB|DTUueO}@X#+#0h7Oyw{_muADqyKXZH}j7_hJX>SfV*jU z=>u<=cf-de@Ai8farA+OE1&oL8CaMknEBrScv-TFrrwM1=s$zS#p>!R`0Nda15b?Q z!?NH%AJH<8@%{SndgTlpCH?n`g~3$-hywT|+Q8`1N1u;P{WF{aH-ngM%7BaXQhabZ zv&y#(V}vt!%U&gdl>K znJ%?>A-q?}CVpsgv8j8$+wy7Z7mfe75We6f696r)e(?Vrc!OUD_UI&DEMOp7s>$K- zj(n{^3KpYuKBnapB@d!$xSF+Wal{wV(lLdDnF2%cR!lU~-jUe-k&@5xH^6tJTx=Z$ z@mNWRId7qT@;`tpatkl&X(;A7O?n-Vnn(#1}*_@8VM z=G&;fU(Em?-uaVx9N%mm@MSBf2I9lPPPRAolWd3t zDv86;Pqu&0$`iW#)(Ri{duIgTw)Ao*FUUXKGz+#UQy0FHf$f* zvy4Skzj*dKho~`pOu>Wl~@`ygk4z#%mN!VeJcqA%Z(t04*0l zJ`lJG6&3&?u5M-CRmg7RRbnEb(P%|5)1O9S3`vx8$?c^S-grL{)8aG$WP0yC5r4|q zHLxa1n?U?i&b547o}mKG;$)l5)Ckyz*30dxwdjA#+*q#4uyhaT*8hH2(*HjH?(lo3 z|J_9sZyFUk4fsgl%O_EsRcHiueMhoGGV&uf3H{l(oX5<)3V_c^$G3-%Zh~m`(mPR? z=n-(;Y{oM7QW?WrSt*TisAH$RTr>C5&M*a^a)uli;&}uSV+kWvlEwdc5$q6cM9=x4 zRnegc*!(>mMF3nfIHmkkmj5ANloB&)XA*b|wbJ+rD#wB;OF1uDzW~wan20hBl=!y+ zp4#7o6w&Bg)PlEo0k{mQ&{(@!G`CARhwqULbaybxQ@EPTSiy5`cC8ujUjAcBl zoC3DWNwl2CU-Qqj6XAad3!CrbmT9vYZ7&yDJqwaj1oraC{u^~%#ZNVsCDU@dEWc!rhL}{Y}D9g0T8PECTuZf8F@^2j2g>#qFSNya6;YNy3ic`4s^o7=SA|KxMxGF`NN@z_Y6B zC6*|J;-;^wACz%k?H}<7(;{9S=9vC0PFmT*&ihDHByko`41JLN!8GI3fqns^6%uP5s>)6Ke+XiN!}T7;XwX3Dqqw;?hxatf321PJzxEEDSxb8 z>ueFExX{GE4v^i~PN!+jw=4{16ly1KfeeacyIgtG>HsegA`k{F_uRk2F(vF< zb_>0#Xt9sIrK`zrz}g7Z)jgh_Ep-D1TW+{$gZ*+CtvziP{GT{kdls-u{{P`wIsW&% z?|*Re|4!Pb{GT-S$jh;@?61(cc<4O~`ngSM7s9@kdsrEitbeaCo=!&4jMKBK^e*QP zmOrsY5Z>#~1jWA%lQBMLnr_5rfrJ<>w_!S z*94T&zvT1msDKSux?f6PIaIrETmj}}9WHuzwiAXLoNO^&k7UU)?+@PE@P9JvCkdQ= z{FF z@%c$wjpL~*tu>_^sj1I}D)2<9sSAX=mQoZ+54wd`C`>a+siS8~$|x{3^hvX;Z+hZs&U>j9nW}ON8}P9H17QxUN455 zPgX%Vxl`YY4=^Vti_iWKIImXPcLY9;VL8)#U zqd={WZuZENd6AUPn}hWBkcG=RI}{V8fJ_!DILY&1nCR&2^uu=-r5`-6kbd`nhT1P) zY11Bnc$Gw$ThM%~|G&G=-GN3Da0DSLqzKJ;s0niXF!A zf^<~)@O_-Y#wPd5pG>AXuM$6=5K*7o)@nV3_ zJjm|9Elz!jIoZUT=?rGxEWV3)x;9J!sRDsR|JDyh-17)8>WKOmhdYg6uv84?!O%P! zde*Qb4syPs(W6I&p)PzMmV&uNYBC?wsUS%mPr`V@lM`d-@zK%A@aLmdHXki!+HlJR z){|sG*~wNz%Z0zUUlzJBaK}Vg3g2T&026l>lYP?t$y+ z(KpKGRhpP9*x;q1)k*(>Fqq|GA!5VZ$i#qrv-z3epAd}auP!e0w5N1#{D|_dH4u`g zg^&e6VOCcB5(yKHD$S%kt6LZ#y-in>MUdgO_-}xEh6h<4_?YN7p%XIc&8l7C!5sry z43ClU?fUE}h3gOx#Q5~e(WJ!>IIs&p=~0rV$6Z9`7chc)qawGgpsYO21IM2=2pyY0 zx*o}|(0+*9=gBbPO5M$Ym-UjIBp}|e9z*Ka=WjQ{V&BBKs5bELO2amxG;Fxg>R;Qx z!6vd-oeEk}->C%ucg1fI0;!;y;wMl2R)!(bFzQtEMpDVO$n^I(@_7noBKR${H$I4RmzpOan@}H}!5GtE3A~ue z@m!QX($7aGCCh&V-jble!gNcA#raoZpKoei4_t4@xyP8)n_?Hx^@U(SU9TF7g zMoFg`k&?gh2)iaQn1X_)A#%`ubFmd5atSw&Z%2M!P7t)@CmL#us0I-f+dAemT|c%P zzY$Y3ZlU8)Wy76f_?_4utUsa5>G%(R2#Q=kPQ6b^Sw8XX!`ibPc^8}4jWz6pvE zRhP!+>>0|PR6{n7D)=Q>6WbhXLpY4%FplRij>9+`bd$HY`LKpLzql^fj5$`qnAr(6 zA4?zUT(kUb+hR@8THGVYd{G{lglt~;p(e{@>sX}o6lyA|_Phf~6p%l>3j#?Qs(mof zY&8R-REeIiHrv;JBnDQ%;R?P_FySY7t}Iv(k*iP3S*J-Tx6}HZo^;cgcjt%U@FqfC zY;MJ&8A&5?dVX=_!9QLfpH(kp=hS#KoFFOisk$0$iJIeF^bMr}pt%K^zc8TM);NYG z32Wbs37P63QTkihfK(Z8&X`2mUf6vjO`DP>Lx^@05p3Lgv>CbT=+gSLAKeB?9AS>a zOYB$ky#VY>?`$;*gXwV?uj({PCfDLsakdf)ld1z4UtgEfQ>Q|aiP)xg_-PzPut~~* z8_jA_J*R%&j|H1nK04{tXq#dkM@J`W{`!l7FZU`A!}ty-$=8ZShE%}nfycrWi=6o$ zaLR33*XwwQY2@IIWSaI>{fSX04La%QI{xPE;rGvK7pEQBf%nVBYt-4&XW~!C^B`j4 zzhC>wf<;Zo>qo0a{Z$@zTny|{nYy80zMh5-?Tc)Eno=EJf5*EoFO`Xl@i+>Y8tsL0 z=-TgyE?&j)1a1VcRX3tjoF>J5R7RWN_UJWtAPO82btglpWE6Bu!vS9~+J(nxB$hvt ztBON0tQ^tZ7{2`uOgNuV=>Le*)%LjNCx8?!>w%G6pfm0Suk)3=K!O(S%nYq zuZhnI2(Du`Wyvgmm1WbeIx&7s4Xi^x%ge?6Q2Q5x>;OS#|5?LAQekT*;LrRF)~(r{ z@s|<&pBwby1q32*;TX{>qKx^NAUd|aWSJkaCgpFDYvCPF@|uV`Cg;EM;*Nlhb(5lZ zE~q2ztYQnmwbK&oERoexD(yZ-e(2#A`ikZ>E4v*jP1`)IfOP;g`6(72WED_kqb zZ5Y~e`9bO6wGY^-rd2FDXxI2lX;p6WuSCi=;e_y&XL)aKUH+efjV>l#>@)xSAv%Co z^1nZS`oj5t?xJa~l22-!K)=oZTFuy5EZbX?_}w{-ekBg0wOGQ)S#&KA(b7Eh_d+;) z@n+KRxu z{wu8r5m=g0;olS7*mqE>t(%n;LgSH!; z7#@6f90ciq{`-^v7~yw+``h3gv#$=m{V&n0W@h-keLCrjI{_TCsVi>nH9}#66m;B; zQc6(umkGTwbr#mSd#dK#J)OJfzmmIWEwU)Mc#{6ePf6-U1VB75) zeN~o=T1yE8VV+r*tj3@$XX-Ht^>&t_)3R;)Z!UOqGRY72&bbBE2WX?%~2l|2O>me}7xQ z>SmjmnLRx57jfoy_zKvX7SH9NB?AthOx$?dj{r}{c-(7VeD*}JP-C&d`|)V>EzWS| zXVZDRjnLd>EEhj7qzbR&3(X#P!vDkI0}ZOb2uEa54w7k0@Z=ggM{G6*72D8wjbtQ> zf@wVK_CK?**Ux~l#AvfMp!*)!=M6zCXWs^3kAO=u>i8qDWG|jubp7T0tx<0y09l-s zk@I8$;BWkUDUKm7BPSo>5-G9Q0mhEZFjm#T`^0>$l64Z9lD2uPM5ZMqhJ<=ZNMP6L zm@_h*zh6pZEy(-{m3sn(Di>pk7ps|MEIl0^5l zJKEMZ%-mt{PQX)c?PKWY$zQ^oE~H||a$Lwk3mg|BAHX}De-JE~P_ptEoi5IUR5l3x z5f6M03gZ&@mWTHk0H^ajxN4zie(XXH>M?YONy%IfHxNBP61ZX9@qjvYj6^ss2Pc1O zfhsSi3~ht1xT`?WT{#@4cYVvpyGR{EhPCx&7> zLB$bj+GkwA()S@9>;iOpKo1>GMoB98&;K&=(#A_G9W{~tzD0LiwBp3P|imIC>hw2M82)5$BDcy)4TPa*u zw>2s1$8oe?*ilNzHJY0aO@eH8=wQu@Zx~kN6<&qDPo$1LY*eG(wrvLX+kS)=bF%~P7!H6t^~(begzYT zf5oJZ@FG2uw8>2pco%e8BL7Dtw&W)q)$t^bC#nC!@gxt!lbGoX3J}MaoYo)+p`7bm z69fncaLpPzjp{o4*J4;`=qRqq4$?egCF&2iR~CDZNqr5VCId6xuP$HQ5V`ih~YztS7he_Un2y)TFyxrM0=AGxW~alBXtSeM3dDof3`Ea}zPPWWhue9_p!cd_o7 ztg#&%{Wz(Z&Gqy{u{UFMZA9OobHbt&#CRiSYLgd<_~>2+Tc}w@=u4!b#h%|LT?<)f zN+QJxw@YsIjmk;ay4%J!F|YO+$o(p~7a(lN^2zpT#I*{aID}Kpu&a$HbzE}pJ$Yhs zIsLvHOWvd_N!6BJfOOxLRN6+B9lm22ZdRW1;uexM7olNr8f?~_)4bT=o^t0DxG2+j zF%i|RIf<4A5Lg)TjObe=CyD7HcZ{xSk~CXr*P1?JlC!iHP`RH%d2K;K9nzgx^Rndi zAj^jE4AZElLp;Sd+d9<#`BAOoBGom4b3v+U&IQT2AUPMLuD;F%Nj2w!^gu31Jvar4 zj1MxYL$hy?AZ3S5#!AyWeqNkVny=Y{5}xAu1`A9PdPPrKdVzE6EwX?fyT(*}O24IF zg=LqL>jMje8J__|&-ti>C>;_{D1#%I`|g;u%JKF|5@PwnGYJreaF+E zr#@-Ae)p4DbBcmjEUG65u)%0rvOFI)Miq8m)?y|QBfqY#V^b3x+J!l7@a5LAgfF zsuf?rCD;Aw!9dkG>#lfp-YzLsrO>U%5$R8I<)WW>?KvW3GkJC;Ft%Pjf$rNOZCM$q zFuej^vIp22!qvDhwT?3E|HFyd^-qvSW*L&It_pkJ2lv3dnx0~4MmHX`7Udul8n1OZP;uy zGxB4#@#aindTYr%B(aW6I;OCXN-Rc5H&qx&U>=2<`YgR>kZY{aeNDF{YbBSJVZ1#z z*}kveT&JaBj;=);*6^{@*z$Ds@0g*tpsf|&c-3omUuQ+U85Stio3(e1`cRzmk>wgS zO#7kRK3Q$1Ub@9LTyHY`5MA$%qS;#CuL)f{kA!@wT@eL!i!;Ps#gDp6WaZ7=4v>#; z0Wn)x4f%64?ro&+$7Tu65SEcNYvSw1Hik-SRBn|why5J(Yx&S&zv@R0`&FAIIBk4% z^O&o)d;)s2c_l;hVJY+{7etJ|c}skM|A^k2dGEfsSwoe;HP*Q0(uS(4&;`YP#!20> zO_Vm5l-Zs_jPoec$-N4JMA252>PSnbi*oYfpuz7~Ptb*4i|@;;PpNFT&uU%&x+vm; zWz+eqJQTsmL|p!er>@5`8>s&zhzMwqR}z zCIAix`~z*2`%a6kbEowBeE)hge996^efknB$mc~5_}M&8IkfRk&V!}!G4`|EqqR%1 z#N9vS1pIcMMwkvD!~c2pIlx*7c!VdhpTb}1FTFsDFR)KI3BMS;+7U)y~ci)loEd>cMS{XvEvymQ*l( z9@^SC;ul`;e;|IR8}=tppELrZfn}XSzwny~F42Z*nQDns;IYBSKCgNS&(Y@zHF4H5 zH9hd|=5fl2g)&YK;9rA-R{MUFFYDW*2tA)K)(&vA7q!B$a5jtn_TMW% zxu*oWIf+xHibh&Inz~`)zSosp;;q8f!aV=nG z!spsUYzMsVj=A04@w*lrudz{jI(aKBcu6>aM_;DPN8_J)3*rq5@G zyzoxB;obAYCSLAc9qhQ`_B*I}=5Aj3cpf8+^DmG^rnvl)k0kQCJVu&D_Zd&Lh=jJH zl*FQvskd5sQyd!lS#ro7x4doNT8Y}ujpHd(uuo%^4(>9Cbx5OW5MqRmthvc)B>Xb> zr#Cw6(5s2d9Ce#+*;zA=8`jjzD|KqFP)Rj&%VQeUsHE=-ZnyCy#ag0$mZ{7O{?;8< z%MCG*2mpn!>lUMg0V=Hrp4AZThLO$zx(MIUs}w~p{){uMgN@qW*ct^WUsXPlOnbBA zbgF!lMUIqSN>5%fc*wK&=IHS!`apl;OoSe4e3Z$-`5$x$-eE3`EV(BNV*^dz zj3U}_+lH$?r_Q+KyZL>chkyCtwX&(Sxoy7x_DME)R!wp1-3Ol zYj4g}^2Hr74-K%Y`7@`3FX4)=B-k-~0g{`--&PZ)mCM&Eh>G{epO60dV~FuMBkmp8~W=V|8F^4{?WOA8~Ar3tUz0h z=338-e;<<*thQX(YfyaPzfYh3@cr}ep1=6Z;qw=VPoF`zr||b@-~I6X z@GsueE$z9`R+tXe^Zo*&)f_HgW52H7ztAR75Ap#|B>q895*CKX=O^j6`CmC!B2KI( zHXa|GmiP$M4B^#^cxua=zlsb-k0_?!JEjaw=K?97pm0I#|06x_q!P1%YbCL>uq3UU zL=xCBi~kAt6wuE9coEEr%+7P|XMrQ+KLrT7FtGex;IYvZu+KY+F-(m5u-WW1`i*4c zqw2D;=Wrpq0=NR|I-Ig0rm<*3kbnL;UKa(I0Fg_)!)repp%P-U3c?v*jifr>?bFfW z^U>2sk^oduL~Z`6N2w_9ex1atHXGHSyKvNl|j~Whmu8U=bFdw~sRbgLddfvgpz?>Z!<&yY+{`-^v z7~xlc``h3glj;q={jU+{mn&0Ul!eb4=4+<8uxZQ@AE1=mECpP<(1OnZhSAa4>4)zw zNTSeN4P3Ee1cyvK@ z6~FZ6S+-1Hj>p$QCh5pDUMw&%&;6KCRxnwi4Qo8}Z~btb2G>KDOy?jPOfxv`F@SqW zn~8Xq^Tq5N#g5mq{xvc~T`HQc!+Ik!GqQpODnApEgxfoRb#bW$uH(7!Bhv0}^JyVu z0Z@Lzzf=j20IyT_L$1ptmL3I>1*$Gln)K7G-U@n51eM~0mxwNERNbc$^ji4gF{{h5 zx#hFNGfIbeAjYTH6wTVKEfu7y@whb%gId_aYlMy{s^{v_mh7J$N>M?M*L6)UmA2tp z?Igkfes%G)uHk-yYW$m*aBSCTa~X;MJq}r#BH^pgeT0hh(m(-sL}qYaq_X2?f;A<|74N-qNeBgvXVwz@r;;HwjEzo-a!6n0la5g|Vxc|XfUJF0HfLZ$Gk z8QUNm>LQ4`x^$`A*o}W5!r>TsX&;Hs`4&?S+f=6SO7EkoDu}Q`b|(Mm0|riX!hSg!vq*UnRZP&P7y8sJ72RJO|I{PlJuq%5u( zzS7}M`D}D|02~%E+LZif=!wXE_ToCDtcSRgzMU+z3|1}sKwbS3kcT947hHXGG@W7s zzs^ZAO5RF$RF$ejd|{QJ1e;Zo0LmMCdSVj#Q-B(-PHnsdpCJ&Rc{~$?4+E2=s#Z}v zKvz*$*JOTaU3k9MA=57{XBTA-b-A_SYdGgSqtG?09i%2Ntakl|Nm74aatTZ3IpUUV zVe{v1GKIWs`c?J*@$r%}r5Me0ReaqzcJXcrE zq#=U)RQ$KR66P_}Wn*K$JU;K1Aa518n-);(ZZJO0h~$^Bc?I?Bz6VTWol0cD40^)v zvplJe=?`jqq%s#MvOP#~bUfQ1<*A=K4=j*HB(Nfl84=1EaLf~|uevQDwcKo}1Y6Al z)WsQRPgwV{ABl-d3D`ct0zScuWx+zFuPVcp&eC|IhQk3sq5|p855wV26yHUfRfJ}y z>IbLi7f0w>{QCIJ`xqxTVa#UUDcU@OTH(3AW#nx^?!*$j>Ok3Y=|?jzER*U_s?NKc zW&Vyel0MjGRp^o06*xsl^}ZUaXIt~hFb{?WF*RSoPzkmMT{UU4qqp%|jm10LyLm26 zGCTrC=F=_&IdRDd{@Wmd29Ba>G|Uw<*mk05xKu3mkr)Es6;azV{WnMl>ZCK%tvKnd zhb=fdwdc|qM@yFmDSWW4b3#YC8C^szO1scwjB6x}OVxo(H`PoQImNNAh zW)=l*I!^fP)w?nJ7QJ{52w@tdCiD2no6sDp4#q9!#6ZD6cRJw)Z}QAW9E?T8xl>T5 zUS8DKIVw56c=-PV>1gx!%geJ1M50+djYCq=lM3D!SxfHW$}GGj#`}~nq5aZej8$bv zINIgg3)G850QaE1zWi03ELir^o7}e+JunMjSsb0^Ii`VuxKd_c* zx0Q@W(jb**04WJ!k`a2){99Blkfrz@-knyRBeK&#H+ zSk|K$^i@X3po$vrsHW}*T&DS3I@RSZ0qy#NTB(dlHOsQ!Vp~_;SFoX9=c)jqP1vNd z>`u|mkoUFo>=fS|hw*CGi(_Yphhg1{rGF&X&@;_`jL07Tn=F^CK68f6;;UM@eVVF>F?B9FBj9=IEb-(5HBxzGq0{(BH<& zQ=S0G)Va}OY#aMo>3j^oC_voc8v#Ruo*Gmh)<~y4XodQ`d5l%PQYk75Wd_fPqi~kl zBgbZMSbO8&pPm@GZP#uJ3G`7ukh8Ek1xazOPC?=nB&F_FI%GgAA|$LI93?KD!&44V z71rwTl*3b{?pty#D{@>mpOp~d4pq#Fe+^fv-3hB!-ae-fH5{XKlpXSPzO;^kn24lEFgThK@vxpudhRd^Ye)NSv*ZgFvW;1 z*za*Pn#PMU%r=D046iX050AJ<;0~`>Z~$X@j&M#x=sB8ai|`w6e8vQPLv3+AsAYb; z>#W{ppp}8^#4lE@^L?+P<>sL)70Z5u`?w33FljDeLaxFEOgQ#)@7d3X514rPcm*I= z7MfCIt&B)6Z<82<6*RRaW>(Q-1>mo$b_8c|D6>MG2>T-^DVShaBehOBt2d|ga7vH0 zx8SJGUMoE$-Y;oY*Tqp~%h}GUet)R|Nk?Z2bMY~quF9rd-+Iz%^V`_cLBMHkrS|Wu#J{;UQ*2}3Hy`Yj z+xU%##J<0@iMz``h>IV=^Og}n&`sP{%u78DmMo<2Iz4<%nH8Rs_Ow$YP!<0O&hm4K zq2(i5^3{fZ6Tj7`NNdPwtwSaEjKS)Wxw4{-rcBlL87k{#8#7YtT#tQZp!#5*tV@O( zFijj)@Jp~JwmH^@a2Us79M54KhjBLQhA5&d)rNq@%L*rE$_bh{J5Vf=6iA3wEcrGF zAOmW+CB9bRy>{60`r@j_ZpW=0V#HH%iv#b8*#5Ver(G1YE}~puNuOkX#E=g>P#obw z0pZtYuGc2!!q7}we+P0l@pokwMv*bNj&hUkGM;%)Qs4I&n+@R~!$9xZO} zAoZd6G6Ep6P`@Oh@s_wQahGLJOmww!8|&&~uh>oFgDh3uzVY?bWI_c?1P&`K1ykfq z$2`lzk_AcAOEIBvbw#4|W`KFkg?S@U-U5je2>KuK9a|ds_GncMZEV$6BCds%?6$tX z-UbUWbzmFfxwi9gHd168O*VO`c%#>Sq}S7B+9<3PUMa9e7w@c$C9mW7I`l;zg0qli z=w<)Vu>l)8{{s|y;INI|5AD3yoo|cbS9_-j@Azv z$>sc$4=f4TB=pmVk-`M$Vpj?f9uY z91+E-%;Zx0E78b)vj#V-iJwG%){M&wFnXi9TQLf(@F9N`UkJ}oSh|TmW5GUaUN_!h zTh|~m?P?sy37xEbE9Wfb*^0QE{xN({$%^trW zF_wLUYNG$F^=a!Ty>`A{b*YDRQfd$H>t)(~1O`(@) zuSs-Df$W+HDcjV+=EdN zk8hujo{tX4NRP+g)OI*z;d0InN5@BU36FbZKGw;(Cy60mB%Yf9NE$3mVh@##n9^^- z3jpGG;KfmRFOvG8C2+ve{-CSay==SBbH}lXbfoiGR^8klWxBg8;Bj2(S>YH0w72%Y z-mD|bTjNq`O#tW|oH1v-SAZ*7s;y~DJo(@tDeO_VxSxK#|{{d|0SBH|>8!_Jc$^p=ko$F*AOFJd7q z60!4qmF1ua;SRCmEVm}nBcyBK0Pg68u-R0BKaCelmShXRT`aIcDaZMc^1|>+0NPx1 zDDA*6q}V-MrSnd`lF?!L(KNZIjdb{|+HCoiKB(L@-8)5!Wd|bA!Z@rz^B|B=@^E)u z33Esk6_HBJ=bFeGMhg`Rp?{TmlaNI>Tw6eT3|RaMqp*Xpy`aGiOU^BV$QP<)U9&KP z<-lRYLK`7D7*W+aPpc0!jI=wg6IC<|ZHH!3w_z~o=zyrqw5r3YVAo9hOQ)_)FOg+U zNKp?`yb%7epdcJan2``yp}|#Xa1|OlySfSuu0q3JRcIg_Z;XaNNQ$8Nq{nH2t5Qro zryUIm)^?Elq_Y<7ZHwKm6LM%M0PbTp^OtcW^opJz)d~n@bC!r5J`-bmCpdU#{P5ny zJX@~YbnV!-k|_m-B;vwCBXwa#q$Qj?&bRIIxjEfsaji~wxlhDfw*A*#?rVY+eBtP` zXiQ2@#0OH_fN3G<3T!G!&g0aaX{kxr)Y?_N(hVfd^VrfS*hFyW!`AJB?|?&}G!JT zkS@AoTogE%cdOi0PMJGTQ1I!n4Q6Lgd|ge zaL(j;6E?lUs&w!~7ASeQ=8?`Dz?IJM0JR`5y@A+EyC#x1D++{zwin`)Z5LUHjp~mF`9{Sd#y+`Z;vQ%d|=);atPt{BhjS7kXb-(JvfB2X>$CikQe z#p}j%Pw8=N2f7C`PZP*2;@qmsKyttEvw7SF=>5tlc%0`-q~Jdd1Qm*$v-8ydgF%Q1 zZ|i^wo1=?nTXA%;hb=g|xYu-X2eM8f=%y;`sG}0{jz(B?T9U=KIMJ`ynXFA=usPIe zwiSmud)R_QoqLTsjl2GK*E_fVkGooCo#Q%Df3b`c26U_ndE?0o&Ww$&jw*@R44=)XiqGmN zQ#|7pN0vc!EtGKlC>sZUME-@l}b{^w{8-lGfs<}ugFkK zWuKtewp_XOlB^w9Kn~oJEK_n?L1Aw@{wlDAt@x^J(^G|8U=SuG4L6%#l)Srr6U=^j zqiCYAQq>|KO?W0XSKH=FhTgQb?K%kO1>#h;PG!3B>RB8{Vt_KcWL@) zQ1k1U{-Cypph}#3L~^6!*`CNw{k*&YxuO;#QMDNb;v-5WNWqSXh|y&6z`;X$I@6z@shZ13)QL&a^#9Y$c+#p{jC-&mQdkdc_eHhmRcS_U#uEQ zs~4sew1_$ipSJU6@Wk*pQnu5SecHR$sl#G;ANP#Z^hjoQUsJOe(z2t9nugb7A4y3c zWW+jwb3a9zO^OHG91|8$(#i-=c#&aAMs8Uc%#eiWc6dDj!N~hr)~#)wCdjoyFaYM& z3smN}%geJ1L}pn$jYE;k!wK$sr3xUy9)^NrH4_fSxI%Zb-7=RFirHeRDi@#LKqPBtr=Y+Nweyjb#XGrt=Y zM>Zl;fLHEVGCKvz0?!lhY=0$*eyR@bWquf@0#Ct#i`fAvY&K+9$R^w+LFUWgy|mz7 z)~=_8A&?4jgB{+iG*~PFo4_e2_tK6?x~?#emx6pvSp=WJO~4ekfCmZXqpd7xj~pO0 zz)K6kD1O8yp+6It7qMLXEuClCGJQE7-`(AfC_)q-0iMz*h{v;dnvP|XMH1)6iN6em zX!i{Z!}#u30XNvyueSmFbxwQlyb6;z1}HV_)QoVA%?)_3Ax&N_Kus~q`OTCI$ihSG z%4Zp+cooOR@5-wvqw;M?@X$$}z|hVB@DKgyHb~+K%U{uDLd$TRWcd7H7VjPO35KHb z*GOn~qx7%?&Ah|>xNL%+PEo!Myi5};xP*R5q=N1|XkYDXoDZ}quhFHCw_-a{pDtXp zNLx;$%R}Wbe)B=;)Zzax;~+BeZ5(i2_h3Zi;Px2~Bm4*a=o)KF(OZh6yyC=Qj6&&v zFCkrBT?L<8r$DCZ%Fm{AeEu?x@l_Hp@+udqOcmx!C|ioVIAvr~g*t%3k(C=9E^y;g zN{AHy2@=>x#E7ljnazQo7e)n8r}Wx_G!BWIsi5N22aK2n)3ht1nNxBE^(+8k4^9t; z6Vjij;V{@YsZ{`CJ_BMeaJ|8c)2eif*XQ2?>{pD$2To(A_sUXJeUO`BW$m$Q(`}4) zIn^*e-^W5W6OS?%Wjr>UC0Kh=$_lzTAgxw%le_|+|Cs@e^heWp@$#?Fo<1Fn9&dVU z#iMQCj<5sXKI`dHe-M~Lz(k>?T706Bez+WdfSM<7aCgq&?K8kua;}*ccTc~x zd}~sf?)I>&LmRYqleeDVrG^fj{N|lMnaA;sQDXX!ysuSidgf%EV|QhN+GSI*ZQHh! zidC`Aif!ArZQHhO+qRQV-TU_F?$IC4`2pv{8GAo#&AGJRW3zQTdEs)!5OzwoN+nC*oSsi8|c6}U+($rH*1P#*=6T%FfbelQ!I@TvP z46jm-h2NAXbk11FjV7a)M%ZO}+2I7BXg(LmYAH#7CrDWxfr6iO^c*P+x63Kg?fo^{oSAP$VS@xYka<6b`+QJN0%fqp;=Rlg-FbS=?k&pqI@BrHC53 zx%zM~Ls=3`b+S#O${p0>K5Tn zDI%)hg{A*ua18u_CwHW4nEu2htUHR`MkF@7)8p%-eR$i-ZT`Ys4A2RZ5aCy8IuaqC ziB8yR@VIpA1?G9J5sB_|kHtQd0XX_rzN^RC?=$$bHtgZ;(7CqrebEe znfrU5mZEcx3axDLC3&)K%i~S4xZW0bar4M6Z6^XQP?1}PJ4)h~hVz;4{D>q}$Ql2_ z2C;LB{vI)!{?1Kv|2C|*xuu}J*D!P?U%ccYNcD+kxD90TK3eX(VjPU?+6;b5IPlbS z7>Hh_Wh+!A&C0PouM`q;!yD)}%d3pTGE)vrcHTSOOk+_gr`2yLq4a3G72*Y!sGupO za!7Wwg&Z_;Rc+a8KSgAj5Q*uQbY%prS++y8Stl{oZ2t+qMzDpv`YTcs0?GRE0AY&A z=(6ymWPIyicEo}UVa(-HZP|B#AcN;gs}T*z)Ry*fjOck^Ih8>sY@JT9Gl9NA`jL@* zY-#wBF$$Si<-7f9eD{)HbK{6cB?3xNxYRrO^h(~Ee~1(3d}w^GKW~qTx==PJLQ{x2 zqWTvFx}W+t+KwVoy@q1mF>=paJG$vl1e%eMuy%2N0^Ze8sp>Kf^b|Uagzon5k7u%P zhQ8{rr{-U8UR!Yg^4)H)#8TR^VI_AEvtt8ER){_TJPV_h{WrWxFzaop!?;_u)90sC zm*Mq(h>W0zF3Dnh@7_BDAZBEo%uKJBtGow6h*Dg8T%rE>ll!qu|5kS{j*RzgCTKU< z*6-j6pFlR5dpTbC#le8^JY~g@*kxpnF>k_2{3Xb}2wQ@5Q=~V;@g2cV|G#K_!So|M zcqonVyoqA8;JjgKD(T1V0}T$bF1|`hVeJux+MLlU+|`>js25TnrXD!4X5rCQd%s5t zQV=FdP2zSR05V&s42bHpUk>c+!x|7yt-i)6dZuFhQbFaA?w~5 zjF4bRsn`P@{_psgf4K;KfF9Ha&Xfs3YkOAzDN=y6F{K=IeG6C)q~8f=Dc$F|Sx=u| zPgU8Na2`6NyRBtGo@2ii;<@1CiZvTm02M%3`HY`^$-Y+R#Cw!4RmBm6xedHd5$>WC z)+g ze*tuKWEVrCp>DFI!l1xVqdej23w8&9f>Q%Q6lv8urx0sYGwmTJZY~5+AXCzXk&Di_ zv+vd5flCsYpCBTfgbi+nD{)epztQl*#BsUahyFfZJO(hO!+jOMD85m7lf1S$klaE= zem!}RC8PzBZ>b(SEud7W@ne4g-TL+A@82iIM&3ECz%U6Y%Th-shYj;N$k<+;Daw!W zl?&wBPci6#%--w0E9EpIOPf-O7*!ezYW(F$&yr@jOSXJs4Q{e{0IOQit3_p-EdB$h zb1UA|K)a3RpNra?x1r@3StCq3wnC_K$!hR#U%i@|9~qkUCcQj-@tX-XKgnEmgt2f7 z(HMTR=|Ri|oo3jG-#*$L&;ygehuqErQQ)gX*eAOJCS=Bj{$A^a5uxt^_+rUPIK)9Z zn|86mgkg`YVoWpHhNY;+X~o?Gl-?rqIY(T zvE<1x3Wxd~j3E~7j>r(Cx-9&P2F_%uj%8A>o6X{JvIsIw>sL}Pi6%J?U>jh=E)wM- znpd9)m;EM|Kzi02EEDnawm-}G@Tc-!PCy(KF*EO9wwZDk z8N`30>V4@~Ay=4QN1-scrtNH%wC;LB)pWb(k+HrCOStS1Q}exBkhwfdU}04$o>-)? zcO2sueU!KyFH;}|(LlNvqW$w^oXaDDoff%7UhX2Zt>sGj69a@Gn?r-%P0A+IeNBQ(0s|1`vA-c7;v#C&+PfisO zbca6DL|NI@Ft5mt>P)KAm!RXdv;O!6%1**;Elxwbcz}86B{8iA&2i6}Fk*46UbXI# zwuvZ2E9|i2pX}cg6Ms*aHL>KH+>|DQI9+Q3GeYKY+#6D0o7lDe5n3GkZC%xMOpsNo zlY(ZbiD4yiYWOA`Z~&$Yfu8xK^!K4&rQe#ra>;c1@UGOMv&tHhw!PFYb-ifRW@W+>WwN~tyM!iZa4~ikWyRtyMR2ji zX7j~28M_FDFxSDUhv_idCDB+7W8MoQrB@bZ7_yVaaFPxNK!;w|<97HgAQWybzuA;}+3N!;srI#ak zT!S7)#Nz?axRUdWfN~Qki{8WDrBY_}^5b*R*!7iTxs6=98mE?=HCI7OOgr)psa6Fj zpjU;-Q&#L7uG$=b;tpa*o_!8{)mmWU*!z&7aP+IQkA3?gw}?{o2T(7b+56&_4N@$@ z(=oJ%_tyopz+mYPZrni7bg8d-Glt>aqK;MxKZ4xBH}NA1FWR!fg4Pn$R!FqPJ=Ld9<5fJ~vcp|}2_H>0ljK;OM)F0D zP=-7qg-882ES?;oud=#w@p(qbuh5!RyEs3KY!S0e0S#+uxdUWjbL(EY%uZ!w@eZ9i zq3ZDbv6SV1zdNM4qs!QCsl2lRw=gc!L0m0wTphWsbMBv(U#RyktlN-qZi}N$dD{%& zgX8SKH`8HMA}ST=oZKF)Ca5eTyv08cSW5f-R;$;9y7wNs`?iA1azH3Xugv^(nqXo#D(79q4JnAQ z(i&1((k9p+ShX^1Xdor?ZaL{7vnl!$rJ%}{Id6|IP#5P*>WUcUEy_sld_LIJ5X-Ez;N*FYhW477~kcooRWxKpfnyMe~8(FU38IP4h(Eycb<>2My z`b^x0>~gU(rrzqx-O?jBGlB6o>yQ;ubw=!${CgQ;CU5Vh5sIu%Lu#YRpZ(WGaAfHn zXiJ@)LxNF}cZDlxao{H{wg+{|vH|43>2v>rE?CUMgVTDt8sFgfbMLBH?XjPDl`(iW z=54Cd2CL&htyX^XzSlZCXkGqL{(Qc}7Erk8$80-ZQ6Qe-CE;F4?#Vt1Btg;MmgU%K zly|jKlgshDf6Dmoh)qd1eljM--ZynIIXtPd4_Hf6TdWxCK>2`O8x*K9!a$dv6-^*= zKn4mnH7xLfJ+G8twg7u7rn;&z8Rh5wPJUYG^+*HHY6z=4v%x_-%A=u=rx($ma1oTj z9g07#7x9g(d`9LT-b()i`t@9^%|6uy+U_;4(7%TfhNn#n)LVz-YqBzR9skMmzIVzI zzWY|Teo+S{Vly$mXVAPE`jFZ8ycYF=S=G-*oZ_3pj%zwO0;jM(@}cpRh%zo&ljm{( zT#1Vj$Nxjq42n2V{cf^nJhYY-N+pMZRA(++h|-LS&*Fw&%Z4ACz+`$Ye+WcK<(scR#!Kk*Ecbm#7zQ^S&xp zunBgaZOGqBs}RDrI!-9njta5RBKb1$5mcjG%9L&i1a-g>A51IpBUET*E(=#rTP3LG zePvjNWu^Ze^s6E&B3IJhIpJgS#frW1hvtKUOx1OXm9a9Y*i*K;;Tt1)tMt;82tZcl zbJH7POm~o#Ub{z32HiyG764f)q@C(S%3!r8v(C)6SBS1C8k7&NVCWpzFA_r+C3uz< zr+<{SgvmoEDyrx?*t9hD|RUN!CG`9mxjv42M z3$CM`b8vFXott>eiy2hy(MMd{)OlQ%q!>0+gRc@lz;UA%scNn~(@*MI<+cV5R+1W~ z|tgsHniBxfv+h zF8@kJVK^>S4#lLBJDS)yTEY$=kx4)H`e`drt*Ifcuh1grEVdXb5`B7ao^J)AF0uD5 zaGIH|BNW1+i?7)Om+BpquvD+$P_)@1u51ws;_;$fbcLd)hoQAk2AdH(rIhf8Ek}CW z$TQE2XN7%B3~{>HW!+4Po%%FcA2a&VLD2l`Dnzv!*(S4G{-JLE=_;uI)vJs=Xs-D* zI59DaCy5IILV{S)16`ezkDLjkTT`}ZGqX4c;UP+x6ij15ch~d45n7kXgz$T$@iEKC z=lv=K-z0(rsUXXTSbRShv)(pCzE>G`yr~If?0vf~_4@Qt{t=lY39A zvn}my81gFg((hlh$s;Sv+(9@C0QsiYk_gNT-)^AV^*|w8rl5ccXn?NJY)O0NUPk>O zm)nF?B~y&<&C~#nt#I)3CJFL&^}p3ssDedJbJ$JlTVmivz)K-Ln=jj$MxQ|vHFB}Y zE<6|8E{3 zby>&w8llI-KS{**G>o%D7Zt23`RRVsrVLR!T44QRR_Tt%0^m7FnE$}4s;q){RjY?Z zK+-!Q}>F%+1kTUo< zX|0UJ7wQ2WO5+YgAcxfSq{@kyva9IpX9|Fk{uyd_m3gnn>ndtoT*dAqZa?_zfKt;I zKos^V3!B@+7C_i&t^RaWxplZtBmmnwU=DXDC3JxIw=-p7rP4b~a@6A2d#r0@+n?3w za+9U3q-k(^1yV`O=fm{RaC!o$@@%%iU=+Fzin&Fq-H@LLP_mP>uY;EpjnCpIFwrQ1 zIpHwqXrea3pZp$4y&w>AV~0qoM>{us$yE$>K_Wi8(~S+76Pajt7Ii)NzS$Q**_Dy} z&!(OO7}{bqWQ}mJ*MoGxI2To@8YjM+Bk0zMcj~qSU4-o`Q?$|GNB9BZ+OdA~UaN;% z&u5D-lP8dvBOp+9lL31NJLL5v^)zxg{~Gq32)4Zd2_@+q)(I4+n&<~=)0o`c(+>GP z(0;DsDI~jVlD;lpm4l7XvUSeYc8>YtrXJslVI8A?l-ZeA&tm*Mt5Yuq8wq&16J5@x zOGU+!vrN`2!G_b{gV4DlI)%i$7|k{;K1yN+WT;$z4wtZ-?fzCg+F_e+ z<8sp^{l{*XRM^1c@tBXg;d!MvW)%!(0#MS{l&~-M4}YZ7F_ghrRph_tw~Dv=1a^K`l~`tg$1JMa5rFq@a@o$wElkMIX91nvh-43YLM%`bj0uJ!TwV_ zVKNNabc5tZK9(jXiSurS@=&sXYhEcpOc+!2-%2n`XwibYUcrwzOUunWHC_F0my>#* zVEu300LzRYtZ>UA3VejtNNy|!bTEuwQCP#He{)+eW^Etow&N5T^+G0v4Fkipk$GXT zFM-O?+|%Sf9jZin5+-h< zOs;CgA(l_hxhFLRBt{ad{)JWmjXG?zOMU}JkY+*&{%s4XE8x$)(pQp`V(p-0ys#H5 z^U0}bGY-#{n&_iyhSd-Mw<+jRldMVw8r|3TTR>6-Lf;s1C$#5KjoO!}=bN}8W7Jn+ z*NWbFwsGzdp=w7Px_(C@hs=wJmAhgQvE<7Ja^LyaY=I2=Hi3S`%Aexcdo%DF`gYPy zXP;^JeVG-*c?D2j@Yp~WR`~5pP#g=BjLU&A6&oKBzXh2YIW(yh_*^kS@mT_gFzStz za*ZTvv9yx{i0@Er?e$&{rN{Pxkn5E>{3%mbs20hmRFrh`9Smm|e4huMI$j zZ8@i9MDEW;_4CTGGziGv=O$LjP$c*kzv;gwLJm#JBPDR}Gb6I9`K{7N7mAW*px^Sj zIw#a;#ih=+cSoCn&cF}9#l9=!zN#v$2`f6#XQo!6X%Zq0g}S^}Ff10-wlf|l)wa8E z&(qZw!;Ey}t)XQ@lPsXWk($bCCPuhsk<+s0RwoH@#2Nk47G<#1nK+vrc(jTl5f${v zs-vfAx76gymu)?8^g|gZ?z~a!pa_yHcYf}o9 zfSNPlu?x2=G;j&O+KgL#{A~?Q5 zls+_}QWwk;aNOqO5Pt+XcSxzp-TPHE^m)rnzTko54Wt1=$ZRNh zwZX7(emoYCTRZH23MIkjy=M*JF1h?nCR0%o)n%s2|TbsSaB zo8`y)hA?gK`M8TM8{I>7$$%E3<}{O>9~$o1f%acY0&Q{Mw#zQ85f9~Fk;^(Q_+Pwm zb=kT|4Se>pSxMas2OheRvC_I76xx8lmO3iNsD0X9&dK_A4LE-QLB9y77pHVbQd{Wd z##DmsfYwRpJ~Iq0uMfY)OQM7kmq-(Qszfzv2G5m36V~}Nu5sfIfApL`9+Q5dsiT(< zqFnx1PsdA5bpH9n`-3dwS|L@I#mzrQD{tECno4u{R3{dr#K7i|Y-~q>VnBRO%-6@5 zVGwy)=wEveCz;Nv_(+WX#$i>Xo}Agh7Pk_}lGj=q9S7r$N%jY;$xWNjcVdfX`NwqB zcb2&2r%*a%6Le8!5!_Yw7<{^MU4;lM5gdb-m_?(@6KaL)Y4OoilcU4ZyvqCB4IUPn z35+RkO-dBE@f!+KbLM*?6x^g@_k>b_IgG984~6n-27~k4*0e7}&%eF_ZBkgNK7fRjmjZ1_~<;Jj6Sp6`_%)h*$0zmpam*a3jm7X(^cQFago<&ZtYvDuwx|#$TZvgqY3hi}4 z;-4(uxHJYB9sMGjVUja_5Pn<+Xem*knr#)wPBC1j>5>s=ZOgrPghWyyA;fYvCuo0a03LCCl%VzsHGRq+C}AZ0(k7yx0e0Af zg?)&bB1C_f^UW09T;Z|O_6@$Ut@E+pCG_*psus_BNKf>g5x_AS8ocR->QvRJ5SAyE zTO{ODMYmBbx!*z+$PwfAisnhrL6u!M(nby6xn8=+9&Pgb5>kk8C5>wvP3J(sh=ueXIt0fK6}_romNa&A(*ReCnMonoQ=J!QmiZX zlp(c%m6VKl({%JNFA{HjRHw4Aiaum^n-O8%`jj^V%Yq~yH`)+f){Ln7T`sJy`rT_F zxKPV)%NJz|vvb)^E46jr^Qt}Vw#3CY4DRnDezkPdLRkUP46x0F^^U^vpJLOyy1R!y zwZHSG#2S>(OOJw=IQq?k6DP8jl61|2F~kQrdcw1x4oq3D)%-`^<^gQdW7B1=GgCT5 zI_tX{G6&X~*`R)y%n7m6l!gwsX}WMELy=uyDAfALJ;f9k5p zV~xASt%8muAG@d^5$RWUUQJj%M=pxP`Bj}D!}(V%(P!cZwNbHq5#D91QL)fHK*9wM z2$~9ma&IhmQ`((KxM5(l+wJ{YZ|XQ#b}i)#ozAc7`x($~$LzIu2-uu&2p#6_33|yN zSfLt&@LSWv#bkY8sV!F>x{}r$RW_RCg^B;oyucmIJ-DV~0m zOmkp~=|%3vVzn1NpHHo)^1esE@pFk!#$jcwu*8sYItGE~`rCtM1rV~waHIrKH?eJg zBeFo%yr3M7!u8cS_NBrV7$FFCfg{Q)=qCVBgTWGM+gYX_==xw1T<4yGRJC*!Iufek z&qE3IxUT0qzURVq_U7aR<<&8@=0nK%S`K?9z#9|1bZ|z*xNy~WrR|N>WeUMC!$qJ#5ZW{$$l80 zE@Aw%OZXk7{%AVW_%oJ0r7&NKrFxa9+hv14wmg$lRr zgTYd1J?!xx8xuhu5BsLj#& zoR*vz2UjS*#N1!y(IkUM?Z3gjLbzD(eq;!O2R$<7>~Lt*9D+DWnrg^U7n^DoM71@Vj zUod`=K-;J+igoyRJGHY_pEMieU`mjh;I-y&);sBe)qOk%TusD!SlSIz<8{;Wy&6>t zfdf4*p4f4?5U8%;b-G|K_*a@8Ylje(-`d)GV@i~*ssvxMD@&@-%s~nw-B%5jaRaQ_ zH+b3Syw446rRxHJWm=?;(&1k(04UI&cSQ~OCwk6|`1^;qZh$Esg>QP59*~Mmcslr*hjs zhB?@$ayu`{j&;wtiEPW{nh}dO6no)kerXl#+G=;rjJP=u_AR|}1(rLiGldiC*s#&k zZlRu$jbZ=taNLx;i$^E+T$)g#-dio}#5UGzy)JR5cr5yvQgAu!v26)lzF7fDt|^7y zlKZIK^9n!}B$DsOf+K>>OMOfTnE|yeYFOn#k07q@iNa5?&mN&8BUQx{J@eX$njcuk6OslI2S~+ zw?XFKWOx}&mo88Y;H3GOWb%YTEn2Ru;Zu|J;MG^e<|Slx1T{f66XtpkYE#I>-LnDv zp9;IX7s^(JDi(Hw#$8VnYy>=hG1)7TV%j0CiChlV%@aK+S_gw_K{je?ofW6>aggE1$31(d zO&-?Z`N^g|8#g!&8+OlX+qOTT1_?AUZK)*fjjAM+CvVD7IlMK}WHzr)ee({16iK+l zJ}w=?ns8<3P;d`@Xd9xbr`;NKK!>$I$H;wJ!oMic(&xOaK7>DK(4E>e-}G#UM6=YH zqc_yE?_3f=_1a1xQj4gKRLjBGcHE|&kxlLRcAOCUKy?`<1QDD>)B{TJaA`?(`;mjn z-vJavYQda1X9?zxCG)vc^3S|T&Mw+Lobo&C*;^DE3k>JnpVdRTP~ViOZlZW}C%dnv zA`l4lot$2N-h4c6#bjb(~X>E~LGN*J)E3l!*$Vu6M<*{}#m z1Inn+be0*9YjK)DRZ&^>EzspN1=KiUWi*<>6sbD@2K~}WJd`aZLwFaJ6f{6bdnA5m zx~(3$jtXQWMjftn#8}e)dMg7NMLaz$cP#Y1C22Y&QnZ;o4Yh?9x`c(_$2;_IB!=E3 z6gABCDzWH6th~;}^s_EiQ37si9JPqv5R@?sjcnR)0iDH7F|Q#$N2H-TXyC|dCx<<; z$?SK8UFOhS=2()p!6`h*7cxpG;FOG?2xPqtTAA6W?sO>G@0E4fql@a!Ctv+6*qjq8J8&ThY%h%*;&PWA`k zZw_LjnGe21_DutU`h^&k2~*c1CO z0Hb6}K$RV%BeeB9@Ma~VTgoF= z03p0rafy>tURQ(@^kCY?LXzrs%_FeYapk)M8nKVHCwpkt!dZnSdv)9|P>fMnlD?~P zD86Y2C0xUtdo!q+tJ{-UM4X@ai?C}FXslX!m6G8}oT4kOzqlttn93*k^qG76Q!<6y z{dVJwwh!&g(frqK$K(FAy&Ne{0Hma&NrA}_bDnCR?9}$~>pL^c@%273x=tgYLHCE} zg)&nGVp^9Y{Zgqrztd*ihmxJ5z%NHG40l~9jnSq0NlhN^0R%lltw_v0ti*_}PxN7& zSBll}Yf4neSNlGuQddpID)dtt>@qFuy*jd%6mm4A#((U_u+U=$Pp`|7=GTRWYh@{q zl*5YB-)?)*sG0kTkpGQtFwYXRVjW0%jHlAfC;EG_b*Oy$F4XIiJdK&O{qD-83q#_f zJY=V%GPldexWzhv;PtCMp^FD_46(8hqXxlD4&Hp`FZSdy5hFiyt zX&WtO_2cnCS}o0H3$XW>A!$^lNhYY8OJOA+(`t}`WV%7S6slo@n#ENOZZgDJ<9qjt zE~ql5iXn2<0UxbztVCCLZ7N8W3j^@)0&PT$P{Ej5sk8o^)IgsXv-jW11b<@q+NG8L z`vY;<{BdoD1*@7_kI%2KbjMR)AAm1vmz!9)>?SKmx5{%in&2hvQgD{Bg8)~|A06RP zdvkS6_cVhYH^EG%m=?d44s)iCEJUUYJ#Uh|q|^7f`u`~ouvTuLE>)I&6=V0bYnFn( zd5j~pc8{Q!8FFUj1?MIG+#_nr&M~+}70gKqS!KX2v3!GAEk65q)lF9WtM}~gl16*wCQ;c3% z&W-(K3kbx!76KD8&w~q4SEXQy3|3ksVDkrM0Kdu$cpwjP#ZIYyVw(ax%lMLa)q&~= zYTt(}!p^pv!_ZwFh}f(7_~{-VAKcKI&{%~|z8D!P%k2oesk}63<@l8QcE<*W6geCQ zX|?L9SiPK6uuZ&XGQ}KL*i6vv<-;tXz?GWzpj?W~} zkPbG?)C9$UmKZ}KVzM@xlfszV))btQ2AX>(WFmg7cqxT1Vrsf~`N5ugltB6Y)~Ry| zatyk%l;xsUM91-Zr*6BgesjemK|R{hr5kTtO&0>&Lm`8}-%WEaUpt-jMY3Mex8tSK z;AnH_`ptPW9n>)r8$GFAzZA3%7_hY?z=g*F#2&8#uaRYQ4WyS}W3_d;e_PMwMm33w zulHL>nstBtDx{JsPH+S2n)_Ay0ebf3NXhbPvZy8tW*Vc%u&d|w`2H&ygM-E94t*{n zeNLF)(3v_l6ObZ&4!}xmA#Hc`$m?4XXKnn09{B?Xwb<_HF(^qre7g8trWK&zT^9c!Q`s%f}oSCpT-xU5-H zMPyCJB#a5eazv9A-)hn2LGJ~rh**&%hwubpy^kY*`HJ3eouS`` zi1I8ukk!2*BR|vzwutJE%NBR^mVAYL%Ad|&^hSK3t^fx|M-&gMOu*I#D!%lo+t={| z4;Aqac&K2{LCRVc=5qEDr`n?t6exdKBNh2sww9hIEJKHge;5nl=Rb_)BIp0bSfb1R zgRy`k{#{GV3H=YoB38_hfeTHx0Ff2cue)8)1{(63!X>xk=8uLZqzT&d`{n%J`qvYc z$Q@)c!@PT8RVDApWKG?ZYU3%ZNPC7EdiJ#e0d$yX>bP_Y19B9jQeP;ox7cFi2C=2h z&rl#gk>|wO>;G^pYlyCxn`_h;mu1>_mxl@TGKn;1J%pu!@BP-a$;4>kO2o8lzm9#! zb4)T7YK&84)321|dhV)|Z!n9uc#NrP^`gXO^rivbrK-=dLL(NZ94^g*KKc422q}#1!ne zkkmcI>cg*4d`ac*;xKY>Qy;F)Y}_q-vCI{ag+ zpWV{!-s88=Ip?a&chiydyJ75f3+WI7sx>L$m)JJuj5ye~BXgMRM)m_ongC@~TO(c9 z(6?LNT?AR{HX7Sr+6WgD@G9tjc{XHUQ)qRkUM*EC=;IQkhg!*C`MIam!c{8{clKwr z;D452sT7z$H84d;!l zVF}pkkgLK7g2f8ux=46dpa>{pLRa+=-$+4djuxkf9aEdd_nAe}^@ILkp-JU~N7QM+*f$vUe4$hBuPJotfi$`pP04HCeWf!p zmJ>Jp&E3K3fK)(jLYci1A0OnX{CR~=b4=8HhCX>R{^Oxt6Qacyeg$Ik5zosz#pgB5 z*Cg+M`1PD3YlbFLW*J7~3di)FQ^yS@RCG`&7;HR+n!7mUP1mh(s-p(u4BmRn*;DO&)Bm z1CVzFE|M#|=xL{8aT;%AM}toZY=;c}0om2M3U$IRHWT)j)64pmr0=7dm%0kOA6g} zvxN>S>5fwtY-oS^$0*$e|A$d(Yr~ZD=fn#Sf;0b`$ljNvlS~jKmjA`Q@=rxNiegfO zTJtmRdOwTj4UMoG#hVk3FVYTF>!U$ukY>nQ%=hgfRGkYS1X2?Sz>Dl#Y$HG0yuJlAAhu&vG5@(oDY~$lpn<(yLdWbos1fwS|1n1s zJisY?q#*#;I6&C@gRMQ5WFye<1fC5yOhIw<`r#b~<@{{yT!S~rHRrz6vTG=$(ia284zB@m!Ee{ru@E_<8++b$WD0^g8_}L7p5QPRizdJa*csxAB4(vBZ z^Ww;@koad7<&UNLy&#lD-KhRsxAQo4d_aB4+6G!k)EaIJp|8*Z{v5O3DBr&)`h1pr zd8d{`%4W6E$nNB_ z`>+3tH>KqFe(b4!hs0<>p;1%Pd>+nnZ@F5|a!@f)?Cjb;7^%<8UdopONp$dVt=ul> z?u8k8fs;#%uyd9i>SPP9F^q`|mBG^;96Qks`wCrzxPE)nEdsWHGAD!fC;;QDQd!Y* zODlJ$#d~$Tr3a=a#_>5BWbc{zq-rnx$j= zVO=O42e;EHFiV=EAAw0q=ZjNzsH^OJ{aBWO+#s$xSModkKs}$fKRs=Do{B?m!-Jcy zZBPO&?S;^h01PRqJQ}!LD-pvL-!9DBaqxVThz#67YX3ZhI@rS@u_nWT|)cFPg&oQh>2!AkW{%(`6<|<2<##VwWspF#NNyk1VbQ9uM6-)^w0ki+eOjFO< zPd9{S%Fon+2wc^{)c}wOc|lCGw*I6pi^Gu0pH`BF<4ZnHv@iNbhg#yZyd#3#V-7x> z0(YW%xkNlTEgJ_LC&1qFN65$=XO`hDoF)Q8ENP^l<&XB~tk&z|ZZ9TJalEzKka87QLYCd ztD{;r(nmuEDi4q=m5OD52d<6-fil9Y4D>t(E>>y8LqhB8;NpW0W|Q+kGi3yt6ys(4 zr;{w<^7iyN@{EX2r~k`zosjolcg3uUA=43xF6~zxr==1LIZDOK<{u)Jwzw2FhkNgD zxsMG3!#2N#{(LhC`84^`RJv=~90~{xZI`LVneX@~8hM5@@12`$n;X`caz&LuB})?B z)JNR#npgyrMVPIH?d$UtqV~%J@=?Y5mvLosDSy2i;heNh0`|`=r3kjwK|~7O&`4+g zUEJfQEGVwVa*L&rBSQsE9a$AJPpjMoZT-Yh6%wq53Vt1yg=Or%0MFHW%OWldMaYR_T}MD0gv9ZO+3u8=vbjvie%W2RdYqF~w+ z7xX_gfPtM|?y(Tbny#2b&+U8DG^4$Ts2J^QnYDVgjG56jgQ*lv=~iG!DvVXPqVSY2 z@#zs-5Awxm9_*6RP3&V2=ou9ZxxOinbq?30)r-HAUoyWfPG3mW%fFX*cXyrXvJyYK zyZqrYzL#m!?!WE6bd{DPNC8zp`KVYuwO&S zQESeed&UoUB80-fht#q*-P}O+=UpCc3plb+JRtTq{hdo0BOD7zYBb}I>le0S=tcDxM z%4`yRChn+Iyy;Zn!4WciO{wVxF$OR{jfHesiF@`N^4NJI6R4q^wU}*jdN2NIusDd0 zbK4?)Sya%m?9H;W*~cki_C#|#jE)ghQ`GVzeiQ-KdX6MgDW<<1knQgj=TsjIB;^-na7nZ0@0^w*IGv9@!wB+n0TvH{{V>#P-=jUkE5)2RVC8 z5%Iz^%HY* zrVj&hruLwcif|ZTfGpx5mSYZ7tZwJ~WR5s*RXe3~FQ=_KfCEr(=N8To2iGB+7a8l( zlUvGglSOVejUK8FEAdO(zaN^DAco8|CY42BqyM;5a#0H~+5=tmBgJS<&=AduV_IZ} z^s`V3nr>nZj07b&lWQMv7^?QV}OUnldqj_=9j*p&bE<9)GM@m zJG9p>5}dmU@?BxOXzmRbMM!sNlP~2-Hm&Xiv{PqVX0X_3sJx`|V5Zxh$TXdnw_E{KxkFFIZ~U-VQwZ|>9jWW&l+zBB?5TL{YrB4s2hWHTjl z>QBeyp!njgIv|dk8Or1pUiA&@+vFRss#9Ah(?V5@gd7!j*&3jV@c|e37fUi=5-k=f zQDNn6^*-!Z=>V0V2Uw(-{dW}SCW~M#wKaK)9aOK`Nq)EC{XPC{VBcD}2kU9|er%`G z8}E0Q1xEjU^BMZx*F)}JpW{^ejQdG!Fm45+4}d=CPx}LUW$B3z(3~29!Gb1$9zlyIRT@Nz zXSOYhZikmV`#IWzhBEsh0qvjK;k~($8F3_0IUbqNoERe`L+<77__Y+{C^~r{b};IH zTiYWHiMJAiWB5^>=Co^sWb6ZRGhQ)`*+G~`#8pAhXv}O=(Jq_WYDS&7-F+}+|os8HE4wW0UHvIz?ZKcLt_4losvXWQKj4!iBPnLU+!jLdt|EO zEtibxdC!+PQi2L^26P}LBJA?UiYZhkoY3>!X6;H1r39Qd@h-mB3X2JxOdkmtxu-5-z?5RbgycsCB^U=Ri%%XS(Mk;D62l;A459YBB8Mi;##MP5 zbf>nH#NxM=e)vKpVe$1ScEV;Y(;u*!PZ_TmS|@q>`RgiLYB>Arq9Gr;k}>))nPx(c z1v7bv&7>J%UBAp_`)otw68Mb^&P^)($vE-@aaig3 zYRO)jq6+~_F)X#4qJ7mC9mOYNGB+l!=TySXH}N@1T(MOaHb>Vid&bCft3vmJ8W~7D zC*ne5yp~*-!BHljFfz%tXd!3HEp|oo;tfVTb^WGYvH~#bW0@F@IV#} zr@CtO(3b* z!|~Z3r#gHkEr&Y%Pp@Gvt8#Sp>}+oVt+Q#E&bD4!TgU3Nl=dhh&#ggeXE(*Cm6`wH zG3S^sO3AtaEtTx`Tq^Oy83;+*8ExtENRe)Pt47$mYppAFy*wM`$cBeiYlEzG1ikaB zex7QxZ^xCUy3Pr*MmNYB`K29X)zvlzWYrcj_2+x{rEHM3K<&0)DwZV?@pc&YIbJdL zV<*f`v+gqqam(bN?H=e<^Uk@JTyqZoZsK3AmTA|*o!R9ZGF>pgY%Ta9{X*y@#pkEq zv{S3weYgC}ZKea_49|-6l(pGQ=C5?Y9+aHZB)`WD$->iN1kBFV(?+#fhX0&RKcv7< zhZV*JWFfMjeW!T*`9iqrm74HPCFnJ7rW532O(obR9b4_F;t!CHyGHtZ?HlCtX|MZw z`~B0a87W0HuNGf45i8#sJ`?u~>C5r-HzND|xb`Vi2{ZG;{~YJ}ZGTeh{?U7A^D2JK zrvw7nBXs6loO=XW9V@9^Oq;qW|C!~X^uqrIkiY_~&b*7Q@*y3l5wd3cC&u7)klQx$ z72`BP_En(Jng4l{P7V1Q1p4Mud}H2YB8cD z2O0)=wfPdY{ZjZdC1OuIplm{)%{}e<*Xs z#M@El5zjhn6Zi|OZWkdy)Z2L`A~}a=&ig!=mM}FiyiszUM0YJtK{JkEE%rDinixW$ zk_7LR^c@0J-7136$h6bqKpTd*U+|$ivJOk&s8flus#6UoOTDU7B?pa#LKT%sh%8t* z+9)3PZkOzgS&nr8*-(Nb%kN~9yzJJ}`K=&(@&{@oZFhy-w~quo$G_|vBkU-NEI-a{ zVhC$sMHG-54o1K+r)z6M`!97Q_+vFdsSBp|bQa{c$CH=_$gc_|lk1PHI^yV=PPO zS;bZH^Rb{6a$H2Z!`MXVw>QVruTH%dWm|xcz+gM2m{se;1saStAJ8X-xn=>AGbYGb z#Q2c5gxN6h69LU$_(F7{;@J$HCBh@g&qwM)1xn9aj$S*|=!X=SR)Ri?22r02&9a7{!VBay$`V*cg>kE}b2D zZhF|YX+%NJ=gthwl8kVB;aaW}ef|34{M3+iP-e!wbLxt6;17&UyFF{7R!ccs#z3+H zekFXnduIXrL1>IxuBD}x+L`&%(@HQlLw7=sK4Rl0b(DNW^F|h{9dK?tr8pe6lWx`Z zjLW`dR@F!%rjV6m!;`1r3OF;QL&pa3Ty_#SUX)vcF+mZW;rMsk3hj?b_V3pg`6~$+ zohaK+5M)0fgGPgVA}G+JIK$FURnDglH!sz9Mgpt93s)ZuI6ES@x`|FHvK92eyWx-n;$c3qBXw)HfX~U{^%rFm)Q4lxo<7WYEEBlf{ zM17bT2I%|~m`DaG-5u7&IvK(a?;d;YmAa^qRpQ`ke)>%bv3jxC3(W~WQZa;6MeAk6 zm|qpVf6Nr%El)D|D|I=zQyPtZNvjvXfZ^>~*QY?148J|!K8D>%-)G%l$j!gk1)ZPp z&A-bx{3l}lj>+=wfLy2%K39w&sJ``@K~-B;ZQv?&Bg_yhUVV?n@+W??3eP?Ae5$4e z(n&KFGar{7O>xd--PhX8XwsdFHmU)xZ$S(#q5}#jO>ji^q;))`NIgscE=zhe$X+U& zBxy6%aPe{Wj*H=I5cX73RHqVO9Q_%88E^0}-%(*A!D)%}4liJLG#~c8gADra*Q!Ca zZ9Oc66D?t8XU&frb<5?i?bsU2<_RgETV8bB`BSS{RbC}tU z^Z2VReB<_b9DVeQ6xMRPeMGq4>lD^xdtVaFfb36?n( zb2ymo`DRasi*fLI-NI^(wYcsmc|yW3d2Gtz)ko&Aw@#&a2!g_Rq{7zaROk$NvK76h zpEOk-0M69*0BpVC#gB$!1f@(y)7rx?2~cS2H1~a6cpoN#i(Ljd*Bkh?!0~gC{|ywB zTH92!Aayx*_$yOiNqZP^_dHD7#?QjX!}pi>`;iu^DrZmSYqGrleQ?{QobDrPMJ3lG zf2GTezm`7mQq8xg-^)jM!`Ct9$MmVc+dPZvcp?_*+)u>q)w$@+!rXzf&Gk&Xl=RK3 zSpMZmkrh@Np*FY$W0G5|kMsNz%qNzSad`9R2mxsiwPfnJ?FG5m@t(WKN z-we8sPFm6i_izMU`J#VPDbhw7r>WHIY_ux5_p1RS-Ph0o9B})Tk$wT0)WGrgF9^+^V!z~46CttT1*zsS~Wh8#F4w7l42Rk7>mhLmVnq;94ROUX`J!rtRHKe1FD4L#38=5S4^`+ zo(2R%!i2pZoCp9`dtcXVSzkGoO+15X9qOf++EnC+T|qlbQYs~)&g*x1LECP9>_dX@ z0wXXeFx(3v)F?c+YE2v$z0K^OS`m8A9hE~DM@L)(f|<8PQH1!9SyQv7s4-Id3%|5H zt-?ZWO$jgz`?9X4UKMzUBK!FejoiNk{M`nJai#b)Tx9YPpJSE78eYf(pE-0I&jDl-@1m&il>2F}Z3Ko~0*&MD~lUu+>*Ux@=ovQw0vmP1R9G+`(6PB+d!Xv;#RiF|`4M zrA2c2p%l|>%q}4J($#YjLz9=zvnOf&Cx*pUZvGges#7xV1pO8EB7D;a(0|?eDYDX~ z`V}q_uaxU+tx$;=y+X?mQy@1x`}P640Py9gKo-?&L~$Xm{m0H52HCcS>FE>9cz_u; zQ|4K%v#SaZTmh_7KD0?brxSAmo~FUH2{;Pa~oBL({z zh3VX=%?1}kAnzx0Pr-$==Q;Mv$PU`STDky;Q2eLOD$ot~zdCv|?s3U#i$ul5>7NpU zI6rCDhzd@MXfmQh!rW;2MgW@FE|;G+tAG8}mGv>ALYV)Fz>b3y<2EU&YJq+m5VHtt z(@&iBD80#LMWwaM8k)adOq)4RHIdsu(Q327uhFyKM9sr-UHz*j>}qL z68QjeIH8Y}Z&)mz1mL_>Qm+(GKOjS|F3jyLd|Z5>~tp-Xc#*E(v~VKpyGeWE_i{aBFgQAOKIkw$i_3vfTPtziguLaSGT z=Tpws2kRq8>36kKUO~!6r-hPlx@mFZGRM-P=+g*%xZ;=(gy}bq?ee zd-RG;Q))HJ!`s8ux=n6_mXUWsI@NUwusSrE6FxzkpdXiZYHAyQeF0^nEfeCUm&>G_$`}a(6{7o|9R=h{^sM2 zGE>{t6q?j=IFe>U%%LsX?`n)pvQNM>rFPTy0?Y2^a{9yyCJi}F@Oqc?teVknk;dra ztP;fMsM@O=L62r-0+X>Dl~!j*)7{nb&e&Qod}dgayw|(Fu+IBt=Atk@s(A&PzdnbA zt!{J%R|H-SH)}6B`MmkX_Q$IR-l*jEG?_)Z$P^u7es~AEK$Z*JYa=?wp=qXD#fh)% zQqj7}&dv4^(7gCccBXm>^A8_#It=2+J%H>nRXHv`!gq5{ru9{?{i)V`$B{9omF4ZL z)h4cU=jycf*un*qp(57UT$q^(aKUYmDNm7M8fYfPZ^aaOWf;aS6Bs<~`gw%M)p4r` zwu>c?i8CMiz4HakN^DcFDKY3vHwNiRM$+M2$1t;pt{hZ>7e&@>Ze-Vdwh$M&zD`Lm z&aQHv3;{1K$m4YG6fbo@v6@iVTf8PaoJflH4=E6or3W@ota&OJ#+9mh$U5Q>u8gE;jr?-TEEVm$D^RUe0Sg4uSYkd ziuTw~w(bjBQZ8uu_sf?J?Gey4ZtpbDE$y+KFuiBqm+fc0Ulp70I|y*wcrR{zB)pz` z{CW6aBRMZ-ZWm_uKFG$R-r$W{6l3rfRfb*!1>??JBud?N4E+{AmpOUaFbJZ6YE38R zG+8(=I-YHKOiD|_I&dkdJ=QZQi*=qeM`I|!v`i;0ZG?g1tU~l+)AU2=8z2fj#*y1 zgX|*Lkh}nE4A%R(0+aZ-W`roAT&R94Gf^o?0)tXgR3L{QJJ4COP)?p~s!>UXYA>%O zD7q-CrF;0e*TEN-lG82>Q6X^|xI{m6p+$mChS}pc{^@eB=xh?7c@;Nft$@B)rPFqC zwVsM1xVoM$kT(AO2ZjtXIdY)8!t`!C!RV#pkC_8;#lT`1q#T0aG#E84v3O^n~NFmT@tm+oiD z*(m{m6+qw0<^y}|vwA9P-VIzhkluXG4)c++7p??8b-*pkKo;x2B3JF9+Wphnu$d9n zvr`{ljWI^gWgqcn23%M9wb`$CONESy6@Bz368kstfevBBI)|0#&NN<_br^6|@MaR^ zDb!@v0&VY=Fp{k}C?1ss7HqBH`-+Pla1&1ze!6u*8dY()(I_pq*3_Fq{}O%S81 z=e5`&kz$Qq`MLo48zarSIi1cZhvG(-oU%O!fVeV&j{89e2|u8W=3uiY@l}V0pEZg! zwxh?}^k+snyMeUnF6Aaukmdnr-@(p7%QT=`E`=7Iw|N@(W{BdPu1-x7|9sUoBEL)l z_#LkzH&D5G#qQd$v-L1`*RY$DN(Q15Haa_{I@Xg4$wb_4+Rr^3UMAS9qjr|@nwh?gA^;Q52>Pw<62mQw(=s6hFyDH3@e_8Gp2L3S=`qwJ-IJ>enOxOjS+-km#^%&r$K6kRye1bbCff zE~u~eMM!p!#8K9LgO^eVsI#R>aCOjDfYX=#y>IxU!s%Ul`dK=JNV8iOI7v}?Di8gl zn%;2g#+w9;YvrDhC+Xn!U~2AxdAeP_JUtSiHg}PV@w(e_Utw3Io#>i{r0ix7<8ifq zu8BRViq-prAm9G4DflW=E#p|MNdq~_U=<*XB9b~>TCdb=I6(?Fm%c=y#rgOn%f9-{ zJkCMEooM}LNJQ;e&G1FxP#pENlo{{y^Y$sP=2m|+i?g{`I;hKp|zJA|)Na|G1$ zh;x(@KHy9@ioU?I^X(q&HVr@KrMzC&5F>WStAW)Z_i8lHJaWQNzM(7eN59ncvJ z6txFH{_w60duy}Z=KT142K##lsQ-9ZsrY6lqV_IXBN(;0rIa*H(OA^JseuLxj{l>` zx#t^L?>M2^%~>3->{ZnbD(?EEbxL^aCt2A+_nXRC@BiWJx?h=2E{7aJDHuq$R)74h zAsLb83ZLO|3;a4i`RGo;_}yFBP!Rv?ghp1q8YygE%ldAzl}>%ESE_$(mC6-k+2|%| zN>1Ksl__3wo%u1sqx7fcBsFzS8AXKaWyGalL zQ?{4#q80eOVfYRxd@te&4kLMyAOm(t3)e+%^uBA9YMP2D9m>w;1~i9 z8ls=uu9ukxEzE5Bi95DFw41DnAv*GhYoQ`#g!x+X>>bazk?_M9BO>R=>6Ek_DUI2d z#y&Zro!$o9j?oL-a^WV{Z%Zy!-vOEoAR4a{zzlt*%7dRn4U?+3Xog`(b(Hh;N;sUx z;jz(M7xgrty>yO&7CzVLN-$KEcL=wGa4qMb6yFYCGP{s4vc=hMIT!=xihj7DFghVH zl3nY-6=M61cQp-YGRZguhVHiek}vC-za4gibt{q3l4%b22R{qt(M>7hp!Y6_W4=lO zv>bjo6%imZ^o4!R&?nmKV zWSniNGZ9>PP?Dk0Q6IMgp0cK!!j8N{lgJX7`~GK~^|gvq>suICwjepuR++R;qh_q? z1e61BBFr{thd0I(ZCW&p3lC#=UlDib&@GA?7T-t((sp>5o6>mVj;`j@-yvj$(g@SP zhyR9Mn|sC2n*$EFz3{U*?!NNThUwkl7NyvYy0<+wgq~?V)bDQ{zQ2;nh~ZN#2DsN& z#A=l`P($P;y`s=PJq(hv z<(Cg9<7qfA!&wUpVSK1|ZA8Tf8Z3R}(%_ybDkmfRVvnozp&(jAWV!~PiT(PJ9NlrQ zkAY+#S$-~GG>o2;-d|}Px)80r5#RXnP-)LfcnhVdg?{1T9ns~YkrXS@%bP$>( zvBP0OzFo)7;J|vEammxKe`w0V;jJ)x9pIf`;cCD61&K#br*SLvWCPs2L6*Cp$w4gW z_t9Fil=DTDvHB8+K2&GI({K%hZCH$btO7_>0t3X6`n(M$xI@<7tRqnVCgz&!Tyc9fz)DEw>Ti;zdyz=G@pCiIi$ea9`_W(Qcj zrTPJf+=g$n1{%KmGGnP=@BHpqE%1|b| zQ*pz?==_Xqt4UipX2~=sO_(LL|pRPI;rxfe$no9&^LT zkEaBqL__%V+t5?vT-x4gcYXO~M=rf8-ln;HG_*tY%iN<95juPyFFMG>=iTMO$Av@8 zi|4DPWsnd$)!B`Mi!VXiV~hB{UT%h066TiZb_+yc!N3s7yUb=N$q!l&0HGI{{`Y`) z{1P~AAn@-eaNXf6(@ozT?%%RS#Mz?_XGc#Ii`%Avu?JGKP@@QW-ObOBX1JO=SgBIh zKmx~qqB#$`fpo@|TzTOX>hN)ppS(s>fD>_DjtLWGy+<*hJ|Z!66jdO^pAbRuYHW=ONvIAT5^+NaQvfG8E8#ujw6HoKX?-Ub5@k>PKyl zzH*Pc&ATCs)U*0Djqkx*-yE|w6j^NIJG0U`+oU0W9DbDBI$xs4g+}u>Fegx9vJ{+= zNy7`vPMBa?ynsV40Usu1tx_x!Jwy-f8Lp*QO{DO2nv>Af7WF9fI!LuOjOF}u?=)?+ zB#5WX$Tz3}JMsu%Pf@SAclO1{>10pDvD?WhH+^Xv6r3?Bhxra8U^N2wd}a)<`@;U0`qr zar%(y5~vT$t{>V+`{mLo5LSwFCOio}OdV+Ms6f~f+>dbh>Yr?MmK+T?8Cx>WYg;sE zzHByG`yhB&a=JsQM8k~`h&EyfngJddk`Uf`E|n%+P!^$L#J4K4!k|^A0P$Hll^H_Q zv9|%VVBEK_juDC*atR7+rr~7w0~;xjo@az2{&S7cHXz3PBPv!rwS4moi?%JZ%L+6t zxLFcAV1ot4!9`3^NazKnMKB^xkOfeD#NqGBp8;%tn!mn{Y;!rbb-Ov9zi4s$G#Ig5ES66pCNquKZdygu7u9Js-#>ZX%p*w6(`3NcAK zRERU#Y-ZgltXgp}d4s@sCu^+B4nU1d72LPQ4$Si-C6-VFMz_l_Z?Q51)&(gb59TTr z)sQNWQe0A(do}b^b2wHy^qaX>vP)hg__%7L0VS2|IB58c_~8Lk&D$8I0V5oM8zqj0 zeE^65N^u?*RoQw(5QrPi#Vl$B|2ESDN87BQ*)w+-_R#C4n)G~q3i3k^v?^zBCl962 zW6$xHiZ$k*fPpB5QnH!HjJ3m-;E#zPxi zW(`M|WizVq*AIs|KipmNi#yI0N|0SuQye!r&YfkTU@?KFWjNBWd&a~TDtJ0y?w=36 z{^LWR{rJ$9TaT@sJMEADTgy!6IIEU89ZJ_<2M5IxpG6FY&m{Qtqx`u!IQH52v%*!s zu4J@j2}q4ix-&SX`J9W(Y_~UIFTZt8LDA6=GwsvFVj~ao0E^hZkf;4-!e2xBmGxa} zL_znrq~#Itmh7d%c^B>u=Y+>zewk)5%Xj4*E4u(SCVkHbSTkq zVv#i!NFt<W;Om$^ahv~v z;C+Tp5$CDoVRif}v0GWQiDSN{HE)qohkM(;(R&0}nhi=`qb@uJdkyQMkh>^TVa>0q z+G{=1ak8-N(0U<$iTYxKRA?QICi7Y>I*-r)RW477EDAOMvvwVro?s)N&bEiB6G&wZ z2XSe?x(~Yuy)p}6GF zy3|c7q>Yu?gv-7-1XpGjfGu$r5DA6GxYy62Le&u-y%-H z{uu*_xSbszf{y*Pv?42}DG=}#sRW_iL`0(bHlZRNhT(N@2k*##qMVO@IOCI}uM1K5 z8y03sM16U+f<<+=RD+n>Pu0Di(8gGnc4|bB(;8;PLHa>M1olL>i2vg_+-d$Vj>Fqw zWhZ$%2uQ`eNm8!HoALiR4##6ZDNgcX?vxRwsMH}>?J_mfIAV1WA-fQSQ~PF(`Vgp} zGXX>B`lUO7H=|_Xov^BHR%O%eafoKHFqoGa6m+BFutaJVI46UJTnv(=!plLC5X_|Y7P&10A zro|V@_}vn17A`Ne${9%Hzyl9;!^zc7>Gnx}-QhtK+HzffoY(mg4YydbQRLTE4h@>~ zCI>;|N5cv3nXlV<91V!+KwVCw3G0*!XdXEM=I_*!56Z3<^!I}ds2)&oI#X~0hK+=s zLTr06z5fE_1L|o(h}{yiL4Kz9bwwO**q`!DrkqaRzFb||i69o-K-?sW3G(C& zFS^hq(j;)O%YB$qh;-U(V2rr;LbdhR_;tgs)jDr@AFHMfF$$DxmYRiF+t~Ga!&=^1 z0a`^V&e)ge2%zCfrPn%1^Azcw+bSOYkWn~A!~&l zkPIHfH+uY2Hc*m##e^EMi^Fj(j&P4gO_3j5qV8<}m}VIClRiijp}HIkzFX-Z_~glY z#?blxP^gh_E_)fG8%|idcvRuHnebAjaNquz;PH|62Lb0D;KW4Wt+Ngod;EXIS2eVw zc9d`qQOUuKQKZ0qAWzTEs9v_XK$&FK{n)Y(?o%zJYZE-;)!P+JD;V^J)|Iw~DTx!Q zmH<(HS8e@q2*gfR5-NI;kL%+j60;tE73)q z&PpIIGY5q5cJN}ziu>Y$4e#%ftOi23$3*ZBJcrfGQY+T(;`>H3=rtnYbHHNmR!_+| z3*LgEwvJWJv@>p+EDyVVEHOJq&7DUXxW^Q`4{!(V;07uh(BZG zSE|47)5EF3gU6yaHJ2%UJe_Rxu0f7(zr1WWR9hkCnXdg*hA3P^TyTBrV;9J?Wb%mptUQLGw?` zstuz`{G|`ZNY)za;{5x>rKkaFjgm!h%%7bPW8mZ%f_&>`(1U74P)W}~=A`H}CfsHK z9hA?gTU5@F=Yr{;xzULUwOyK{LQYbU+7F80e0c&LqabSg!B+t#+I8hO`b4+4;QOb< z^a3nu&V}i8lYm+O-Q#mtho+iP2By_*=%12TszzSm%*+JOt@%6fKG*U(pPZ~a&n-Mo zZdp5^^95Ve> zQW7^x30b2-_4HRVm>RbE(xM+XX>s*gt*AO}Gv{sQ3U+j+59P+F=zbT`@7&Q>q3C@` z%lmDJD~_1nNToSM$Hvtfx!{acVw%ybi4j~wiazm%35qL!xDEv}M}1rz-XHJsez`4X zn&}H8)hQ!HLP%_9A-oE_D^2m~xJ{U@YQ)k~19pO&VVg%L{&Dwz8tK(vv~|6*9zIFS6_=$3EZlYfnbO3id1#;Plw?_|Fhx5xq-5+swi~e>$jk&>KSU%XQ zEPL4LP=?PE)6)`A0WUZrYac{GCT~Xo4)4YNxx&}B#?H9sHnaFl*P){K5IxokQEtQO zh1f1luo+tn1MAlzap+N}fv~on)U~kf=gYk}%*e}IW`C&}_b=$o0knNV+4c{pVU=BW zgw`V1@JSjZ%dc@{ajDT#?NclS>lWz03-UrQD)10e@fz7~idu{4q3n%l5gvHMG5IRz z%|pv}Wfz3j%+MvHk+Sqr(Pilc@q~^e#w<`P(llN8OhTui&}*GCD=Idy%uGz`M|cFc zWfkc>ZFUZzgJD6jw)tqE2eAyvI&;y7#b5bI*S|~EOQ8@(t<=uDo+Bl z0ra_!<;TCRJ~5LUA{lqYsX=>d#y?tNwf}eCy)_G=JA3#UDu~{Zowf#Xeezg2A!p{L zx;@b6V_5jH{dwHW{~C+v_t@GCJNJD5pXH;M$4VJjMZTI1Y6Wh5?FLB%uonaBLA=#( z3qFr`Gpf|+S*hMu`BCm2Jh?wCMbEli+3(OP_}F>*CnpyVgYM|X_!;q3T%As}23Mnv8pyWva+Vf9XW=?s6qaBSeEZ#C0T$H$vFgnO+pG z)7OW`_KuuW#YM?Nm`N8Pfui>pmXBWP81Z#Ua`ci@_j`=WQ)doPjc&Y+qm4XqVNWk z-Wt_M1^pZ@TZ#maL&*{1&VFd~^A~aEo(MJPr32(@VJi9fe`4-km&`wkc zHnIz<2v+NcKa%~4v`xcM&`4TNANv40uBD@E#+mW38=$A`?VK(RjRO^k_vAvrd(_!M zj?#CMIXct<^rs7=StjRlOH8GORTpC` z10G~X(^XMUZE*6E=Mr@GHc!3QYN$lS`qVaRo~zDAic=Zf`Vjn~L5y*_Wo`g)?u@># z*h2!_c3s$GGXEh395TngZmH-IrvRw76rny(o5VLX=QCb6|EbbDMzK$mIvHE7s#t%d zKE;%*$(|JGG#7$rGo=P(Ih8>6_)K;pIUl0@0D4)`pkRiDVZE((r-bI1lHe{P-`1mg z+Xz)0gVdRpykNz&)FUZjJMDBTS2>V%1k~T8TeIHgia#hM8)S-O#8@L;M z927Gt!Be-KC`+_}ig@10eTx(Nly%=r9-xUEc-%2;Im(j42*@vJ^9JECr+%Ac2bmZa zj{Dl+@_CzoJ4E8b0+nR$eMH}vT9}l88jzgrOs=|M^hbWu{qW^1q zD6Mk}IqxD`24(@yjxy-q@(|!5)peSJ|48tnMW3b3p(0g~jLdm}A9mHO35G6hpK_^~Pd^vCwoya#`k z^!!P{d-N0Hs9i(0xMzj2+R}3psPgQATsAmn0kJ4!w%99ne6YHXZ*}AGE64XJp4X*C z3WZw5apfYCi|uvxWV2U)M|C|?;x|y9Ny*7qnS$tKZ+JlR3FEbqg5iU8s~dN}T1UnE zT(3G_jIDB+ycMJVE=?k2Q_{xn33KjYt^lgnPanqbVM+Jeso~P}cJ`ds)w^$Q{d1Rb z?WyOio^5lyJPwsq>Sv{%S5@B*WLhJTPOWgnS)p#FuNKHext5Ve?qu0OzZqQbVeZX7 z`)i&)=$S^VwA}>c+cr)xzXd5N(gr~+_?e?TR71VysBXG z1Fupz)he3wzVds?v+#-S7NF0{-o+ftk#GI!796=POgNDCzH&?@^y!wNs|H_Tx@>U{ zIbPl|eBH>1I^>TUf_$oEVt&rB-Sh(2SF>^e#_{L8CiLbA1#u$ZjHD;pHuG4BXYpPJ zQ)n;xtM9AiRMNUTj>Mmz9+nE;8g&V; zNyvBSpOaAExsY>XWDI4V&g*3Jl+yJNNr-gJ=$$Bu6Tm+N=1k04979<>V_}jbRz9;U zZc>xX@*Ys!U*P09yiqZ%hLh*yQ9bJ+)o&phHNDq_M7&Zc&3;*RDBkGqu{ykTep47^ zl%m`qls}fA_1t(O@&jF4&(bcr7c*||) zl0AcIiC{WG@G;KK<)3RsSR-XC{*!ZLv*R-^}<1b%A3Hf{*9MB@a( z`;!MAC*=ySABDKZpm!8;r2}u!MtE)Nh6`^O?~cd8gYf6Awhh48^LdXaY|fcw7HyAx zOZX4ox0xElRNK*xu5~%84Oj+^N6-i3ikQR$)o*(|eHF)Ei|C_hJa zO6MpWUd~ZOr4(8}n-pgk%>@$sp9y4Mx;UyMUH|#rH7W;{;pCG$un#Ej(g;$cALElm zZ9AcD2Vm>cMjKz%JmxCpT2McK3sUHfK%VW`v>DlCd(|%%x*O{m8h=Qik=IJ%qD}cN zlf9vqexp9|sesZ-2p+|?3h%&qa{v7VaHMHyN{Cs$J_w~+-2<#Cj((4?yPx9e3;x@- z3g$?>=8%}Z+|Xhh@z*YUM4Ct%bA7I8x@z8JB)|3aEW}n(11V1R0{y>|*{+ByrpEUZ!62PYL zMBu807_+tH&>c_OJa6*GKye>_p{5V}-4|vp*KvBo38N-PTSLro{78Uq8U^8q=+zBz zj2BjR-Mb3X3QFWqQm7$27FV~>WG?%!Lm$6@O|Cy^;p5#RF?VxGfLa>-J>F;2Fe>k+ z1Q-rEx@clOIiUK#O)A!y6egx`=hshbKh$o9ow&uT-=d{SKqhYYqkXUYfiR``QK$$jSLs`&MRXuV|HmGDL z`*%WI+XSsdqqj^LWu`d5!S(G3RwRB}4r3}bMWcwau#sek4q-m7)!kJZPi~1`WM^!< zZw|~mFPBBd_fCHcQLi9VFi^_bBG$D57GP55_%%cQGQu5@56s8(uNSf>hm7MU(SP3W zQ!G#BFPUE6sc!ANO!drU4UvTciMW$_i`yZ)Y8l8cpX#GhjPJyy~tpj=9@2h)>_Zq|&8n(7j$>Xd|i zn_~r~ZR+qY0=Q6oH81XDRDHA6-!t#FF&5qZ_o$ER<4f^OlV?-4bZ`=^AH>QySI;KG zjOK(fYKRl=h%<7_#BYRs!+q!6jyt-+A##8aBg|s$DvUCpysX)`Zz?LqlB^^v)ItHR z+W(;%HHRbsw&Wnq)M`+-VK{mIt5T~Cx78`4y(UT<$3uSTsdqRbufW1Jz7=@>B~oMp zTndMm!Xf2RRH-?#JwthX8JIKkr4!0d5LL+m_b`xh(&LC%(s$R`hAqS&h7{K#b3x|Y z%4cJ(yD9MZjw$az36?hP$jl2@ZuL*urT9Xbcl)pUuxgS~Sy2a2qpsz7=ywk70l~hh z)O?fEvf`GunbpuF#`Kvqw!xknwr%H4+qP}nwr%UCZQHK=-+R@lQJpxa6|Hk(#JAU)d%mAs zT%G=H!7L;rxfACq)O5owRcr6)2xyzbE{y1e8>^SwptKP~?%gN8kV7O6OC(n9K71`cAD!6u zjE4DsTdw~iGqkGQwQ9|)OjJK8464{=^BI^}kS=?#{Heyc+@JUm`~o~fRYv6n-Tc@(ZdV&%!GU(L=G zDN%GjOLQPlD%sm*CCu@QO!SX_cdDJVx9p0ApR4w9=6k?gZxV(X7bAaCF~Ce=M}cQL zIM>Y;LARo`#N36y_Ax8us)G8C_~o!u4jO$>>xjMcI(N}{6JsnNLmiy9su;~l#~bC> zsBU|BJ?fxf&@VSr0|WE+F|t8VY;~V>g(d?|Uq6y}xg3&6ZVEw06iSSMV%;5Xm7Wnn)h>#H9~ zll%{F*ry>Wq<`-28mgF4H3HZLi6nfE3rYSxdI!Q#52gjxsbzqa=!_W9lT6njfqc03 zGpm*xZ9wF;zh`d(oD!5~9q-s#I|q+VW;ln1A@CYghW=PG+xO!NtRn!L)6x^2R)Sy9 zd|Kgh{jZ7BuFFrQr&EUMX5(Oybdo%Xt`3I)e%hUu`b}nBAf7<#2Z|5}2HFF-JzOWr7EdmGIDwDCrqs zRGRo1vO|f&x_Z5~-Kpw%Cw9v6pY?=~wn*c1lb5JpI-|xy8{N*OUJ#^)`d1ePca__o zOni+lq!=~(CA+}NBiX3f&N*K*AG-re03tF6pO*^h?vvCx%Jds{kON}0vW@t`Oo0qu!38ynR|&k$ii_P z>wX$Cr2<3zHoxB7IOl$Aok~nX3%FQ19tM1|q*~v2BMR_`U2!0Amk+S>513BnHQYx( zX<{;_ld{qaa7|8DOFi^V8-;*+vnSxyh>=NikLUdq{9X$akz7*Bi`zh`|4;XqdwNAu z=;6ZFs{%oIzB&ZxM88td5AMvD9Y@FqkCsd$O6AY+q!T%}PRL;@PfkrjQTs$5Bm@2U zu=3;R9$uEXS`B&3Pys!5v1Dih7u1VD&9bA*OmkI+USo*?>Lh6c6$#IPn;#!}(5i{N z9VUu4Sa2;_V1U8>q~a=^7s1*NH;xmSnpeb|7lkMVu$7QdW!5rPvZf!>-18t#+PGcF zTdhT1j~foq16S2eT~80_e@aeUTeu58jO7!+wv@eA_cqb2cSQHhxk^k=^SC?5O?FvU z=xLMKm`dNbl%fPkAuC!!D&@z5Tl3w&c=Fw=i&5vLjx0bW#v#5AoDLJj}hje`^e# zhaXn#4E9#WxjUp%E(Ozzio*Y<=w1EalAtUIc{$6&?>*40Fuj!Sw-T5n`54-kvJp4l zNXtU>!022WCO&~g(@Rd+#>YU-ed|MH^^~aev)@alvOD2Q8&x zpr?c1ghS0E{#AoQZm@y(FQI`fniq(05GjfL4DSG*8KU;n)=V3AMeNX5xO`6Dx52P5 zs0o;R$+;!5%Ba0>Z7PhQ(aoT~Z~Q%u@O50B+Rt)UjP5%vW=>z=?gw@cX)?H>pur(& zGD;$FB?8ES(9Rk{;sAb=>EZDacmkY?sE-U@_KSJnH!+OVDK3msUIsdfbb zGb0uG1jKac4+KGoAc;r7s6NJPCxQ^4$H8#VfL z8fL3jy{OO%?HF)#Unhsi@Nn~!gJiBNN$fXuF8=;051plOJ78ypau#hW%V- zO#kLv5fj}%4bJEn&b%L2+0MK_KdJ9bRj5(}XL=d=Nj4c9z2?@b1e=I%hnLKKCe7HO zorxzP{;Tp?pUaqUFTOZqv`$Kc;&S))a=Dv{(Cm&mxImCfGD!Jpj58tc87CvPq~{CM zG5DG)2MG3Np}G}_4MVU^>UmTo5M)u0eCwlF&o0=aSby;=Gu-eM>9k+zRK$Ax-?H6a z#C)eNf7H+Rtw=S~Gp1Tk`Y?{>G42|(X-@)A6#@4OXi+9JdK|6V9?-%cnoUp5*IU3d z)|N{o66T-%?(v3D6~hK01#fdfLg-dqL=}^beT9+o2v!7vj*XOwKx$)Trh4wN?S}Ad z?HRr|e3-^F-Ph+k5;$~08u^2*(Zhk_uBA|mHFe3Bsd{k32aj)WvA0(2_e+lQ4J#_y z^J@d>n8jDMxDJ`GIqCBd44HBjHJ!!k9I5M8p7e!L z3Gz-7>Yw$?7XNW06stw*RRF}Td)yaDX5LqN;C<6)G3g1gQkWCafy^BeMBTB8iYegc zuofR89NuC(UpJz$c3;3?}$_6iAc*If|SfhN(F#K$g zR-`fgm+Im@_%j4xy>E#oJtb{Z%D0aB>)2~;@y5jA!KJIwxE6G>=IZ_(TD>{Chg$0d zjx|+MR8n_|d>)nov!epc&yUrHa?{2I7(yVFirsWgf~3b`@+vvWTra*6>qn{;sC(S3 z?~~}&G-nab_B37fq>5FjV)OVK#M?fo%jmsw8u-{Nn;hC_Prd>sKGH<@nNCsIEYi#L zn9fjcEoXUYo6GXL+}C5Bw$ko4;!R3XShM%eGK7kN61z0luhNm375FWH)V}==Q3%1r)+Jmj;aY+ z6L|@ue-=h?P1SWV8hE)KH+hM!vzDBDmoEK%we}laf8ysc{9rA;7*(IpoG~a9 zr4@W>y#s8*y%jZX&%!d|xahFtxX5_Jo1KCK&n)t(2?fh#3^7yazj4;sf8(s*qxabS zz(_&okf!~phjp%`JRl{cBVN$cC84Tr3~M}^<$QpSusXM$(=Zw#6?FmSvInf`JPJ?n z!3&1S-oSn5dd%Rx_2nj*0@4TsJCpqsZ5Bnc<9>PeeH7V|;4)9aa`CkF^N{ov#-Hx-d}#5Wq#uKd6TVW4$Mvv!*CVsVcGdQf4}DwH zzB^%mKqFw`@!1yTG#sU}S<_o1I?OKVie^r%?ZA-fF`jruEIDs^~J%@xg8RnWLecREn5dh6o9RIFTTdV%}Zt1ha2sF@mw zYE~n`+U{nHhBd3WvZPGp*BNwke($bB3HnM-oG&C8&WTpvCbRz-b~N#7a5tVuBMT*_ z5-=Auh)yDNAHJ7!+}>Ni4jH~Yvs{7@A}k3cZX8%31JA@aSc2jD<<%v}lEOJTxrWn4 zYPS8H8lfq$f+W?#e_Pq)>Tbb&@7$$y&_WBe6%Arx{%pEc^G_QR2Do2i9~3i# zjVtRV!7TW(>xxtqws(3fK0%&Gh;jTMt5*KPF_sjaB z?D@WTQ5{8@@d6&JLhyX3l2})o-1K;-ipw2I%`}#-9e3N%qO=+r1;SR5U@SPUq#<$Y zvT)(ft*cHs*m=UbR-r~XG4|}omHiAQ5&|e(%ky%B>X{X`(TkzvOIOu+BT|>P6W8Z! zq98s=binL4jc%uqI zzt6Ns8lJJpi)6(4l%UMX%8020J5nP=2O|&325%pjTdFtU`Z^^QW7Lv9e<50+%;A4{ zmPE}U{~QG#>-L_qk6?Hjim^dRmUwSJn@ZjWD||R2OsshQM>~r3U;;TNd-}Djfofd8Ky6`trKa zKP}{8oimTouSi|%s#>jMyEE6MSDV&m3&z&u)u_;vyadVW9k&~(+y~Y<#vXN=4@Um4 z)Y`>fI3d*%e;}!txXdx%A^A!y{u5+!s zvK(Qoa}KGP zO%smP-29igJaCpCeg4hZTokpEtar6~OXe-xWZvQui7)}bZk?4$g<^&Wf+P{pb8&On;4e7c zr6G;O-DZhGi+EMO)<SczxFD7sA z8HwcQMU3=L@hSBstscaWx_q40SekQyhw_@CZtx4!^IgfE`74_tT{1^lDPT@mUC&$DZz%h90if~@*Q@*#!Q&FD(=*L8cJhr~0 zt+Z=~h)PRgI=A?>KJi`*Qsizbb2h@Gzx|3}%kkA9t#)m>$%)!x!jgvH1KhJH?JUeU zb)P)ca&84RSeHFlMj!IQ{D|LSF1*^>@CjV;O?z>E#yp ziYt#8W$9-#iz?EPQLpc!D#SwwcFT77Z?gy?FVbL@S`lp$NAJ$})xpigT>K_6M9dg1 zJmP&zecgArz$kX5+&!2{MP$B4-2j7CEF|4fZ0XA&>3MO4w5RmRc1;XtijaPbc=17O`z_ zae0^2HZt&A=xSI`IyF7gi#g_7yJ>SX2R!d@(lj`3py%C}`y3ku6C>dCo3GI_MEzAtE{cGjYcJ`SMs*h~@L7B7X3H zC6CYMd%?=-N6j_e1@tWx>Vmqzhf=Ch93O_d z3ms?~G0bDgVtjWlwN55(+4K8()qZI&?UvX=WAq#_VcZECGXocIZ-eK<0yYFwr31#| z{SK)$gxFlWePO?6+fJtBu<5FYehWTe!pT+W1RXVF=Pm5!;63nvg>jtkq``ZpFMX%+ z=Jb9-z0%Mke<|dU`npgVm^!@g>P^O!%seqbjs@}#CQBp`r5ObQs98!`1D?+g0fgC} zSutRuqZS2@kd&B#T(O5+qG6xuw}-p3=HmcYI>8KAitb3JKPrI=@q;@hgpGZ;4;EeS zq(W<1-l5R0Ud+%yc?;;Yx4&zV=TkU_*sZ*XNyr_-KWj9tF+`kIy*grtp7a*mk}79& zc9LMYYaXdz9;#cnbM`em z>%!|TxZ)Bfia6ks=6XoTOvTjt?XeeI%COV_iZN(k;T%?IPtVJHwg+-&G*F5nZFHsk ziSujcaVEy;vRlcg%ad8D5l?v68c8hqyw*@SLF*1GT7?^D^*J<~*MWkXi(ZgfyYGk` znDq2{{Z*$j+5D)o_W+Mw-2SJ*{!n>qI=sG{x0L|i;;bJ8z`-+)m%l*mDiC=e3=!Ng zFI`EZ7R(C3$0#v9naT3HOp&P%)mso$j8cJ~A*kYpQX$Y_NnM^blVDQ*R_rP<^TOd; z0(s>*K(uGs>l4eN5Sw>POJ|>HR)4QOI+`2u#xm2##T8)V=LO9L+d=O(Oydu_s*#l1 z(`-j2(CKtYd0$JW1>lm72GC%}eA(sn{rn}>pno-8N#IBWAFSh6p;h)0L=}8QEHHxUYzhD|Ht^J-bcyWZm=qiW-f0ia4EKH5F_WT|Et6et@N{ zT$B;Mg{vRN4>?{HiNPrv2O;#6kO^?gUvKy&1TU%X3c|W-8B@{SpKE>5SN@wN~Y|Bo6&aK16X{qE`Jru|yNT4jzv-I0ayw z*hV>@E0y}kv4Lx~>&CtNNIsw#Fh?S?Vh>jHy6G&oF&ONIyJ7xBV6eGXzhKLITw&*9 z5{(;ACX1DP>`yMiSyhM_9$knZS*TYL!3tR2m{Fq!KBdE~H|qnkEIH}RfVsf>Y7sBY zZnqzN%@bB)43FeB&w3= z?H=9#ZL8e#_2;HLpie8n1b@ibVrW>jW8WXOV-K%rqZCy6I>idJ@6D^X7f|89oK&~g z?`sk<`)R9uqW+R+Rjt&Y|6TZS2-dJ^UFw+-n|hEdCl}LdlhmVNP$@zrYqkZnwww>f zM+cfymfmRW{cpfxvPJNa-+dQU+j0*^zXTO^RN!~)zb(i7!GU+Iq?Td3R)yNoBD&=q zUJYb#vx1_nNdl+?#}gE_Ut1Z+#-?*&x+hv#&sdKf{iB~sg=5a2;A9*`LUZuL) z$cL!;7W7GY4@Vg^YWiuiABhs74pY*wJRc9lw7`Jx8)BrT8NPB%HB~+MePR-a&;t0) z9AnzqS@AZdi$*P+K%p6MSIv^s1X`=Usd;e5+O_p|MZ_QpM0kgOXN@cG(>$RXUN0wk zw}itBYD%pM$SvJ_@^-B^aJUwEqUws+b6cwg9d=TU=bIDBc!sa6ULiyz!c>Adj1=tf z%{#MGTF&8%SK9Ylfr{~QbYs+-VURrcSRGg|y9L~Ek#bm@P>2LowlTg6thFlHl4E^* zzDE!PfqZ814`HXw`0;9#{d!DpT-QsjUVbbWIY(mppuk~R+;&g?$^$u-r+-+uJ+16N zbc2ty;^Y!?gz4noY-hq!=E|U8u!iv?o5?OH$4oTo|0OrVN$c0}&GxlstkP?69y|BC z&*6)5K&k50L>FQ2VZ9ZyRuw2l-8D24v8Tg!@a1k1y+nM|LCTn{CRH+9*~+^806H*q>K7m?1<}o9qb6ARG{5FdbJoX*Q^W*2ipdXw-WX zYClMbf`_q5GY!Slji+Sn`+4lqA+P?MN;(^gMUb}J5aOdC&;LIYC#TN>DbG08aTM(Q z7p)8)tcA~?MZAB{aYyrEp27}=2WvTFvR_>US=e5b<-->3F!sqKO#%wn1>TBj{=I+6 z0OB$IRBYcHO}&OBQFIO89J;yKmU!*dgjB-2rfAQ+Dn*HVTsKTkH)YaayOOH zGs9hWhi8Hme;$^M=*;-SY~{wKlB2rlVw<%4uZdH!%gCI%*iAyaHQ2%z@xbx_4{>me z9RWTLS77#XZY+A$5}@YH(D$w$F%5-^=GPbWG@?PXm?@m8Yz}HAOCn3hAv^a%KkX~| z)CL(GOqgLVg&+r0v0HnCZpt)B2mCwf`9lqJ(0Q#k#rItJyZN9Hv?_a3K)xJ_46{Hb zZQK*wJHX&rjNjo}EPx_nVNl=&m;5zSW=%_lF?HfYbC+fFj~e~aYf|D2z$B?DO6bvV_mGDy)K$RJUdn2Oj;b*Nh;E^C zp!5m|R2%j)np&8-n2POk0=Cm(P*1MiVCE*vpV}DQY z3o~3>dadF?9juAa*02i{c&Xi~cBK;gC3gStF$$bXaA19nfPrAC?Ht;gdx_{)$)#AF z>Bb*w>zMb;B`*H8MU3gy|JI9useya_2e5=zdLEoR_xu7Z3T``B(TImF1{k}k53St4 z2x3~V-hU8_!G917l+I`Z34oegZ_zM9EpC|W1@R=p7*BL*h!810kQ)&B*h-)mM_EzX zP4SB#KAJAlXz;ns6**91v`SuJp#da^bwi4PanR#oPqT8?{8COsBXJL`sgI!-LEH%S zu-F7pA|B5rv=S!9AD_vjlBfv4Kt&B62W*a97^xNLS=1z65mu0A59y=joq7}WcUDnq z(%07Hkq7XbV20p(&nP`vXTi2O-QVOe_}Ii>c^_=%5jO3dFm~_OP?%g3jK9K_&a2c9 z>W^c`Ozb1j0i;JDoO2V8{OB8X9BrrRnf@K&v)|Zdc^Eut)(EMH7+rHsT z02Eg22C74@n*{s^6WW9(grx;etue!;vbd+a!u&10?hX78A|~)hy zxr197?tl$1$q@GYhY^l2F|Gno%?(ym_!rvPqaOeoeK>Z1OfgIzusVkwDFem4B`K<` ztlV|HKp896d0TUenbjjb>Xh6MR%gGYQVs7e&=?kIj${$5QO9s1?@Wm3=?87u!Y?!u z{-szD&iH;Q787vm{}09DrTjk>i*OwMVhxDQ|4=LfoY8xa`t13#x6C+TAEZkx{8OWw z;Xo_cU2XXLi~w*0dbz&Pb$Sd5YV6VJ+f>fhPXaa}$<3n`GOY~jN6Le*UU<+3^4t^J zE(1~dc1h((vQBS86*Whs=7B^%Ou$%YMyj*_hq3%i8CyJEHZ`zSdrGjSD-Ck5uu=d< zlc3qe^Kon^Zdb1&w4aA#ht~!ZxyTCO84Ox?s*Qj`ld)<=nC4<#BW!UEKdvUqHI4O; z0IJU3xT>ee*ZNI5t}=;D24WqDcrGOt`d+6FS&WyyD+s4v;x*X~7gY?ZbUBq+15 zL1j)!{ULVR)t>$$Q1uO@ICK5uy#A#X(+mEqg-h|Z34&A`a?jtEMi>Yf*0wtF(@H$s z4HHUPUZu?>Vm$$p*SQmY_BIQ9`F?`E+YH%we}0J2Hw$SSKtKuRx^wM`lX)3*9v)Fw zWu&<2;KqQJF{5x8E=kN)auxX7;G&*haJI9+&M1+pp+3g9OJt%StX3~c7}NOCelH46 zhSA@nUIN{(R?rG=!6&bU|9|{XMyg_BiAI$8916D=v2kRLYJ6QlvG6r}`CwhDawDHM zc&pF9ry|${nDh&yDJM|SuwxtKv0~`%1d7!8J1+ro!dO=V7;@m13M2*fkm;=A4 zr_Zw&>G~x{@{nS>Zg-3k+5q}x^)3;B5swuUJX9U;U|scJ@~2H7OGH0u%Ob6i2KaeY z9&;t<9+s+Xn94C_KjCF3DPDslp9LCpXICYyp{_d2MNX4;#{BmPy;N0s*kQ)s%4fO% z5Fm2dq2WZwe~|llXa9Z2^ajEZ-xcYF{-|r`8K^q4j_$RT-C&9(Gsepn1D?7Zt8K)`3e(_@$qbCR}q z+xnkQ)6!b#pf`UNd1NtD;&V-K*?qECU92xYmovY_Ir|HO1!z?^?#vBpC!il*Goj|3(q|&jT5mPuAKl)K7K7HMDe#MK=~}d+579WR<0a9B1}Xk3 zpg1hD)JZUBzwl8|r<>r^NYn`xIpNYeh@^EWvxyWd z0vy74sNH_F@0JYl8vej$zwVdO+}Q75&BB<=BcKil!}^~v&`l4_MdC(|ueTE-57=OO zYCjt7u?x>58v$RM;DbZF?2lG|U$XtIrlr3OlH6m-Tx^z;M@s-tj)XaL9|!Q#>ZmoQ zJ#6H`sU?M#Dj0^X{%iq9@Rf*M9DyOg(KBBNOnT-^Xy2nBH!3izF|2ghBq;YIURs#_ z@QGiaZ>8V4zV=rHns0DX$(g4ZbgXiZjGdu@@FiW7r#Uo&l^i*Oo_C)d-#2^kKR?gg zUG3IXo(NaHPZaczwi7c+;*u?w#*d^-h2oM0G3d!ftBc9!W_k$-;b;)Qo9h}DwraOm z-1KaD$GJ{6LD4_DiNz-&ZB$&T_pc|{Qd7a-efRWc;q|!r-Y56wD+dEls``km4zzoj1+cW8n8xc{8-wZ*i2j|BH$bqGsEs)~$ zhlxM2H@N|1ejI=>kk{&ot{VM_?&Z``Mh-0k7VbO!oKdty)1WYy3=+phZs8?0y%e)p zmoK3knKo%-E1pNIV(T3${7!p+c(l=_Pgn_$6yBBCF!VXk<`*8wyjYQ#I4T4C_vUx? zoobRH$48q;c9Y zGlG!9MQXzIN1N5tnz<2-)#9CZzsneG?h{IR!~y``F+vypP{g}jQCkxc6b9BDqZ-6? zf5r55(B8jpL5J~tTg<;aHLn{q|I`roVpI-W%2UPLNDI7(;e|W|xBrH_y0hD>)$m1i zo9BugVA3-g!LV`!U0p940(pD|z8!`W5=f0NAt7bfv>X}iS)l?&}P7t9EYhV*jw z_DG26IN1!mD5%h^{Yo&~8tW7efewbvljw>~NX)~+%35%yyNQ>9>(ID(NdV*)mO<~$ zNbYAN0W79-Tajv`KJ*BnllKcTZl~38 zG%-Y8ZyqjOJJ>HUDN(NF6PSkm{DALvly-ZKK+KLb9Z~R3I0;{rQ3}X#;Q{^1bd~h~ zsz}^ML3}JfAa^<6%+_Qh4swJNB6qFkFcN)ejx?_)(>GdkD}!dh--0QoK3rM2uSM~`Ag+i3I-$Ls zg%4_7(*<_ocfr3GjfOF4*YfT-B!(nR6RHWN8L$t;NjVjRpe6a3K~Pt*Rf&?RvqF}` z4!P0|bk`67RW}}zYuf)6Wo*3Ftx{F=FyUyCHWE%>8>6lncrH-Py1lMo3M{4&&8sLp zANazaRo@+c2nmaE_(E)kA-#8qgV7yI{Snbw>Dn%b1+A9oVL*ijZ&|^=>#BaINHwKy zOw26Cm@Gv{ez2XOH?3kCn4!huskM3m{{8!=@DeAp6quWlLup;Ia%es@Xv^oSWKz4- zG($kOsc6vywoPCG92)H?h&v*_(9T>FT-q7cVQ3ndR5Y7D5fS>>u;D)*Os0i(GUu@L zxbLnA1yG^vBvTy+p9xl=o8w3!)}zdVbsj^9aLx8du4qs?r9fpD59E$1_7g(%U+>oB z*?nu*shxszzHapIzOVC!9)V~H4WnyBD-VSMj=A5fIq0q<|8vMW=oFY2 z`&!yUD^7dltu8L>u@L1nPBN2{>3OLt7!YdUDz|#+65&Ni_atDDf`CJe_Ewbq&^Om1 z+K?s$6bcD5K_dpz2iC?N(E(`bY@;wy3Uj;g`6)fwDK z`>6Y;{%azm!8b+CDCHi~2}?-NR;1dl!F4^@~#%Qmr$c61Doy=`~+_z8^y*K&))RfEPvq^mo zwI~^#nZ1UK_U!9;38rqdS}#;51z+F`G-sD@OUsL$+TyGHW&GV&(99GaLUY}aj-{-| zqDo1aoRPBoRvK{pl88yjjr^)M)D)~HMC&Wa_a5FiKvpzQ9@^$)Mt|jr*q-$7E$xJG zX4c-WpasnyHjg~ehlV-Ua?gz^y70q&@6OMAgP_u(*EF&=_k+yb#b*KX5pj z8OtJ2D|4ILQ0#D@$Q8Fnk!*Q`1T3}A7cvx_9|Cl;h}ux1{;vRs-XF}J#twjNkG0K` z15WsT$SCvUgz%AVdRX%#dI_@!14YhEc#g=K<3giLJRYN;BWvC%vpG7#z!qaf)qD=W zQOk7Kam-SDq7@%uxHOl_dG;2>$iU9Od+){J62ZPH`#P;Bh0P`#C|oJ3Z+`gIQuG9@ zFW?fOz}0R3|0ZEMXD?>ZiylDV9$o{Q4&AMpH77EgNsO0Py^%F6g*J<{x^%Egy$&WNvT-OD*_G*Zh6+pEWF5|`?*+q^DTLZ` zsI9^0B786njO9aF;O?c8xrs{q5ba@tJ4%}MjW@_*jN?W>;TkfEiA0Q1(8z2-fxax_ z?d8||*(rx`iYYf?RUuO$$!0T@^1KPz?<6$UBU{D0L;a}~!q0tjE%s6osZ9+9|B*CG zSK#ZYug_cl>0X|<^u_hChK+v*%X3#Nc!8>KgiC&rvV^$or(h9==CrZ`Q>p<2 zT}zD&j%8vFIDjfNb=DOOepR7JcP&VM9l#2w`AdWw3fD%W{IB3RUr0j6WR!edw8b`0 zFsc28*!_y4WQco)7tpa zGWB59BTbv@2C8UMbu(x*v}_j(;E)j%eereT&`g_uG;n-)%h6$H}DykT{62dE4SRYVe+(HkXd!wl;@EHj>~d-I!^K?j-;~ zc*qppxzD#W_5(pp$><{d+gC}G*0H<4e zgdlMQyyx>1Sy^hUAi)cKosiWcEhHx7X5!y;@8$-)X~j>PVmb%i!52-f+A_^Wnd>pS zL4V0%!x2yt3Ydcv_!cZv(ud^QE1uhq*%>~NMRy`3H37pj!}LzRM1w~m$8-?+8_1gc z73LIKEAJ)jG$G|Ll2MXTw2)@0P*sVAupuu*0;n#K?NJQgPf9GS zzM7kijS&s2U+GGwpp;ea#R%vpMcZ`?=5@p5I>XkV#jXD?I*KlXPS_6{P#(_Qt$6HcQA#<-Q zl`Gtr7~284wl=i2i1WmI?2n+iwMcl#;B>mw_;VA0XuPI~BR$G$VL`yFTeDXr}8|4zy*$R184sog=fx7iJIa1wlYGL1-q~s;#yIsIvc# z=keB9*41pgkd?=e%l>j;ya`vw@&&nt=gSzDj+(6y*{jHHm1_!QVN+WslH$wKizlth z)!v$JEc`m=QC_^UOmw?qm@hLQ|FPBO;iGqsX7Z`#X|Y7?f4~s z`NdTwOfm8_RZ82|6*F|>(7|~Zs6gd0xd280wxX_pitVMLv^wnrmco#M=s+&7&FjNg zB@UIN;9P6aL65nWF1<;1+-~uv6<`(f|w?>CmhzV~MOZ z(8*N)H=#?HQ0lh@fH;jXX4emyfRnBhgr``{?$4|(9s4XN$6P5Qd#Qq5^Pp}KW~w&z z5KnKv%h4X+^Z*tX((NONugT_)42Q_optgraye(t$6b#dF8=?4cMQkV_9ky)-537mN z;k6!|lmpDFn+Jz3icm?Fd)LgUHiv^}oKoMz@9%E?q2v}DXrIJ=rMw`cbf&z}tn~Yh zOI2bA)6~AsA!6lylla2w3g0>I0Z~xwv^X&4kJF}A#qWi}$*PLb7+|R?tS(%GXD3d> zoh}GA09HvcS}|UDS#as;7L}uW$zZlzLF-9%))SazMmX|VLXUENX)^O;=DhCOXsL!d zOTSybT`o&xxKt9i`nhaRpwDzu8RB(`A{S7f`fJ;3M2#6p4bm=IgjyXhsKxElZfX*O z1v8>Jqr)GXTTR`6$j42deN5_b_{<(LSwE#gupx00TKkL;W*;j2CNw<)R(7VE`;~jG z08U|_8YRA6K)y(Nq@~d0jjDikRb^c?4z#{UK7c>jv^JGjq6<=79ASW@m0ZAor&N22 zngGYUzdP8n1xIbTy}D=TtKwipj_io7?pRlC=M0L+SMMy^A(zs|VSM%xgC4}LDJcz8 z2)4dF^UIn;5mIO2EFZ!e6OZfX^JSawXHCqD-fi}?IMQnuCA!8z}pk@<@c6k z6;hu^AlLdaagpGyQahs-JZ1okH#?R}(jU9xm|R%e?M~1voe#boKc1Kaoq5BPf`+zc zW2wn=-}!&vcYcR7Z6|04V&k!oullX z^o&6oVWS2R$HiO%)O{y>1s_J?ppeD3d_F0lv`Q6I`7l}*(=39z|N7V&bT+WGAzJ8D z^2P@R!#FZ&di;dUjTR=GIoG#yV`g7XjMI6*pfGLZ4ZFj6&iN^dGnIGX_=ltap%*a;ax<@I~m`C5oafl8HB1&VOa zit%BLhyX|uK|;o03v|acYQuke0f%=V;ePo*Lnd&+c1UqHon-GDjW!%ILIlkWa~SkV ziI`c?!XXzoE#=%CMHaWIrcpfcmpylx#+*Nm5rAYPV_swp0OLQx&lA2mo{ z=CS0$`;0Uw?v|ufBA)VF!*!G5t-Ge!eSh5d~qNkn^I7SX~I3B>x-!zlNu>F6BUP;M!Nm{&I6K%_*zzn=Re`e9KlfuA>@5{Jt0AUys zyM&EDDPlG;wcPV1^+@BRM^?DV`h{?D$Hl-qW4^h|A+!e;G4tvvQk^WT#i26!{5jK^ z;IwG3=Fnb%l^YHmFRX`Ugh@hJ_ksS@7?AX+K;KzwhT7_5ZtJ6N+vPbg?_z5Ptl$`p z>&r+cVI4`7f3cwx5>SJs096YNX-%)nN1ER_W*Tt{lDjT_UJ11U{-nj;rJ2$33g(TccE}ly^o@;Wjx6aUbYf%7tuib)SrzCMS z=Dzv0JRGWuM9+!eQ`lB`wOW);n2bdSLX7i**Gar(WcDrd?bDY*59DCp)-u$KZi1v( z!m`6?-9#+eH0iG_iyW;4YzSq#|4T2N(SW*^#O}k6Q6g2q8a6${p9e0u*f7DjLNx+9 z!AUAq@~YB^m*K(3s?3p;DVs8l_U?A!XcQ{%=U7LBpb9Lx>NCu{V!joaCFw%uywdtI zmR6j|#AIO3pl5zHyi@NS8^|-a?&e&T$K136ualtyaQrFYG;vC-m8mY3;p--0k?(YP z>}ab^vtiO^6`H`-okfGABig}O5Yp*dFgpHl8T?m%!cIv322hw@AD?YgvpDT+5~x`C zli3E%=f&@SCA!`#`nB^AXbb=F-g|PJEBn(LsOabLH}c-5fB3Hm>Mo(IijUd3k8zUM zGQeLtc)LSXYV&f+iV|Mz_MQ^iD%|9iOB2+vuEYRb#_kW$YTa_AMrpQm7aINCBsVDE ziZmfLhJx3-q|e(j6A*R4moGE;yOlvp+iQ|`(qX8sa%smtGKkAXw`a|b(vs|iX}#3T zRVv;9ZXyOj*;lRASrK*$h1e7;)9DbORMyFIj>cB~?#$cln5l{5SvI1``wTP9v`X4% zT9|`rf9dw16@oXeCAsfO(avNUq5GD%el+Z|7qQYdX+a14>0^{Ugy?ka%qz3Jb(52f z9rydKrv&gkAEdO5o|vi7=x1ZJk#Q}ycKE{1sifwe#fgD2TB7;0(l}c?P9wzFe2p%; z^rc}HsY)H)X(aeubg`)_C7qHGqEb$tleHmiGFjUwXig1k5X#eWk`g`+o_RvY)L#E` zKs9=l4EYqu}d~@Z0dib`wjp)fEFI4*mh<=jzQ`>MZFG-<|6eLdVY&w34AH` z)bs(rzcR;qs?T}zrCp%DwLF>TXQE4kA=v`+PZ^hm{|A~tWxou5Ixa4vWC7WBeW%~& zY(}5g=@VRr&Hi3vWGYWNH%x9vibE;f`n~>Q>sr+fs~g|sJIVfj^~1?&<-(OIJf2e@ zwc5eh^WEtCTjpnC&z7{w-RZj4v=aC!I}f!OR4moppUr1qv6luD$X?OLnON*e&G}Xx zddV~|m+`D&ZVRYldPWVGJT-Va^)Vt@VMxs)YK%4XyI$MaWOTBRo4AuVF2KBT8Nr~f zT2}7Qsdk?eCHj1tyEEJp0jf%OLww8aLurf3(IOU1UFSbA- zC`=A!gk8c1oLpJ0s&|`hy}^|({D?jC^NRcE#{d1Xwr}FYz^5n!19Ga%N=V?BoP4*R z(h`^OHG>Kp`=dUEF8tWJIn5^m;RK23rj87uBRUUO+3ww7)gH}J7*k2#vh_-Ya%~Kq zeWek5ANtF=`v!^6+yqwImps;%GpOvzYG!5gI#rZqzIo~3T6K-gt>!)ogHD2&3l-BE z>ZgX)r%lZ$?CN9C*2;H3!>3kndTcRr=I#aI#J!fO0RN@DedOn^^8=Fl=Tw7v^$d|^o0>=Ts;}RlIlwPvF#}>LO+~7TrV)f4O~2wv3vZ@ zRZg&~i8kGeDjQE;yXlL_3yF_Isv^r27CvPVKuyEOF@bGfxZ`ttEZIuA=ai{f5cA}Q z>IC_Jv{EDghnq3+;%G^lz|nFtb=On`)XV=54xhhxk(2+wI6OS+m3n2h$mv~EC(YYp~k?gb|*)Dn%a|B?mlM$I!Y-edyo z^REHRO(FAJ^CU{T$V_9~nuQL-?6%av|7gU<8)|-eY0&?^e07lX|2+Km+ZUbwcPAwW zR_M${K!Sb_;L|q@HUKo~?>zKg>IJ&C)R!ZVD@^pO3v+~5DVJKNUpZvh46USMz7(vk zBdyL^$yBt?n95{8X$#mXTKb$!0$yWp565qe~ms-Isbd z<*KX0#t|E%LW68*wYveo>o76*`Ig;j2RE(gWa=fe<*4+NOg)wULa|vU4*-6=aegzU z3v_$0s~TjGDxYyxema9RwDtDqz@3`sY_XH|ZUDCUYzeKZKXX@h0>p*s4K_Rz9$%$q$6R;>Whlvx}9R zR_=Oazw+2+5=vyq@h_|(F_1(xNdX_H8C8w`SDek}D&QvjpO=TPa`rz5N3Xj0zdI>8 zNFq(n<=I!*{wO#hfZ~QQBDGL|bdJFJ4JpJX-rOCS$FL9(_u_J|VCy5t_7*<^$LT-l zj&a%F%A74ro7I^X<=T1DJUjinZWxcOvc(3Z%YR-nuI3KWr$0))A7uYo_lGjK2YAQ; zgt;3<*6AVd2$8j7THj>p=#fEl`_c^mg*IseG|&M5559f*Z4Uke0(jZM|6P>ww=W&E zD=dNX;9t_jmRO*MHvZChpwv}6vo+*~lJ&`}hx{c#x32#d5ltO{YJUIoFz_&>?zZ0l z;qz}_<>S9RKlt`l_x^WLG|A#KXK>0EYy>zQ}>_;fh$0bqzBWW$RfO$i1j*t5s{Flw=dWt}B-=KFR=P z!@`F$uirAC#GLbE(*&a8(9uSNBH+lqR+Sq_NnN*y?!;g_Mtk{TJ2bc^8kBcsr|3|g z-XleeYI=Vh^w{)DzX~*I^yoWu*`dptLF+LmCMG#mO26G8P|C}vg@W2KBCQy{L#S&a z)Z7)SjhoaqTFss1V@0p|Y5sB0tm#|+I?$~RXz0*xhjz`-ZnXxWM*hFznZ#i94U<4O z=>J|G9UkT7|1Y0+@n3dQti%}T>=5HWBX{aij*BwevG?lUh~yWidhXd0%1!fF=!;9| z=8#*7sTO9rT!aDK{$nSf0y&#gI-CQF%|in_O#Yt)aCZ*H$8ezn&0#O6SLY`; z=e>QW_v_`E_}AI_>vQ!sF`q?y{v}$VLz*5=CL=efmo&1aA%o_K1 z^8q3^aPNO`Z&iXE?T5x;_};C5^Zw*$-MARw|Bds2HrRh3K7XF~|9k%GMd$yqlVT_T z-Aew?M~I&*f!yx%g36KC$Pbz}-AwzXOxpvJI(7|7mLy7N8dM_q$!`i?hQR>a_cg1+Ku`Kiw>SNu9-!tDZ)11frGuLMiO6>mpAX8`IMjoNX zR46Y?C1P^FK>EpaOLmopvMRA;ipvpf#<|lVTI79n##g-QPl1ukky%ij#Ww&ip8IkFRXnSF{4{PV8lzlzU{FHKbk0(#1oY5o9SD8-j ztIb=faM`-~D-Aj@GE(iz){24rcjG*eP4VB3j`Hz8j$Xbz?Bu_@DE9i_N0lN{-O-(; zifmQATajI|ZmTV_Oxkr*=jT=1lU>$H+pcn2!QIHK|2(M30R9UryDi3lE9Cz@{PyTY z2mg0bn&JPmAWTa6&YTg@H+kr<`RtO(-ycm$*oF}gZ`h(P(H3$Uz5fm405{qH9Uc|p zKOP=-_J2Dmk06ZA*ZZVPHm)Xsat#>RVwB-hOM@oaNy>&>a?uRk#IpQM=M|MlZQH_3kvUOms@{})HEI{d$rVlV&M zWr%1Kqo2n^JJeA5+YKFEWyf-ZTiK)?i(e{O8p{j{iG+_59l| z|I1Fw6X%j8cm-kr1P(uU=YHk}&S>fRn3Tx@ka@?Z0A}`{I5#sdcH-rN=x8T~9q}F1 zT5?$|)ZV9{HHo)!$&N((9Z(E(5Mbt1wBUQTh}>K6GmyUo%Kp<<1vnd84h=x6W0{6Em*J&U|>89Nte=P)kN z-smE|H}EDd8}jP#)81eekHd*O+`}=h&YY|Ble0JHgZX4HC9cNNBp#+8zS#K}-oKht zdt=nU|D)$G4s!8-4qqO1@}J$5C(gOJ0YXe0@9q7!#ee^A-hArB(fIownwZ7M!(qhk z2Rupc637fRfJ28Mj8s>+LWLjZ%nODrj_G3@_Wf`Q|A&0)U=d7v00Ab+?EBu~!NKPq zP$_=$eSre&{rCSiT>SUFz2AnzzaKkKoM-BuIwG4eK$6jeGY#Pp`UB_eo%8nHjdOl> zaWnX4Z|^_-c0(c$wjeJGgg}3PCYJ_l9mF%w^(Q3m8L`RK9b)~yR=w{9w_!AAJdoZ0 zh?583{r(mnb3gvyOBT7`IREK4Mg1NO{_EiIRl^9PcAr;n?0;`hofO{_hkMPUGl~OE zonMwC`0N670l)*G`t|>HcD48E)29)OXM2d%(ZM8i*kn?WayfFMWO`kim6`Y81@K$| z<`+6>yaYHCn%4#I0x?T6a~(46-n$7^*Z3n%gChp~6B9GC*d%lFK%4Yy z;8kqXGSD}wxEW6BqLka0v}NhKZ*WzB5s?*)+@e|rrV&&_FJN9l-1_@*nEH-kO zqlHXJ&pgtOhAsp3I2n|UR_gb;cpxMlX~Wr^_|**o2^_~=l;tNH;RD#f@*PsAylzhuUFMfb41ck@)o0#V3Vt5z%Oy86_BL#D>erD zRkSX4&Yed9WUl~b7*43*aFS_uar-t*E+aRFlWgb$L3@mGBy(L%XJZPh&izU5W9_^IwnHbV!Hs$;DMsy&TNVU>2O8Q;nDdkL* zO#n&GnbQ=SU51m2-tq=S*)DtHta`_EMEgD6=BamlNJC=hsdb~n%^RIQ5ma;Jg(q$T zO>i?^MJ3%Sbcj!D5{_d`TRe6b@U9R#_#p%C4kj26)4tH|1!TA{aGCncF0uXuL}Fe+u*yUs5axYtvWocI-|(_-%EGtD(|n%x?avJ-(tFr zW_O{~Av~SlLG}a@CNb_38{RrIYU7PzJ7{g!2;%R)Qzv-mx(nx-{cB0dBzQE6ei=R) z1LYX>my13(CjF*tJvrai0g$cX{bl5>Jl~zV=kb{NTuK>OQMmZ+;P2VP zIeGoM@^tdON!OgKFid_#q0F646jR_$Rd`+WV=|O-`7UnYiToCq^$vsHxdH4N zaZ0J1Ha--rz9>gsMh94q-w+n%Wdrax zym#c|85{t`TvVfr{UC~>hmV(K6xwTijW1Ykvil(A3`2;rkcGX6LfC$I(JY#>Q; zjP#1@J^4hUN!$P}(t6Oxii_P!DETbVr0R6rgq>7w&x-9}!TeH+ONvq*q7Xy}c~R@2 zVj%vNwql)8^sOumak-3V_6xvPxk+P%cN7qn=zjyy738mi2&fv?3PxI$giL~%M^i27 zAf53isP>N2?4PicNwyPQMF209vsvn07bpv{n-vcL`NPOX*g%~*DV3CANf}YCl2PRR zpMVnsIh%TE4plf!QBx_5s+aK?3U!*LIHqEoyc=o7(#rf`>#H_``bHPb(pIK&rDmB@ zlMo5fru>SSlTAt5aL*LV#3}$d81k;(?X64jp1PF{;Z{jDcpp}ChZSR-81hJnWS1jv z;9PQv14i9f8V5S7#WZ3Q0jrZ)6fUPT zjcDM5!?{fWf%*JhR6(l!q6&jR6+mfJO^IF{dTAsqfqUx``3uttko|;Raf5gn;T_@P z6B2cyPG45STU!{0BZ4B4VJ;SIT@Lt#1%Itc`b@yBvW#o2XaH$9l=6M0V!cK=p1I%- zYXU98xv;YBd*GOsxJ$I?m&hbXr)(_o1HuCyb!A@Toh@?Aqelu5y>R-B+ z^-j^?E7u_jUXh>2J*b*UgMhSAvia4EGAaMVMCE-bd81W)G*WmWn#@I#d!L{risVjF zxbl#z(6Sq&36k#^29KoWrjI{aHY1H7f) z9CzAFVAcRa32kPcB&RCKR+P1XP! zN0&+pV^O#Xc(Mc}BfKJTQ}1Wqn8Z8}T75FP;^HsmpN3rUz3ck}=ak#gTwJb>u)iUI zTKM;?LD_U4${2~Bm^97ATkvT=h)5MU=vX8W0(81W3N>NlJE9VPJ1zN=nt0WL1~IX? zFE~Az%rNa^_(XpHMqkEO1mQcEeD6Tiy;a%v;}>Q(C);h7IG6NO!|xQ&3f~Q13(KV| zRv4T}?=fP=;}p#rJC0l z2V6v;^s*_3RWtQsLfJg*vVt= z7NuLFU{U(hkPOh1;aJa^W7rCS-* zY~s~=ySJTf%kEd{c4gBVXSVG5OMcJfM_?`F{Tj32DG_H8{@|`SsfSc|K;jZ7y}5h; zavG(amg3Elz<;I0cy;*SGD`*lM#h3Jr)A(v#fHRI2}?YLhm>NhFa}1x%=A1==8GYP zj_j3Asv$961rmg>Kvcq&B4i)JI9(z&VK>KZI!mX@jp~##U~j zZ|C&Hk@Iu}Ko$nY#67ai>;S7Q7uZ=O$9o!M4`L1|gd@^^^pITuyug&h$uBKS$iNCc zIiIS%E#x99ML>PM@jDo!O*8kclRJPt8TXrRr7rHxy$jN#9r z*4?mvYAVZ4=AM4{?W==Xh7={!pBNFoYVph)z`}j+*2SmsjifEK{V|v?AVAarjNKty zczycmNipTtKCf35MfUpw6{HcjOL(NR8 z(JVap5kB?G_jjdI8mDAKf%E0^xm>nW?9@St2p1}6redggFUi_{qvUx5+d;9&eKNx5#c6hy4h60t8!rMMCsU&lgHkPqb{q-THYIJhxJ;? zV(yTJ1OMu^l-2P{O`(HSpl3VVOp{mDk)=kjsvA4aUS%%^8ot`LHJZMvc57?=3?fNw ztb?i=W9z!8`UqyN>ZA-Je{HUtsu<)E>Zr=|wA9e4Rs!lT9Yf`7F4ggW20WBX3u83J ze}4YrRX+aLx6co{_&>WS7})PWh{L?X;4sJEJH6&uFg*(QNKM0NN{tGJ1$ zo#dJXM7cj;m>(W&vZRLaL((dhb~H(Yp~zYd!F@2}A|A`X|4si)4;R|j2gpfV4ohZ| zM(%DemQU?4Re^nK8(A;nD+=~gzgs4ef0}n>H(`Lv-UIpcU31?8F`+LF9sl^H08Pi< zusO4|KI8u_VEAZi-T`F_`i!% zasD4a#sOQzvoQJiapTKifBxTod-S|J|6LT5^G99p$5dVL5vvekFu+R9V>ICZqnG*k z@6TT$Nzmc{9TXwIrZ_B)JaZHgOCtnM2O@=Vc=45m?cou7fSH8Qmq7NNh4 zkDWvHOHly{Uk}9RZ2BiUwqkD8wN7i}Ly%$uZECq2Rd%b^oU7ICYUPGm-ZJZ(mc1A> zn$)FbGxOY_4(*tfJX+;cnU>w#zQdwv?l=BBF0^8uyh6V}jwXE(>aZ{U7QTNuFRd%I zRDImm@9Q5-nt!dn8+tz9XJ1dTKK8l!sHzu}L4|=(A1xxjKSqO~(rPo8`N{0}wAx{= z8t3U8W4pEEvRme)5lf_fp>9N%tIKbF-6hv@@8y@IE*Vc+SGw?G{ z2UJ7+&1iO2ji*Jlsc=%YOWi2S^`wuKvcWJAPj zyv$X|Dff$$tg@RpcF0_!P+vEXC|fdGOqBhOXLa)Wb>U07okRhah{~EdL|qlMdO!?y zP~5Mqbwk-qd49{Rk%LxEGfge-Q8Cq+0XYBCWZc@ti2h%n^rzB5|GzjoJj~JmN8f(i z<$u{p>F9q{N>?}78NL69r|| zarNN$OC#bhRE+4qXcDhS`SDUm{~aD2zWDYIQ^rDLJX|RMv3q;n6@s8V;<*t)|dorCIF= zSZEkF4G^A=kG98&I|T&vAt&q+!XF)ajeQTW6OR>@g=zvOLHmvWY zv6ldmqhRwC-Gqyq`j0paF6m?j@&Rx!1Za!FUjQV{7p2$aX*`PxFNg;1kfnbB*RK^88O@dZ5H?O@Zxkc_^RP&4984~#riIq`Bd_mWsfqrpQi@BEYyHAM?uOvvr2wAw`gHRrB>N>;yRHMV@Vcz$so z56b#<%fu4dhWsFkoK22lm!%3~QE(OgHA6)&!k3nyc{IFPt#a8e)`1)>Dg9Lb-R#C< z`8DQ|Ik`MZlW1n< zj5Zo$5a?COtpM%Ld4ic3x^hMgryYt&Phz(oDke~SC&X@JY+bXxlpB^t71hG?MtaJ7 z@e@5)44di}FBUIz_W3eBaHlAu5!JlFq*~M6!NTs`2Ta>gT_d;Z6VV`DcFgylkSATB zhhVIt`f~R{)#yF(9dbiz#x$9`KL4LSoGiRwuU^;Ju3F1a9}xRGzeYZXp(=WhVTjaL(!zRc3K7+!5GJS$b2K#N!qDZR$q%=8n@UTf#CA8sfBr15CC zS?E9!sWRXJeZ^CMaDk2me#QW%%z>Kw#^ZgWNA*d@JyLN8VHC^-?1p*e^=}Clq@rLr>Ff_j7(GZhm4i@EG2 zXpCoOx(?@Q>YiA!mAoP+Rjj5Z&J+(1CPR^xVoZi1s2=1ykn}Y@1T5CC6-!_Nc=)`v zqsT{6$n7UAgvY}Ho+~2qZzs#)lE58ZuQXqU*n^tIi+FSsjH)GsDv5l|BGW^kCyN;5 z{+8FJjCZRpLNE~&r^v_N@@drahGiwItJ*CV$%X(app!(q=}b>im_;q-J9<$bdql?` z`=vDm!sBEYVlo|Ow~NurhZmvKBobQkG!^E^EoBriMy@`4g0C*yD&}F!iHEXGizb@^ zh!ZiOI#-Uihk>AiNLs$lN%(JD9?WUtOPI zfXbgvFP#q}I+k-W!7EZJBq%R$y2c$Fw6L1H6|1_=UA~fPGm2m) zX=4iEEeH$A-BH+1Lt#rsnqw!V!m?8V%Z*|QPDD2{(`|wND5zW8S^$=g+w8c_tu3d+ zi=upO+$INYH`r{;Z01R7Lcwu?($_r&Sn*V*Ut#$hZO&6&1Wud*8T-yB==u{^tjp)k z;YTOe>mY5}ygNv%mk!c4G+7JMUb2YIJ8)Z8I&j;8+Ya1z;I^WPF}Ss;Zd`TDepm*R za_1zy*xrE4^EX4ZNqcn!0A(DaRQUA78PQl=P5^r^(m-jp8p4Pnn;47?$c%I#BBSO> z&KSzU|9R2~>*t%B%WLJxmI($=5_X;eA;MG!+BBLk7UrritPFd+d3}v?p@4^dZMsvp zVKisSu`_x|ngFKpl^e6m28%vf3eZa#?MYQedW<4NmGMC~3~gm;WriLLt3f<`~E`7j%SSWi^d;E*%w{&Ze!*_Gsk&~XT2t3(mH;$IY*cD| zFH6?UmDjhkI2LVA{ct&H)!^8`!nJJn(pHyiNtk9YKq>d&$WU>>{8kNfqbRQ3=JE;a z>KHb~`6g;V`R-m_U7u`2Hxqi3v4n#sQK=_YS+OQTaU2S&fZp)rNVlz#FpJtYR}#Y; zhEuaKwzV9?Rv(%>LyK0K6ESQj_Sdkswp))!`Kkd31!A39=ucv48Ks0jJYSxIhvq;7 z%X*5^U)+a_Gh_FJ(gh*`aguh{X-qr8QYTpI1WS#5?M#jjU`e=Pwg>0L#q3~R2kSam z*TK4`zP1kQ)-!9&Qh&Bhb3pYAiTWdV3Rkb$;ICBXa7B$ZSf%8_1~P?onvExGEVo%U zutt5g-i^gH_ZD8^AFl1hGf-cEvo{p8G5Vr18Uv_A@*6#p0|vH0jRiTSx_ILmpuW60 ztCF95xHi$^sO-G=uz!;~nU@eFd}!PrcT#@j@9wYq1NIFtvb7}u& z7ZM}LN4VfK_uVR*a2ssA1u?iTrhbG(V9NxcHGZ}s{B}e1t+fOzVsG8+^H_|DeF0BBhe4KCnl^S-}F7g~`b2wjOyxUF7($vx+p*p6>7F9(m7o&)7TIzLO? zfFX^#uc+1i3^J+CT%rxmnY@YA@b~11;&-L`NA}p82C2$^5l)P zhBfCxt!@G~$t}j9g*0(6Y-0SP?rscZ9 zSo0}{YgwjYZUAKaIN#IZ`WwqqUUYyD%{N z!iGg#FetJxBw7oZni&qY5;34vL!lkT4KTdikC9GmgGT#uZu-Xbo1`)9B98DzXIZ(Y zWK{WAo}4Y1WHR`*B`h@FSR(I!jeM)+ak=c5NUvoo6PL@{RNj5d?rI-(W?O!Q%KC{j zA?7CJiWvgEF;dh+9<^n`yr}M_nPS(f#D{t@gz|O*4rB3#CE}p%*^;`3yH5=n~aTNWgqWAGYx9XRX5QLra%B+#-;#ZI$p%J%vuHl0BLvq#!A`s zQy5NtSHv~B^jU&lD38gPWF{r5 zd0gIzwy{BS*w?O=Vemc81~o-k?UE|SRTrkHIrCZ+LYM7PkrqjrCthw5tGFFWr&;i4=uj9EFx!w%ct9)3sFgl87`BHkUV9ZdQB@S!Ft}S z6i0JC+AeKhBYf!ce{|A^vUzvX2fcLEPeYS!NFQvJzNT@>c19+oyd^6o8OT0P5)G{> zbeER8T+Atw(^BWqB~}?YXX0YwWs^atIQc=brgp5~ZHSv@bjIGuT(NWUgPmFj>py~6 z^=r{fcm;6&az&zQn^e;-^f(KzQ{r@FR88r~sE&-vl#YxdAx+CeeURrdm$>Q=tnP|@0sc*bHLm5sZeb>4qx)r6P{!B_o{dLq|rhHx0pOb?24$zjB4$yXhwga>s zpsi@Ktm8G*WEH542sw|Jo z#?Y#IVrs#ZxjK}Po_ozw2L&H9WIVQj@J23++-QBPtB$oQDIIIou~u7KPKTmI`NOhS z7U8_-VdB0uy%Z|yn)ru}cIt|T(l)y+ATZlLMF+{fE^n_Gnh#dj>8+6Ux)Q_FfgHd&> z`#xruLN98)$fudSjqYc7^8xew;=T;LaJV`c91RYKNDB;~lvX%o{$j=s2d5`;8c(-C z2(25xR3c!!a6Hxtpi!8lghndrJcS{IR|5!l-w6Z%K?Ep6EpCsamL65IfNpYc%G+O9Q24CQ;FEhvEp$>g(b59H+V^&U^<_8CmZga?)h9$sY7ON|?kHU@K zDM7+FY|%4ajai&VS)>KBMFTaZ?QE~DADA(>*f)KYRs?~82*}(xL4roa@_7>#JCyc z8r@bH8V{Uz3m%2ahk7_hE_7$w!st=5U@`=72wg`MAW8C{@p7^7JuQz7LQ(*(GW>%I z=t_1))Lf1H#Z1F<7K1Y1C1#jpF<#0jTuw284A38mx5@-B+D63pTR0Fm!hKm!QSxKj zURphBT}oo(Bt9mpk}pGw%-m>7|2z4NbNdUPYte3|pk|wRKG_hMpc9 z!L(vP>#>5@q8sPk>BX7wX(o;!4T92ojCJhadfLB=NwGl0(%>`BI}HhQnJ*Zz+|=ue zWG{U44Qk?^wWU}Yhw}xClDXT=+Sr1r)3j@O!FbjgZA?12<^#%vPrqEoGb5Rt@u9lG zIC`Lk8Q5LhZo|3Wx!ef4uZkp)6^Kv}#sO-(zWDAad4O5IBfH(n3VfBwgG+o%h(Z*u z`0j1ujC>Z{amfkEbzmV&jKtvi%?WiTn9_Cb1+Eb1>ynlMObHGt6w(<33O!{+L;V2$xua`S%k0D0=Q`oT2^{)57soMexZT$6+$c zJ-=$u%^K}0Ds+HCnoQh9Si7@Xi=tvSp1gWe#0&)Hpj0IjiH}f%5`xH?R z!9=nobRQ-Td9P`Pl{pN$4I``tYrY(^C}UDM4qU-HRHp?(y@aU=&aJTXDPii2*!T`J z!@{$gqg0|Xr$Aa@zv+DUQF?Uj^u)TFCBVE`V?pe^IXSK@>+TzFlAcX>A_?549VDq` z>?Vl~jkR19>@4*|2~5p9d08W`()kdP`etjck^VP$j670V^A6$jPuI`a6aMbh}@^)-DHPNuOldLY3w zUQHfvPxnFH*RAk*xK(E9qgtE_k{WY2nS~|?@OY!Mo~ChV$(dK%mI5V^vLaOfy`d`? zFY9g{mJSzpxVXc`9WJhDV&M_7GoCHus( zLoQ^^=5`V+Px`?-C1dC8H*=93bgN6W1L1-}xZDUNU5hZJOdJz+A~heMXa-ljt54{} z_=k#PtF}G09u>$9<-^jDlq93+j^a+a^F;1l~)u(|$Z z1FM`&xK z)(fg$Kw{exDz0V71bpq5`LLW36Ed&cvtNScY}IyY7nVz01BUxV9DlX?OY!r&Q-M8R z>m-fcS0Znu`w4c%=Hl;ExYDZByI%IvSp#*V@3MJ!qHn!CX7cIl5`AyU8pvuEW{Ik0 zS?AtpA{rC{6In5vDox zy|HI5Q2YvrNnyEO*lAX?HAq2PnY!nFjb*45ZtadzGR9Fq`b{a*b*vID6HeBeXL$NV z@amHc9U$I5jS>c~M3HJ39tjD*j9_sS8=@E+;+Day@ZBgQfbZztj@~VscSrB)rK5Kn znpj!ft*J_C0fp>Z^;Dr&(Xq#VX$^txI7N$SOo!PmqO@6#xVmhsXs1gU+$9XQY1SnSb~==rDP6+gwP({$g&v0W4ZZiRX6;YdVq3DT*U-0- z6l*DhS`B3cH2C60hB0Na;FWuQc5=BhE77&|mbllu6;hS$NsF3GT+r48Q;*r-MBP(K zt>8u*4@hz3rA=S68m{CJjfd)-R65B;<-9w|g+Y!0kGS}T+-#y*gT(>5td9Qw5l*3N>UO!IoW5uPS~Bc%onyA7S>y1BnB?(ZW+ zX)U9(FPE>nH59hvY}FRm=dq%!w$RmDf%T1`m3dAe_QNumG^@yMj}VjoxxqL@;os?r zGonEh%33ksn;^QZz!eH#e8p2pE%a9Qq>N%YsF4 za0yQ<<%wGZj85Y~3m@1c>Qi-zOj7`REYsDHl-%X1XC z!w~=elJF8NOM-wjkv%3l;tSh%LV1}vqL*3i?Q*bNNtgS`OI+zN8)wWIn`PqA4Prm8fW?~_d-DZAD7X#LgVYU^jL;9mg+QBQ z7C;lYF&H8oU@V~*)OC8z-ZKPJc-lMY$qm@ZcP9e6BlK*(*|Q{B#K*(o{r&xbJh$QT z;OPy#a5xFa@lXbqCDwHqxeH&gu}_%qhxZ@F9>VHAt~~Z}Mtfj%O-5k|J611~G=^Qa zx8tn_lzG+|HPj%bk7F(%5SD+pE=wpxwm3$9kIsXHQkWv5MJEi}mS$pvH+F-S7li?) zaiq(JmH=UtAP9kxoHtO%>x-okNOfS@nL!T(D0%(t2 zfUb!*j!hyGa7qG?VB!H&3nvR*3NF{tU?J=SiJM>-T?WL4z#oRM)C)LEfgSM|>#pET zmW@Je95$Imn6Fs!J331vHH>n> zJo~HrnE@Vl2jg&l{N3||gWlk2Ten+0_U5GtUGD06%MakafHq>E6nHCzNgBxk7s&Nc zO6T-ehdsxDP4BIq1Jsk_O|aAKGK`3Gpwo*3neO5MaRc5=6D>Ev#; zSEG}>HIz>B)=A!)>vfVhBgq>V&@{plAhJEqUS3+jAB%0u+@YWOq}o~|H?XjR=#r3n zV`7_O!TZLx$&Rn6i9mJ+xtVOx!$0CMFm;yBOzN-DP)SkXg)O6iln23R+YM61kbm0XP`Diqw|!7_|6I{WL^xNXp$V1HsP5gnl{dMf+tS_5y;T z&|vDV+`t4(7`k-W*|M11RAhEKm8~KtBGPc%Y$X;;9loX2Z!Tb>u=u#uPhQKoY}BQ_ z6|9vF?D{5(kI-0X3)Zh|m?zpp*jh&ZS|lH|!>co9zQ0?kBBu@;D#7eF6)HY3peH^9 z=X*B_KO2h{|3@M&8nqP1{UvyztJ2y3XpT`HTjqez=@q6ndrfbDojc?o;S^ zFvhO~zFC97O^%O_UOfG#t=qX?US@!TULd@kbv04o0uGKbf_&akolP+(X_7mH7MTbf zEsVV1-N`TR!v<#CGgs@12ow@0KHF6Q zV7KeTWW)P;lIC>~IY6YP%Ts`OPg5(m+&eW3Z*mbhaR%t^i@erkij@rr*SYU>xS_)h z9d2kDH?(9HVlY}`6*3*Zc1aUZ3BWQJ=~p3dRva5ye^EymHwo^+E%W?JI|LJOIcJ}* zcz1O>q~*_fQD?7XmM@1k;sVXo0I${!YjArScD?@i`*jR$ZO>>VxGdeW5^?l3GT7JD zBEtoSBcdQC+~hC|^{7qRD{DGb%pFDQzRCk_&@Y%FS)awpO~itj#zOH={R@*f2&t$( zN|@?JP-b9bj*RoPVPs=T4~v_O{|vjTz8uzFHLLNBIx>NReKGIaDz)lsV<1^2gRJSG z;uC*B$4(Mu6qqQ|!ixd$(T_79mLrK_a|a|2&H3lQ)zAHm>Kvh3bQ=20dH!kjQ*Ep> zn7au>DsO;pAo6WM=kvU60+C71$xr8{E5&zGJqVxdAbsn<{f}q=bATpO8BiUwp?QPV;RsGow_1(I zGk4Ci#|_{yIJvxd|KhsjV>JRmBXe${&;3xyE5!YrU?c{g8#Br9gLr*4XMuf@%2U|- zYMJ;LnGBLg%O?`!VsbssH59^G*THqR@ZMKjKbsJ@xFM-Iu|jdeSQ&8~ZI=l48ECyyvx?Z@%H;o8ndMKtWN`OgjMn($a6VrKUh*)caMIpr2`3Z}C+^Dihp{*9 zvuHd6WIRSsq#+zZpB5F+T_4ORPZXr81eFzF|IQ0aLA}XA{^Ie#U`%u_tQpjK$=E4Z z=hrt{jsQGUVV9TQ^ZJv-9f!>$^9WCP#Bd&32=vKWfTwi_xIOohSoh{D@BEY^c@ks2 zKp0xV=3WF)bME@5EUpmRZ24^Q{Ng?yl=bOK3{!T(^+fB!9b1vCP(SmUp&|)v6ur82 zTD6KDf~{eAMVNESIb9vlqAE*@u~I*`0pJ7QT`_1y7OzmN*mgaV*_`_?j(95fK=d-A z(6nl2FxHJqMygen$yHJJS8tS5puT`%RgOG|Mhd@NA3$^lfk%j%Y;M5jzLOL)p+b zdLwP$yQc|Z7E_!jSKd6k$BrWO$`>J^R!F79*^uQVARwq`(a(kg>!OA?sT$fJo zVJwy}n)terJ7&i-<2eX~4`-K(ba9NzTBt}UMa%8{%pxb5X6bShhs?u?)WHjesb0G6 z@6uvfX<|7Aw<#6cX$}RMjqQl601qVVxHvv(1UAYhvwv?uOP8g`9l`)4z=xVzt$Gk) zsbe;li4uM5Mv7MEAO8;;v91w5X8!r$52NK9?L~)z{P9iWEU{NijG_$oGYFz+$*3cb zXk+D~+~BY{rH{5{YCe+S*5xo+=^;x!<71##qNOfMt)aQ7amAo;&gN#wCb~K|xT^U9 zKc#FfY}5U=QL6+q^W2m&`gzrk(HXd1Wto&T)js_IO&8TndFPg!vic2q)zfJoQaGH6 zxNl#Gq_UGYPPTS;aJSnYl&>0FBSo=Bic(4Q zTWI!fPJeOi&Y2t~8K%9ojp8t7eF<2jw(NZ4u)N|pBR9Gp4Kk}e&=*_OhjMbrjOzb? zKIiE=XzN-&rvZ=}wszS#T1uJU6;bG7vq=nW!tNJHQeWb2z4Tx-G+$4|lma*B7XP`` z!_BGw=QeHta0|Eb^I!MBvp8@wZcC+^0gO#Ea<`e~C|!tI;?vm($+3uq-#|15+k}UG z0&_^&wC}8O$;?=YLYW18o&L%1!;+T2NzMk8Yaex#wRI2(OI1Xlwxs?2n&qz^N=g`e zahV!Ym^352sn{X0+PfNDy_pE&k4B|R2B|p2T5Sgym%-j+yCubEZT40&zY8>g8t`fr zO0k=1G~P9Jwx<>}8g>Dp*BhLgV*JNViK2sAJwnVwpu$!g1U{|zN+}{Nn_Ip5jPu+M zeeO0p+uHb6Llh|<$P6J3p}bu)sl!_{6Qh%> z4>6h|RyHs-8^*H2^to%hLmC@5J$N*AVFf4<&L@O?^O0gHN|EI-%69TNXL{;#LY~l{ zclRc!a4hxx#Rakovz^PH~}ecM@+ z=lH(?Wp(NUX0|DmGhv}J9YU@AYDdW=Uv1L9<;!7ASQ)!9c^Tzw^}x2IJCkIQ{DNQ{ zvLxA>@U>INp+APGv#}RDp+}WL@~}wD$ukUaj+rw72eH&h1$5eGAVtx}L1xWbDCxy@ z*&D^>N5*sfiQ2j+`GX5k2((mmP{1$bjngYJ z-6Pdn1VujXcW1z>Y%sp`G3krW`%qBn6vQH1z}j;Blp&KsH&sApFEMMt z7nvh9^Z>Ww{J&4o3(K;4xEfP&;Zr7lQ%u7bjchj|Gc&vKOVAIu*2EIgO^-b<^nDP< ziA~DfDWd8B8Css+VEJHmDflCtQ$ z@7@2t+0LA0DGgLkqP!n8f#-;yYo}ii5mDZu5x?X(awm#+04aSqENp1vd9r71kk-O1 zTY_TPiC24zZ}~0?k7Oh{v=jy^Xz6s&pP5lP?|^+Ej>%>Y?B0FywSgpl7%c}00$z@e zp6_nG553p9Z*_fjnfgJ5Z{#aB8GRBXAy&xkwIV$%MsmujJjZqX6(R=FZWqKB{`9PS zPz#n^vUlnZ+-I6WiLAMf0Sh#TISYzF{o9`()jYX3HVRMp-k2S$>@6!|I{$7AJKE@x zrfY~=7fcJLm=-q^5V3=c$psk)G^zk~)Y2pAorrnE>OeeXTf(mX0SDdA#VBDX%I3kLPuMY@U6 z(^_It>8_Oa!K&bcRc3VQxZb+LeA0y_Q9n;vfQguj~74@~0+y#f@N{~XFj~l5vKosu9w$czI+=a8;6t-nJ3uk$1wM`cH zM(i5cQ@Yh)Z5=TK=bkc~D%_egKb0zvc1dFpW-^7BK|EY^4n1tTjfKZ}qEln#6izuY zdjJ4URT4u4Vcj2=VVEM#T-GBHdtYo2*yIn$Fuz2U#W8KL?2M85up8Soz$nw7G2Zq+AcKfb z%v?&4_Pudq+gIv%{B<;?fzCMlHxG9~7JWX-x?Ull_0`SwDA5B@&J*^co&YyXy)Mxl ztzBw>sKI~YsnCUUf{)nG(F1XO*N%xai z!|U?dW|g>XyiZjx^XDl>w;}LnPj&c(`+f^&C?=Nczx7SfV=vK3Pv^>T`38<1L9_UT zAd#^mFQy+m*=Lr4L-`@lI!Dyl{aF~q;7=o;J=ojsS%jE;YOqboS!v4MRfu%@2AZtQ?}-97yb=IvkCvTt+YX0(XT7Ub zoo?)(iO{MnLTgmao`>_;~9ynfkTfNsLIaYJ;cUg?U)HFvk%<&#oJ95EI~ zd{FW$)eoZG`65F)bxsK@x-U87DQjxVnktY?sva*GV`DRgEU{7>u=yclLzHUj;$4Wf z5+aE;RDpZfP{RgZG-i7F=Pyp@*yMu<1)V-_U=~vZWID0N#mR-RD<3* zpMlc#p?ygMf;l_&t7*rO$#BwpN+#HHuNnBHWexpo%P3R*1erw95(^l4feUg*c63+2_im)i!>RGdKbSa)*h`9QssJ z;09YLazQl~$TJ_F=gV`l&iSuK{5;?$T=bfN>rrKz(hK{=A?YlnG*@f70A4R6(4vTE z`PpO*(S|~vX4RLxTA`qd+1L?a zIw8Dmr4yV6Z)~Q$RwNt!jrV0_RS1p9gainWoQ83i(NG{@)B8~Yv9Hk6WqZD{;d5qD z;}^s)_|VaI>`~x1(%VOAE>7ZO@F=rg#bQs23dFE7)CWJUi0{WdYFft-;>MqSHU1G8 zvqikN(IMhWE80PGEdw}w&~dWaBW^H%D85cmug?-49o%P0!%q_il#}Tsq(e=<7R-+9 zNy8P?D>&OqXo9)vo!rEe!AE+5valo|j?#nu>bFKOC4m=m8pgM=)o|V!kzWKci3A?Nnt~+@#7|}q>!Ep|F zKE&)1MI=u66x2g&&h!QUIZzk#Hf%CD>8j1<^Tmz9-%})mld7|~9PMz&M&2$9{*nRP z-U*<41SM>`hoPmG+qLWO@trAnNSua~XcMP$9E&d;VUV^hzQmP+@glE9iOEQT7R$z? zaMg`U?TN3!$hNUx>h$g?<8@Z^B%@|E z?U4+NH|!9fgCCXuc%g*s8>1|m9q zDJFhgYYim;oqb^R(>E)!4`Nn8^(aJDPI4B=YhV_{LJaukh&=;}#_ zLFFl4-I{?OZZqfS{OQmIf**z%Vn&ZLmhSsKLHYpO znHAl(pY(kvs*_i}cO6?C#jF;VtN}7$0cSPkMH@D0)66BzZ9#%c-CBxI+@MdY^<$cG|7a)YuEomU8Bb|P0Zz^hd;Rki;nvqPpd!h43z#f{^Etz)3e zgvrsoXk>(J1|kDPpa0@)`ZL||NK;fy0JVKJL_ihghxo{s8Av(Xqd8NA)kJwp^lZYd zv3wYeMuGIDc$H-)eK}O?=>0UFX?YA{mlT)9A}Qfg8IGz?T_?!Q;a=EUhJ-)43{)N@ z;+nA{i@a`bfFG%Scx!1eSx-mHv%V4ykkHT*{UWE{6`%@yER9U(8$s@R??#}2D%r;0 zSXjiQx#!doX304`G4>PA>F_ZO#`T5jFO5CrXS|;%nnp4*J)A#y6~OcNG5ai8sUg;l z^aykH5en)V(l50|@l(NFD)HkYmH12Ylb!fWGCOW4a-`lxM;fm3_^w?RZSALH4^kbx zI=y5>YLL~8MsL5#NZ0B&zX9$Z{UTvKa7H1lS7CR14W#D;$WSJU4p7kF?Q8Mz-U6hz zvLD`f#in3PK*a0!&ua#1vEh(uw@lYxD@cDXqPT{3i4gX~! zeD*_LBHs^wDDoft(27FF(GU~9+E_z(eMaVdpE#QV4|aHM>U8ktK{~x}aH?Kv+ZcnL z%=U2lJTboe9Bx>L?EC!&k_J2O_n*hsJbB&svH-qNxauM~3Y$qcN37uS!9fjToe?DB z{P>+-{3j6@xmxZ8@=`xEDLq9}6`@uo_w|Ji=DzYNHv^SnS6j+Os{<+uJ!bfGP(SL912J`!%4Hi*`nyV24RD02u+W)j9QyE;GnU|$ zfxz}Q2V#91mYmjoZb7avUUsG4=stuhC*EJRl#Dr55y?nLA07~M9Wt)`<*)e(%&AzM zVo3(c6=zC)0GF@VEBw{4+~T%IReYTnr9SHRM677d&~wWzF22*?6RK4+HA?GV7k;iKc; zpa5DM8OiRQdEH3gL|2k#)b$Li@Fr_#hIGho+0Ulm`dXpx)bblYRVMmAF(d(J{f=x^ zlq}difJ05S5}tL?@;I>DT@-hzcYbPea#i>G>6!M)f2XV1>tn;-f+HJ)zoJ zCbQy(U8UK6(3O`ridXJs&=oicGfdVrf1{^sbPE^S42^cUgN$bB6Kn!GEx(%1q!jDo zx(yd$+enJJSO~)AurODAlMd9N_m+Ste$k*cFrrOSjN0`X@Rk)Cv2`F5bELNC#RIk> zK`Q7Z&MygMvDF>C6!VemaWx8)F`9m5z^||%Y57^KmI?KXD4`8nC>$3kZ#z!5PYM4h zgIebSk$1cOK9kUQ=LyZjmvtAXvpX}jBw0;8MJ{tea@vukYAMZ~!rV^a2IHcC=;tMX zHMkjdhSjh=jpdcK%-}ia4U{Ygg_F#s%F?oihStHF2UTUXsmo#B14Kt+(?Pn?X>!BU zIpeda1*4PLxQ_U5h}de9pwP1Ux1niDO#7i}>+; zT+q*}w7`ftMsTtsLpZJ@0;R8-34eQ)D9Q8|TN(P`l|#cl)OwxqS^PN^id9X7D-vq>Ak9hXIJHDr`K ziW#KJ@cj&7l-j`sG-OpY^23Zi-+%cM5}RZTVRt3axtgI0xO6U zK^}M=3gNn*V_QWvU0=w$ogOXsV;QHih$jprq z!__4ZY9@?WD%OEei}97&1X!Np%>aC^8^wvINd?K&&yN)5e9QgQzzz4;|G8a?O|d0U zU)c9H?1W@EA*NCNmn%Iw5bfm}4^dkqG;>ul-9*bJ7ZVfYwSui`4R+}&;Rbt zH1D#&0Ci+7lcZY2^Xu>rCbRKsn`PWupuHiVRwrVq50LDO$AWhyY+~L=4_Gc$@p$!* zL>b+;VE@&$Wff%Sxc64Kx5)MuGu>8TyPoR$MIW1dGZ%@!N)Mc_8$+`#8sn_} zCp|!bJyLw#xW=8;2TkbPZ+_BRVT8J^Mn*}OK9%+lNO4*=TLOCer2BL~{+RbJq2Ob- zM(Imf?Ksg3#7z2fKy^s^lxq>yypBI$Yo`SefeDDiz|dn8mquA+nO6K@K4kU+dQqrM zKy3wXrh=|9Gfr2F^TsQYyErmgc}otoTbQA3?}xXn$cAS=*g}+3ol2`m+$zY_N9|#b zNKSU1tQlJH_$kaAi~#^#IShFn>>0KnAP(x)fk(~*fAM8euF$z+{%;i6et2r5jH?O7 zqpErg8eH$q+2tR6TTD0d&Utn@288dNDuK@3anb}p0fxr{#A zZtoz#{isiO1^<4S>+KF9(@LZ_L&?&QMCodx4)Gha0kG@kliq{1CEsm48FF{^uqV5? z43`p5jBLc~hktL*d8#&0q=_>zVA=PAQHCyOiR85Xb@GdC z?X&R(+I>g`l`=|$5^7ivsEfK8b1@|#UI|lOJ7x59RWLH0OZKyas$cu^ESnIThKP(N zHJfZq@+>qXL^ln>GU%wBl~(PQEwHgRdSGYLG|99LK##ViE(%Us|JsMVDM>>QE1^nc zyJopZ6U!q9?-suaDW633y~%i2pjoFHTwP|?N?`w@R;q49I8^-w)`&}2;uy}LC3%cA z+lR!J8YXs$l((SLlzzg+kWP=AcF}XN_EUL0C;&@j%Tn||V=-3(>9c0kV%8d83Xrgj z5!epFwTvvzsQ18@F?4~7wcDfXTbmru1d)OkQPuaPtb3*4USY=Rp(vOx7#D9C!m?WO zH!kONW6hjw+@UK2f$)WyuPku^I-9WLV$S+u8BI7m+$%t!QmV&cjxFuG1tDfpbfeBr zo6n?K5}<6PLBy5|X|oGiCxbYH9PRh1ZirPkYcbaR%o2ucpw}!;>5p?56`Um&D|x#_ z>e+C$Muf*GfV@+>B-bTCL!ib$yY0y-Y_SlqU>`>vb)9pm_Gi8woK%9*!x74i203vI zdo@TWpdG~gkH#DJWS{0nb+}f?4GFFBx-SM>sI=U=*~B7ggIcKqx;q30 z=ySQgl&o~1?lhSb>!2WvEH%@?01W(8zFqE)8kr%7r9Mc>xaSdCHPy4kKY{FOx(&XN zh#TA$I%01;uxekky4wDrW8kBQ#AcE{CxW+l^pL!N0dH`wfV&>%`!YC9{et`v$!j5q zqmn6WIft4;Jo)orrn}&8uJC*8ViA91MTPCsL#mD$a&_}9HZK$0jmU)U0V^7C`+HV5 zZ@H@Kz%Zz9RlUD^Ub?{QYwZ(eP0Qoki30_4Dzc!_r46Z(M-|iD`23Y!@8Z60=sLa2 zRTMk;jS<>4ElN1V>AYjfV~EHv>@~`m4ZQL9b`uM`ik`B2->P=TRhujtZ=18Tc}Dq< zz2VGt6Xk6ZnpKH8UV_ASgpBz;##Wx^w1s&o4KD>&gQ)JhX3U=vRVpV9jIatAIL%?H zFc^WY0c^Krwn09%4wKXT1HAQU;VviUos;0 zb}L}3;RK_N1aXoa^aXqNHQ!Jwr;CTd?@7jdw_38^8b{_UsPVS%14wx1yZ^b#wPa639V=t9%6q-olR;RAp!?1bAeadS>?gnyiFp1f2zITn^xO%%_AblCC?aOUPL zRw&M9QaHg?i*%ZUqsPXA(cgxq4+6c+H%<1K^>m(_%gD_3?Ow&cx|UQUT2q@wXc+`j z=LCoiAxiz(gLPN$yeh=@jd^r+Q^e28Pr{Ge?80#qDbCGsQlUEB4{LeLaGk0w?on3i zWY_xfuu5VjoR!c6Mf+t#9BSsJdi{3-?ZExG7;M?D;31E5{<|S$>kvo>9C*y)f9tkJ zb!yx(9eaPZTH*xPtQ&SY-tA`KRbZ2&@PI1W=^=l7?j1zqZA?mUQmbG3pqI!B^&35O$Lt^CGy$mv^fAx3TYArb*>~gdJIN#rfHS2|6~E`s7g*uh z^oVxV9ysDEn`PSFH#UQB$&lf2!~!ouuuuN85<+_-se-t7ep$;VLBV#R}7y`r(U3 zx<5NBa2DvMs(gY)<$_TT2`=i$S?=Hq=|wL^HEFCfL&T{4H${zf5xsSY7Pjdg<_a`{ zMFpB)xys{M2SFhBP%VwX)YbLbE;n_7sVYT*se^O9QsZAPQ1p~Il%P5rIOqUSN1(e7taj2 z9Fjl3uS57G?-B`*Bwo!=Wj6BIUrS(L5dYpHKIPNXd+?-@-XBSynxDo=g=)L~JbRzU zMT*p4jy;KgIXd9$)^JY$Z7PPl`IqATsQRxIZ_~8rxuXO)^bbbsrk?ydHDQd*z87Zt zKmemy*rhZEwhMDKqgQrplH=CA2eiWlxg&hl2R_n+gx9v}9(Y$7p2;vBi;KdOBD{({ zF%#&HBMRLI*8s|c!uFHn9Wk^_5W^Z@P->|e_4-@$3eewC(bAJ-FM(gp>0sW>nkoD&v&C0$_G>h}$FrW@47E~eH;ywMEs=7zhMVzY|&=SHWQk;+a6gzpN)yULHT@B zF*iY>7EN<*kmd>vXec=}Wgf)J{?u`WR;qs`4>B!);gTj5J;LI0 zHIT*Q!GKpeW{-N?I>LB zd7dlaKwmk@va`PLO?b~7dExJPRRtT%CPNw`wV=qs1A|~rc-e&%;uw$xi`j21tj}mO zOm3O3|7bClekO2%qgU0AT)^bFM?^KaGJ`KBTCm!{i8MA%i*d9a0S_Ta@8s;H4#&Gx zuvd2N(kb!DmkLk8xOf197&Q8}R&P76QZf2Sqvgcs?@*N+=GIY}R_4KrpCmf0Z7xEY8Q>>t^Czk|V1W`tGZ$8K zhb0Rpr@9K0nbD?c68K|QGVUDK`ylQjg`9$~%xRuR{2CQ{Q55-JMmdnEJr&-8K&@hd zH2EtB3fN4fJBlZ%k!|o!cAQ?tp(cwc37%>(M+^F^&C@_p_?-HJrF<2ukf5^f{jhQ{ z*O#^rUvd;f>=Q#mJNP`2Ga{^lHBv145V^NpZmwRW!yAhfMmwBnk>On#`hQt28~@94 z2`150N?rhhT5UWl6{^fPjQyO$V){+4&=5A{l(tF4MUB56S|3!51Wbz#jc;lZY>N^c zw@3=D@`XPpZuEDpO)S53JwN5je%+=aO0qdO{#^b{Ek<#9)>^}CqlqR@Q3$^OE#%dz z;{S49perM9)k9wTy)k5VBGK(-g3@`Qt2WN-6UVbPTB?eg2RgSZl3w=r`i*>Pl|}^e@<`WbRzuOME%<{Ezrz{%EkwdE1QTx0k0r4<65N>Q#7-mjGm8*glYvPAvkY~t$lG>4}++C>7-v!&t7$!3AUAO)iD(wA}{h0=;Tq=71PsY_Wiu)i8o8NLAD*~omzB#xgw3bxi+J4vq4&38tvZgy;kX9+?wt@CmU{f28Ld{UcE?cf z_|3n)p{+wqB-3Yq0_{E>AX2jp04$zBOaP7m7Y$77MZjJnw@8Xa2zbG01-%Lo9}QnL z=92va2r#)3-p7!r(15w1*VEr8s~?-zri@*aM~u0s_b`)Iyzs&qwQ7t~)e(-Z@f%~n z6na+Pd|=KJ@O+?#R202HUmZJtG)|qW0;Du2sVUU>BwUUesFsEk+f{U$F!(^ft0W7LL3jIowu-!Yfg5u_af^TafK;Ly2Pp0WcA*_rZs3 zzW^!KVeAyz=V1ASfBr2-;n4Z#rWuL2wea@ZADN2LN9*F@wZw~*`Lytx6$zoecaCBe zYmbL7>c4knaEh?8KpxE>nr@f?Mm^DF=m3JjYQfp5%%Ye~-`RX<;C4x>uA#YL=tI*$ zt8 zTkclG@RsLJGNSqrTq<7C8Yet_tU+qqu;DIIPGqR1+xO>scoIUju)lZ+p};-6H6IvX z*SdDxXAl?JY8!P6(fb94(;3~@st!;=uM7wtDAAj{7E~!h9*#FZ5W7sDa z-Fa)a!}JyySrczs!+TOGcI602(Aa34Vytqp|; zO)N0D2ELHa@3#}9PQ`y4JoJJ8pTYA&{~v<~N9X@x@a&?(a~?u7Mlj*nS76X)rzVsE zwd!eJ^n6YLz_(+ZzPz*_gM)VpD%lzCuV=qh>YieEZJ?YNpF#@bISF6d4Zpx1Yu|fy z!3-F09J6@(SYi#Mk`R+I=C-H)EJPDQL&pd88dwUW$q@Zi>HlSZD*^MS5LWE{aT2uC zH-qsN_c4HKTUzig%gL5!h#j9ZaI@Slv`O$)|7Ct1cUs%xAD#grB0GJBc5Lb}u&403 zb+(QA^w^8E z%MHKyfeNm(rMuFe7&V{>NgzpTf`o5K9|GV+%`4|A^2X-yWXwc8hZJsHG9j!@rp?fc zb)KlAS$Y(dHah?#Gw5SrOv(-!(^-yQI9XT(W`)JAul{qOwzlRnsZ(8~#RF^}6Pslw zY_7MA4mTRQu1ASch-`W*&0)Kdxl@aObFx&nB1oAQKUU8hqEa=T4jFlasYVcDUt{l| zcz>dRU?rHN1LO^i@6Z54xtUiTJ2^-zsr`7_n5MC zJoX=gFso2gD;#5r40}W2!VkST_cS{qw2a=)&PM*-ZgBQh7_nkKMsASXv%%JWsa{6~ zqtiRZsFyy*MO{rI#H!|uL(_o6;ruTrDzl^?al808B4>fsL524+w@CG>BA19QwA2Yo zr!S1i^%M&zrgXz^=U&+fQMoyu%MBrkRtX-F@vH?As%P0rMJVlmgJ~ZB2GgF9o8>0S zekK6?xHe{XL*UpOqOdiYh-&o?KkU4IL@%v7+iLf6=QOs}2EzrjGu;pc_!U#vF z%Y4|pa;pl`x$V~6KGZk zuR5ExYLmChmYM0NL{WzDfh;>K16e~66L~2Iq^zX_jM3oXvGLMo)W;io^`@eiFEp|? zGw7*9LWvO`^5oaN3H9C$YbBM|5Z%WVy=iPwR#v>ddt{+p{Z(bv4bEzl#kZok>A3}6 zX|C4Enu&IXRw~BJO7nt)1r-;5>L_ z7$deXw}v=1B0SKEvG}2V??=QwSU-Vk{+?KqPeV?>yR#FBZ_k&@ap2ExbseAgkM(XG zyiP72H@HQ(o#$h*Y2D7ZNJl-sfj3YJUNp`r-J>nHqcFqcD;4rgd{8O7jgxh34zK3WP z9tac{M&auYV_%BVK}>e}{qiyS^6_Ugx$TrCmR(?lgztl27s6LZF_pdAYY(_R|8SBm1+SLnyc>($9)87jA{S&!5ohTgD-j@SzOf(ahsMzr)fv?RGRXMyS}e=bkvkK*TyLN za9OC%HOGL_49}AA!VE4z;zD*m__LuXLJ6wu{67oDODOwPZvLTHw5C08)Oe0)(uMq?SkuSRtINb>KPIKqFJ0@bSW013VuCLEM!Xf`99DRjG( zW*v7LAX#BuKzU%Wi>P_ZR*Ui(-KkA>h!@FgKEM$vpO?j&UpnJu)ee?W)Y-xIcS|x? z{U=p!()M6<-K>MeUJ{@jzj=Rcsn|b$JU6Qrj@&b?(f@ifpX&E@N}%6HR~@3p2E$10 z1Q_T72{x1dR=e?a`@>h9Kb7>T6*_FT_YD^V=kS5g;1`Pf-5iNT-w6T=W7h>D?DYCX za(IyROxd-^w|McF_1#>ijl|O_+)d?x8zx;EPH%5z5h8`p)|nP7-iUaaR#bfyQHp|? z|Mq$n_?lU^mtg8cS;kA?5)3+eY%T+}c<<}lSe0*b6uhtp+#wX~K}}Na+vVn2(8h&J zemK}N&;(7jjv~jLWV4<~p`~5oIAls1*U(oC1B_H`iv?#m0HVmjL%aYx=daVRsz6B# zNy|^6rY*hlv6LcPS+LWTX|K=3TC9x_uL@$^@zEioQ8Q^ewzvpma55?0aCJevlx?|a zMaz~<%nVr>*Ff$hd`*5KPJ!3LdKh>{O4!8dCJc(EBlmU&;9#(B@Asdcr3b0k{k_g1 z5NNx39g~9djuL`TD-(s5`fB-I@*xPge3_c4{9^0lDwVdV=m@0)y8cBT%Cu0i{IY&u zaX|5BRaHjXer-LD`=>%!L^{V)v!7vvrv)X~Yt^4Wdu&fS;}J7UU*Bq3FS0HR7I(vI z2VE@QYsr@>RA1GkORx7RIzO2)9ewk1YqzU@vxW#B*9P+S9PW?bbSE-CYfw>wdDgu57M~@A=V-Q@{s(gO$Hg6y8e%}`q z1j2n?=bzl@;1>Oi|@e0L1BFvE zT<9QMJyo_8yeK^JjO5VXf%oOfpdq@K{|E<9)>(Y!hrjWTzoA8-mp3>r0eZVeGBsM% z7Y9W^tQ(j!OIegdx{eEuTol^-G@K5?7>oflQn%^f!J*SOo9xM*Cc5jOz=WZ(5jlYj zOy6*c=Itcu(U}X?l&zv&`1)(R{zQ&1AR(tgOkq3piAl1}z4URe2NsuY*1KrIG6ost z26(@q5K<~xEr}ICBb1vq(8E2T!()kPU7f(-)!fns7vxTX-9Rrhrb0Xd8{bq4ya_wZ z%DFK%+LlE@jOw%zBK+tEee2D6M5xU0I0@c0KE`B0or9YHfzn1-H?sn@aTbD|dEOG< z>#Y9D8Y;kmx>&orFkZ%(*)v$Oc#x@w6#VM z&sJlpnhFpLTmbky>;|z@oz`BhrN@--TQ7FoGuT*MefgOxUmIzv5)xwl(Ox6TgXTty)ifM zS6(l#AI6Cu`cL~Gfr8Wj-VlcG?2_Jbw|5SGY!0WbLO!`m6gF`Xp8n)W^xQ}?4g?g;vy#> zfS**(xOm%iP(%T}l0iC{z!gXrmyZu!AYi!&bu+uaTAZ#l;=*!D$&@4Y-gkYGuT_Wo zmuAH-YJSuZ68vY}?e_i%#;Zc?;leJk6^`60I(>3V&-U|=15$o^u^PD#^4#y8EJjX0 z&EGVRpaTYl6i1yOH*rInImTf7)}3jgo}pt<)BRW==L@Ab6yN>9NH?0z=2CqNyLUUQVitGN2@^3gysFDueb%1 zXBanyg8O2rTx5>aeDcYW@c=MfQo#{~_`*ZGhk#|L6-2y4JqJkpiDC$Qb1#Yy{*b}n zcG?lcT%7l0izvYX1(=y|IV5^6!!|4cF|=S0V7PHZWOGK~{^^-C@J9wS2LtkQ4)%A; zH986Cy)O!y4&>$A;)El|r2S+om(7~6vgO6E1~08*)VxZZkO`+e2LZ2yh6%uNcY|fw z=JVfup8F%~o!eO#3)x(5;5BiE_~WgVG=sHN2@o=%GP`0zr$}TS>cPJc z9oe3kx*L#X`VmmWe1K;NGYq*M6PbcEAm6r=o2wV{;thUVW06Tz-ox^cri%pFYE@n3 z-ed_&`jz9$%HjsdP9;qEihGT97ZLLlG8{}IX1ha9sHZbUlH=VY_iNt${csq3uNI}A^J$JE(?fFtkrJq1y80IEjpCi2UD;ol0^MEz6>2|L8ug6o6s7#$Cb zvvZmD$3kl6nF21f>rDd^JnBbO>au;LtH7LP*HLLfiY~pnDhP< zSe#1y*UE=ms{mvqciyt_GD%-3WZbR~SOfRhL+&E@4#Xw`SNzc=2o}(q-cVD=f>5Na$-bBw9-E#X;rl|^G5Fy z3{BKkD2Rq*(BmLLkqE{sRCHEV=M_7M#_>X5Y1qc{lGs0>g(2~g9mdv1cw{3hN@aYd z$5+N4Q8-2M89=gvUBrpM9(m}bI1zH3UI+1u=TDEqgA!NRrRr+hG#WAmpc2 z&-yn^;1AfKZ&Zg~AMzvw)WDATa}w)cGg=b7RhX0D`0KOd5*rl#%dl*4YM)1Ua@!Rld7c^c$YO2eU|4hk)g))T zfZAYA=VF;(AT0~neUqp%TahQJYc0X5j_z_sIcVM!&mO`vLiB3|qg&ffV1La^jJ7)7 z9?b}6lwj-=Jh`25wfiE2wzf606?It?Q!4+!&nK)(e5smL}z0Z=;0UCogTdi~!0-cy+MMTe>O+mQ^ zeinohf3iSG5b+d~csYhupgjuAcnVhuMKsQDqV*MT4PR2yDV?q&ozkC%BtB1uk}jhM zn)~Ys-@}h$Y9GbakeuRQ(ov<94ps!nfHfe@PrHO>p***Wiant{!bi<$%VKh*EBKh;&6Hn_HcbTgo zIpkiX(cV*1L?c1Y3CiH(L_mVfJ`2b&!AYHI;9x?Z3qCYRl|lf-oO;OkBIL?Et5^|x zIJvZ+lKB?ll%f-J&bWD6b*~`c-pk9k_v*#lds*o=O$0DCKou`Uj+M7uOtlmp3nf+@<~NU6ayYf zbTk&MHDL_|^qBjBws*uKv_+S zmUBcG<#SpwBcXSkQgOaEGza9GB`gWzI39xW_at1G`9QY7br--yQBt$1tfnwTi}IQ> ziOo7Pn=+}*T5_8*$&Iz_W;@awO@33N$QEEqSi0p&9qd@QO_P#Dk^NOj>~ZMP(!*yM zNHH)IwxPg`0?a}YV<;w?>F=3f!Z5EVr~X|3;Tzg-JYYB&V7-CZCA&}2AzgJeZqK|S z-Q*MY9CnBUgGuOePeL)0=p-`>L{$I)_JI{lQAOS+kB~(f#g@IW9sHhw`Coy17v^b6lT+P_ZEsTIk=B2Ho=t)d>pGywt`ZH=&6bgN9NQyZ}<7kcGdtIyoHJm>bJfJecBC8IA9vLVa|E_g+F*#mfpzIW>) zwfT*Ney92~ZF?8A&b6(0;=F?!{NS=kb0U_>cQtpj@4kI?Fv~3O=kT7tIKg1kW8>;0 z36m!JP5eLq@BfF2sD&3jsBW_0Q-uH#&};l9wgX5km#c_0A0Z8iPw5s%oQEAqTgC#> zuKEM8K6Z-)4d*WC9o#ZfHnGWy@v1DzFCVs3bx5?ZJfW#()0Lw2qKWuv351Y+ z7Wm(yJ=b27LDyd1qDxJylJ9k#3eHz+xHI0Dg3y+6zoiJS$N^XIz$$F#G2angyiycc zWJD}m6ta|ySo)WFr8ip8t&= z{}rD8W%{uSEf%kCp*ky9p4F(&=pkn{Wnzw+woLY*Y0TtLS*=+%B}T0nP{*95vFYoj zf;7FQTM+I2!6b0omrv=krIzJIo!F-n`)pC{(+0ttRd{5eSE3F3Dg+7jAByu@ig{kB2mw(2Ybs hT0^`F@P)29wf)lGg Date: Mon, 22 Apr 2024 13:33:53 +0200 Subject: [PATCH 1154/1263] Updated vault-helm to v0.28.0 --- hashicorp-vault/Chart.yaml | 2 +- hashicorp-vault/charts/vault-0.27.0.tgz | Bin 49088 -> 0 bytes hashicorp-vault/charts/vault-0.28.0.tgz | Bin 0 -> 49079 bytes hashicorp-vault/values.yaml | 2 +- ...ault-industrial-edge-factory.expected.yaml | 22 ++++++++++-------- ...rp-vault-industrial-edge-hub.expected.yaml | 22 ++++++++++-------- ...-vault-medical-diagnosis-hub.expected.yaml | 22 ++++++++++-------- tests/hashicorp-vault-naked.expected.yaml | 22 ++++++++++-------- tests/hashicorp-vault-normal.expected.yaml | 22 ++++++++++-------- 9 files changed, 62 insertions(+), 52 deletions(-) delete mode 100644 hashicorp-vault/charts/vault-0.27.0.tgz create mode 100644 hashicorp-vault/charts/vault-0.28.0.tgz diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index b9a20124..e1577595 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -6,5 +6,5 @@ name: hashicorp-vault version: 0.0.1 dependencies: - name: vault - version: "0.27.0" + version: "0.28.0" repository: "https://helm.releases.hashicorp.com" diff --git a/hashicorp-vault/charts/vault-0.27.0.tgz b/hashicorp-vault/charts/vault-0.27.0.tgz deleted file mode 100644 index 24a07991517b476c1391b305d0953e9e231a030c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49088 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

      e9?vX&2{P`z*yEQ@&1J8iL1Z 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 Ls1SjK0N4Zo6jePG literal 0 HcmV?d00001 diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index cb1b769f..fa73a060 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -48,4 +48,4 @@ vault: termination: "reencrypt" image: repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.15.6-ubi" + tag: "1.16.1-ubi" diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index 0f2218b0..74212ee2 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,12 +18,13 @@ metadata: name: hashicorp-vault-config namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm data: extraconfig-from-values.hcl: |- + disable_mlock = true ui = true listener "tcp" { @@ -42,7 +43,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +64,7 @@ metadata: name: hashicorp-vault-internal namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +96,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +127,7 @@ metadata: name: hashicorp-vault-ui namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -167,10 +168,11 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server + annotations: spec: affinity: @@ -204,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -346,7 +348,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -373,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index 0f2218b0..74212ee2 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,12 +18,13 @@ metadata: name: hashicorp-vault-config namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm data: extraconfig-from-values.hcl: |- + disable_mlock = true ui = true listener "tcp" { @@ -42,7 +43,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +64,7 @@ metadata: name: hashicorp-vault-internal namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +96,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +127,7 @@ metadata: name: hashicorp-vault-ui namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -167,10 +168,11 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server + annotations: spec: affinity: @@ -204,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -346,7 +348,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -373,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index 0f2218b0..74212ee2 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,12 +18,13 @@ metadata: name: hashicorp-vault-config namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm data: extraconfig-from-values.hcl: |- + disable_mlock = true ui = true listener "tcp" { @@ -42,7 +43,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +64,7 @@ metadata: name: hashicorp-vault-internal namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +96,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +127,7 @@ metadata: name: hashicorp-vault-ui namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -167,10 +168,11 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server + annotations: spec: affinity: @@ -204,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -346,7 +348,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -373,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index 68b5a897..1fe2cd90 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,12 +18,13 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm data: extraconfig-from-values.hcl: |- + disable_mlock = true ui = true listener "tcp" { @@ -42,7 +43,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +64,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +96,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +127,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -167,10 +168,11 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server + annotations: spec: affinity: @@ -204,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -346,7 +348,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -373,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index 0f2218b0..74212ee2 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,12 +18,13 @@ metadata: name: hashicorp-vault-config namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm data: extraconfig-from-values.hcl: |- + disable_mlock = true ui = true listener "tcp" { @@ -42,7 +43,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -63,7 +64,7 @@ metadata: name: hashicorp-vault-internal namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -95,7 +96,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -126,7 +127,7 @@ metadata: name: hashicorp-vault-ui namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -167,10 +168,11 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server + annotations: spec: affinity: @@ -204,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -346,7 +348,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.27.0 + helm.sh/chart: vault-0.28.0 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -373,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.15.6-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR From dd6be56971f161e47bec55f5aeb5863eac9873be Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 22 Apr 2024 16:19:43 +0200 Subject: [PATCH 1155/1263] Use --recurse-submodules when cloning In case a git repo has some things split over submodules, let's clone those too. The version of git in the imperative image is currently: sh-5.1# git version git version 2.39.3 Tested with: sh-5.1# git clone --recurse-submodules --single-branch --branch main --depth 1 -- "https://github.com/validatedpatterns/multicloud-gitops" /tmp/ Cloning into '/tmp'... remote: Enumerating objects: 426, done. remote: Counting objects: 100% (426/426), done. remote: Compressing objects: 100% (343/343), done. remote: Total 426 (delta 87), reused 221 (delta 40), pack-reused 0 Receiving objects: 100% (426/426), 545.98 KiB | 1.78 MiB/s, done. Resolving deltas: 100% (87/87), done. Co-Authored-By: Sergio Garcia Martinez --- clustergroup/templates/imperative/_helpers.tpl | 4 ++-- tests/clustergroup-industrial-edge-factory.expected.yaml | 2 +- tests/clustergroup-industrial-edge-hub.expected.yaml | 4 ++-- tests/clustergroup-medical-diagnosis-hub.expected.yaml | 4 ++-- tests/clustergroup-naked.expected.yaml | 2 +- tests/clustergroup-normal.expected.yaml | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl index f28b5668..d55220e4 100644 --- a/clustergroup/templates/imperative/_helpers.tpl +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -65,7 +65,7 @@ OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; - git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; {{- end }} @@ -109,7 +109,7 @@ OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; - git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; {{- end }} {{/* Final done container */}} diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 84d4eaa6..f34b5f18 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -408,7 +408,7 @@ spec: OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; - git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index d8158428..6641ff73 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -569,7 +569,7 @@ spec: OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; - git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -668,7 +668,7 @@ spec: OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; - git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 4449986d..a61ce547 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -496,7 +496,7 @@ spec: OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; - git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -595,7 +595,7 @@ spec: OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; - git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 13e66fc8..fdaf4293 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -261,7 +261,7 @@ spec: OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; - git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 0cb1cc33..93f6892a 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -458,7 +458,7 @@ spec: OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; - git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -557,7 +557,7 @@ spec: OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; mkdir /git/{repo,home}; - git clone --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest From 60600dc8b27b64f14040af3a2ee4ed191f83229d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 22 Apr 2024 17:55:34 +0200 Subject: [PATCH 1156/1263] Drop support for IIB on OCP 4.12 The last job we ran with that for IIB was more than 4 months ago. --- ansible/roles/iib_ci/README.md | 11 ----- ansible/roles/iib_ci/defaults/main.yml | 4 -- ansible/roles/iib_ci/tasks/main.yml | 12 ++--- .../iib_ci/tasks/mirror-related-images.yml | 18 -------- .../iib_ci/tasks/setup-external-registry.yml | 45 ------------------- 5 files changed, 6 insertions(+), 84 deletions(-) delete mode 100644 ansible/roles/iib_ci/tasks/setup-external-registry.yml diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 98355ad9..251123f8 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -64,17 +64,6 @@ use that. Run `make iib` with the following environment variables set: * `INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329` * `KUBEADMINPASS="11111-22222-33333-44444"` -### OCP 4.12 and previous versions - -Due to the lack of v2 manifest support on the internal registry, we use an external -registry. Run `make iib` with the following environment variables set: - -* `INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329` -* `REGISTRY=quay.io/rhn_support_mbaldess/iib` -* `REGISTRY_TOKEN=:` - -*Note*: For the REGISTRY_TOKEN go to your quay repository, add a robot with "Write" permissions. The robot created will have a "username" and "password" fields. Set the REGISTRY_TOKEN environment variable to that value. - ## Useful commands * List IIBs for an operator: diff --git a/ansible/roles/iib_ci/defaults/main.yml b/ansible/roles/iib_ci/defaults/main.yml index 7605dba5..9873e072 100644 --- a/ansible/roles/iib_ci/defaults/main.yml +++ b/ansible/roles/iib_ci/defaults/main.yml @@ -1,10 +1,6 @@ rh_internal_registry: registry-proxy.engineering.redhat.com iib_image: "{{ lookup('env', 'INDEX_IMAGE') }}" -external_registry: "{{ lookup('env', 'REGISTRY') }}" -external_registry_token: "{{ lookup('env', 'REGISTRY_TOKEN') }}" -external_registry_email: noemail@localhost - kubeadminpass: "{{ lookup('env', 'KUBEADMINPASS') }}" internal_registry_ns: openshift-marketplace diff --git a/ansible/roles/iib_ci/tasks/main.yml b/ansible/roles/iib_ci/tasks/main.yml index ba6eb7c8..bb50f286 100644 --- a/ansible/roles/iib_ci/tasks/main.yml +++ b/ansible/roles/iib_ci/tasks/main.yml @@ -21,15 +21,15 @@ - name: Is OCP pre OCP 4.13? (aka registry supports v2 manifests) ansible.builtin.set_fact: - use_internal_registry: "{{ oc_version_raw.stdout is version('4.13', '>=') }}" + ocp_413: "{{ oc_version_raw.stdout is version('4.13', '>=') }}" + +- name: Fail if OCP < 4.13 as we do not support it for IIB testing any longer + ansible.builtin.fail: + msg: "OCP versions < 4.13 are not support for IIB loading" + when: not ocp_413 - name: Set up internal registry (OCP >= 4.13) ansible.builtin.include_tasks: setup-internal-registry.yml - when: use_internal_registry - -- name: Set up external registry (OCP < 4.13) - ansible.builtin.include_tasks: setup-external-registry.yml - when: not use_internal_registry - name: Install new IIB in cluster ansible.builtin.include_tasks: install-iib-in-cluster.yml diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml index 32a36c07..bf7356d2 100644 --- a/ansible/roles/iib_ci/tasks/mirror-related-images.yml +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -91,16 +91,6 @@ 'mirrordest_nosha': (mirror_dest + item | basename) | regex_replace('@.*$', ''), 'mirrordest_tag': 'tag-' + item | basename | regex_replace('^.*@sha256:', '')}}, recursive=true) }}" loop: "{{ all_images }}" - when: use_internal_registry - -- name: Create dict with full image name+sha -> mirror destination (OCP < 4.13) - ansible.builtin.set_fact: - image_urls: "{{ image_urls | default({}) | combine({item: - {'mirrordest': mirror_dest + '@' + item | basename | regex_replace('^.*@', ''), - 'mirrordest_nosha': mirror_dest, - 'mirrordest_tag': 'tag-' + item | basename | regex_replace('^.*@sha256:', '')}}, recursive=true) }}" - loop: "{{ all_images }}" - when: not use_internal_registry - name: Create dict with full image name+sha -> image key without sha ansible.builtin.set_fact: @@ -124,14 +114,6 @@ src: ./templates/imageDigestMirror.yaml.j2 dest: "{{ iib_local_folder }}/imageMirror.yaml" mode: "0644" - when: use_internal_registry - -- name: Template out imageMirror.yaml (OCP < 4.13) - ansible.builtin.template: - src: ./templates/imageContentSourcePolicy.yaml.j2 - dest: "{{ iib_local_folder }}/imageMirror.yaml" - mode: "0644" - when: not use_internal_registry - name: Template out mirror.map ansible.builtin.template: diff --git a/ansible/roles/iib_ci/tasks/setup-external-registry.yml b/ansible/roles/iib_ci/tasks/setup-external-registry.yml deleted file mode 100644 index a9a9b10a..00000000 --- a/ansible/roles/iib_ci/tasks/setup-external-registry.yml +++ /dev/null @@ -1,45 +0,0 @@ -- name: Check that we can push to the external registry - ansible.builtin.fail: - msg: "REGISTRY: '{{ external_registry }}' and REGISTRY_TOKEN: '{{ external_registry_token }}'. Both need to be set" - failed_when: > - (external_registry is not defined or external_registry | length == 0) or - (external_registry_token is not defined or external_registry_token | length == 0) - -- name: Get current cluster pull secrets - ansible.builtin.command: - oc extract secret/pull-secret -n openshift-config --to=- - register: pull_secrets_raw - -- name: Add external registry to pull secrets and set auth fact - ansible.builtin.set_fact: - pull_secrets_new: "{{ pull_secrets_raw.stdout | from_json }}" - external_registry_auth: "{{ external_registry_token | b64encode }}" - -- name: Add local registry to pull secrets - ansible.builtin.set_fact: - pull_secrets: "{{ pull_secrets_new | combine({'auths': {external_registry.split('/')[0]: {'email': external_registry_email, 'auth': external_registry_auth}}}, recursive=true) }}" - -- name: Get a tempfile for the pull secrets - ansible.builtin.tempfile: - state: directory - register: pull_secrets_tempfolder - -- name: Store pull secrets in tempfile - ansible.builtin.copy: - dest: "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" - content: "{{ pull_secrets | to_nice_json }}" - mode: "0644" - -# We cannot store the logins back in the cluster, because quay.io would be overwritten and not have -# access to the images openshift needs. See: -# https://github.com/moby/moby/issues/37569 -# - name: Update pull-secret in the cluster -# ansible.builtin.shell: | -# oc set data secret/pull-secret -n openshift-config --from-file="{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" -- name: Set Mirror URL fact for external mirror IIB - ansible.builtin.set_fact: - mirror_iib: "{{ external_registry }}" - -- name: Set Mirror URL fact for external mirror - ansible.builtin.set_fact: - mirror_dest: "{{ external_registry }}" From f50deb9c11090b2dffbb6da07431eb560cda671b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 22 Apr 2024 18:08:17 +0200 Subject: [PATCH 1157/1263] Stop saving stderr in a file when running skopeo --- ansible/roles/iib_ci/tasks/mirror-related-images.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml index bf7356d2..16b386ff 100644 --- a/ansible/roles/iib_ci/tasks/mirror-related-images.yml +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -33,9 +33,9 @@ right=$(grep "{{ image }}" "{{ iib_local_folder }}/mapping.txt" | cut -f2 -d=) right_base=$(echo $right | sed -e 's/:.*$//' -e 's/@.*$//') right_log=$(echo "${right_base}@${left_sha}" | sed -e 's/\//-/g') - if skopeo inspect --authfile "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" --no-tags docker://"{{ image }}" &> /tmp/skopeo-"{{ image | regex_replace('/', '-') }}".log; then + if skopeo inspect --authfile "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" --no-tags docker://"{{ image }}" > /tmp/skopeo-"{{ image | regex_replace('/', '-') }}".log; then echo "{{ image }}" - elif skopeo inspect --authfile "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" --no-tags docker://"${right_base}@${left_sha}" &> "/tmp/skopeo-${right_log}.log"; then + elif skopeo inspect --authfile "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" --no-tags docker://"${right_base}@${left_sha}" > "/tmp/skopeo-${right_log}.log"; then echo "${right_base}@${left_sha}" else echo "ERROR: both {{ image }} and echo ${right_base}@${left_sha} could not be found" From 5702167854b652b95f917b53a12d5aa858932650 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 22 Apr 2024 18:36:29 +0200 Subject: [PATCH 1158/1263] Rearchitect the code so we can print out the actual error --- .../iib_ci/tasks/mirror-related-images.yml | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml index 16b386ff..f502197a 100644 --- a/ansible/roles/iib_ci/tasks/mirror-related-images.yml +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -33,13 +33,24 @@ right=$(grep "{{ image }}" "{{ iib_local_folder }}/mapping.txt" | cut -f2 -d=) right_base=$(echo $right | sed -e 's/:.*$//' -e 's/@.*$//') right_log=$(echo "${right_base}@${left_sha}" | sed -e 's/\//-/g') - if skopeo inspect --authfile "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" --no-tags docker://"{{ image }}" > /tmp/skopeo-"{{ image | regex_replace('/', '-') }}".log; then + left_out=$(skopeo inspect --authfile "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" --no-tags docker://"{{ image }}" 2>&1) + left_ret=$? + if [ $left_ret -eq 0 ]; then echo "{{ image }}" - elif skopeo inspect --authfile "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" --no-tags docker://"${right_base}@${left_sha}" > "/tmp/skopeo-${right_log}.log"; then - echo "${right_base}@${left_sha}" else - echo "ERROR: both {{ image }} and echo ${right_base}@${left_sha} could not be found" - exit 1 + echo "${left_out}" > /tmp/skopeo-"{{ image | regex_replace('/', '-') }}".log + right_out=$(skopeo inspect --authfile "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" --no-tags docker://"${right_base}@${left_sha}" 2>&1) + right_ret=$? + if [ $right_ret -eq 0 ]; then + echo "${right_base}@${left_sha}" + else # both left_ret and right_ret were != 0 + echo "${right_out}" > "/tmp/skopeo-${right_log}.log" + echo "ERROR: both {{ image }} and echo ${right_base}@${left_sha} could not be found" + echo "Printing both outputs:" + echo "Left out: ${left_out}" + echo "Right out: ${right_out}" + exit 1 + fi fi register: all_existing_images with_items: "{{ all_images }}" From 109027c4330d1e59032cc8042f88e3d22077a2cc Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 22 Apr 2024 20:55:18 +0200 Subject: [PATCH 1159/1263] Check for prereqs for IIB --- ansible/roles/iib_ci/tasks/main.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ansible/roles/iib_ci/tasks/main.yml b/ansible/roles/iib_ci/tasks/main.yml index bb50f286..6500b135 100644 --- a/ansible/roles/iib_ci/tasks/main.yml +++ b/ansible/roles/iib_ci/tasks/main.yml @@ -4,6 +4,14 @@ failed_when: (iib_image is not defined or iib_image | length == 0) +- name: Check for pre-requisite binaries presence + ansible.builtin.shell: | + which "{{ item }}" + with_items: + - skopeo + - oc + - podman + - name: Set IIB fact ansible.builtin.set_fact: iib: "{{ iib_image.split(':')[1] }}" From a073b8c4c8101c42cc7f8c52b81b3c2a5324a667 Mon Sep 17 00:00:00 2001 From: Tomer Figenblat Date: Tue, 23 Apr 2024 17:07:47 -0400 Subject: [PATCH 1160/1263] chore: added annotations controling gitops and fail for missing meta for clusterdeployments Signed-off-by: Tomer Figenblat --- acm/templates/provision/clusterdeployment.yaml | 12 ++++++++++++ tests/acm-normal.expected.yaml | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/acm/templates/provision/clusterdeployment.yaml b/acm/templates/provision/clusterdeployment.yaml index cc37c161..f7f71a52 100644 --- a/acm/templates/provision/clusterdeployment.yaml +++ b/acm/templates/provision/clusterdeployment.yaml @@ -3,6 +3,14 @@ {{- range $group.clusterDeployments}} {{ $cluster := . }} + +{{- if (eq $cluster.name nil) }} +{{- fail (printf "managedClusterGroup clusterDeployment cluster name is empty: %s" $cluster) }} +{{- end }} +{{- if (eq $group.name nil) }} +{{- fail (printf "managedClusterGroup clusterDeployment group name is empty: %s" $cluster) }} +{{- end }} + {{- $deploymentName := print $cluster.name "-" $group.name }} {{- $cloud := "None" }} @@ -30,6 +38,8 @@ metadata: namespace: {{ $deploymentName }} labels: vendor: OpenShift + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: baseDomain: {{ $cluster.baseDomain }} clusterName: {{ $deploymentName }} @@ -65,6 +75,8 @@ metadata: {{- end }} {{- end }} name: {{ $deploymentName }} + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: hubAcceptsClient: true {{- end }}{{- /* range $group.clusterDeployments */}} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 1e63d2e7..b3627634 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -101,6 +101,8 @@ metadata: namespace: aws-cd-one-w-pool-acm-provision-edge labels: vendor: OpenShift + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: baseDomain: blueprints.rhecoeng.com clusterName: aws-cd-one-w-pool-acm-provision-edge @@ -128,6 +130,8 @@ metadata: namespace: aws-cd-two-wo-pool-acm-provision-on-deploy labels: vendor: OpenShift + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: baseDomain: blueprints.rhecoeng.com clusterName: aws-cd-two-wo-pool-acm-provision-on-deploy @@ -719,6 +723,8 @@ metadata: cluster.open-cluster-management.io/clusterset: acm-provision-edge clusterGroup: region name: aws-cd-one-w-pool-acm-provision-edge + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: hubAcceptsClient: true --- @@ -730,6 +736,8 @@ metadata: cluster.open-cluster-management.io/clusterset: acm-provision-on-deploy clusterGroup: acm-provision-on-deploy name: aws-cd-two-wo-pool-acm-provision-on-deploy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: hubAcceptsClient: true --- From c6ffd0ecdab72ff98f3cf89271ab4c5433c9f5bf Mon Sep 17 00:00:00 2001 From: Tomer Figenblat Date: Fri, 26 Apr 2024 11:05:33 -0400 Subject: [PATCH 1161/1263] chore: removed managedclusterset spec Signed-off-by: Tomer Figenblat --- acm/templates/provision/managedclusterset.yaml | 3 --- tests/acm-normal.expected.yaml | 6 ------ 2 files changed, 9 deletions(-) diff --git a/acm/templates/provision/managedclusterset.yaml b/acm/templates/provision/managedclusterset.yaml index dce01f73..2c8eaffa 100644 --- a/acm/templates/provision/managedclusterset.yaml +++ b/acm/templates/provision/managedclusterset.yaml @@ -8,9 +8,6 @@ metadata: cluster.open-cluster-management.io/submariner-broker-ns: {{ .name }}-broker argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true name: {{ .name }} -spec: - clusterSelector: - selectorType: LegacyClusterSetLabel {{- end }}{{- /* if .clusterPools) */}} {{- end }}{{- /* range .Values.clusterGroup.managedClusterGroups */}} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index b3627634..3e6fbc74 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -749,9 +749,6 @@ metadata: cluster.open-cluster-management.io/submariner-broker-ns: acm-provision-edge-broker argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true name: acm-provision-edge -spec: - clusterSelector: - selectorType: LegacyClusterSetLabel --- # Source: acm/templates/provision/managedclusterset.yaml apiVersion: cluster.open-cluster-management.io/v1beta2 @@ -761,9 +758,6 @@ metadata: cluster.open-cluster-management.io/submariner-broker-ns: acm-provision-on-deploy-broker argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true name: acm-provision-on-deploy -spec: - clusterSelector: - selectorType: LegacyClusterSetLabel --- # Source: acm/templates/multiclusterhub.yaml apiVersion: operator.open-cluster-management.io/v1 From 04951900f44864adafac72c18f1506a608d5edb3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 30 Apr 2024 11:23:43 +0200 Subject: [PATCH 1162/1263] Rework IIB loading support This adds multiple IIB support in order to cater for the ACM IIB case, where the MCE operator might be in another IIB completely. To load multiple IIB for different operators, one needs to set the env variables as follows: export OPERATOR=advanced-cluster-management,multicluster-engine export INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:713808,registry-proxy.engineering.redhat.com/rh-osbs/iib:718034 It is implied that the first IIB is for the first operator in the list, and the second one for the latter. Tested gitops-iib and it all worked with no changes. --- Makefile | 4 +- ansible/roles/iib_ci/README.md | 8 ---- ansible/roles/iib_ci/defaults/main.yml | 2 +- .../iib_ci/tasks/fetch-operator-images.yml | 20 ++++---- .../iib_ci/tasks/install-iib-in-cluster.yml | 10 ++-- ansible/roles/iib_ci/tasks/main.yml | 48 ++++++++++++++----- .../iib_ci/tasks/mirror-related-images.yml | 10 ++-- .../iib_ci/templates/catalogSource.yaml.j2 | 6 +-- .../templates/imageDigestMirror.yaml.j2 | 12 ++--- 9 files changed, 69 insertions(+), 51 deletions(-) diff --git a/Makefile b/Makefile index 7882a9ce..86cb5177 100644 --- a/Makefile +++ b/Makefile @@ -113,9 +113,7 @@ secrets-backend-none: ## Edits values files to remove secrets manager + ESO .PHONY: load-iib load-iib: ## CI target to install Index Image Bundles @set -e; if [ x$(INDEX_IMAGES) != x ]; then \ - for IIB in $(shell echo $(INDEX_IMAGES) | tr ',' '\n'); do \ - INDEX_IMAGE="$${IIB}" ansible-playbook common/ansible/playbooks/iib-ci/iib-ci.yaml; \ - done; \ + ansible-playbook common/ansible/playbooks/iib-ci/iib-ci.yaml; \ else \ echo "No INDEX_IMAGES defined. Bailing out"; \ exit 1; \ diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 251123f8..6afccb1a 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -56,14 +56,6 @@ make EXTRA_HELM_OPTS="--set main.extraParameters[0].name=clusterGroup.subscripti *Note*: In this case `acm` is the name of the subscription in `values-hub.yaml` -### OCP 4.13 and onwards - -Since 4.13 supports an internal registry that can cope with v2 docker manifests, we -use that. Run `make iib` with the following environment variables set: - -* `INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:492329` -* `KUBEADMINPASS="11111-22222-33333-44444"` - ## Useful commands * List IIBs for an operator: diff --git a/ansible/roles/iib_ci/defaults/main.yml b/ansible/roles/iib_ci/defaults/main.yml index 9873e072..397be608 100644 --- a/ansible/roles/iib_ci/defaults/main.yml +++ b/ansible/roles/iib_ci/defaults/main.yml @@ -1,5 +1,5 @@ rh_internal_registry: registry-proxy.engineering.redhat.com -iib_image: "{{ lookup('env', 'INDEX_IMAGE') }}" +iib_images: "{{ lookup('env', 'INDEX_IMAGES') }}" kubeadminpass: "{{ lookup('env', 'KUBEADMINPASS') }}" diff --git a/ansible/roles/iib_ci/tasks/fetch-operator-images.yml b/ansible/roles/iib_ci/tasks/fetch-operator-images.yml index 11df26cc..391f2ac2 100644 --- a/ansible/roles/iib_ci/tasks/fetch-operator-images.yml +++ b/ansible/roles/iib_ci/tasks/fetch-operator-images.yml @@ -2,9 +2,9 @@ # the operator name is defined in the variable "item". This # set of tasks is to be included in a loop that goes over the # needed operators -- name: Get default channel in the IIB for "{{ item }}" +- name: Get default channel in the IIB for "{{ item.key }}" ansible.builtin.shell: | - oc get -n "{{ internal_registry_ns }}" packagemanifests -l "catalog=iib-{{ iib }}" --field-selector "metadata.name={{ item }}" \ + oc get -n "{{ internal_registry_ns }}" packagemanifests -l "catalog=iib-{{ item.value['iib'] }}" --field-selector "metadata.name={{ item.key }}" \ -o jsonpath='{.items[0].status.defaultChannel}' register: default_channel_raw retries: 10 @@ -15,9 +15,13 @@ ansible.builtin.set_fact: default_channel: "{{ default_channel_raw.stdout }}" -- name: Get all related images in the IIB for "{{ item }}" +- name: Print default channel + ansible.builtin.debug: + msg: "Default channel for {{ item.key }}: {{ default_channel }}" + +- name: Get all related images in the IIB for "{{ item.key }}" ansible.builtin.shell: | - oc get packagemanifests -l "catalog=iib-{{ iib }}" --field-selector "metadata.name={{ item }}" \ + oc get packagemanifests -l "catalog=iib-{{ item.value['iib'] }}" --field-selector "metadata.name={{ item.key }}" \ -o jsonpath="{.items[0].status.channels[?(@.name==\"{{ default_channel }}\")].currentCSVDesc.relatedImages}" register: related_images_raw retries: 5 @@ -29,7 +33,7 @@ related_images: "{{ related_images_raw.stdout }}" # NOTE(bandini) -# The following code is here to fund out what the operator bundle image is and to make +# The following code is here to find out what the operator bundle image is and to make # sure it is on the internal registry. # This is all potentially hacky, but so far I could not find a single place in the cluster # where the olm.bundle image is available. The info is in there in the IIB, but it certainly @@ -37,7 +41,7 @@ # alpha commands inside the IIB image locally - name: Pull the IIB locally ansible.builtin.command: - podman pull "{{ iib_image }}" + podman pull "{{ item.value['iib_image'] }}" # $ opm alpha list channels /configs advanced-cluster-management # PACKAGE CHANNEL HEAD @@ -46,7 +50,7 @@ - name: Read the operator bundle from the default channel ansible.builtin.shell: | set -o pipefail - podman run -it --rm "{{ iib_image }}" alpha list channels /configs "{{ item }}" | grep -E "(\s){{ default_channel }}(\s)" | awk '{ print $3 }' + podman run -it --rm "{{ item.value['iib_image'] }}" alpha list channels /configs "{{ item.key }}" | grep -E "(\s){{ default_channel }}(\s)" | awk '{ print $3 }' register: bundle_channel_raw - name: Set bundle fact @@ -70,7 +74,7 @@ - name: Get bundle image ansible.builtin.shell: | set -o pipefail - podman run -it --rm "{{ iib_image }}" alpha list bundles /configs "{{ item }}" | grep -e "{{ default_channel }}\s\+{{ bundle_channel }}" | awk '{ print $NF }' + podman run -it --rm "{{ item.value['iib_image'] }}" alpha list bundles /configs "{{ item.key }}" | grep -e "{{ default_channel }}\s\+{{ bundle_channel }}" | awk '{ print $NF }' register: bundle_image_raw - name: Set bundle image fact diff --git a/ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml b/ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml index 4b39184c..76a649b2 100644 --- a/ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml +++ b/ansible/roles/iib_ci/tasks/install-iib-in-cluster.yml @@ -1,3 +1,7 @@ +- name: Set IIB local folder fact + ansible.builtin.set_fact: + iib_local_folder: "/tmp/manifest-{{ item.value['iib'] }}" + - name: Remove manifest folder "{{ iib_local_folder }}" ansible.builtin.file: path: "{{ iib_local_folder }}" @@ -16,14 +20,14 @@ - name: Mirror catalog manifests only to "{{ iib_local_folder }}" ansible.builtin.shell: | oc adm catalog mirror --insecure --manifests-only --to-manifests=. \ - "{{ iib_image }}" "{{ rh_internal_registry }}/rh-osbs" > catalog.log 2>&1 + "{{ item.value['iib_image'] }}" "{{ rh_internal_registry }}/rh-osbs" > catalog.log 2>&1 args: chdir: "{{ iib_local_folder }}" - name: Mirror IIB to "{{ mirror_iib }}" ansible.builtin.shell: | oc image mirror -a "{{ pull_secrets_tempfolder.path }}/.dockerconfigjson" \ - "{{ iib_image }}={{ mirror_iib }}" --insecure --keep-manifest-list 2>&1 + "{{ item.value['iib_image'] }}={{ mirror_iib }}" --insecure --keep-manifest-list 2>&1 args: chdir: "{{ iib_local_folder }}" register: oc_mirror_result @@ -43,7 +47,7 @@ - name: Wait for catalogsource to show up ansible.builtin.shell: | - oc get -n "{{ internal_registry_ns }}" packagemanifests -l "catalog=iib-{{ iib }}" --field-selector "metadata.name={{ operator }}" \ + oc get -n "{{ internal_registry_ns }}" packagemanifests -l "catalog=iib-{{ item.value['iib'] }}" --field-selector "metadata.name={{ item.key }}" \ -o jsonpath='{.items[0].status.defaultChannel}' register: oc_catalogsource_result retries: 30 diff --git a/ansible/roles/iib_ci/tasks/main.yml b/ansible/roles/iib_ci/tasks/main.yml index 6500b135..fcaad76a 100644 --- a/ansible/roles/iib_ci/tasks/main.yml +++ b/ansible/roles/iib_ci/tasks/main.yml @@ -1,9 +1,3 @@ -- name: Check that INDEX_IMAGE env variable is set - ansible.builtin.fail: - msg: "INDEX_IMAGE: '{{ iib_image }}' is not set" - failed_when: - (iib_image is not defined or iib_image | length == 0) - - name: Check for pre-requisite binaries presence ansible.builtin.shell: | which "{{ item }}" @@ -12,13 +6,11 @@ - oc - podman -- name: Set IIB fact - ansible.builtin.set_fact: - iib: "{{ iib_image.split(':')[1] }}" - -- name: Set IIB local folder fact - ansible.builtin.set_fact: - iib_local_folder: "/tmp/manifest-{{ iib }}" +- name: Check that INDEX_IMAGES env variable is set + ansible.builtin.fail: + msg: "INDEX_IMAGES: '{{ iib_images }}' is not set" + failed_when: + (iib_images is not defined or iib_images | length == 0) - name: Get cluster version # E.g. 4.13.0-rc.6 or 4.12.16 @@ -36,14 +28,44 @@ msg: "OCP versions < 4.13 are not support for IIB loading" when: not ocp_413 +- name: Set images array + ansible.builtin.set_fact: + iib_image_list: "{{ iib_images.split(',') }}" + +- name: Set operator array + ansible.builtin.set_fact: + operator_list: "{{ operator.split(',') }}" + +# Creates a dict like: +# "advanced-cluster-management": { +# "iib": "713808", +# "iib_image": "registry-proxy.engineering.redhat.com/rh-osbs/iib:713808" +# }, +# "multicluster-engine": { +# "iib": "713809", +# "iib_image": "registry-proxy.engineering.redhat.com/rh-osbs/iib:713809" +# } +- name: Set IIB dict + ansible.builtin.set_fact: + iib_dict: "{{ iib_dict | default({}) | combine({item.0: {'iib_image': item.1, 'iib': item.1.split(':')[-1]}}) }}" + with_together: + - "{{ operator_list }}" + - "{{ iib_image_list }}" + +- name: Working with the following IIB data + ansible.builtin.debug: + msg: "{{ iib_dict }}" + - name: Set up internal registry (OCP >= 4.13) ansible.builtin.include_tasks: setup-internal-registry.yml - name: Install new IIB in cluster ansible.builtin.include_tasks: install-iib-in-cluster.yml + with_items: "{{ iib_dict | dict2items }}" - name: Mirror all related images ansible.builtin.include_tasks: mirror-related-images.yml + with_items: "{{ iib_dict | dict2items }}" - name: Remove pullsecrets tempfolder ansible.builtin.file: diff --git a/ansible/roles/iib_ci/tasks/mirror-related-images.yml b/ansible/roles/iib_ci/tasks/mirror-related-images.yml index f502197a..74a0bc3b 100644 --- a/ansible/roles/iib_ci/tasks/mirror-related-images.yml +++ b/ansible/roles/iib_ci/tasks/mirror-related-images.yml @@ -1,8 +1,7 @@ -# This is needed because some operators like "advanced-cluster-management" -# install a second operator "multicluster-engine" -- name: Set operators list +# We redefine this var so it is easier to run this task independently +- name: Set IIB local folder fact ansible.builtin.set_fact: - operator_list: "{{ [operator] + (operator == 'advanced-cluster-management') | ternary(['multicluster-engine'], []) }}" + iib_local_folder: "/tmp/manifest-{{ item.value['iib'] }}" - name: Set all images to empty list ansible.builtin.set_fact: @@ -10,7 +9,6 @@ - name: Fetch operator images tasks ansible.builtin.include_tasks: fetch-operator-images.yml - loop: "{{ operator_list }}" - name: Print all_images ansible.builtin.debug: @@ -119,7 +117,7 @@ ansible.builtin.debug: msg: "{{ image_urls }}" -# OCP 4.13 uses the new fangled "ImageDigestMirrorSet", older OCPs use "ImageContentSourcePolicy" +# OCP 4.13 uses the new fangled "ImageDigestMirrorSet" - name: Template out imageMirror.yaml (OCP >= 4.13) ansible.builtin.template: src: ./templates/imageDigestMirror.yaml.j2 diff --git a/ansible/roles/iib_ci/templates/catalogSource.yaml.j2 b/ansible/roles/iib_ci/templates/catalogSource.yaml.j2 index 99087603..e7498892 100644 --- a/ansible/roles/iib_ci/templates/catalogSource.yaml.j2 +++ b/ansible/roles/iib_ci/templates/catalogSource.yaml.j2 @@ -1,9 +1,9 @@ apiVersion: operators.coreos.com/v1alpha1 kind: CatalogSource metadata: - name: iib-{{ iib }} + name: iib-{{ item.value['iib'] }} namespace: {{ internal_registry_ns }} spec: - image: {{ mirror_iib }}:{{ iib }} + image: {{ mirror_iib }}:{{ item.value['iib'] }} sourceType: grpc - displayName: IIB {{ iib }} + displayName: IIB {{ item.value['iib'] }} diff --git a/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 b/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 index 1b04f321..08a24735 100644 --- a/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 +++ b/ansible/roles/iib_ci/templates/imageDigestMirror.yaml.j2 @@ -3,16 +3,16 @@ kind: ImageDigestMirrorSet metadata: labels: operators.openshift.org/catalog: "true" - name: iib-{{ iib }} + name: iib-{{ item.value['iib'] }} spec: imageDigestMirrors: -{% for item in image_urls.values() %} +{% for data in image_urls.values() %} - mirrors: - - {{ item.mirrordest_nosha }} - source: {{ item.source_nosha }} + - {{ data.mirrordest_nosha }} + source: {{ data.source_nosha }} mirrorSourcePolicy: AllowContactingSource - mirrors: - - {{ item.mirrordest_nosha }} - source: {{ item.image_nosha }} + - {{ data.mirrordest_nosha }} + source: {{ data.image_nosha }} mirrorSourcePolicy: AllowContactingSource {% endfor %} From 00aae8c1ef4f8771003450eea8544537d9887769 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 30 Apr 2024 12:09:09 +0200 Subject: [PATCH 1163/1263] Switch to gitops-1.12 --- acm/templates/policies/ocp-gitops-policy.yaml | 2 +- acm/values.yaml | 2 +- operator-install/templates/pattern.yaml | 2 +- operator-install/values.yaml | 2 +- reference-output.yaml | 2 +- tests/acm-industrial-edge-factory.expected.yaml | 2 +- tests/acm-industrial-edge-hub.expected.yaml | 2 +- tests/acm-medical-diagnosis-hub.expected.yaml | 2 +- tests/acm-naked.expected.yaml | 2 +- tests/acm-normal.expected.yaml | 2 +- tests/operator-install-industrial-edge-factory.expected.yaml | 4 ++-- tests/operator-install-industrial-edge-hub.expected.yaml | 4 ++-- tests/operator-install-medical-diagnosis-hub.expected.yaml | 4 ++-- tests/operator-install-naked.expected.yaml | 4 ++-- tests/operator-install-normal.expected.yaml | 4 ++-- 15 files changed, 20 insertions(+), 20 deletions(-) diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index f46b30c6..bec5b343 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -45,7 +45,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: {{ default "gitops-1.11" .Values.main.gitops.channel }} + channel: {{ default "gitops-1.12" .Values.main.gitops.channel }} installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/acm/values.yaml b/acm/values.yaml index fb7cb03a..e9e5035b 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -1,6 +1,6 @@ main: gitops: - channel: "gitops-1.11" + channel: "gitops-1.12" global: extraValueFiles: [] diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index 8bf7761a..ca3234f8 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -13,7 +13,7 @@ spec: tokenSecretNamespace: {{ .Values.main.tokenSecretNamespace }} {{- end }} {{/* if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace */}} gitOpsSpec: - operatorChannel: {{ default "gitops-1.11" .Values.main.gitops.channel }} + operatorChannel: {{ default "gitops-1.12" .Values.main.gitops.channel }} operatorSource: {{ default "redhat-operators" .Values.main.gitops.operatorSource }} multiSourceConfig: enabled: {{ .Values.main.multiSourceConfig.enabled }} diff --git a/operator-install/values.yaml b/operator-install/values.yaml index 28bc3d4b..8ce7fe67 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -7,7 +7,7 @@ main: revision: main gitops: - channel: "gitops-1.11" + channel: "gitops-1.12" operatorSource: redhat-operators multiSourceConfig: diff --git a/reference-output.yaml b/reference-output.yaml index cdc1a303..54f4052a 100644 --- a/reference-output.yaml +++ b/reference-output.yaml @@ -112,7 +112,7 @@ metadata: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: "" spec: - channel: gitops-1.11 + channel: gitops-1.12 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index 0291231a..9dfa7d79 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -109,7 +109,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.11 + channel: gitops-1.12 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 21a1c30d..453e8a9e 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -316,7 +316,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.11 + channel: gitops-1.12 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 40df35e2..8b50de7a 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -307,7 +307,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.11 + channel: gitops-1.12 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index 4f0ac751..561fbd7b 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -110,7 +110,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.11 + channel: gitops-1.12 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 20a38d52..66f1c590 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -806,7 +806,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.11 + channel: gitops-1.12 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/operator-install-industrial-edge-factory.expected.yaml b/tests/operator-install-industrial-edge-factory.expected.yaml index 0ef51dc0..018eb4c5 100644 --- a/tests/operator-install-industrial-edge-factory.expected.yaml +++ b/tests/operator-install-industrial-edge-factory.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.11 + gitops.channel: gitops-1.12 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan @@ -26,7 +26,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: gitops-1.11 + operatorChannel: gitops-1.12 operatorSource: redhat-operators multiSourceConfig: enabled: false diff --git a/tests/operator-install-industrial-edge-hub.expected.yaml b/tests/operator-install-industrial-edge-hub.expected.yaml index 0ef51dc0..018eb4c5 100644 --- a/tests/operator-install-industrial-edge-hub.expected.yaml +++ b/tests/operator-install-industrial-edge-hub.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.11 + gitops.channel: gitops-1.12 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan @@ -26,7 +26,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: gitops-1.11 + operatorChannel: gitops-1.12 operatorSource: redhat-operators multiSourceConfig: enabled: false diff --git a/tests/operator-install-medical-diagnosis-hub.expected.yaml b/tests/operator-install-medical-diagnosis-hub.expected.yaml index 0ef51dc0..018eb4c5 100644 --- a/tests/operator-install-medical-diagnosis-hub.expected.yaml +++ b/tests/operator-install-medical-diagnosis-hub.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.11 + gitops.channel: gitops-1.12 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan @@ -26,7 +26,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: gitops-1.11 + operatorChannel: gitops-1.12 operatorSource: redhat-operators multiSourceConfig: enabled: false diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index 024ea7ea..fc0d7699 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.11 + gitops.channel: gitops-1.12 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan @@ -26,7 +26,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: gitops-1.11 + operatorChannel: gitops-1.12 operatorSource: redhat-operators multiSourceConfig: enabled: false diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index 0ef51dc0..018eb4c5 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.11 + gitops.channel: gitops-1.12 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan @@ -26,7 +26,7 @@ spec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main gitOpsSpec: - operatorChannel: gitops-1.11 + operatorChannel: gitops-1.12 operatorSource: redhat-operators multiSourceConfig: enabled: false From 75c51cc6bc0cba6c9ae721b1aea5ff139d4e233a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 24 Apr 2024 15:57:09 +0200 Subject: [PATCH 1164/1263] Add overrides for MCE IIB We need to be able to explicitely override the Source and the Channel used to install the MCE multicluster-engine operator. The reason for this is that when we install ACM from an IIB, the default channel that will be used for MCE won't be present in the IIB, so we need to be able to be flexible in determining where the MCE gets installed from (sourcecatalog and channel) We default to redhat-operators because otherwise when installing gitops via IIB the ACM operator finds a channel for MCE that does not exist, so we force the source all the time. --- acm/templates/multiclusterhub.yaml | 8 +++++--- acm/values.yaml | 9 +++++++++ tests/acm-industrial-edge-factory.expected.yaml | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/acm/templates/multiclusterhub.yaml b/acm/templates/multiclusterhub.yaml index 79ef9339..a4e8b89b 100644 --- a/acm/templates/multiclusterhub.yaml +++ b/acm/templates/multiclusterhub.yaml @@ -1,3 +1,7 @@ +{{- $channel := "" }} +{{- if .Values.acm.mce_operator.channel }} +{{- $channel = printf ",\"channel\": \"%s\"" .Values.acm.mce_operator.channel }} +{{- end }} apiVersion: operator.open-cluster-management.io/v1 kind: MultiClusterHub metadata: @@ -5,7 +9,5 @@ metadata: namespace: open-cluster-management annotations: argocd.argoproj.io/sync-wave: "-1" - {{- if kindIs "map" .Values.clusterGroup.subscriptions }} - installer.open-cluster-management.io/mce-subscription-spec: '{"source": "{{ default "redhat-operators" .Values.clusterGroup.subscriptions.acm.source }}" }' - {{- end }} + installer.open-cluster-management.io/mce-subscription-spec: '{"source": "{{ default "redhat-operators" .Values.acm.mce_operator.source }}" {{- $channel }} }' spec: {} diff --git a/acm/values.yaml b/acm/values.yaml index fb7cb03a..667bf89c 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -30,6 +30,15 @@ clusterGroup: # - name: clusterGroup # value: region-one +acm: + # Just used for IIB testing, drives the source and channel for the MCE + # subscription triggered by ACM + mce_operator: + source: redhat-operators + channel: null + + + secretStore: name: vault-backend kind: ClusterSecretStore diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index 0291231a..4f0ac751 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -22,6 +22,7 @@ metadata: namespace: open-cluster-management annotations: argocd.argoproj.io/sync-wave: "-1" + installer.open-cluster-management.io/mce-subscription-spec: '{"source": "redhat-operators" }' spec: {} --- # Source: acm/templates/policies/ocp-gitops-policy.yaml From 8119368ab5387dc99c01021eea13138cb5f44294 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 30 Apr 2024 15:51:25 +0200 Subject: [PATCH 1165/1263] Update README for IIB changes --- ansible/roles/iib_ci/README.md | 38 +++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 6afccb1a..36784ed5 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -38,6 +38,8 @@ oc patch configs.imageregistry.operator.openshift.io cluster --type merge --patc oc patch configs.imageregistry.operator.openshift.io cluster --type merge --patch '{"spec":{"storage":{"emptyDir":{}}}}' ``` +### Gitops operator + Then in case of the `openshift-gitops-operator` we would install with: ```sh @@ -45,16 +47,42 @@ export CHANNEL=$(oc get -n openshift-marketplace packagemanifests -l "catalog=ii make EXTRA_HELM_OPTS="--set main.gitops.operatorSource=iib-${IIB} --set main.gitops.channel=${CHANNEL}" install ``` -To install ACM (`export OPERATOR=advanced-cluster-management`) or any other -operator (except the gitops one) from an IIB we would call the following as a -final step: +### ACM operator + +The advanced-cluster-management operator is a little bit more complex than the others because it +also installes another operator called MCE multicluster-engine. So to install ACM you typically +need two IIBs (one for acm and one for mce). With those two at hand, do the following (the ordering must be +consistent: the first IIB corresponds to the first OPERATOR, etc). + +```sh +export OPERATOR=advanced-cluster-management,multicluster-engine +export INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:713808,registry-proxy.engineering.redhat.com/rh-osbs/iib:718034 +make load-iib +``` + +Once the IIBs are loaded into the cluster we need to run the following steps: + +```sh +export ACM_CHANNEL=$(oc get -n openshift-marketplace packagemanifests -l "catalog=iib-713808" --field-selector "metadata.name=advanced-cluster-management" -o jsonpath='{.items[0].status.defaultChannel}') +export MCE_CHANNEL=$(oc get -n openshift-margetplace packagemanifests -l "catalog=iib-718034" --field-selector "metadata.name=multicluster-engine" -o jsonpath='{.items[0].status.defaultChannel}') +make EXTRA_HELM_OPTS="--set main.extraParameters[0].name=clusterGroup.subscriptions.acm.source --set main.extraParameters[0].value=iib-713808 \ + --set main.extraParameters[1].name=clusterGroup.subscriptions.acm.channel --set main.extraParameters[1].value=${ACM_CHANNEL} \ + --set main.extraParameters[2].name=acm.mce_operator.source --set main.extraParameters[2].value="iib-718034" \ + --set main.extraParameters[3].name=acm.mce_operator.channel --set main.extraParameters[3].value=${MCE_CHANNEL}" install +``` + +*Note*: In this case the `acm` in `clusterGroup.subscriptions.acm.*` is the name of the key in the subscriptions in `values-hub.yaml` + +### Other operators + +To install operators other than gitops and acm do the following: ```sh export CHANNEL=$(oc get -n openshift-marketplace packagemanifests -l "catalog=iib-${IIB}" --field-selector "metadata.name=${OPERATOR}" -o jsonpath='{.items[0].status.defaultChannel}') -make EXTRA_HELM_OPTS="--set main.extraParameters[0].name=clusterGroup.subscriptions.acm.source --set main.extraParameters[0].value=iib-${IIB} --set main.extraParameters[1].name=clusterGroup.subscriptions.acm.channel --set main.extraParameters[1].value=${CHANNEL}" install +make EXTRA_HELM_OPTS="--set main.extraParameters[0].name=clusterGroup.subscriptions..source --set main.extraParameters[0].value=iib-${IIB} --set main.extraParameters[1].name=clusterGroup.subscriptions..channel --set main.extraParameters[1].value=${CHANNEL}" install ``` -*Note*: In this case `acm` is the name of the subscription in `values-hub.yaml` +*Note*: Replace `` with the actual name of the subscription dictionary in `values-hub.yaml` ## Useful commands From 6a4e5c69f558d4cd689faabbfccb208142c739b2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 2 May 2024 15:28:20 +0200 Subject: [PATCH 1166/1263] Release clustergroup v0.8.5 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index c8ba873f..e2d4f98b 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.8.4 +version: 0.8.5 From ff3147addcd49eaec6dee2de8bcc1ab4d676830b Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Thu, 2 May 2024 09:02:59 -0600 Subject: [PATCH 1167/1263] New global scope argocdServer section for values-global.yaml - Added new section for to configure the ArgoCD server to support tls argocdServer: route: tls: insecureEdgeTerminationPolicy: Redirect termination: reencrypt - Default for ArgoCD is to create route with the following: route: enabled tls: insecureEdgeTerminationPolicy: Redirect termination: passthrough For more information please refer to https://issues.redhat.com/browse/GITOPS-3918. - Changed default value for termination --- acm/templates/policies/ocp-gitops-policy.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index bec5b343..399f52a9 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -195,6 +195,11 @@ spec: memory: 128Mi route: enabled: true + {{- if and (.Values.global.argocdServer) (.Values.global.argocdServer.route) (.Values.global.argocdServer.route.tls) }} + tls: + insecureEdgeTerminationPolicy: {{ default "Redirect" .Values.global.argocdServer.route.tls.insecureEdgeTerminationPolicy }} + termination: {{ default "reencrypt" .Values.global.argocdServer.route.tls.termination }} + {{- end }} service: type: "" sso: From aa0b9699874de5fa5796c8957914525ea62fabf8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 3 May 2024 13:49:49 +0200 Subject: [PATCH 1168/1263] Improve readme for ACM IIB --- ansible/roles/iib_ci/README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 36784ed5..8c654dbb 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -52,12 +52,17 @@ make EXTRA_HELM_OPTS="--set main.gitops.operatorSource=iib-${IIB} --set main.git The advanced-cluster-management operator is a little bit more complex than the others because it also installes another operator called MCE multicluster-engine. So to install ACM you typically need two IIBs (one for acm and one for mce). With those two at hand, do the following (the ordering must be -consistent: the first IIB corresponds to the first OPERATOR, etc). +consistent: the first IIB corresponds to the first OPERATOR, etc). The following operation needs to be done +on both hub *and* spokes: ```sh -export OPERATOR=advanced-cluster-management,multicluster-engine -export INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:713808,registry-proxy.engineering.redhat.com/rh-osbs/iib:718034 -make load-iib +for i in hub-kubeconfig-file spoke-kubeconfig-file; do + export KUBECONFIG="${i}" + export KUBEADMINPASS="11111-22222-33333-44444" + export OPERATOR=advanced-cluster-management,multicluster-engine + export INDEX_IMAGES=registry-proxy.engineering.redhat.com/rh-osbs/iib:713808,registry-proxy.engineering.redhat.com/rh-osbs/iib:718034 + make load-iib +done ``` Once the IIBs are loaded into the cluster we need to run the following steps: From 23614a40ae0fa1cbad3e6b69b1aae97da27279fe Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 3 May 2024 15:16:03 +0200 Subject: [PATCH 1169/1263] Drop gitopsspec from pattern's CR We drive this from the patterns-operator-config configmap these days, which makes more sense (it is a clusterwide setting and not really a per pattern one). --- operator-install/templates/pattern.yaml | 3 --- tests/operator-install-industrial-edge-factory.expected.yaml | 3 --- tests/operator-install-industrial-edge-hub.expected.yaml | 3 --- tests/operator-install-medical-diagnosis-hub.expected.yaml | 3 --- tests/operator-install-naked.expected.yaml | 3 --- tests/operator-install-normal.expected.yaml | 3 --- 6 files changed, 18 deletions(-) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index ca3234f8..728726f0 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -12,9 +12,6 @@ spec: tokenSecret: {{ .Values.main.tokenSecret }} tokenSecretNamespace: {{ .Values.main.tokenSecretNamespace }} {{- end }} {{/* if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace */}} - gitOpsSpec: - operatorChannel: {{ default "gitops-1.12" .Values.main.gitops.channel }} - operatorSource: {{ default "redhat-operators" .Values.main.gitops.operatorSource }} multiSourceConfig: enabled: {{ .Values.main.multiSourceConfig.enabled }} {{- if .Values.main.analyticsUUID }} diff --git a/tests/operator-install-industrial-edge-factory.expected.yaml b/tests/operator-install-industrial-edge-factory.expected.yaml index 018eb4c5..5e4aa02e 100644 --- a/tests/operator-install-industrial-edge-factory.expected.yaml +++ b/tests/operator-install-industrial-edge-factory.expected.yaml @@ -25,9 +25,6 @@ spec: gitSpec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main - gitOpsSpec: - operatorChannel: gitops-1.12 - operatorSource: redhat-operators multiSourceConfig: enabled: false --- diff --git a/tests/operator-install-industrial-edge-hub.expected.yaml b/tests/operator-install-industrial-edge-hub.expected.yaml index 018eb4c5..5e4aa02e 100644 --- a/tests/operator-install-industrial-edge-hub.expected.yaml +++ b/tests/operator-install-industrial-edge-hub.expected.yaml @@ -25,9 +25,6 @@ spec: gitSpec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main - gitOpsSpec: - operatorChannel: gitops-1.12 - operatorSource: redhat-operators multiSourceConfig: enabled: false --- diff --git a/tests/operator-install-medical-diagnosis-hub.expected.yaml b/tests/operator-install-medical-diagnosis-hub.expected.yaml index 018eb4c5..5e4aa02e 100644 --- a/tests/operator-install-medical-diagnosis-hub.expected.yaml +++ b/tests/operator-install-medical-diagnosis-hub.expected.yaml @@ -25,9 +25,6 @@ spec: gitSpec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main - gitOpsSpec: - operatorChannel: gitops-1.12 - operatorSource: redhat-operators multiSourceConfig: enabled: false --- diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index fc0d7699..3d58b474 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -25,9 +25,6 @@ spec: gitSpec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main - gitOpsSpec: - operatorChannel: gitops-1.12 - operatorSource: redhat-operators multiSourceConfig: enabled: false --- diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index 018eb4c5..5e4aa02e 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -25,9 +25,6 @@ spec: gitSpec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main - gitOpsSpec: - operatorChannel: gitops-1.12 - operatorSource: redhat-operators multiSourceConfig: enabled: false --- From d9c7350f1bb700dbcf93d3421322da575f73ddf5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 3 May 2024 15:39:40 +0200 Subject: [PATCH 1170/1263] Allow customizing the VP operator subscription MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested with: ❯ helm template operator-install --show-only templates/subscription.yaml --set main.patternsOperator.installPlanApproval=Manual --- apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: patterns-operator namespace: openshift-operators labels: operators.coreos.com/patterns-operator.openshift-operators: "" spec: channel: fast installPlanApproval: Manual name: patterns-operator source: community-operators sourceNamespace: openshift-marketplace ❯ helm template operator-install --show-only templates/subscription.yaml --set main.patternsOperator.installPlanApproval=Manual --set main.patternsOperator.startingCSV=1.2.3 --- apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: patterns-operator namespace: openshift-operators labels: operators.coreos.com/patterns-operator.openshift-operators: "" spec: channel: fast installPlanApproval: Manual name: patterns-operator source: community-operators sourceNamespace: openshift-marketplace startingCSV: 1.2.3 --- operator-install/templates/subscription.yaml | 7 +++++-- operator-install/values.yaml | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/operator-install/templates/subscription.yaml b/operator-install/templates/subscription.yaml index 41d0d211..e8285cae 100644 --- a/operator-install/templates/subscription.yaml +++ b/operator-install/templates/subscription.yaml @@ -7,7 +7,10 @@ metadata: operators.coreos.com/patterns-operator.openshift-operators: "" spec: channel: {{ .Values.main.patternsOperator.channel }} - installPlanApproval: Automatic + installPlanApproval: {{ .Values.main.patternsOperator.installPlanApproval }} name: patterns-operator source: {{ .Values.main.patternsOperator.source }} - sourceNamespace: openshift-marketplace + sourceNamespace: {{ .Values.main.patternsOperator.sourceNamespace }} + {{- if .Values.main.patternsOperator.startingCSV }} + startingCSV: {{ .Values.main.patternsOperator.startingCSV }} + {{- end }} diff --git a/operator-install/values.yaml b/operator-install/values.yaml index 8ce7fe67..a46cca2e 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -20,6 +20,9 @@ main: patternsOperator: channel: fast source: community-operators + installPlanApproval: Automatic + sourceNamespace: openshift-marketplace + startingCSV: null clusterGroupName: default From 258af6b8a9d58a9cadeadf5fba2e91ff60eca629 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 6 May 2024 09:43:01 +0200 Subject: [PATCH 1171/1263] Add retries when checking oc version --- ansible/roles/iib_ci/tasks/main.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ansible/roles/iib_ci/tasks/main.yml b/ansible/roles/iib_ci/tasks/main.yml index fcaad76a..4e8df11f 100644 --- a/ansible/roles/iib_ci/tasks/main.yml +++ b/ansible/roles/iib_ci/tasks/main.yml @@ -17,6 +17,9 @@ ansible.builtin.shell: | oc get openshiftcontrollermanager/cluster -o yaml -o jsonpath='{.status.version}' register: oc_version_raw + retries: 10 + delay: 10 + until: oc_version_raw is not failed changed_when: false - name: Is OCP pre OCP 4.13? (aka registry supports v2 manifests) From 1cbcc9806685f3bc0ab5600659132022d8dfbc36 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 23 Apr 2024 11:35:18 +0200 Subject: [PATCH 1172/1263] Add an imperative-admin-sa service account --- .../templates/imperative/clusterrole.yaml | 16 ++++++++ clustergroup/templates/imperative/rbac.yaml | 21 +++++++++- .../templates/imperative/serviceaccount.yaml | 10 ++++- clustergroup/values.schema.json | 9 ++++ clustergroup/values.yaml | 4 ++ ...roup-industrial-edge-factory.expected.yaml | 41 ++++++++++++++++++- ...tergroup-industrial-edge-hub.expected.yaml | 41 ++++++++++++++++++- ...rgroup-medical-diagnosis-hub.expected.yaml | 41 ++++++++++++++++++- tests/clustergroup-naked.expected.yaml | 41 ++++++++++++++++++- tests/clustergroup-normal.expected.yaml | 41 ++++++++++++++++++- 10 files changed, 252 insertions(+), 13 deletions(-) diff --git a/clustergroup/templates/imperative/clusterrole.yaml b/clustergroup/templates/imperative/clusterrole.yaml index e3646917..0ad8ff64 100644 --- a/clustergroup/templates/imperative/clusterrole.yaml +++ b/clustergroup/templates/imperative/clusterrole.yaml @@ -1,5 +1,6 @@ {{- if not (eq .Values.enabled "plumbing") }} {{/* This is always defined as we always unseal the cluster with an imperative job */}} +{{- if $.Values.clusterGroup.imperative.serviceAccountCreate }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -18,4 +19,19 @@ rules: - list - watch {{- end }} +{{- end }} {{/* if $.Values.clusterGroup.imperative.serviceAccountCreate */}} +{{- if $.Values.clusterGroup.imperative.adminServiceAccountCreate }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ $.Values.clusterGroup.imperative.adminClusterRoleName }} +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +{{- end }} {{/* if $.Values.clusterGroup.imperative.adminServiceAccountCreate */}} {{- end }} diff --git a/clustergroup/templates/imperative/rbac.yaml b/clustergroup/templates/imperative/rbac.yaml index 1a4b3e2b..8bfad5b3 100644 --- a/clustergroup/templates/imperative/rbac.yaml +++ b/clustergroup/templates/imperative/rbac.yaml @@ -1,10 +1,11 @@ {{- if not (eq .Values.enabled "plumbing") }} {{/* This is always defined as we always unseal the cluster with an imperative job */}} +{{- if $.Values.clusterGroup.imperative.serviceAccountCreate -}} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: {{ $.Values.clusterGroup.imperative.namespace }}-cluster-admin-rolebinding + name: {{ $.Values.clusterGroup.imperative.namespace }}-cluster-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -17,7 +18,7 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: {{ $.Values.clusterGroup.imperative.namespace }}-admin-rolebinding + name: {{ $.Values.clusterGroup.imperative.namespace }}-rolebinding namespace: {{ $.Values.clusterGroup.imperative.namespace }} roleRef: apiGroup: rbac.authorization.k8s.io @@ -28,3 +29,19 @@ subjects: name: {{ $.Values.clusterGroup.imperative.serviceAccountName }} namespace: {{ $.Values.clusterGroup.imperative.namespace }} {{- end }} +{{- if $.Values.clusterGroup.imperative.adminServiceAccountCreate }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ $.Values.clusterGroup.imperative.namespace }}-admin-clusterrolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ $.Values.clusterGroup.imperative.adminClusterRoleName }} +subjects: + - kind: ServiceAccount + name: {{ $.Values.clusterGroup.imperative.adminServiceAccountName }} + namespace: {{ $.Values.clusterGroup.imperative.namespace }} +{{- end }} +{{- end }} diff --git a/clustergroup/templates/imperative/serviceaccount.yaml b/clustergroup/templates/imperative/serviceaccount.yaml index ac051348..a171d300 100644 --- a/clustergroup/templates/imperative/serviceaccount.yaml +++ b/clustergroup/templates/imperative/serviceaccount.yaml @@ -1,10 +1,18 @@ {{- if not (eq .Values.enabled "plumbing") }} {{/* This is always defined as we always unseal the cluster with an imperative job */}} -{{- if $.Values.clusterGroup.imperative.serviceAccountCreate -}} +{{- if $.Values.clusterGroup.imperative.serviceAccountCreate }} apiVersion: v1 kind: ServiceAccount metadata: name: {{ $.Values.clusterGroup.imperative.serviceAccountName }} namespace: {{ $.Values.clusterGroup.imperative.namespace }} {{- end }} +{{- if $.Values.clusterGroup.imperative.adminServiceAccountCreate }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ $.Values.clusterGroup.imperative.adminServiceAccountName }} + namespace: {{ $.Values.clusterGroup.imperative.namespace }} +{{- end }} {{- end }} diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 071805fe..8d206957 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -677,6 +677,15 @@ }, "roleYaml": { "type": "string" + }, + "adminServiceAccountCreate": { + "type": "boolean" + }, + "adminServiceAccountName": { + "type": "string" + }, + "adminClusterRoleName": { + "type": "string" } }, "required": [ diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index c74db48c..c3611241 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -51,6 +51,10 @@ clusterGroup: clusterRoleYaml: "" roleName: imperative-role roleYaml: "" + adminServiceAccountCreate: true + adminServiceAccountName: imperative-admin-sa + adminClusterRoleName: imperative-admin-cluster-role + managedClusterGroups: {} namespaces: [] # - name: factory diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index f34b5f18..c3eabd83 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -64,6 +64,13 @@ metadata: name: imperative-sa namespace: imperative --- +# Source: clustergroup/templates/imperative/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: imperative-admin-sa + namespace: imperative +--- # Source: clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap @@ -116,6 +123,9 @@ data: initContainers: [] imperative: activeDeadlineSeconds: 3600 + adminClusterRoleName: imperative-admin-cluster-role + adminServiceAccountCreate: true + adminServiceAccountName: imperative-admin-sa clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob @@ -264,11 +274,24 @@ rules: - list - watch --- +# Source: clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: imperative-admin-cluster-role +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- # Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: imperative-cluster-admin-rolebinding + name: imperative-cluster-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -278,6 +301,20 @@ subjects: name: imperative-sa namespace: imperative --- +# Source: clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: imperative-admin-clusterrolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: imperative-admin-cluster-role +subjects: + - kind: ServiceAccount + name: imperative-admin-sa + namespace: imperative +--- # Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 @@ -340,7 +377,7 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: imperative-admin-rolebinding + name: imperative-rolebinding namespace: imperative roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 6641ff73..393e530c 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -109,6 +109,13 @@ metadata: name: imperative-sa namespace: imperative --- +# Source: clustergroup/templates/imperative/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: imperative-admin-sa + namespace: imperative +--- # Source: clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap @@ -237,6 +244,9 @@ data: initContainers: [] imperative: activeDeadlineSeconds: 3600 + adminClusterRoleName: imperative-admin-cluster-role + adminServiceAccountCreate: true + adminServiceAccountName: imperative-admin-sa clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob @@ -425,11 +435,24 @@ rules: - list - watch --- +# Source: clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: imperative-admin-cluster-role +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- # Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: imperative-cluster-admin-rolebinding + name: imperative-cluster-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -439,6 +462,20 @@ subjects: name: imperative-sa namespace: imperative --- +# Source: clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: imperative-admin-clusterrolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: imperative-admin-cluster-role +subjects: + - kind: ServiceAccount + name: imperative-admin-sa + namespace: imperative +--- # Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 @@ -501,7 +538,7 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: imperative-admin-rolebinding + name: imperative-rolebinding namespace: imperative roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index a61ce547..f4933c53 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -109,6 +109,13 @@ metadata: name: imperative-sa namespace: imperative --- +# Source: clustergroup/templates/imperative/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: imperative-admin-sa + namespace: imperative +--- # Source: clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap @@ -220,6 +227,9 @@ data: initContainers: [] imperative: activeDeadlineSeconds: 3600 + adminClusterRoleName: imperative-admin-cluster-role + adminServiceAccountCreate: true + adminServiceAccountName: imperative-admin-sa clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob @@ -352,11 +362,24 @@ rules: - list - watch --- +# Source: clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: imperative-admin-cluster-role +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- # Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: imperative-cluster-admin-rolebinding + name: imperative-cluster-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -366,6 +389,20 @@ subjects: name: imperative-sa namespace: imperative --- +# Source: clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: imperative-admin-clusterrolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: imperative-admin-cluster-role +subjects: + - kind: ServiceAccount + name: imperative-admin-sa + namespace: imperative +--- # Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 @@ -428,7 +465,7 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: imperative-admin-rolebinding + name: imperative-rolebinding namespace: imperative roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index fdaf4293..6f1c6b2e 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -28,6 +28,13 @@ metadata: name: imperative-sa namespace: imperative --- +# Source: clustergroup/templates/imperative/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: imperative-admin-sa + namespace: imperative +--- # Source: clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap @@ -43,6 +50,9 @@ data: initContainers: [] imperative: activeDeadlineSeconds: 3600 + adminClusterRoleName: imperative-admin-cluster-role + adminServiceAccountCreate: true + adminServiceAccountName: imperative-admin-sa clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob @@ -117,11 +127,24 @@ rules: - list - watch --- +# Source: clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: imperative-admin-cluster-role +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- # Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: imperative-cluster-admin-rolebinding + name: imperative-cluster-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -131,6 +154,20 @@ subjects: name: imperative-sa namespace: imperative --- +# Source: clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: imperative-admin-clusterrolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: imperative-admin-cluster-role +subjects: + - kind: ServiceAccount + name: imperative-admin-sa + namespace: imperative +--- # Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 @@ -193,7 +230,7 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: imperative-admin-rolebinding + name: imperative-rolebinding namespace: imperative roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 93f6892a..baad3fd0 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -96,6 +96,13 @@ metadata: name: imperative-sa namespace: imperative --- +# Source: clustergroup/templates/imperative/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: imperative-admin-sa + namespace: imperative +--- # Source: clustergroup/templates/imperative/configmap.yaml apiVersion: v1 kind: ConfigMap @@ -128,6 +135,9 @@ data: initContainers: [] imperative: activeDeadlineSeconds: 3600 + adminClusterRoleName: imperative-admin-cluster-role + adminServiceAccountCreate: true + adminServiceAccountName: imperative-admin-sa clusterRoleName: imperative-cluster-role clusterRoleYaml: "" cronJobName: imperative-cronjob @@ -314,11 +324,24 @@ rules: - list - watch --- +# Source: clustergroup/templates/imperative/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: imperative-admin-cluster-role +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- # Source: clustergroup/templates/imperative/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: imperative-cluster-admin-rolebinding + name: imperative-cluster-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -328,6 +351,20 @@ subjects: name: imperative-sa namespace: imperative --- +# Source: clustergroup/templates/imperative/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: imperative-admin-clusterrolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: imperative-admin-cluster-role +subjects: + - kind: ServiceAccount + name: imperative-admin-sa + namespace: imperative +--- # Source: clustergroup/templates/plumbing/argocd-super-role.yaml # WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS apiVersion: rbac.authorization.k8s.io/v1 @@ -390,7 +427,7 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: imperative-admin-rolebinding + name: imperative-rolebinding namespace: imperative roleRef: apiGroup: rbac.authorization.k8s.io From 3a4eaf900a3a2bae40730ab0a731972d590638fa Mon Sep 17 00:00:00 2001 From: "Sergio G." Date: Thu, 9 May 2024 22:12:08 +0200 Subject: [PATCH 1173/1263] Added support to label/annotate nodes --- clustergroup/templates/core/nodes.yaml | 25 ++++++ clustergroup/values.schema.json | 81 ++++++++++++++----- clustergroup/values.yaml | 7 +- examples/values-example.yaml | 11 +++ ...roup-industrial-edge-factory.expected.yaml | 1 + ...tergroup-industrial-edge-hub.expected.yaml | 1 + ...rgroup-medical-diagnosis-hub.expected.yaml | 1 + tests/clustergroup-naked.expected.yaml | 1 + tests/clustergroup-normal.expected.yaml | 37 +++++++++ 9 files changed, 142 insertions(+), 23 deletions(-) create mode 100644 clustergroup/templates/core/nodes.yaml diff --git a/clustergroup/templates/core/nodes.yaml b/clustergroup/templates/core/nodes.yaml new file mode 100644 index 00000000..5106447d --- /dev/null +++ b/clustergroup/templates/core/nodes.yaml @@ -0,0 +1,25 @@ +{{- if not (eq .Values.enabled "plumbing") }} +{{- range $node := .Values.clusterGroup.nodes }} +apiVersion: v1 +kind: Node +metadata: + {{- range $k, $v := $node }} + name: {{ $k }} + labels: + argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} + {{- if $v.labels }} + {{- range $key, $value := $v.labels }} + {{ $key }}: {{ $value | default "" | quote }} + {{- end }} + {{- end }} + + {{- if $v.annotations }} + annotations: + {{- range $key, $value := $v.annotations }} + {{ $key }}: {{ $value | default "" | quote }} + {{- end }} + {{- end }}{{- /* if $v.annotations */}} + {{- end }}{{- /* range $k, $v := $node */}} +--- +{{- end -}} +{{- end -}} diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 8d206957..76c997b4 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -271,6 +271,20 @@ "$ref": "#/definitions/Namespaces" } }, + "nodes": { + "anyOf": [ + { + "type": "array" + }, + { + "type": "object" + } + ], + "description": "Description of those nodes which ArgoCD will control the labels and/or annotations.", + "items": { + "$ref": "#/definitions/Nodes" + } + }, "indexImages": { "anyOf": [ { @@ -389,28 +403,51 @@ "type": "string" } ], - "description": "Description of the applications that will be created in the ArgoCD instances. The Application CRD is the Kubernetes resource object representing a deployed application instance in an environment. Two ways of defining applications: Using a list or using a dictionary.", - "additionalProperties": true, - "properties": { - "name": { - "type": "string", - "description": "Name of the namespace." - }, - "labels": { - "type": "array", - "items": { - "$ref": "#/definitions/NameValue" - } - }, - "annotations": { - "type": "array", - "items": { - "$ref": "#/definitions/NameValue" - } - } + "description": "Description of the applications that will be created in the ArgoCD instances. The Application CRD is the Kubernetes resource object representing a deployed application instance in an environment. Two ways of defining applications: Using a list or using a dictionary.", + "additionalProperties": true, + "properties": { + "name": { + "type": "string", + "description": "Name of the namespace." + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/definitions/NameValue" + } + }, + "annotations": { + "type": "array", + "items": { + "$ref": "#/definitions/NameValue" } + } + } }, - "NameValue": { + "Nodes": { + "type": "object", + "description": "Description of those nodes which ArgoCD will control the labels and/or annotations.", + "additionalProperties": true, + "properties": { + "name": { + "type": "string", + "description": "Name of the node." + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/definitions/NameValue" + } + }, + "annotations": { + "type": "array", + "items": { + "$ref": "#/definitions/NameValue" + } + } + } + }, + "NameValue": { "type": "object", "description": "Description of the applications that will be created in the ArgoCD instances. The Application CRD is the Kubernetes resource object representing a deployed application instance in an environment. Two ways of defining applications: Using a list or using a dictionary.", "additionalProperties": true, @@ -423,8 +460,8 @@ "type": "string", "description": "Name of the namespace." } - } - }, + } + }, "Applications": { "type": "object", "description": "Description of the applications that will be created in the ArgoCD instances. The Application CRD is the Kubernetes resource object representing a deployed application instance in an environment. Two ways of defining applications: Using a list or using a dictionary.", diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index c3611241..9b9f943e 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -10,7 +10,6 @@ global: installPlanApproval: Automatic applicationRetryLimit: 20 - enabled: "all" # Note that sometimes changing helm values might require a hard refresh (https://github.com/helm/helm/issues/3486) @@ -73,6 +72,12 @@ clusterGroup: # - OpenShift # # - open-cluster-management +# + nodes: [] +# nodes: +# - m-m00.mycluster.domain.tld: +# labels: +# cluster.ocs.openshift.io/openshift-storage: "" # subscriptions: {} # - name: advanced-cluster-management diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 84682e20..542ec7c9 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -50,6 +50,17 @@ clusterGroup: - include-default-og: operatorGroup: true + nodes: + - m-m00.cluster.example.tld: + labels: + cluster.ocs.openshift.io/openshift-storage: "" + - m-m01.cluster.example.tld: + labels: + cluster.ocs.openshift.io/openshift-storage: "" + - m-m02.cluster.example.tld: + labels: + cluster.ocs.openshift.io/openshift-storage: "" + operatorgroupExcludes: - exclude-og diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index c3eabd83..c9911469 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -152,6 +152,7 @@ data: - manuela-stormshift-machine-sensor - manuela-stormshift-messaging - manuela-factory-ml-workspace + nodes: [] operatorgroupExcludes: - manuela-factory-ml-workspace projects: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 393e530c..7b2b387c 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -291,6 +291,7 @@ data: - manuela-data-lake - staging - vault + nodes: [] operatorgroupExcludes: - manuela-ml-workspace projects: diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index f4933c53..b20cde33 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -270,6 +270,7 @@ data: - staging - vault - golang-external-secrets + nodes: [] projects: - hub - medical-diagnosis diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 6f1c6b2e..d21a4f42 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -73,6 +73,7 @@ data: managedClusterGroups: {} name: example namespaces: [] + nodes: [] projects: [] sharedValueFiles: [] subscriptions: {} diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index baad3fd0..c7491805 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -234,6 +234,16 @@ data: operatorGroup: false - include-default-og: operatorGroup: true + nodes: + - m-m00.cluster.example.tld: + labels: + cluster.ocs.openshift.io/openshift-storage: "" + - m-m01.cluster.example.tld: + labels: + cluster.ocs.openshift.io/openshift-storage: "" + - m-m02.cluster.example.tld: + labels: + cluster.ocs.openshift.io/openshift-storage: "" operatorgroupExcludes: - exclude-og projects: @@ -1174,6 +1184,33 @@ spec: location: ApplicationMenu text: 'Example ArgoCD' --- +# Source: clustergroup/templates/core/nodes.yaml +apiVersion: v1 +kind: Node +metadata: + name: m-m00.cluster.example.tld + labels: + argocd.argoproj.io/managed-by: mypattern-example + cluster.ocs.openshift.io/openshift-storage: "" +--- +# Source: clustergroup/templates/core/nodes.yaml +apiVersion: v1 +kind: Node +metadata: + name: m-m01.cluster.example.tld + labels: + argocd.argoproj.io/managed-by: mypattern-example + cluster.ocs.openshift.io/openshift-storage: "" +--- +# Source: clustergroup/templates/core/nodes.yaml +apiVersion: v1 +kind: Node +metadata: + name: m-m02.cluster.example.tld + labels: + argocd.argoproj.io/managed-by: mypattern-example + cluster.ocs.openshift.io/openshift-storage: "" +--- # Source: clustergroup/templates/core/operatorgroup.yaml --- apiVersion: operators.coreos.com/v1 From 308d708f922e86383f8b4b8e1708a87d8625ff42 Mon Sep 17 00:00:00 2001 From: "Sergio G." Date: Thu, 9 May 2024 22:17:44 +0200 Subject: [PATCH 1174/1263] Added support to enable user workloads in control plane nodes --- clustergroup/templates/core/scheduler.yaml | 8 ++++++++ clustergroup/values.schema.json | 4 ++++ clustergroup/values.yaml | 2 ++ examples/values-example.yaml | 2 ++ tests/clustergroup-industrial-edge-factory.expected.yaml | 9 +++++++++ tests/clustergroup-industrial-edge-hub.expected.yaml | 9 +++++++++ tests/clustergroup-medical-diagnosis-hub.expected.yaml | 9 +++++++++ tests/clustergroup-naked.expected.yaml | 9 +++++++++ tests/clustergroup-normal.expected.yaml | 9 +++++++++ 9 files changed, 61 insertions(+) create mode 100644 clustergroup/templates/core/scheduler.yaml diff --git a/clustergroup/templates/core/scheduler.yaml b/clustergroup/templates/core/scheduler.yaml new file mode 100644 index 00000000..fd2cdb67 --- /dev/null +++ b/clustergroup/templates/core/scheduler.yaml @@ -0,0 +1,8 @@ +{{- if not (eq .Values.enabled "plumbing") }} +apiVersion: config.openshift.io/v1 +kind: Scheduler +metadata: + name: cluster +spec: + mastersSchedulable: {{ $.Values.clusterGroup.mastersSchedulable }} +{{- end -}} diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 8d206957..8b6bfea6 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -257,6 +257,10 @@ "type": "array", "description": "Templated value file paths." }, + "mastersSchedulable": { + "type": "boolean", + "description": "if set to true, the control plane nodes will be marked as schedulable for user workloads also." + }, "namespaces": { "anyOf": [ { diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index c3611241..2b1018da 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -20,6 +20,8 @@ clusterGroup: targetCluster: in-cluster sharedValueFiles: [] + mastersSchedulable: false + argoCD: initContainers: [] configManagementPlugins: [] diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 84682e20..d5b45f71 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -15,6 +15,8 @@ clusterGroup: - /values/{{ .Values.global.clusterPlatform }}.yaml - /values/{{ .Values.global.clusterVersion }}.yaml + mastersSchedulable: true + # # You can define namespaces using hashes and not as a list like so: # namespaces: diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index c3eabd83..727a2b4f 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -146,6 +146,7 @@ data: verbosity: "" isHubCluster: false managedClusterGroups: {} + mastersSchedulable: false name: factory namespaces: - manuela-stormshift-line-dashboard @@ -774,6 +775,14 @@ spec: targetNamespaces: - manuela-stormshift-messaging --- +# Source: clustergroup/templates/core/scheduler.yaml +apiVersion: config.openshift.io/v1 +kind: Scheduler +metadata: + name: cluster +spec: + mastersSchedulable: false +--- # Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 393e530c..cf2e69ea 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -280,6 +280,7 @@ data: - name: clusterGroup.isHubCluster value: "false" name: factory + mastersSchedulable: false name: datacenter namespaces: - golang-external-secrets @@ -1547,6 +1548,14 @@ spec: targetNamespaces: - vault --- +# Source: clustergroup/templates/core/scheduler.yaml +apiVersion: config.openshift.io/v1 +kind: Scheduler +metadata: + name: cluster +spec: + mastersSchedulable: false +--- # Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index f4933c53..3226ebd7 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -259,6 +259,7 @@ data: - name: clusterGroup.isHubCluster value: false name: region-one + mastersSchedulable: false name: hub namespaces: - open-cluster-management @@ -1705,6 +1706,14 @@ spec: targetNamespaces: - golang-external-secrets --- +# Source: clustergroup/templates/core/scheduler.yaml +apiVersion: config.openshift.io/v1 +kind: Scheduler +metadata: + name: cluster +spec: + mastersSchedulable: false +--- # Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 6f1c6b2e..5ab214b9 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -71,6 +71,7 @@ data: verbosity: "" isHubCluster: true managedClusterGroups: {} + mastersSchedulable: false name: example namespaces: [] projects: [] @@ -463,3 +464,11 @@ spec: href: 'https://example-gitops-server-common-example.' location: ApplicationMenu text: 'Example ArgoCD' +--- +# Source: clustergroup/templates/core/scheduler.yaml +apiVersion: config.openshift.io/v1 +kind: Scheduler +metadata: + name: cluster +spec: + mastersSchedulable: false diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index baad3fd0..18f43d52 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -211,6 +211,7 @@ data: - domain: syd.beekhof.net name: sydney name: argo-edge + mastersSchedulable: true name: example namespaces: - open-cluster-management: @@ -1217,6 +1218,14 @@ spec: targetNamespaces: - include-default-og --- +# Source: clustergroup/templates/core/scheduler.yaml +apiVersion: config.openshift.io/v1 +kind: Scheduler +metadata: + name: cluster +spec: + mastersSchedulable: true +--- # Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription From 0123fc78e1de0ba539ef968b71a58d696038f0a6 Mon Sep 17 00:00:00 2001 From: "Sergio G." Date: Fri, 10 May 2024 00:04:09 +0200 Subject: [PATCH 1175/1263] Added full support for the scheduler --- clustergroup/templates/core/scheduler.yaml | 5 ++++- clustergroup/values.schema.json | 6 +++--- clustergroup/values.yaml | 5 ++++- examples/values-example.yaml | 3 ++- tests/clustergroup-industrial-edge-factory.expected.yaml | 9 --------- tests/clustergroup-industrial-edge-hub.expected.yaml | 9 --------- tests/clustergroup-medical-diagnosis-hub.expected.yaml | 9 --------- tests/clustergroup-naked.expected.yaml | 9 --------- tests/clustergroup-normal.expected.yaml | 3 ++- 9 files changed, 15 insertions(+), 43 deletions(-) diff --git a/clustergroup/templates/core/scheduler.yaml b/clustergroup/templates/core/scheduler.yaml index fd2cdb67..5061065e 100644 --- a/clustergroup/templates/core/scheduler.yaml +++ b/clustergroup/templates/core/scheduler.yaml @@ -1,8 +1,11 @@ {{- if not (eq .Values.enabled "plumbing") }} +{{- if hasKey .Values.clusterGroup "scheduler" }} apiVersion: config.openshift.io/v1 kind: Scheduler metadata: name: cluster spec: - mastersSchedulable: {{ $.Values.clusterGroup.mastersSchedulable }} +{{- toYaml .Values.clusterGroup.scheduler | nindent 2 }} {{- end -}} +{{- end -}} + diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 8b6bfea6..c4f97634 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -257,9 +257,9 @@ "type": "array", "description": "Templated value file paths." }, - "mastersSchedulable": { - "type": "boolean", - "description": "if set to true, the control plane nodes will be marked as schedulable for user workloads also." + "scheduler": { + "type": "object", + "description": "If set, it will become the spec of the scheduler/cluster in the managed cluster." }, "namespaces": { "anyOf": [ diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 2b1018da..bb149567 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -20,7 +20,10 @@ clusterGroup: targetCluster: in-cluster sharedValueFiles: [] - mastersSchedulable: false +# scheduler: +# mastersSchedulable: true +# defaultNodeSelector: type=user-node,region=east +# profile: HighNodeUtilization argoCD: initContainers: [] diff --git a/examples/values-example.yaml b/examples/values-example.yaml index d5b45f71..0226efe7 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -15,7 +15,8 @@ clusterGroup: - /values/{{ .Values.global.clusterPlatform }}.yaml - /values/{{ .Values.global.clusterVersion }}.yaml - mastersSchedulable: true + scheduler: + mastersSchedulable: true # # You can define namespaces using hashes and not as a list like so: diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 727a2b4f..c3eabd83 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -146,7 +146,6 @@ data: verbosity: "" isHubCluster: false managedClusterGroups: {} - mastersSchedulable: false name: factory namespaces: - manuela-stormshift-line-dashboard @@ -775,14 +774,6 @@ spec: targetNamespaces: - manuela-stormshift-messaging --- -# Source: clustergroup/templates/core/scheduler.yaml -apiVersion: config.openshift.io/v1 -kind: Scheduler -metadata: - name: cluster -spec: - mastersSchedulable: false ---- # Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index cf2e69ea..393e530c 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -280,7 +280,6 @@ data: - name: clusterGroup.isHubCluster value: "false" name: factory - mastersSchedulable: false name: datacenter namespaces: - golang-external-secrets @@ -1548,14 +1547,6 @@ spec: targetNamespaces: - vault --- -# Source: clustergroup/templates/core/scheduler.yaml -apiVersion: config.openshift.io/v1 -kind: Scheduler -metadata: - name: cluster -spec: - mastersSchedulable: false ---- # Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 3226ebd7..f4933c53 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -259,7 +259,6 @@ data: - name: clusterGroup.isHubCluster value: false name: region-one - mastersSchedulable: false name: hub namespaces: - open-cluster-management @@ -1706,14 +1705,6 @@ spec: targetNamespaces: - golang-external-secrets --- -# Source: clustergroup/templates/core/scheduler.yaml -apiVersion: config.openshift.io/v1 -kind: Scheduler -metadata: - name: cluster -spec: - mastersSchedulable: false ---- # Source: clustergroup/templates/core/subscriptions.yaml apiVersion: operators.coreos.com/v1alpha1 kind: Subscription diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 5ab214b9..6f1c6b2e 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -71,7 +71,6 @@ data: verbosity: "" isHubCluster: true managedClusterGroups: {} - mastersSchedulable: false name: example namespaces: [] projects: [] @@ -464,11 +463,3 @@ spec: href: 'https://example-gitops-server-common-example.' location: ApplicationMenu text: 'Example ArgoCD' ---- -# Source: clustergroup/templates/core/scheduler.yaml -apiVersion: config.openshift.io/v1 -kind: Scheduler -metadata: - name: cluster -spec: - mastersSchedulable: false diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 18f43d52..3e2e2849 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -211,7 +211,6 @@ data: - domain: syd.beekhof.net name: sydney name: argo-edge - mastersSchedulable: true name: example namespaces: - open-cluster-management: @@ -239,6 +238,8 @@ data: - exclude-og projects: - datacenter + scheduler: + mastersSchedulable: true sharedValueFiles: - /values/aws.yaml - /values/4.12.yaml From 48b584e59c7129d104fa4b53b27cec11729cf09b Mon Sep 17 00:00:00 2001 From: "Sergio G." Date: Fri, 10 May 2024 00:21:37 +0200 Subject: [PATCH 1176/1263] Simplified PR for auto approve install plans --- .../auto-approve-installplans.yaml | 40 ++++++++++++++ .../imperative/auto-approve-installplans.yaml | 52 +++++++++++++++++++ clustergroup/values.schema.json | 4 ++ 3 files changed, 96 insertions(+) create mode 100644 ansible/playbooks/auto-approve-installplans/auto-approve-installplans.yaml create mode 100644 clustergroup/templates/imperative/auto-approve-installplans.yaml diff --git a/ansible/playbooks/auto-approve-installplans/auto-approve-installplans.yaml b/ansible/playbooks/auto-approve-installplans/auto-approve-installplans.yaml new file mode 100644 index 00000000..6b6802d4 --- /dev/null +++ b/ansible/playbooks/auto-approve-installplans/auto-approve-installplans.yaml @@ -0,0 +1,40 @@ +# This playbook will watch for pending install plans of managed operators +# if they are in Manual and there's a startingCSV that must be installed +--- +- name: InstallPlan Auto-Approver + hosts: localhost + connection: local + gather_facts: false + become: false + + tasks: + - name: Get all installPlans from OpenShift + kubernetes.core.k8s_info: + api_version: operators.coreos.com/v1alpha1 + kind: InstallPlan + register: installplans + + - name: Get required CSVs from clusterGroup data + ansible.builtin.set_fact: + expected_csv: "{{ expected_csv | default([]) + [item.csv] }}" + when: item.csv | default(false) and + ((item.installPlanApproval | default("") == "Manual") or + (item.installPlanApproval | default("") == "" and global.options.installPlanApproval | default("") == "Manual")) + with_items: "{{ clusterGroup.subscriptions.values() }}" + + # TODO: loop over clusterGroup.subscriptions instead of installplans + # to allow certain control on the order of approvals + # IDEA: allow adding a per-installplan delay after the approval before + # moving forward to the next one + - name: Approve the missing installPlans + kubernetes.core.k8s_json_patch: + api_version: operators.coreos.com/v1alpha1 + kind: InstallPlan + name: "{{ item.metadata.name }}" + namespace: "{{ item.metadata.namespace }}" + patch: + - op: replace + path: /spec/approved + value: true + when: (item.spec.clusterServiceVersionNames | intersect(expected_csv | default([]))) | length > 0 + loop: "{{ installplans.resources }}" diff --git a/clustergroup/templates/imperative/auto-approve-installplans.yaml b/clustergroup/templates/imperative/auto-approve-installplans.yaml new file mode 100644 index 00000000..e6ebf26c --- /dev/null +++ b/clustergroup/templates/imperative/auto-approve-installplans.yaml @@ -0,0 +1,52 @@ +{{- if $.Values.global.options.autoApproveManualInstallPlans }} +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: auto-approve-installplans-cronjob + namespace: {{ $.Values.clusterGroup.imperative.namespace}} +spec: + schedule: "*/5 * * * *" + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: {{ $.Values.clusterGroup.imperative.activeDeadlineSeconds }} + template: + metadata: + name: auto-approve-installplans-job + spec: + serviceAccountName: {{ $.Values.clusterGroup.imperative.serviceAccountName }} + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + {{- include "imperative.initcontainers.gitinit" . | indent 12 }} + - name: auto-approve-installplans + image: {{ $.Values.clusterGroup.imperative.image }} + imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} + env: + - name: HOME + value: /git/home + workingDir: /git/repo + command: + - timeout + - {{ .timeout | default "600" | quote }} + - ansible-playbook + {{- if $.Values.clusterGroup.imperative.verbosity }} + - {{ $.Values.clusterGroup.imperative.verbosity }} + {{- end }} + - -e + - "@/values/values.yaml" + - common/ansible/playbooks/auto-approve-installplans/auto-approve-installplans.yaml + volumeMounts: + {{- include "imperative.volumemounts" . | indent 16 }} + containers: + {{- include "imperative.containers.done" . | indent 12 }} + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} + restartPolicy: Never +{{- end }} diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 8d206957..9c3a3651 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -226,6 +226,10 @@ "deprecated": true, "description": "This is used to approval strategy for the subscriptions of OpenShift Operators being installed. You can choose Automatic or Manual updates. NOTE: This setting is now available in the subcriptions description in the values file." }, + "autoApproveManualInstallPlans": { + "type": "boolean", + "description": "This is used to approve automatically those subscriptions of OpenShift Operators that are in Manual with a startingCSV version. You can choose True or False. Defaults: False." + }, "applicationRetryLimit": { "type": "integer", "description": "Number of failed sync attempt retries; unlimited number of attempts if less than 0" From 04a6d6dbb3a04f40d879bdbfed8c2a9e47a15e32 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 13 May 2024 23:09:15 +0200 Subject: [PATCH 1177/1263] Switch to registry.redhat.io for the initContainer image This makes the registry url more consistent with what we already use (e.g. in the imperative templates) --- acm/templates/policies/ocp-gitops-policy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index 399f52a9..e72434ae 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -140,7 +140,7 @@ spec: - -c - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true - image: registry.access.redhat.com/ubi9/ubi-minimal:latest + image: registry.redhat.io/ubi9/ubi-minimal:latest name: fetch-ca resources: {} volumeMounts: From f6c7a47c1b6e8637339bb5678bd785648a8ad3c1 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Tue, 14 May 2024 14:42:27 -0600 Subject: [PATCH 1178/1263] Update for ACM chart to application-policies.yaml - If statement was checking for .Values.global.extraValueFiles. - We now checking at the .extraValueFiles in the managedClusterGroups section. managedClusterGroups: aro-prod: name: innovation acmlabels: - name: clusterGroup value: innovation extraValueFiles: - '/overrides/values-common-capabilities.yaml' helmOverrides: - name: clusterGroup.isHubCluster value: "false" --- acm/templates/policies/application-policies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 131f4f3e..0194d6bb 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -45,7 +45,7 @@ spec: ignoreMissingValueFiles: true valueFiles: {{- include "acm.app.policies.valuefiles" . | nindent 22 }} - {{- range $valueFile := $.Values.global.extraValueFiles }} + {{- range $valueFile := .extraValueFiles }} - {{ $valueFile | quote }} {{- end }} parameters: From 4d422a4e7e3d78fba1f0420790e586f01461ad03 Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 15 May 2024 17:12:30 -0600 Subject: [PATCH 1179/1263] Add extraParameters to values.schema.json - Problem Statement The current **clustergroup** schema does not allow the definition of **extraParameters** under the **main** section of a values file. - Caveat The user defined variables in the **extraParameters** section would only be applied if the user deploys the pattern via the command, using `./pattern.sh make install` or `./pattern.sh make operator-deploy` and not via the OpenShift Validated Patterns Operator UI. - Fix Description Add the **extraParameters** to the definition of **Main.properties** in the values.schema.json: "extraParameters": { "type": "array", "description": "Pass in extra Helm parameters to all ArgoCD Applications and the framework." }, - This will allow users to define extra parameters that will be added by the framework to the ArgoCD applications it creates. - For more information see https://github.com/validatedpatterns/common/issues/510 --- clustergroup/values.schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 8d206957..ea115f2a 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -65,6 +65,10 @@ "clusterGroupName": { "type": "string" }, + "extraParameters": { + "type": "array", + "description": "Pass in extra Helm parameters to all ArgoCD Applications and the framework." + }, "experimentalCapabilities": { "type": "string", "description": "String to enable certain experimental capabilities in the operator and the framework." From ab630bb7953a1fcf84945e923a21e537e899a3ee Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 17 May 2024 10:28:49 +0200 Subject: [PATCH 1180/1263] Update ESO to 0.9.18 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.9.16.tgz | Bin 102068 -> 0 bytes .../charts/external-secrets-0.9.18.tgz | Bin 0 -> 76336 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 159 +++++++++++------- ...-secrets-industrial-edge-hub.expected.yaml | 159 +++++++++++------- ...ecrets-medical-diagnosis-hub.expected.yaml | 159 +++++++++++------- ...olang-external-secrets-naked.expected.yaml | 159 +++++++++++------- ...lang-external-secrets-normal.expected.yaml | 159 +++++++++++------- 9 files changed, 514 insertions(+), 289 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.16.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.9.18.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 961f3b29..4aef19aa 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.3 dependencies: - name: external-secrets - version: "0.9.16" + version: "0.9.18" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.9.16.tgz b/golang-external-secrets/charts/external-secrets-0.9.16.tgz deleted file mode 100644 index ca268d8459d75ca2b82392618d1bda98327bb264..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102068 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


      q0j)i=a#pu1jU+b_58*02a$>ktMv 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 literal 0 HcmV?d00001 diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index 8d30c3df..e3d14148 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -37,10 +37,10 @@ clusterGroup: external-secrets: image: - tag: v0.9.16-ubi + tag: v0.9.18-ubi webhook: image: - tag: v0.9.16-ubi + tag: v0.9.18-ubi certController: image: - tag: v0.9.16-ubi + tag: v0.9.18-ubi diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index f0969200..712ec247 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -263,7 +263,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -780,11 +780,13 @@ spec: 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 @@ -827,11 +829,13 @@ spec: 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 @@ -844,7 +848,7 @@ spec: x-kubernetes-map-type: atomic type: array namespaces: - description: Choose namespaces by name. This field is ORed with anything that NamespaceSelector ends up choosing. + description: Choose namespaces by name. This field is ORed with anything that NamespaceSelectors ends up choosing. items: type: string type: array @@ -916,7 +920,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -2484,11 +2488,13 @@ spec: 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 @@ -2921,6 +2927,23 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity. properties: + clientCertificate: + description: The Azure ClientCertificate of the service principle used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object clientId: description: The Azure clientId of the service principle or managed identity used for authentication. properties: @@ -5079,7 +5102,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -5246,7 +5269,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -6051,7 +6074,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6127,7 +6150,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6255,7 +6278,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: githubaccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6357,7 +6380,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6455,7 +6478,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -6586,11 +6609,13 @@ spec: 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 @@ -6831,7 +6856,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -8399,11 +8424,13 @@ spec: 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 @@ -8836,6 +8863,23 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity. properties: + clientCertificate: + description: The Azure ClientCertificate of the service principle used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object clientId: description: The Azure clientId of the service principle or managed identity used for authentication. properties: @@ -10994,7 +11038,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -11686,7 +11730,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: webhooks.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -11834,10 +11878,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11901,10 +11945,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12012,10 +12056,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -12054,10 +12098,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -12100,10 +12144,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12121,10 +12165,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12141,10 +12185,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12177,10 +12221,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12216,10 +12260,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12237,10 +12281,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -12261,10 +12305,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12276,10 +12320,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -12294,7 +12338,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -12324,10 +12368,10 @@ metadata: name: golang-external-secrets namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12339,10 +12383,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -12357,7 +12401,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -12366,6 +12410,7 @@ spec: - containerPort: 8080 protocol: TCP name: metrics + dnsPolicy: ClusterFirst --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-deployment.yaml apiVersion: apps/v1 @@ -12374,10 +12419,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12389,10 +12434,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -12407,7 +12452,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index 0ecbf649..08528096 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -263,7 +263,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -780,11 +780,13 @@ spec: 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 @@ -827,11 +829,13 @@ spec: 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 @@ -844,7 +848,7 @@ spec: x-kubernetes-map-type: atomic type: array namespaces: - description: Choose namespaces by name. This field is ORed with anything that NamespaceSelector ends up choosing. + description: Choose namespaces by name. This field is ORed with anything that NamespaceSelectors ends up choosing. items: type: string type: array @@ -916,7 +920,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -2484,11 +2488,13 @@ spec: 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 @@ -2921,6 +2927,23 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity. properties: + clientCertificate: + description: The Azure ClientCertificate of the service principle used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object clientId: description: The Azure clientId of the service principle or managed identity used for authentication. properties: @@ -5079,7 +5102,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -5246,7 +5269,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -6051,7 +6074,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6127,7 +6150,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6255,7 +6278,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: githubaccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6357,7 +6380,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6455,7 +6478,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -6586,11 +6609,13 @@ spec: 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 @@ -6831,7 +6856,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -8399,11 +8424,13 @@ spec: 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 @@ -8836,6 +8863,23 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity. properties: + clientCertificate: + description: The Azure ClientCertificate of the service principle used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object clientId: description: The Azure clientId of the service principle or managed identity used for authentication. properties: @@ -10994,7 +11038,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -11686,7 +11730,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: webhooks.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -11834,10 +11878,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11901,10 +11945,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12012,10 +12056,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -12054,10 +12098,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -12100,10 +12144,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12121,10 +12165,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12141,10 +12185,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12177,10 +12221,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12216,10 +12260,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12237,10 +12281,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -12261,10 +12305,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12276,10 +12320,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -12294,7 +12338,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -12324,10 +12368,10 @@ metadata: name: golang-external-secrets namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12339,10 +12383,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -12357,7 +12401,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -12366,6 +12410,7 @@ spec: - containerPort: 8080 protocol: TCP name: metrics + dnsPolicy: ClusterFirst --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-deployment.yaml apiVersion: apps/v1 @@ -12374,10 +12419,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12389,10 +12434,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -12407,7 +12452,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index 0ecbf649..08528096 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -263,7 +263,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -780,11 +780,13 @@ spec: 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 @@ -827,11 +829,13 @@ spec: 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 @@ -844,7 +848,7 @@ spec: x-kubernetes-map-type: atomic type: array namespaces: - description: Choose namespaces by name. This field is ORed with anything that NamespaceSelector ends up choosing. + description: Choose namespaces by name. This field is ORed with anything that NamespaceSelectors ends up choosing. items: type: string type: array @@ -916,7 +920,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -2484,11 +2488,13 @@ spec: 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 @@ -2921,6 +2927,23 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity. properties: + clientCertificate: + description: The Azure ClientCertificate of the service principle used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object clientId: description: The Azure clientId of the service principle or managed identity used for authentication. properties: @@ -5079,7 +5102,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -5246,7 +5269,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -6051,7 +6074,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6127,7 +6150,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6255,7 +6278,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: githubaccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6357,7 +6380,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6455,7 +6478,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -6586,11 +6609,13 @@ spec: 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 @@ -6831,7 +6856,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -8399,11 +8424,13 @@ spec: 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 @@ -8836,6 +8863,23 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity. properties: + clientCertificate: + description: The Azure ClientCertificate of the service principle used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object clientId: description: The Azure clientId of the service principle or managed identity used for authentication. properties: @@ -10994,7 +11038,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -11686,7 +11730,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: webhooks.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -11834,10 +11878,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11901,10 +11945,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12012,10 +12056,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -12054,10 +12098,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -12100,10 +12144,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12121,10 +12165,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12141,10 +12185,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12177,10 +12221,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12216,10 +12260,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12237,10 +12281,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -12261,10 +12305,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12276,10 +12320,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -12294,7 +12338,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -12324,10 +12368,10 @@ metadata: name: golang-external-secrets namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12339,10 +12383,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -12357,7 +12401,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -12366,6 +12410,7 @@ spec: - containerPort: 8080 protocol: TCP name: metrics + dnsPolicy: ClusterFirst --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-deployment.yaml apiVersion: apps/v1 @@ -12374,10 +12419,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12389,10 +12434,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -12407,7 +12452,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 55750256..c4f6c22f 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -263,7 +263,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -780,11 +780,13 @@ spec: 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 @@ -827,11 +829,13 @@ spec: 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 @@ -844,7 +848,7 @@ spec: x-kubernetes-map-type: atomic type: array namespaces: - description: Choose namespaces by name. This field is ORed with anything that NamespaceSelector ends up choosing. + description: Choose namespaces by name. This field is ORed with anything that NamespaceSelectors ends up choosing. items: type: string type: array @@ -916,7 +920,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -2484,11 +2488,13 @@ spec: 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 @@ -2921,6 +2927,23 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity. properties: + clientCertificate: + description: The Azure ClientCertificate of the service principle used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object clientId: description: The Azure clientId of the service principle or managed identity used for authentication. properties: @@ -5079,7 +5102,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -5246,7 +5269,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -6051,7 +6074,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6127,7 +6150,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6255,7 +6278,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: githubaccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6357,7 +6380,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6455,7 +6478,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -6586,11 +6609,13 @@ spec: 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 @@ -6831,7 +6856,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -8399,11 +8424,13 @@ spec: 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 @@ -8836,6 +8863,23 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity. properties: + clientCertificate: + description: The Azure ClientCertificate of the service principle used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object clientId: description: The Azure clientId of the service principle or managed identity used for authentication. properties: @@ -10994,7 +11038,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -11686,7 +11730,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: webhooks.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -11834,10 +11878,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11901,10 +11945,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12012,10 +12056,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -12054,10 +12098,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -12100,10 +12144,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12121,10 +12165,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12141,10 +12185,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12177,10 +12221,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12216,10 +12260,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12237,10 +12281,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -12261,10 +12305,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12276,10 +12320,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -12294,7 +12338,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -12324,10 +12368,10 @@ metadata: name: golang-external-secrets namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12339,10 +12383,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -12357,7 +12401,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -12366,6 +12410,7 @@ spec: - containerPort: 8080 protocol: TCP name: metrics + dnsPolicy: ClusterFirst --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-deployment.yaml apiVersion: apps/v1 @@ -12374,10 +12419,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12389,10 +12434,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -12407,7 +12452,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 0ecbf649..08528096 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: acraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -263,7 +263,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: clusterexternalsecrets.external-secrets.io spec: group: external-secrets.io @@ -780,11 +780,13 @@ spec: 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 @@ -827,11 +829,13 @@ spec: 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 @@ -844,7 +848,7 @@ spec: x-kubernetes-map-type: atomic type: array namespaces: - description: Choose namespaces by name. This field is ORed with anything that NamespaceSelector ends up choosing. + description: Choose namespaces by name. This field is ORed with anything that NamespaceSelectors ends up choosing. items: type: string type: array @@ -916,7 +920,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: clustersecretstores.external-secrets.io spec: group: external-secrets.io @@ -2484,11 +2488,13 @@ spec: 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 @@ -2921,6 +2927,23 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity. properties: + clientCertificate: + description: The Azure ClientCertificate of the service principle used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object clientId: description: The Azure clientId of the service principle or managed identity used for authentication. properties: @@ -5079,7 +5102,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: ecrauthorizationtokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -5246,7 +5269,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: externalsecrets.external-secrets.io spec: group: external-secrets.io @@ -6051,7 +6074,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: fakes.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6127,7 +6150,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: gcraccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6255,7 +6278,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: githubaccesstokens.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6357,7 +6380,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: passwords.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -6455,7 +6478,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: pushsecrets.external-secrets.io spec: group: external-secrets.io @@ -6586,11 +6609,13 @@ spec: 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 @@ -6831,7 +6856,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: secretstores.external-secrets.io spec: group: external-secrets.io @@ -8399,11 +8424,13 @@ spec: 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 @@ -8836,6 +8863,23 @@ spec: authSecretRef: description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity. properties: + clientCertificate: + description: The Azure ClientCertificate of the service principle used for authentication. + properties: + key: + description: |- + The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be + defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: |- + Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults + to the namespace of the referent. + type: string + type: object clientId: description: The Azure clientId of the service principle or managed identity used for authentication. properties: @@ -10994,7 +11038,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: vaultdynamicsecrets.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -11686,7 +11730,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: webhooks.generators.external-secrets.io spec: group: generators.external-secrets.io @@ -11834,10 +11878,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -11901,10 +11945,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12012,10 +12056,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -12054,10 +12098,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -12100,10 +12144,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12121,10 +12165,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12141,10 +12185,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12177,10 +12221,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -12216,10 +12260,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -12237,10 +12281,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -12261,10 +12305,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12276,10 +12320,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: external-secrets-cert-controller @@ -12294,7 +12338,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - certcontroller @@ -12324,10 +12368,10 @@ metadata: name: golang-external-secrets namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12339,10 +12383,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: serviceAccountName: golang-external-secrets @@ -12357,7 +12401,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -12366,6 +12410,7 @@ spec: - containerPort: 8080 protocol: TCP name: metrics + dnsPolicy: ClusterFirst --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-deployment.yaml apiVersion: apps/v1 @@ -12374,10 +12419,10 @@ metadata: name: golang-external-secrets-webhook namespace: default labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -12389,10 +12434,10 @@ spec: template: metadata: labels: - helm.sh/chart: external-secrets-0.9.16 + helm.sh/chart: external-secrets-0.9.18 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.9.16" + app.kubernetes.io/version: "v0.9.18" app.kubernetes.io/managed-by: Helm spec: hostNetwork: false @@ -12407,7 +12452,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - image: ghcr.io/external-secrets/external-secrets:v0.9.16-ubi + image: ghcr.io/external-secrets/external-secrets:v0.9.18-ubi imagePullPolicy: IfNotPresent args: - webhook From 7d147b241dfa3b9feaa449eef23a3df8b037454f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 17 May 2024 12:28:40 +0200 Subject: [PATCH 1181/1263] Update vault to 1.16.2 --- hashicorp-vault/values.yaml | 2 +- tests/hashicorp-vault-industrial-edge-factory.expected.yaml | 4 ++-- tests/hashicorp-vault-industrial-edge-hub.expected.yaml | 4 ++-- tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml | 4 ++-- tests/hashicorp-vault-naked.expected.yaml | 4 ++-- tests/hashicorp-vault-normal.expected.yaml | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index fa73a060..691f7094 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -48,4 +48,4 @@ vault: termination: "reencrypt" image: repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.16.1-ubi" + tag: "1.16.2-ubi" diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index 74212ee2..8156e2af 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index 74212ee2..8156e2af 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index 74212ee2..8156e2af 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index 1fe2cd90..8727ef0e 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index 74212ee2..8156e2af 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.16.1-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR From 4eeb69ba01e2227d060946d9a8632c36d2d8b3de Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Fri, 17 May 2024 10:24:44 -0600 Subject: [PATCH 1182/1263] Feat: Followup to definition of extraParameters under the main section of a values file. - The operator adds these extraParameters to the extraParametersNested section as key/value pairs in the Cluster Wide ArgoCD Application created by the Validated Patterns operator. - This update will add the user defined extra parameters on the ArgoCD Applications on the Spoke Clusters. efinition of extraParameters under the main --- .../policies/application-policies.yaml | 9 +++++++ .../templates/plumbing/applications.yaml | 10 +++++++ tests/acm-industrial-edge-hub.expected.yaml | 2 ++ tests/acm-medical-diagnosis-hub.expected.yaml | 2 ++ tests/acm-normal.expected.yaml | 4 +++ ...roup-industrial-edge-factory.expected.yaml | 2 ++ ...tergroup-industrial-edge-hub.expected.yaml | 14 ++++++++++ ...rgroup-medical-diagnosis-hub.expected.yaml | 26 +++++++++++++++++++ tests/clustergroup-normal.expected.yaml | 4 +++ 9 files changed, 73 insertions(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 0194d6bb..2a815913 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -43,6 +43,11 @@ spec: path: {{ default "common/clustergroup" .path }} helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: + {{- range $k, $v := $.Values.extraParametersNested }} + {{ $k }}: {{ printf "%s" $v | quote }} + {{- end }} valueFiles: {{- include "acm.app.policies.valuefiles" . | nindent 22 }} {{- range $valueFile := .extraValueFiles }} @@ -73,6 +78,10 @@ spec: value: {{ $group.name }} - name: global.experimentalCapabilities value: {{ $.Values.global.experimentalCapabilities }} + {{- range $k, $v := $.Values.extraParametersNested }} + - name: {{ $k }} + value: {{ printf "%s" $v | quote }} + {{- end }} {{- range .helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 29db6f39..870babe3 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -149,6 +149,11 @@ spec: {{- else }} helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: + {{- range $k, $v := $.Values.extraParametersNested }} + {{ $k }}: {{ printf "%s" $v | quote }} + {{- end }} valueFiles: {{- include "clustergroup.app.globalvalues.prefixedvaluefiles" $ | nindent 8 }} {{- range $valueFile := $.Values.clusterGroup.sharedValueFiles }} @@ -216,6 +221,11 @@ spec: {{- else if not .kustomize }} helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: + {{- range $k, $v := $.Values.extraParametersNested }} + {{ $k }}: {{ printf "%s" $v | quote }} + {{- end }} valueFiles: {{- include "clustergroup.app.globalvalues.valuefiles" $ | nindent 6 }} {{- range $valueFile := $.Values.clusterGroup.sharedValueFiles }} diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 453e8a9e..eb1df26f 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -214,6 +214,8 @@ spec: path: common/clustergroup helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-factory.yaml" diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 8b50de7a..6a99a29c 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -205,6 +205,8 @@ spec: path: common/clustergroup helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-region-one.yaml" diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 66f1c590..d29937be 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -608,6 +608,8 @@ spec: path: common/clustergroup helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-acm-edge.yaml" @@ -704,6 +706,8 @@ spec: path: common/clustergroup helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-acm-provision-edge.yaml" diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index c3eabd83..356b7e7e 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -559,6 +559,8 @@ spec: path: charts/datacenter/opendatahub helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-factory.yaml" diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 393e530c..3291aeb2 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -857,6 +857,8 @@ spec: path: common/acm helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" @@ -922,6 +924,8 @@ spec: path: charts/datacenter/opendatahub helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" @@ -978,6 +982,8 @@ spec: path: charts/datacenter/pipelines helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" @@ -1034,6 +1040,8 @@ spec: path: charts/datacenter/manuela-data-lake helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" @@ -1120,6 +1128,8 @@ spec: path: charts/datacenter/external-secrets helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" @@ -1176,6 +1186,8 @@ spec: path: common/golang-external-secrets helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" @@ -1259,6 +1271,8 @@ spec: chart: vault helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-datacenter.yaml" diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index f4933c53..6e300fc3 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -742,6 +742,8 @@ spec: path: common/golang-external-secrets helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" @@ -798,6 +800,8 @@ spec: path: charts/all/kafdrop helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" @@ -854,6 +858,8 @@ spec: path: charts/all/kafka helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" @@ -910,6 +916,8 @@ spec: path: charts/all/opendatahub helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" @@ -966,6 +974,8 @@ spec: path: charts/all/openshift-data-foundations helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" @@ -1022,6 +1032,8 @@ spec: path: charts/all/openshift-serverless helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" @@ -1078,6 +1090,8 @@ spec: path: charts/all/medical-diagnosis/service-account helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" @@ -1134,6 +1148,8 @@ spec: chart: vault helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" @@ -1208,6 +1224,8 @@ spec: path: charts/all/medical-diagnosis/database helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" @@ -1264,6 +1282,8 @@ spec: path: charts/all/medical-diagnosis/grafana helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" @@ -1320,6 +1340,8 @@ spec: path: charts/all/medical-diagnosis/image-generator helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" @@ -1385,6 +1407,8 @@ spec: path: charts/all/medical-diagnosis/image-server helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" @@ -1450,6 +1474,8 @@ spec: path: charts/all/medical-diagnosis/xray-init helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-hub.yaml" diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index baad3fd0..41eb68b8 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -707,6 +707,8 @@ spec: path: common/acm helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-example.yaml" @@ -774,6 +776,8 @@ spec: path: charts/datacenter/pipelines helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - "/values-global.yaml" - "/values-example.yaml" From e23fea077bd59d7a2291acfa512825ed2a90de01 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 17 May 2024 17:37:51 +0200 Subject: [PATCH 1183/1263] Use golang-external-secrets for the acm hub-ca bits We'd like to make the imperative namespace optional, so let's use the golang-external-secrets one, which is probably more correct anyways since the acm hub ca is tied to ESO anyways. --- acm/templates/policies/acm-hub-ca-policy.yaml | 2 +- golang-external-secrets/values.yaml | 2 +- tests/acm-industrial-edge-hub.expected.yaml | 2 +- tests/acm-medical-diagnosis-hub.expected.yaml | 2 +- tests/acm-normal.expected.yaml | 2 +- ...olang-external-secrets-industrial-edge-factory.expected.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/acm/templates/policies/acm-hub-ca-policy.yaml b/acm/templates/policies/acm-hub-ca-policy.yaml index 890e6bae..3d02d62f 100644 --- a/acm/templates/policies/acm-hub-ca-policy.yaml +++ b/acm/templates/policies/acm-hub-ca-policy.yaml @@ -31,7 +31,7 @@ spec: type: Opaque metadata: name: hub-ca - namespace: imperative + namespace: golang-external-secrets data: hub-kube-root-ca.crt: '{{ `{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}` }}' hub-openshift-service-ca.crt: '{{ `{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | base64enc hub}}` }}' diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index e3d14148..8c1cab77 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -23,7 +23,7 @@ golangExternalSecrets: type: Secret name: hub-ca key: hub-kube-root-ca.crt - namespace: imperative + namespace: golang-external-secrets global: hubClusterDomain: hub.example.com diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 453e8a9e..620591a2 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -167,7 +167,7 @@ spec: type: Opaque metadata: name: hub-ca - namespace: imperative + namespace: golang-external-secrets data: hub-kube-root-ca.crt: '{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}' hub-openshift-service-ca.crt: '{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | base64enc hub}}' diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 8b50de7a..18a2f921 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -158,7 +158,7 @@ spec: type: Opaque metadata: name: hub-ca - namespace: imperative + namespace: golang-external-secrets data: hub-kube-root-ca.crt: '{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}' hub-openshift-service-ca.crt: '{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | base64enc hub}}' diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 66f1c590..28c5eea7 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -561,7 +561,7 @@ spec: type: Opaque metadata: name: hub-ca - namespace: imperative + namespace: golang-external-secrets data: hub-kube-root-ca.crt: '{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}' hub-openshift-service-ca.crt: '{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | base64enc hub}}' diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index 712ec247..f7aee2c2 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -12502,7 +12502,7 @@ spec: type: Secret name: hub-ca key: hub-kube-root-ca.crt - namespace: imperative + namespace: golang-external-secrets auth: kubernetes: From f6734917fc9094cb597e01521c1888bbbff4756c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 17 May 2024 18:17:06 +0200 Subject: [PATCH 1184/1263] Only do the acm hub ca policy when vault is the backend The acm hub ca is needed for ESO on spokes to connect to the vault on the hub, there is no need for this when vault is not used, so let's drop it in that case --- acm/templates/policies/acm-hub-ca-policy.yaml | 7 ++++--- acm/values.yaml | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/acm/templates/policies/acm-hub-ca-policy.yaml b/acm/templates/policies/acm-hub-ca-policy.yaml index 3d02d62f..ef15b136 100644 --- a/acm/templates/policies/acm-hub-ca-policy.yaml +++ b/acm/templates/policies/acm-hub-ca-policy.yaml @@ -1,5 +1,6 @@ # This pushes out the HUB's Certificate Authorities on to the imported clusters -{{ if .Values.clusterGroup.isHubCluster }} +{{- if .Values.clusterGroup.isHubCluster }} +{{- if (eq (((.Values.global).secretStore).backend) "vault") }} --- apiVersion: policy.open-cluster-management.io/v1 kind: Policy @@ -67,5 +68,5 @@ spec: operator: NotIn values: - 'true' -{{ end }} - +{{- end }} +{{- end }} diff --git a/acm/values.yaml b/acm/values.yaml index c5f222c9..6919b419 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -9,6 +9,8 @@ global: targetRevision: main options: applicationRetryLimit: 20 + secretStore: + backend: "vault" clusterGroup: subscriptions: From 6cd4e85d5bf7955b172b935dd05cd360fd0c4443 Mon Sep 17 00:00:00 2001 From: Tomer Figenblat Date: Tue, 28 May 2024 13:36:43 -0400 Subject: [PATCH 1185/1263] fix: when using clusterdeployments, secrets should exist in the cluster-namespace Co-authored-by: Michele Baldessari Co-authored-by: Alejandro Villegas Signed-off-by: Tomer Figenblat --- acm/templates/provision/secrets-aws.yaml | 10 ++++++++-- acm/templates/provision/secrets-azure.yaml | 10 ++++++++-- acm/templates/provision/secrets-common.yaml | 15 ++++++++++++--- tests/acm-normal.expected.yaml | 10 ++++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/acm/templates/provision/secrets-aws.yaml b/acm/templates/provision/secrets-aws.yaml index a671638d..911aff4a 100644 --- a/acm/templates/provision/secrets-aws.yaml +++ b/acm/templates/provision/secrets-aws.yaml @@ -15,9 +15,9 @@ {{- $deploymentName := print .name "-" $group.name }} {{- if .platform.aws }} --- -{{- template "externalsecret.aws.creds" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore) }} +{{- template "externalsecret.aws.creds" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore "namespaced" true) }} --- -{{- template "externalsecret.aws.infra-creds" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore) }} +{{- template "externalsecret.aws.infra-creds" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore "namespaced" true) }} {{- end }}{{- /* if .platform.aws */}} {{- end }}{{- /* range .clusterDeployments */}} @@ -29,6 +29,9 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: {{ .name }}-creds + {{- if .namespaced }} + namespace: {{ .name }} + {{- end }} spec: dataFrom: - extract: @@ -51,6 +54,9 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: {{ .name }}-infra-creds + {{- if .namespaced }} + namespace: {{ .name }} + {{- end }} spec: data: - secretKey: openshiftPullSecret diff --git a/acm/templates/provision/secrets-azure.yaml b/acm/templates/provision/secrets-azure.yaml index 21c9d482..1ef5842c 100644 --- a/acm/templates/provision/secrets-azure.yaml +++ b/acm/templates/provision/secrets-azure.yaml @@ -16,9 +16,9 @@ {{- $deploymentName := print .name "-" $group.name }} {{- if .platform.azure }} --- -{{- template "externalsecret.azure.creds" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore) }} +{{- template "externalsecret.azure.creds" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore "namespaced" true) }} --- -{{- template "externalsecret.azure.infra-creds" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore) }} +{{- template "externalsecret.azure.infra-creds" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore "namespaced" true) }} {{- end }}{{- /* if .platform.azure */}} @@ -31,6 +31,9 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: {{ .name }}-creds + {{- if .namespaced }} + namespace: {{ .name }} + {{- end }} spec: data: - secretKey: azureOsServicePrincipal @@ -57,6 +60,9 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: {{ .name }}-infra-creds + {{- if .namespaced }} + namespace: {{ .name }} + {{- end }} spec: data: - secretKey: openshiftPullSecret diff --git a/acm/templates/provision/secrets-common.yaml b/acm/templates/provision/secrets-common.yaml index 474347c6..6901c79c 100644 --- a/acm/templates/provision/secrets-common.yaml +++ b/acm/templates/provision/secrets-common.yaml @@ -14,11 +14,11 @@ {{- range .clusterDeployments }} {{- $deploymentName := print .name "-" $group.name }} --- -{{- template "secret.install-config" (dict "name" $deploymentName "context" .) }} +{{- template "secret.install-config" (dict "name" $deploymentName "context" . "namespaced" true) }} --- -{{- template "externalsecret.pull-secret" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore) }} +{{- template "externalsecret.pull-secret" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore "namespaced" true) }} --- -{{- template "externalsecret.ssh.private.key" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore) }} +{{- template "externalsecret.ssh.private.key" (dict "name" $deploymentName "context" . "secretStore" $.Values.secretStore "namespaced" true) }} {{- end }}{{- /* range .clusterDeplyments */}} {{- end }}{{- /* range .Values.clusterGroup.managedClusterGroups */}} @@ -28,6 +28,9 @@ apiVersion: v1 kind: Secret metadata: name: {{ .name }}-install-config + {{- if .namespaced }} + namespace: {{ .name }} + {{- end }} data: # Base64 encoding of install-config yaml install-config.yaml: {{ include "cluster.install-config" .context | b64enc }} @@ -39,6 +42,9 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: {{ .name }}-pull-secret + {{- if .namespaced }} + namespace: {{ .name }} + {{- end }} spec: data: - secretKey: openshiftPullSecret @@ -65,6 +71,9 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: {{ .name }}-ssh-private-key + {{- if .namespaced }} + namespace: {{ .name }} + {{- end }} spec: data: - secretKey: sshPrivateKey diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 3e6fbc74..7969434f 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -36,6 +36,7 @@ apiVersion: v1 kind: Secret metadata: name: aws-cd-one-w-pool-acm-provision-edge-install-config + namespace: aws-cd-one-w-pool-acm-provision-edge data: # Base64 encoding of install-config yaml install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXdzLWNkLW9uZS13LXBvb2wnIApiYXNlRG9tYWluOiBibHVlcHJpbnRzLnJoZWNvZW5nLmNvbQpjb250cm9sUGxhbmU6CiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgbmFtZTogY29udHJvbFBsYW5lCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF3czoKICAgICAgdHlwZTogbTUueGxhcmdlCmNvbXB1dGU6Ci0gaHlwZXJ0aHJlYWRpbmc6IEVuYWJsZWQKICBhcmNoaXRlY3R1cmU6IGFtZDY0CiAgbmFtZTogJ3dvcmtlcicKICByZXBsaWNhczogMwogIHBsYXRmb3JtOgogICAgYXdzOgogICAgICB0eXBlOiBtNS54bGFyZ2UKbmV0d29ya2luZzoKICBjbHVzdGVyTmV0d29yazoKICAtIGNpZHI6IDEwLjEyOC4wLjAvMTQKICAgIGhvc3RQcmVmaXg6IDIzCiAgbWFjaGluZU5ldHdvcms6CiAgLSBjaWRyOiAxMC4wLjAuMC8xNgogIG5ldHdvcmtUeXBlOiBPVk5LdWJlcm5ldGVzCiAgc2VydmljZU5ldHdvcms6CiAgLSAxNzIuMzAuMC4wLzE2CnBsYXRmb3JtOgogIGF3czoKICAgIHJlZ2lvbjogYXAtc291dGhlYXN0LTEKcHVsbFNlY3JldDogIiIgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cwpzc2hLZXk6ICIiICAgICAjIHNraXAsIGhpdmUgd2lsbCBpbmplY3QgYmFzZWQgb24gaXQncyBzZWNyZXRz @@ -46,6 +47,7 @@ apiVersion: v1 kind: Secret metadata: name: aws-cd-two-wo-pool-acm-provision-on-deploy-install-config + namespace: aws-cd-two-wo-pool-acm-provision-on-deploy data: # Base64 encoding of install-config yaml install-config.yaml: CgphcGlWZXJzaW9uOiB2MQptZXRhZGF0YToKICBuYW1lOiAnYXdzLWNkLXR3by13by1wb29sJyAKYmFzZURvbWFpbjogYmx1ZXByaW50cy5yaGVjb2VuZy5jb20KY29udHJvbFBsYW5lOgogIGFyY2hpdGVjdHVyZTogYW1kNjQKICBoeXBlcnRocmVhZGluZzogRW5hYmxlZAogIG5hbWU6IGNvbnRyb2xQbGFuZQogIHJlcGxpY2FzOiAzCiAgcGxhdGZvcm06CiAgICBhd3M6CiAgICAgIHR5cGU6IG01LnhsYXJnZQpjb21wdXRlOgotIGh5cGVydGhyZWFkaW5nOiBFbmFibGVkCiAgYXJjaGl0ZWN0dXJlOiBhbWQ2NAogIG5hbWU6ICd3b3JrZXInCiAgcmVwbGljYXM6IDMKICBwbGF0Zm9ybToKICAgIGF3czoKICAgICAgdHlwZTogbTUueGxhcmdlCm5ldHdvcmtpbmc6CiAgY2x1c3Rlck5ldHdvcms6CiAgLSBjaWRyOiAxMC4xMjguMC4wLzE0CiAgICBob3N0UHJlZml4OiAyMwogIG1hY2hpbmVOZXR3b3JrOgogIC0gY2lkcjogMTAuMC4wLjAvMTYKICBuZXR3b3JrVHlwZTogT1ZOS3ViZXJuZXRlcwogIHNlcnZpY2VOZXR3b3JrOgogIC0gMTcyLjMwLjAuMC8xNgpwbGF0Zm9ybToKICBhd3M6CiAgICByZWdpb246IGFwLXNvdXRoZWFzdC0zCnB1bGxTZWNyZXQ6ICIiICMgc2tpcCwgaGl2ZSB3aWxsIGluamVjdCBiYXNlZCBvbiBpdCdzIHNlY3JldHMKc3NoS2V5OiAiIiAgICAgIyBza2lwLCBoaXZlIHdpbGwgaW5qZWN0IGJhc2VkIG9uIGl0J3Mgc2VjcmV0cw== @@ -294,6 +296,7 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: aws-cd-one-w-pool-acm-provision-edge-creds + namespace: aws-cd-one-w-pool-acm-provision-edge spec: dataFrom: - extract: @@ -315,6 +318,7 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: aws-cd-one-w-pool-acm-provision-edge-infra-creds + namespace: aws-cd-one-w-pool-acm-provision-edge spec: data: - secretKey: openshiftPullSecret @@ -372,6 +376,7 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: aws-cd-two-wo-pool-acm-provision-on-deploy-creds + namespace: aws-cd-two-wo-pool-acm-provision-on-deploy spec: dataFrom: - extract: @@ -393,6 +398,7 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: aws-cd-two-wo-pool-acm-provision-on-deploy-infra-creds + namespace: aws-cd-two-wo-pool-acm-provision-on-deploy spec: data: - secretKey: openshiftPullSecret @@ -624,6 +630,7 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: aws-cd-one-w-pool-acm-provision-edge-pull-secret + namespace: aws-cd-one-w-pool-acm-provision-edge spec: data: - secretKey: openshiftPullSecret @@ -648,6 +655,7 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: aws-cd-one-w-pool-acm-provision-edge-ssh-private-key + namespace: aws-cd-one-w-pool-acm-provision-edge spec: data: - secretKey: sshPrivateKey @@ -672,6 +680,7 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: aws-cd-two-wo-pool-acm-provision-on-deploy-pull-secret + namespace: aws-cd-two-wo-pool-acm-provision-on-deploy spec: data: - secretKey: openshiftPullSecret @@ -696,6 +705,7 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: aws-cd-two-wo-pool-acm-provision-on-deploy-ssh-private-key + namespace: aws-cd-two-wo-pool-acm-provision-on-deploy spec: data: - secretKey: sshPrivateKey From ff40ddc8cc1e85ba5bd6d54654c572d07ae12f1c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 30 May 2024 10:03:47 +0200 Subject: [PATCH 1186/1263] Force rolebindings as early as possible This is important because in some situations (we've observed this on the clusterwide argo instance on spokes) the permissions are not there yet when argo tries to create service accounts for the imperative SAs. This means that the very first sync works up to the service account creation which then fails due to lacking RBACs. This triggers a gitops issue where selfheal never retries because the previous run failed and so the app is in a stuck loop forever Co-Authored-By: Jonny Rickard Closes: GITOPS-4677 --- clustergroup/templates/plumbing/argocd-super-role.yaml | 8 ++++++++ tests/clustergroup-industrial-edge-factory.expected.yaml | 8 ++++++++ tests/clustergroup-industrial-edge-hub.expected.yaml | 8 ++++++++ tests/clustergroup-medical-diagnosis-hub.expected.yaml | 8 ++++++++ tests/clustergroup-naked.expected.yaml | 8 ++++++++ tests/clustergroup-normal.expected.yaml | 8 ++++++++ 6 files changed, 48 insertions(+) diff --git a/clustergroup/templates/plumbing/argocd-super-role.yaml b/clustergroup/templates/plumbing/argocd-super-role.yaml index 2d5f8f76..11366a0b 100644 --- a/clustergroup/templates/plumbing/argocd-super-role.yaml +++ b/clustergroup/templates/plumbing/argocd-super-role.yaml @@ -4,6 +4,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: openshift-gitops-cluster-admin-rolebinding + # We need to have this before anything else or the sync might get stuck forever + # due to permission issues + annotations: + argocd.argoproj.io/sync-wave: "-100" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -22,6 +26,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }}-cluster-admin-rolebinding + # We need to have this before anything else or the sync might get stuck forever + # due to permission issues + annotations: + argocd.argoproj.io/sync-wave: "-100" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 356b7e7e..4e18f8cb 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -321,6 +321,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: openshift-gitops-cluster-admin-rolebinding + # We need to have this before anything else or the sync might get stuck forever + # due to permission issues + annotations: + argocd.argoproj.io/sync-wave: "-100" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -340,6 +344,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: mypattern-factory-cluster-admin-rolebinding + # We need to have this before anything else or the sync might get stuck forever + # due to permission issues + annotations: + argocd.argoproj.io/sync-wave: "-100" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 3291aeb2..1af3211d 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -482,6 +482,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: openshift-gitops-cluster-admin-rolebinding + # We need to have this before anything else or the sync might get stuck forever + # due to permission issues + annotations: + argocd.argoproj.io/sync-wave: "-100" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -501,6 +505,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: mypattern-datacenter-cluster-admin-rolebinding + # We need to have this before anything else or the sync might get stuck forever + # due to permission issues + annotations: + argocd.argoproj.io/sync-wave: "-100" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 6e300fc3..2d9a4d36 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -409,6 +409,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: openshift-gitops-cluster-admin-rolebinding + # We need to have this before anything else or the sync might get stuck forever + # due to permission issues + annotations: + argocd.argoproj.io/sync-wave: "-100" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -428,6 +432,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: mypattern-hub-cluster-admin-rolebinding + # We need to have this before anything else or the sync might get stuck forever + # due to permission issues + annotations: + argocd.argoproj.io/sync-wave: "-100" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 6f1c6b2e..a7fee415 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -174,6 +174,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: openshift-gitops-cluster-admin-rolebinding + # We need to have this before anything else or the sync might get stuck forever + # due to permission issues + annotations: + argocd.argoproj.io/sync-wave: "-100" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -193,6 +197,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: common-example-cluster-admin-rolebinding + # We need to have this before anything else or the sync might get stuck forever + # due to permission issues + annotations: + argocd.argoproj.io/sync-wave: "-100" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 41eb68b8..7bb75394 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -371,6 +371,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: openshift-gitops-cluster-admin-rolebinding + # We need to have this before anything else or the sync might get stuck forever + # due to permission issues + annotations: + argocd.argoproj.io/sync-wave: "-100" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -390,6 +394,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: mypattern-example-cluster-admin-rolebinding + # We need to have this before anything else or the sync might get stuck forever + # due to permission issues + annotations: + argocd.argoproj.io/sync-wave: "-100" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole From 113a9076b64ac070866050c31e692c2da62b3cdd Mon Sep 17 00:00:00 2001 From: Lester Claudio Date: Wed, 29 May 2024 14:06:55 -0600 Subject: [PATCH 1187/1263] bug: Invalid OperatorGroup generated when ommitting targetNamespaces Problem Statement: When setting a namespace like this: - openshift-distributed-tracing: operatorGroup: true targetNamespaces: [] The chart generates the following yaml: ```yaml apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: name: openshift-distributed-tracing-operator-group namespace: openshift-distributed-tracing spec: targetNamespaces: ``` Which k8s rejects the targetNamespaces key as invalid when it attempts to apply it and removes it since it doesn't have a value, which just so happens to have the desired result of not setting the targetNamespaces (or a selector) to enable it for All Namespaces. --- clustergroup/templates/_helpers.tpl | 12 +++++++++--- clustergroup/templates/core/operatorgroup.yaml | 10 +++++++--- clustergroup/templates/plumbing/applications.yaml | 2 +- tests/clustergroup-normal.expected.yaml | 2 -- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/clustergroup/templates/_helpers.tpl b/clustergroup/templates/_helpers.tpl index 5001a06e..c6d14d08 100644 --- a/clustergroup/templates/_helpers.tpl +++ b/clustergroup/templates/_helpers.tpl @@ -202,13 +202,19 @@ kind: OperatorGroup metadata: name: {{ $k }}-operator-group namespace: {{ $k }} + {{- if (hasKey $v "targetNamespaces") }} + {{- if $v.targetNamespaces }} + {{- if (len $v.targetNamespaces) }} spec: targetNamespaces: - {{- if (hasKey $v "targetNamespaces") }} - {{- range $v.targetNamespaces }}{{- /* We loop through the list of tergetnamespaces */}} + {{- range $v.targetNamespaces }}{{- /* We loop through the list of tergetnamespaces */}} - {{ . }} - {{- end }}{{- /* End range targetNamespaces */}} + {{- end }}{{- /* End range targetNamespaces */}} + {{- end }}{{- /* End if (len $v.targetNamespaces) */}} + {{- end }}{{- /* End $v.targetNamespaces */}} {{- else }} +spec: + targetNamespaces: - {{ $k }} {{- end }}{{- /* End of if hasKey $v "targetNamespaces" */}} {{- end }}{{- /* End if $v.operatorGroup */}} diff --git a/clustergroup/templates/core/operatorgroup.yaml b/clustergroup/templates/core/operatorgroup.yaml index 4d8c3014..6adfef47 100644 --- a/clustergroup/templates/core/operatorgroup.yaml +++ b/clustergroup/templates/core/operatorgroup.yaml @@ -21,15 +21,19 @@ kind: OperatorGroup metadata: name: {{ $k }}-operator-group namespace: {{ $k }} + {{- if (hasKey $v "targetNamespaces") }} + {{- if $v.targetNamespaces }} spec: targetNamespaces: - {{- if (hasKey $v "targetNamespaces") }} {{- range $v.targetNamespaces }}{{- /* We loop through the list of tergetnamespaces */}} - {{ . }} {{- end }}{{- /* End range targetNamespaces */}} - {{- else }} + {{- end }}{{- /* End if $v.targetNamespaces */}} + {{- else }} +spec: + targetNamespaces: - {{ $k }} - {{- end }}{{- /* End of if operatorGroup */}} + {{- end }}{{- /* End of if (hasKey $v "targetNamespaces") */}} {{- end }}{{- /* range $k, $v := $ns */}} {{- end }}{{- /* End of if operatorGroup */}} {{- else if kindIs "string" $ns }} diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 870babe3..0b9f4eda 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -78,7 +78,7 @@ spec: - name: global.namespace value: {{ $.Values.global.namespace }} - name: clusterGroup.name - value: {{ .Values.clusterGroup.name }} + value: {{ $.Values.clusterGroup.name }} {{- range .extraHubClusterDomainFields }} - name: {{ . }} value: {{ $.Values.global.hubClusterDomain }} diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 41eb68b8..c594b65e 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -1197,8 +1197,6 @@ kind: OperatorGroup metadata: name: exclude-targetns-operator-group namespace: exclude-targetns -spec: - targetNamespaces: --- # Source: clustergroup/templates/core/operatorgroup.yaml --- From ad87c84b9643b66e3994215dcd6cb830fcefad52 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 30 May 2024 17:26:23 +0200 Subject: [PATCH 1188/1263] Fix CI issue --- tests/acm-normal.expected.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 8abd52ef..8e78a98f 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -1219,15 +1219,17 @@ spec: path: common/clustergroup helm: ignoreMissingValueFiles: true + values: | + extraParametersNested: valueFiles: - - "/values-global.yaml" - - "/values-acm-provision-on-deploy.yaml" - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' - - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-on-deploy.yaml' - # We cannot use $.Values.global.clusterVersion because that gets resolved to the - # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - "/values-global.yaml" + - "/values-acm-provision-on-deploy.yaml" + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' + - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-on-deploy.yaml' + # We cannot use $.Values.global.clusterVersion because that gets resolved to the + # hub's cluster version, whereas we want to include the spoke cluster version + - '/values-{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}.yaml' parameters: - name: global.repoURL value: https://github.com/pattern-clone/mypattern @@ -1241,10 +1243,8 @@ spec: value: apps.hub.example.com - name: global.localClusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - # Requires ACM 2.6 or higher - name: global.clusterDomain value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}' - # Requires ACM 2.6 or higher (I could not come up with something less terrible to get maj.min) - name: global.clusterVersion value: '{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}' - name: global.localClusterName From beb71a1ba529dc6f50454ff947e2f8e547ae1cae Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 3 Jun 2024 19:09:52 +0200 Subject: [PATCH 1189/1263] Actually use adminServiceAccountName for the auto approve job --- .../templates/imperative/auto-approve-installplans.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/templates/imperative/auto-approve-installplans.yaml b/clustergroup/templates/imperative/auto-approve-installplans.yaml index e6ebf26c..23e6b133 100644 --- a/clustergroup/templates/imperative/auto-approve-installplans.yaml +++ b/clustergroup/templates/imperative/auto-approve-installplans.yaml @@ -16,7 +16,7 @@ spec: metadata: name: auto-approve-installplans-job spec: - serviceAccountName: {{ $.Values.clusterGroup.imperative.serviceAccountName }} + serviceAccountName: {{ $.Values.clusterGroup.imperative.adminServiceAccountName }} initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there From 39addf213792aa75d323f83c618fe49fa77c9819 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Jun 2024 10:39:39 +0200 Subject: [PATCH 1190/1263] Make sure that the if condition on chart split is not always true This should fix the fact that jobs are triggered on unrelated changes --- .github/workflows/chart-branches.yml | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/.github/workflows/chart-branches.yml b/.github/workflows/chart-branches.yml index 1a4fb455..4fb784f0 100644 --- a/.github/workflows/chart-branches.yml +++ b/.github/workflows/chart-branches.yml @@ -49,9 +49,7 @@ jobs: acm: needs: changes - if: | - ${{ needs.changes.outputs.acm == 'true' }} && - github.repository == 'validatedpatterns/common' + if: ${{ (needs.changes.outputs.acm == 'true') && (github.repository == 'validatedpatterns/common') }} uses: validatedpatterns/common/.github/workflows/chart-split.yml@main permissions: actions: write @@ -63,9 +61,7 @@ jobs: golang-external-secrets: needs: changes - if: | - ${{ needs.changes.outputs.golang-external-secrets == 'true' }} && - github.repository == 'validatedpatterns/common' + if: ${{ (needs.changes.outputs.golang-external-secrets == 'true') && (github.repository == 'validatedpatterns/common') }} uses: validatedpatterns/common/.github/workflows/chart-split.yml@main permissions: actions: write @@ -77,9 +73,7 @@ jobs: hashicorp-vault: needs: changes - if: | - ${{ needs.changes.outputs.hashicorp-vault == 'true' }} && - github.repository == 'validatedpatterns/common' + if: ${{ (needs.changes.outputs.hashicorp-vault == 'true') && (github.repository == 'validatedpatterns/common') }} uses: validatedpatterns/common/.github/workflows/chart-split.yml@main permissions: actions: write @@ -91,9 +85,7 @@ jobs: letsencrypt: needs: changes - if: | - ${{ needs.changes.outputs.letsencrypt == 'true' }} && - github.repository == 'validatedpatterns/common' + if: ${{ (needs.changes.outputs.letsencrypt == 'true') && (github.repository == 'validatedpatterns/common') }} uses: validatedpatterns/common/.github/workflows/chart-split.yml@main permissions: actions: write @@ -105,9 +97,7 @@ jobs: clustergroup: needs: changes - if: | - ${{ needs.changes.outputs.clustergroup == 'true' }} && - github.repository == 'validatedpatterns/common' + if: ${{ (needs.changes.outputs.clustergroup == 'true') && (github.repository == 'validatedpatterns/common') }} uses: validatedpatterns/common/.github/workflows/chart-split.yml@main permissions: actions: write From 10b06654858958f9af5ddb7adbf7fafd65cd9b99 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Jun 2024 10:49:05 +0200 Subject: [PATCH 1191/1263] Bump super-linter from 5 to 6 --- .github/workflows/superlinter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index 7430db09..ca6daab0 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -21,7 +21,7 @@ jobs: # Run Linter against code base # ################################ - name: Lint Code Base - uses: github/super-linter/slim@v5 + uses: github/super-linter/slim@v6 env: VALIDATE_ALL_CODEBASE: true DEFAULT_BRANCH: main From c85ada60e6d5be2478a5ab2941a9ceddfbe6eb3a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Jun 2024 10:53:41 +0200 Subject: [PATCH 1192/1263] Drop some validations for now --- .github/workflows/superlinter.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index ca6daab0..55acbdb0 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -29,8 +29,11 @@ jobs: # These are the validation we disable atm VALIDATE_ANSIBLE: false VALIDATE_BASH: false + VALIDATE_CHECKOV: false VALIDATE_JSCPD: false VALIDATE_KUBERNETES_KUBECONFORM: false + VALIDATE_PYTHON_PYLINT: false + VALIDATE_SHELL_SHFMT: false VALIDATE_YAML: false # VALIDATE_DOCKERFILE_HADOLINT: false # VALIDATE_MARKDOWN: false From a232db50c15ca3e3d450e8d5cdb7b4a5ed83c48a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Jun 2024 11:05:24 +0200 Subject: [PATCH 1193/1263] Add some debugging to the chart split action --- .github/workflows/chart-split.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/chart-split.yml b/.github/workflows/chart-split.yml index 2792d6ad..ad356e61 100644 --- a/.github/workflows/chart-split.yml +++ b/.github/workflows/chart-split.yml @@ -31,8 +31,10 @@ jobs: set -e N="${{ inputs.chart_name }}" B="${N}-main-single-chart" + echo "Running subtree split for ${B}" git push origin -d "${B}" || /bin/true git subtree split -P "${N}" -b "${B}" + git remote -v git push -f -u origin "${B}" #git clone https://validatedpatterns:${GITHUB_TOKEN}@github.com/validatedpatterns/common.git -b "acm-main-single-chart" --single-branch git push --force https://validatedpatterns:"${GITHUB_TOKEN}"@github.com/${{ inputs.target_repository }}.git "${B}:main" From 821727ad13f3b779d47efd1444bb5fcda7dc4738 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Jun 2024 16:01:46 +0200 Subject: [PATCH 1194/1263] Use a specific git version when running git subtree split Otherwise we will get errors during the command as git subtree has regressed since v2.44.0 --- .github/workflows/chart-split.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/chart-split.yml b/.github/workflows/chart-split.yml index ad356e61..39ee9c01 100644 --- a/.github/workflows/chart-split.yml +++ b/.github/workflows/chart-split.yml @@ -31,10 +31,12 @@ jobs: set -e N="${{ inputs.chart_name }}" B="${N}-main-single-chart" + GITIMG="quay.io/hybridcloudpatterns/gitsubtree-container:2.40.1" + sudo apt-get update -y && apt-get install -y podman echo "Running subtree split for ${B}" + podman pull "${GITIMG}" git push origin -d "${B}" || /bin/true - git subtree split -P "${N}" -b "${B}" - git remote -v - git push -f -u origin "${B}" + # Git subtree got broken on recent versions of git hence this container + podman run --net=host --rm -t -v .:/git "${GITIMG}" subtree split -P "${N}" -b "${B}" #git clone https://validatedpatterns:${GITHUB_TOKEN}@github.com/validatedpatterns/common.git -b "acm-main-single-chart" --single-branch git push --force https://validatedpatterns:"${GITHUB_TOKEN}"@github.com/${{ inputs.target_repository }}.git "${B}:main" From 3061644432685ff5b77276d194739e1af71c4d3b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Jun 2024 16:17:00 +0200 Subject: [PATCH 1195/1263] Release clustergroup v0.8.6 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index e2d4f98b..2ddd15ba 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.8.5 +version: 0.8.6 From ae14cd2bfab2a3fc7fe8cb83941b52c87c97547a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 4 Jun 2024 16:22:57 +0200 Subject: [PATCH 1196/1263] Add a sudo to apt-get command --- .github/workflows/chart-split.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/chart-split.yml b/.github/workflows/chart-split.yml index 39ee9c01..150e419b 100644 --- a/.github/workflows/chart-split.yml +++ b/.github/workflows/chart-split.yml @@ -32,7 +32,7 @@ jobs: N="${{ inputs.chart_name }}" B="${N}-main-single-chart" GITIMG="quay.io/hybridcloudpatterns/gitsubtree-container:2.40.1" - sudo apt-get update -y && apt-get install -y podman + sudo apt-get update -y && sudo apt-get install -y podman echo "Running subtree split for ${B}" podman pull "${GITIMG}" git push origin -d "${B}" || /bin/true From 17325d65776a7508c89f84ebbec9eebf37f04ad7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 6 Jun 2024 08:52:27 +0200 Subject: [PATCH 1197/1263] Add some READMEs in the individual charts --- acm/README.md | 5 +++++ clustergroup/README.md | 5 +++++ golang-external-secrets/README.md | 4 ++++ hashicorp-vault/README.md | 4 ++++ letsencrypt/README.md | 4 ++++ 5 files changed, 22 insertions(+) create mode 100644 acm/README.md create mode 100644 clustergroup/README.md diff --git a/acm/README.md b/acm/README.md new file mode 100644 index 00000000..c8c67635 --- /dev/null +++ b/acm/README.md @@ -0,0 +1,5 @@ +# Validated Patterns ACM chart + +This chart is used to set up ACM in Validated Patterns https://validatedpatterns.io + +Please send PRs to https://github.com/validatedpatterns/common diff --git a/clustergroup/README.md b/clustergroup/README.md new file mode 100644 index 00000000..8983ddf9 --- /dev/null +++ b/clustergroup/README.md @@ -0,0 +1,5 @@ +# Validated Patterns ClusterGroup chart + +This chart is used to set up the basic building blocks in Validated Patterns https://validatedpatterns.io + +Please send PRs to https://github.com/validatedpatterns/common diff --git a/golang-external-secrets/README.md b/golang-external-secrets/README.md index e12d58f1..6d9e0f41 100644 --- a/golang-external-secrets/README.md +++ b/golang-external-secrets/README.md @@ -12,3 +12,7 @@ we just override the tag with the version + "-ubi" 4. Tweak `values.yaml` with the new image versions 5. Run `make test` 6. Commit to git + +## PRs + +Please send PRs to https://github.com/validatedpatterns/common diff --git a/hashicorp-vault/README.md b/hashicorp-vault/README.md index 26252b7e..6d5fefd6 100644 --- a/hashicorp-vault/README.md +++ b/hashicorp-vault/README.md @@ -1,5 +1,9 @@ # VP hashicorp-vault +## PRs + +Please send PRs to https://github.com/validatedpatterns/common + ## Updating the chart 1. Edit Chart.yaml with the new version diff --git a/letsencrypt/README.md b/letsencrypt/README.md index d277abaa..549e44e8 100644 --- a/letsencrypt/README.md +++ b/letsencrypt/README.md @@ -22,6 +22,10 @@ In order to enable this chart in your patterns, please add and edit the followin Once the above is enabled in a pattern, a certain amount of time (~15/20 minutes or so) is needed for all the cluster operators to settle, all the HTTPS routes will have a wildcard certificate signed by letsencrypt. By default also the API endpoint will use a certificate signed by letsencrypt. +## PRs + +Please send PRs to https://github.com/validatedpatterns/common + ## Limitations Please be aware of the following gotchas when using this chart: From 55230973793444aa5d0e43088ef9b637d17f8e33 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 6 Jun 2024 09:01:37 +0200 Subject: [PATCH 1198/1263] Fix super-linter issues and upgrade local super-linter target --- Makefile | 11 +++++++---- acm/README.md | 4 ++-- clustergroup/README.md | 4 ++-- golang-external-secrets/README.md | 2 +- hashicorp-vault/README.md | 2 +- letsencrypt/README.md | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 86cb5177..f9b00e57 100644 --- a/Makefile +++ b/Makefile @@ -230,17 +230,20 @@ kubeconform: ## run helm kubeconform super-linter: ## Runs super linter locally rm -rf .mypy_cache podman run -e RUN_LOCAL=true -e USE_FIND_ALGORITHM=true \ + -e VALIDATE_ANSIBLE=false \ -e VALIDATE_BASH=false \ + -e VALIDATE_CHECKOV=false \ + -e VALIDATE_DOCKERFILE_HADOLINT=false \ -e VALIDATE_JSCPD=false \ -e VALIDATE_KUBERNETES_KUBECONFORM=false \ - -e VALIDATE_YAML=false \ - -e VALIDATE_ANSIBLE=false \ - -e VALIDATE_DOCKERFILE_HADOLINT=false \ + -e VALIDATE_PYTHON_PYLINT=false \ + -e VALIDATE_SHELL_SHFMT=false \ -e VALIDATE_TEKTON=false \ + -e VALIDATE_YAML=false \ $(DISABLE_LINTERS) \ -v $(PWD):/tmp/lint:rw,z \ -w /tmp/lint \ - docker.io/github/super-linter:slim-v5 + ghcr.io/super-linter/super-linter:slim-v6 .PHONY: ansible-lint ansible-lint: ## run ansible lint on ansible/ folder diff --git a/acm/README.md b/acm/README.md index c8c67635..56b39ae3 100644 --- a/acm/README.md +++ b/acm/README.md @@ -1,5 +1,5 @@ # Validated Patterns ACM chart -This chart is used to set up ACM in Validated Patterns https://validatedpatterns.io +This chart is used to set up ACM in [Validated Patterns](https://validatedpatterns.io) -Please send PRs to https://github.com/validatedpatterns/common +Please send PRs [here](https://github.com/validatedpatterns/common) diff --git a/clustergroup/README.md b/clustergroup/README.md index 8983ddf9..bb522d12 100644 --- a/clustergroup/README.md +++ b/clustergroup/README.md @@ -1,5 +1,5 @@ # Validated Patterns ClusterGroup chart -This chart is used to set up the basic building blocks in Validated Patterns https://validatedpatterns.io +This chart is used to set up the basic building blocks in [Validated Patterns](https://validatedpatterns.io) -Please send PRs to https://github.com/validatedpatterns/common +Please send PRs [here](https://github.com/validatedpatterns/common) diff --git a/golang-external-secrets/README.md b/golang-external-secrets/README.md index 6d9e0f41..4316d3a1 100644 --- a/golang-external-secrets/README.md +++ b/golang-external-secrets/README.md @@ -15,4 +15,4 @@ we just override the tag with the version + "-ubi" ## PRs -Please send PRs to https://github.com/validatedpatterns/common +Please send PRs [here](https://github.com/validatedpatterns/common) diff --git a/hashicorp-vault/README.md b/hashicorp-vault/README.md index 6d5fefd6..28362080 100644 --- a/hashicorp-vault/README.md +++ b/hashicorp-vault/README.md @@ -2,7 +2,7 @@ ## PRs -Please send PRs to https://github.com/validatedpatterns/common +Please send PRs [here](https://github.com/validatedpatterns/common) ## Updating the chart diff --git a/letsencrypt/README.md b/letsencrypt/README.md index 549e44e8..ded97205 100644 --- a/letsencrypt/README.md +++ b/letsencrypt/README.md @@ -24,7 +24,7 @@ Once the above is enabled in a pattern, a certain amount of time (~15/20 minutes ## PRs -Please send PRs to https://github.com/validatedpatterns/common +Please send PRs [here](https://github.com/validatedpatterns/common) ## Limitations From e3babbbaeea77385cc9f6423b4433fce15a15781 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 6 Jun 2024 10:41:00 +0200 Subject: [PATCH 1199/1263] Skip unreachable spokes when setting up vault MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When setting up vault we loop through all the managed clusters and set up the token so ESO can fetch certain paths in vault. This happens in the unseal vault ansible job and will fail if one of the managed clusters is unreachable. This is undesirable because a cluster might have been shut down on purpose or might be temporarily not reachable and this is no reason to stop the configuration of vault. Tested as follows: 1. Deployed mcg on sno1 and sno2. All green. 2. Shut off sno2 so it is unreachable. observed unseal-cronjob fail (took a while but eventually failed with: ``` TASK [vault_utils : Fetch remote ansible to remote cluster] ******************** ok: [localhost] => (item=local-cluster) An exception occurred during task execution. To see the full traceback, use -vvv. The error was: urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='api.sno2.ocplab.ocp', port=6443): Max retries exceeded with url: /version (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 110] Connection timed out')) failed: [localhost] (item=sno2) => {"ansible_loop_var": "item", "changed": false, "item": {"key": "sno2", "value": {"bearerToken": "eyJhbGciOiJSUzI1... ``` 3. Imported sno3 into the hub on sno1. observed unseal-cronjob still fail: ``` TASK [vault_utils : Fetch remote ansible to remote cluster] ******************** ok: [localhost] => (item=local-cluster) An exception occurred during task execution. To see the full traceback, use -vvv. The error was: urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='api.sno2.ocplab.ocp', port=6443): Max retries exceeded with url: /version (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 110] Connection timed out')) failed: [localhost] (item=sno2) => {"ansible_loop_var": "item", "changed": false, "item": {"key": "sno2", "value": {"bearerToken": "ey... ok: [localhost] => (item=sno3) PLAY RECAP ********************************************************************* localhost : ok=37 changed=11 unreachable=0 failed=1 skipped=13 rescued=0 ignored=0 ``` 4. After the ignore_errors patch: ``` TASK [vault_utils : Fetch remote ansible to remote cluster] ******************** ok: [localhost] => (item=local-cluster) An exception occurred during task execution. To see the full traceback, use -vvv. The error was: urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='api.sno2.ocplab.ocp', port=6443): Max retries exceeded with url: /version (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 110] Connection timed out')) failed: [localhost] (item=sno2) => {"ansible_loop_var": "item", "changed": false, "item": {"key": "sno2", "value": {"bearerToken": "eyJhb.... ok: [localhost] => (item=sno3) ...ignoring # sno2 correctly gets skipped in the subsequent tasks ``` sno3 did manage to login to the vault and everything just worked Reported-by: François Charette --- ansible/roles/vault_utils/tasks/vault_spokes_init.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml index e930252a..92000b90 100644 --- a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml @@ -72,6 +72,11 @@ api_version: v1 validate_certs: "{{ validate_certs_api_endpoint }}" register: remote_external_secrets_sa + # We are allowed to ignore errors here because a spoke might be down or unreachable + # if a spoke is not reachable then its ['token'] field will not be set which + # will leave the ['esoToken'] field empty in the dict which will make it so that + # the spoke gets skipped + ignore_errors: true when: - clusters_info[item.key]['bearerToken'] is defined - clusters_info[item.key]['server_api'] is defined From 3ec1839635f24172ab6d1ebda88cf6983b3724a5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jun 2024 09:28:55 +0200 Subject: [PATCH 1200/1263] Add no_log to spokes initialization task --- ansible/roles/vault_utils/tasks/vault_spokes_init.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml index 92000b90..060378bc 100644 --- a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml @@ -77,6 +77,11 @@ # will leave the ['esoToken'] field empty in the dict which will make it so that # the spoke gets skipped ignore_errors: true + # We add no_log: true here because in case of a remote failure secret bits might + # end up in the log. Unfortunately ansible is currently not easily able to control + # output in a loop (see + # https://serverfault.com/questions/1059530/how-to-not-print-items-in-an-ansible-loop-error-without-no-log) + no_log: true when: - clusters_info[item.key]['bearerToken'] is defined - clusters_info[item.key]['server_api'] is defined From 9809c86af214c3d383afc2ce2bbe03f396a688e4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 6 Jun 2024 17:48:15 +0200 Subject: [PATCH 1201/1263] Drop initContainers variable and make it the default This will allow us to work with external CAs out of the box and also it will allow the framework to be able use internal gitea instances out of the box. Tested as follows: 1. Deployed stock MCG with no changes 2. Changed the MCG repo to one with this patch 3. Observed initContainers to show up on: A. namespaced argo on hub B. clusterwide argo on spoke C. namespaced argo on spoke 4. All applications still worked 5. Deployed mcg from scratch using a branch with this patch (with multisource set to false so we're sure we're testing the right common bits in the branch) --- acm/templates/policies/application-policies.yaml | 1 - acm/templates/policies/ocp-gitops-policy.yaml | 3 --- .../imperative/auto-approve-installplans.yaml | 11 ++++------- clustergroup/templates/imperative/job.yaml | 14 -------------- clustergroup/templates/imperative/unsealjob.yaml | 15 --------------- clustergroup/templates/plumbing/argocd.yaml | 7 ------- 6 files changed, 4 insertions(+), 47 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 2a815913..01073bd4 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -1,5 +1,4 @@ # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io -{{- $hasInitContainerCapability := and (.Values.global.experimentalCapabilities) (has "initcontainers" (splitList "," .Values.global.experimentalCapabilities)) }} {{- range .Values.clusterGroup.managedClusterGroups }} {{- $group := . }} {{- if not .hostedArgoSites }} diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index e72434ae..29204254 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -1,4 +1,3 @@ -{{- $hasInitContainerCapability := and (.Values.global.experimentalCapabilities) (has "initcontainers" (splitList "," .Values.global.experimentalCapabilities)) }} apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: @@ -54,7 +53,6 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" -{{- if $hasInitContainerCapability }} - complianceType: mustonlyhave objectDefinition: apiVersion: argoproj.io/v1beta1 @@ -215,7 +213,6 @@ spec: provider: dex tls: ca: {} -{{- end }}{{/* if hasInitContainerCapability */}} --- apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding diff --git a/clustergroup/templates/imperative/auto-approve-installplans.yaml b/clustergroup/templates/imperative/auto-approve-installplans.yaml index 23e6b133..7b935e77 100644 --- a/clustergroup/templates/imperative/auto-approve-installplans.yaml +++ b/clustergroup/templates/imperative/auto-approve-installplans.yaml @@ -20,7 +20,8 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there - {{- include "imperative.initcontainers.gitinit" . | indent 12 }} + {{- include "imperative.initcontainers.fetch-ca" . | indent 12 }} + {{- include "imperative.initcontainers.gitinit-ca" . | indent 12 }} - name: auto-approve-installplans image: {{ $.Values.clusterGroup.imperative.image }} imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} @@ -39,14 +40,10 @@ spec: - "@/values/values.yaml" - common/ansible/playbooks/auto-approve-installplans/auto-approve-installplans.yaml volumeMounts: - {{- include "imperative.volumemounts" . | indent 16 }} + {{- include "imperative.volumemounts_ca" . | indent 16 }} containers: {{- include "imperative.containers.done" . | indent 12 }} volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} + {{- include "imperative.volumes_ca" . | indent 12 }} restartPolicy: Never {{- end }} diff --git a/clustergroup/templates/imperative/job.yaml b/clustergroup/templates/imperative/job.yaml index 0b82d47c..55400e8c 100644 --- a/clustergroup/templates/imperative/job.yaml +++ b/clustergroup/templates/imperative/job.yaml @@ -1,5 +1,3 @@ -{{- $hasInitContainerCapability := and (.Values.global.experimentalCapabilities) (has "initcontainers" (splitList "," .Values.global.experimentalCapabilities)) }} - {{- if not (eq .Values.enabled "plumbing") }} {{/* Define this if needed (jobs defined */}} {{- if (and $.Values.clusterGroup.imperative (gt (len $.Values.clusterGroup.imperative.jobs) 0)) -}} @@ -24,12 +22,8 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there -{{- if $hasInitContainerCapability }} {{- include "imperative.initcontainers.fetch-ca" . | indent 12 }} {{- include "imperative.initcontainers.gitinit-ca" . | indent 12 }} -{{- else }} - {{- include "imperative.initcontainers.gitinit" . | indent 12 }} -{{- end }} {{- range $.Values.clusterGroup.imperative.jobs }} {{- if ne (.disabled | default "false" | toString | lower ) "true" }} - name: {{ .name }} @@ -60,21 +54,13 @@ spec: {{- end }} - {{ .playbook }} volumeMounts: -{{- if $hasInitContainerCapability }} {{- include "imperative.volumemounts_ca" . | indent 16 }} -{{- else }} - {{- include "imperative.volumemounts" . | indent 16 }} -{{- end }} {{- end }} {{- end }} containers: {{- include "imperative.containers.done" . | indent 12 }} volumes: -{{- if $hasInitContainerCapability }} {{- include "imperative.volumes_ca" . | indent 12 }} -{{- else }} - {{- include "imperative.volumes" . | indent 12 }} -{{- end }} restartPolicy: Never {{- end }} {{- end }} diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index e0ff2c78..a7553da8 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -1,5 +1,3 @@ -{{- $hasInitContainerCapability := and (.Values.global.experimentalCapabilities) (has "initcontainers" (splitList "," .Values.global.experimentalCapabilities)) }} - {{/* If the backend is not set at all we default to "vault". See https://www.github.com/helm/helm/issues/3308 why we avoid using the default function */}} {{- if or (eq .Values.global.secretStore.backend "vault") (not (hasKey .Values.global.secretStore "backend")) }} @@ -26,13 +24,8 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there -{{- if $hasInitContainerCapability }} - {{- include "imperative.initcontainers.fetch-ca" . | indent 12 }} {{- include "imperative.initcontainers.gitinit-ca" . | indent 12 }} -{{- else }} - {{- include "imperative.initcontainers.gitinit" . | indent 12 }} -{{- end }} - name: unseal-playbook image: {{ $.Values.clusterGroup.imperative.image }} imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} @@ -55,19 +48,11 @@ spec: - 'vault_init,vault_unseal,vault_secrets_init,vault_spokes_init' - "common/ansible/playbooks/vault/vault.yaml" volumeMounts: -{{- if $hasInitContainerCapability }} {{- include "imperative.volumemounts_ca" . | indent 16 }} -{{- else }} - {{- include "imperative.volumemounts" . | indent 16 }} -{{- end }} containers: {{- include "imperative.containers.done" . | indent 12 }} volumes: -{{- if $hasInitContainerCapability }} {{- include "imperative.volumes_ca" . | indent 12 }} -{{- else }} - {{- include "imperative.volumes" . | indent 12 }} -{{- end }} restartPolicy: Never {{- end }} {{- end }} diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index dcce1b4b..7d42ec36 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -1,4 +1,3 @@ -{{- $hasInitContainerCapability := and (.Values.global.experimentalCapabilities) (has "initcontainers" (splitList "," .Values.global.experimentalCapabilities)) }} {{- if (eq .Values.enabled "all") }} {{- $namespace := print $.Values.global.pattern "-" $.Values.clusterGroup.name }} @@ -70,7 +69,6 @@ spec: rbac: defaultPolicy: role:admin repo: -{{- if $hasInitContainerCapability }} initContainers: - command: - bash @@ -109,11 +107,6 @@ spec: {{- if len $.Values.clusterGroup.argoCD.initContainers }} {{ $.Values.clusterGroup.argoCD.initContainers | toPrettyJson }} {{- end }} -{{- else }} -{{- if len $.Values.clusterGroup.argoCD.initContainers }} - initContainers: {{ $.Values.clusterGroup.argoCD.initContainers | toPrettyJson }} -{{- end }} -{{- end }}{{/* if $hasInitContainerCapability */}} {{- if len $.Values.clusterGroup.argoCD.configManagementPlugins }} sidecarContainers: {{- range $cmp := $.Values.clusterGroup.argoCD.configManagementPlugins }} From 75f8385c1ea3c7743b52d4c13378504f8ce668a8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jun 2024 10:19:23 +0200 Subject: [PATCH 1202/1263] Update tests after dropping initContainers --- .../acm-industrial-edge-factory.expected.yaml | 155 ++++++++++++++++++ tests/acm-industrial-edge-hub.expected.yaml | 155 ++++++++++++++++++ tests/acm-medical-diagnosis-hub.expected.yaml | 155 ++++++++++++++++++ tests/acm-naked.expected.yaml | 155 ++++++++++++++++++ tests/acm-normal.expected.yaml | 155 ++++++++++++++++++ ...roup-industrial-edge-factory.expected.yaml | 71 ++++++++ ...tergroup-industrial-edge-hub.expected.yaml | 107 ++++++++++++ ...rgroup-medical-diagnosis-hub.expected.yaml | 107 ++++++++++++ tests/clustergroup-naked.expected.yaml | 71 ++++++++ tests/clustergroup-normal.expected.yaml | 107 ++++++++++++ 10 files changed, 1238 insertions(+) diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index 561fbd7b..fa867edd 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -119,3 +119,158 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" + - complianceType: mustonlyhave + objectDefinition: + apiVersion: argoproj.io/v1beta1 + kind: ArgoCD + metadata: + name: openshift-gitops + namespace: openshift-gitops + spec: + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + webhookServer: + ingress: + enabled: false + route: + enabled: false + controller: + processors: {} + resources: + limits: + cpu: "2" + memory: 2Gi + requests: + cpu: 250m + memory: 1Gi + sharding: {} + grafana: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + route: + enabled: false + ha: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + monitoring: + enabled: false + notifications: + enabled: false + prometheus: + enabled: false + ingress: + enabled: false + route: + enabled: false + rbac: + defaultPolicy: "" + policy: |- + g, system:cluster-admins, role:admin + g, cluster-admins, role:admin + scopes: '[groups]' + redis: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + repo: + initContainers: + - command: + - bash + - -c + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt + || true + image: registry.redhat.io/ubi9/ubi-minimal:latest + name: fetch-ca + resources: {} + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - mountPath: /etc/pki/tls/certs + name: ca-bundles + volumes: + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - emptyDir: {} + name: ca-bundles + resourceExclusions: |- + - apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + service: + type: "" + sso: + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + provider: dex + tls: + ca: {} diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 4199ba03..d715989d 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -327,3 +327,158 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" + - complianceType: mustonlyhave + objectDefinition: + apiVersion: argoproj.io/v1beta1 + kind: ArgoCD + metadata: + name: openshift-gitops + namespace: openshift-gitops + spec: + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + webhookServer: + ingress: + enabled: false + route: + enabled: false + controller: + processors: {} + resources: + limits: + cpu: "2" + memory: 2Gi + requests: + cpu: 250m + memory: 1Gi + sharding: {} + grafana: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + route: + enabled: false + ha: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + monitoring: + enabled: false + notifications: + enabled: false + prometheus: + enabled: false + ingress: + enabled: false + route: + enabled: false + rbac: + defaultPolicy: "" + policy: |- + g, system:cluster-admins, role:admin + g, cluster-admins, role:admin + scopes: '[groups]' + redis: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + repo: + initContainers: + - command: + - bash + - -c + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt + || true + image: registry.redhat.io/ubi9/ubi-minimal:latest + name: fetch-ca + resources: {} + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - mountPath: /etc/pki/tls/certs + name: ca-bundles + volumes: + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - emptyDir: {} + name: ca-bundles + resourceExclusions: |- + - apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + service: + type: "" + sso: + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + provider: dex + tls: + ca: {} diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index f2a8fdd6..2fd25e75 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -318,3 +318,158 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" + - complianceType: mustonlyhave + objectDefinition: + apiVersion: argoproj.io/v1beta1 + kind: ArgoCD + metadata: + name: openshift-gitops + namespace: openshift-gitops + spec: + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + webhookServer: + ingress: + enabled: false + route: + enabled: false + controller: + processors: {} + resources: + limits: + cpu: "2" + memory: 2Gi + requests: + cpu: 250m + memory: 1Gi + sharding: {} + grafana: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + route: + enabled: false + ha: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + monitoring: + enabled: false + notifications: + enabled: false + prometheus: + enabled: false + ingress: + enabled: false + route: + enabled: false + rbac: + defaultPolicy: "" + policy: |- + g, system:cluster-admins, role:admin + g, cluster-admins, role:admin + scopes: '[groups]' + redis: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + repo: + initContainers: + - command: + - bash + - -c + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt + || true + image: registry.redhat.io/ubi9/ubi-minimal:latest + name: fetch-ca + resources: {} + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - mountPath: /etc/pki/tls/certs + name: ca-bundles + volumes: + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - emptyDir: {} + name: ca-bundles + resourceExclusions: |- + - apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + service: + type: "" + sso: + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + provider: dex + tls: + ca: {} diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index 561fbd7b..fa867edd 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -119,3 +119,158 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" + - complianceType: mustonlyhave + objectDefinition: + apiVersion: argoproj.io/v1beta1 + kind: ArgoCD + metadata: + name: openshift-gitops + namespace: openshift-gitops + spec: + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + webhookServer: + ingress: + enabled: false + route: + enabled: false + controller: + processors: {} + resources: + limits: + cpu: "2" + memory: 2Gi + requests: + cpu: 250m + memory: 1Gi + sharding: {} + grafana: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + route: + enabled: false + ha: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + monitoring: + enabled: false + notifications: + enabled: false + prometheus: + enabled: false + ingress: + enabled: false + route: + enabled: false + rbac: + defaultPolicy: "" + policy: |- + g, system:cluster-admins, role:admin + g, cluster-admins, role:admin + scopes: '[groups]' + redis: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + repo: + initContainers: + - command: + - bash + - -c + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt + || true + image: registry.redhat.io/ubi9/ubi-minimal:latest + name: fetch-ca + resources: {} + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - mountPath: /etc/pki/tls/certs + name: ca-bundles + volumes: + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - emptyDir: {} + name: ca-bundles + resourceExclusions: |- + - apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + service: + type: "" + sso: + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + provider: dex + tls: + ca: {} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 8e78a98f..356d0658 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -1330,3 +1330,158 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" + - complianceType: mustonlyhave + objectDefinition: + apiVersion: argoproj.io/v1beta1 + kind: ArgoCD + metadata: + name: openshift-gitops + namespace: openshift-gitops + spec: + applicationSet: + resources: + limits: + cpu: "2" + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + webhookServer: + ingress: + enabled: false + route: + enabled: false + controller: + processors: {} + resources: + limits: + cpu: "2" + memory: 2Gi + requests: + cpu: 250m + memory: 1Gi + sharding: {} + grafana: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + route: + enabled: false + ha: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + initialSSHKnownHosts: {} + monitoring: + enabled: false + notifications: + enabled: false + prometheus: + enabled: false + ingress: + enabled: false + route: + enabled: false + rbac: + defaultPolicy: "" + policy: |- + g, system:cluster-admins, role:admin + g, cluster-admins, role:admin + scopes: '[groups]' + redis: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + repo: + initContainers: + - command: + - bash + - -c + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt + || true + image: registry.redhat.io/ubi9/ubi-minimal:latest + name: fetch-ca + resources: {} + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - mountPath: /etc/pki/tls/certs + name: ca-bundles + volumes: + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - emptyDir: {} + name: ca-bundles + resourceExclusions: |- + - apiGroups: + - tekton.dev + clusters: + - '*' + kinds: + - TaskRun + - PipelineRun + server: + autoscale: + enabled: false + grpc: + ingress: + enabled: false + ingress: + enabled: false + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + service: + type: "" + sso: + dex: + openShiftOAuth: true + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + provider: dex + tls: + ca: {} diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 4e18f8cb..2242f75d 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -417,6 +417,25 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there + - name: fetch-ca + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - >- + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + ls -l /tmp/ca-bundles/ + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles - name: git-init image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -426,6 +445,8 @@ spec: volumeMounts: - name: git mountPath: "/git" + - name: ca-bundles + mountPath: /etc/pki/tls/certs command: - 'sh' - '-c' @@ -477,6 +498,12 @@ spec: - name: values-volume mountPath: /values/values.yaml subPath: values.yaml + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles containers: - name: "done" image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -493,6 +520,15 @@ spec: - name: values-volume configMap: name: helm-values-configmap-factory + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - name: ca-bundles + emptyDir: {} restartPolicy: Never --- # Source: clustergroup/templates/core/subscriptions.yaml @@ -673,6 +709,41 @@ spec: rbac: defaultPolicy: role:admin repo: + initContainers: + - command: + - bash + - -c + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + name: fetch-ca + resources: {} + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - mountPath: /etc/pki/tls/certs + name: ca-bundles + volumes: + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - emptyDir: {} + name: ca-bundles sidecarContainers: - name: helm-with-kustomize command: [/var/run/argocd/argocd-cmp-server] diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 1af3211d..5e64dc60 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -578,6 +578,25 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there + - name: fetch-ca + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - >- + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + ls -l /tmp/ca-bundles/ + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles - name: git-init image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -587,6 +606,8 @@ spec: volumeMounts: - name: git mountPath: "/git" + - name: ca-bundles + mountPath: /etc/pki/tls/certs command: - 'sh' - '-c' @@ -638,6 +659,12 @@ spec: - name: values-volume mountPath: /values/values.yaml subPath: values.yaml + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles containers: - name: "done" image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -654,6 +681,15 @@ spec: - name: values-volume configMap: name: helm-values-configmap-datacenter + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - name: ca-bundles + emptyDir: {} restartPolicy: Never --- # Source: clustergroup/templates/imperative/unsealjob.yaml @@ -677,6 +713,25 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there + - name: fetch-ca + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - >- + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + ls -l /tmp/ca-bundles/ + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles - name: git-init image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -686,6 +741,8 @@ spec: volumeMounts: - name: git mountPath: "/git" + - name: ca-bundles + mountPath: /etc/pki/tls/certs command: - 'sh' - '-c' @@ -739,6 +796,12 @@ spec: - name: values-volume mountPath: /values/values.yaml subPath: values.yaml + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles containers: - name: "done" image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -755,6 +818,15 @@ spec: - name: values-volume configMap: name: helm-values-configmap-datacenter + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - name: ca-bundles + emptyDir: {} restartPolicy: Never --- # Source: clustergroup/templates/core/subscriptions.yaml @@ -1403,6 +1475,41 @@ spec: rbac: defaultPolicy: role:admin repo: + initContainers: + - command: + - bash + - -c + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + name: fetch-ca + resources: {} + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - mountPath: /etc/pki/tls/certs + name: ca-bundles + volumes: + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - emptyDir: {} + name: ca-bundles sidecarContainers: - name: helm-with-kustomize command: [/var/run/argocd/argocd-cmp-server] diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 2d9a4d36..1ea53dc6 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -505,6 +505,25 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there + - name: fetch-ca + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - >- + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + ls -l /tmp/ca-bundles/ + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles - name: git-init image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -514,6 +533,8 @@ spec: volumeMounts: - name: git mountPath: "/git" + - name: ca-bundles + mountPath: /etc/pki/tls/certs command: - 'sh' - '-c' @@ -565,6 +586,12 @@ spec: - name: values-volume mountPath: /values/values.yaml subPath: values.yaml + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles containers: - name: "done" image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -581,6 +608,15 @@ spec: - name: values-volume configMap: name: helm-values-configmap-hub + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - name: ca-bundles + emptyDir: {} restartPolicy: Never --- # Source: clustergroup/templates/imperative/unsealjob.yaml @@ -604,6 +640,25 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there + - name: fetch-ca + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - >- + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + ls -l /tmp/ca-bundles/ + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles - name: git-init image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -613,6 +668,8 @@ spec: volumeMounts: - name: git mountPath: "/git" + - name: ca-bundles + mountPath: /etc/pki/tls/certs command: - 'sh' - '-c' @@ -666,6 +723,12 @@ spec: - name: values-volume mountPath: /values/values.yaml subPath: values.yaml + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles containers: - name: "done" image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -682,6 +745,15 @@ spec: - name: values-volume configMap: name: helm-values-configmap-hub + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - name: ca-bundles + emptyDir: {} restartPolicy: Never --- # Source: clustergroup/templates/core/subscriptions.yaml @@ -1588,6 +1660,41 @@ spec: rbac: defaultPolicy: role:admin repo: + initContainers: + - command: + - bash + - -c + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + name: fetch-ca + resources: {} + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - mountPath: /etc/pki/tls/certs + name: ca-bundles + volumes: + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - emptyDir: {} + name: ca-bundles resources: limits: cpu: "1" diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index a7fee415..bd89773a 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -270,6 +270,25 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there + - name: fetch-ca + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - >- + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + ls -l /tmp/ca-bundles/ + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles - name: git-init image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -279,6 +298,8 @@ spec: volumeMounts: - name: git mountPath: "/git" + - name: ca-bundles + mountPath: /etc/pki/tls/certs command: - 'sh' - '-c' @@ -332,6 +353,12 @@ spec: - name: values-volume mountPath: /values/values.yaml subPath: values.yaml + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles containers: - name: "done" image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -348,6 +375,15 @@ spec: - name: values-volume configMap: name: helm-values-configmap-example + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - name: ca-bundles + emptyDir: {} restartPolicy: Never --- # Source: clustergroup/templates/plumbing/argocd.yaml @@ -419,6 +455,41 @@ spec: rbac: defaultPolicy: role:admin repo: + initContainers: + - command: + - bash + - -c + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + name: fetch-ca + resources: {} + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - mountPath: /etc/pki/tls/certs + name: ca-bundles + volumes: + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - emptyDir: {} + name: ca-bundles resources: limits: cpu: "1" diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index bc04b88e..13daced6 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -486,6 +486,25 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there + - name: fetch-ca + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - >- + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + ls -l /tmp/ca-bundles/ + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles - name: git-init image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -495,6 +514,8 @@ spec: volumeMounts: - name: git mountPath: "/git" + - name: ca-bundles + mountPath: /etc/pki/tls/certs command: - 'sh' - '-c' @@ -546,6 +567,12 @@ spec: - name: values-volume mountPath: /values/values.yaml subPath: values.yaml + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles containers: - name: "done" image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -562,6 +589,15 @@ spec: - name: values-volume configMap: name: helm-values-configmap-example + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - name: ca-bundles + emptyDir: {} restartPolicy: Never --- # Source: clustergroup/templates/imperative/unsealjob.yaml @@ -585,6 +621,25 @@ spec: initContainers: # git init happens in /git/repo so that we can set the folder to 0770 permissions # reason for that is ansible refuses to create temporary folders in there + - name: fetch-ca + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - >- + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + ls -l /tmp/ca-bundles/ + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles - name: git-init image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest imagePullPolicy: Always @@ -594,6 +649,8 @@ spec: volumeMounts: - name: git mountPath: "/git" + - name: ca-bundles + mountPath: /etc/pki/tls/certs command: - 'sh' - '-c' @@ -647,6 +704,12 @@ spec: - name: values-volume mountPath: /values/values.yaml subPath: values.yaml + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles containers: - name: "done" image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -663,6 +726,15 @@ spec: - name: values-volume configMap: name: helm-values-configmap-example + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - name: ca-bundles + emptyDir: {} restartPolicy: Never --- # Source: clustergroup/templates/core/operatorgroup.yaml @@ -1152,6 +1224,41 @@ spec: rbac: defaultPolicy: role:admin repo: + initContainers: + - command: + - bash + - -c + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true + image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest + name: fetch-ca + resources: {} + volumeMounts: + - mountPath: /var/run/kube-root-ca + name: kube-root-ca + - mountPath: /var/run/trusted-ca + name: trusted-ca-bundle + - mountPath: /tmp/ca-bundles + name: ca-bundles + resources: + limits: + cpu: "1" + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - mountPath: /etc/pki/tls/certs + name: ca-bundles + volumes: + - configMap: + name: kube-root-ca.crt + name: kube-root-ca + - configMap: + name: trusted-ca-bundle + optional: true + name: trusted-ca-bundle + - emptyDir: {} + name: ca-bundles resources: limits: cpu: "1" From b489135f33510dc1c489e246c8c9248151a57d52 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jun 2024 11:14:41 +0200 Subject: [PATCH 1203/1263] Release clustergroup v0.8.7 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 2ddd15ba..222a2482 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.8.6 +version: 0.8.7 From d846593fe8bd2a4be8537c93d5784afc818a27b5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 7 Jun 2024 14:48:36 +0200 Subject: [PATCH 1204/1263] Upgrade ESO to v0.9.19 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.9.18.tgz | Bin 76336 -> 0 bytes .../charts/external-secrets-0.9.19.tgz | Bin 0 -> 76457 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 100 ++++++++++-------- ...-secrets-industrial-edge-hub.expected.yaml | 100 ++++++++++-------- ...ecrets-medical-diagnosis-hub.expected.yaml | 100 ++++++++++-------- ...olang-external-secrets-naked.expected.yaml | 100 ++++++++++-------- ...lang-external-secrets-normal.expected.yaml | 100 ++++++++++-------- 9 files changed, 279 insertions(+), 229 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.18.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.9.19.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 4aef19aa..afffe393 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.3 dependencies: - name: external-secrets - version: "0.9.18" + version: "0.9.19" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.9.18.tgz b/golang-external-secrets/charts/external-secrets-0.9.18.tgz deleted file mode 100644 index 7aa6d4505f93b7754cdc2f6c712ff8724153f85c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76336 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
      q0j)i=a#pu1jU+b_58*02a$>ktMv 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 diff --git a/golang-external-secrets/charts/external-secrets-0.9.19.tgz b/golang-external-secrets/charts/external-secrets-0.9.19.tgz new file mode 100644 index 0000000000000000000000000000000000000000..66fffaa0b08d686867fc9e4307204eaae1e22964 GIT binary patch literal 76457 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 Date: Fri, 7 Jun 2024 19:49:02 +0200 Subject: [PATCH 1205/1263] Update helm version in CI to 3.14.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's the version gitops 1.12 is using these days: ❯ oc exec -it -n openshift-gitops openshift-gitops-repo-server-66bf746964-926qk -c argocd-repo-server -- bash -c "helm version" version.BuildInfo{Version:"v3.14.0", GitCommit:"2a2fb3b98829f1e0be6fb18af2f6599e0f4e8243", GitTreeState:"", GoVersion:"go1.21.9 (Red Hat 1.21.9-1.module+el8.10.0+21671+b35c3b78)"} --- .github/workflows/linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 39843f26..f82194ee 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -36,7 +36,7 @@ jobs: - name: Setup helm uses: azure/setup-helm@v4 with: - version: 'v3.13.2' + version: 'v3.14.0' ################################ From 429f55cea86954bbe92c4f36094751847d8930ee Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 10 Jun 2024 10:37:05 +0200 Subject: [PATCH 1206/1263] Drop imperative.initcontainers.gitinit Now that we switched to imperative.initcontainers.gitinit-ca, the older define is not used anywhere, so we can drop this. --- .../templates/imperative/_helpers.tpl | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl index d55220e4..18735dec 100644 --- a/clustergroup/templates/imperative/_helpers.tpl +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -27,48 +27,6 @@ name: ca-bundles {{- end }} -{{/* git-init InitContainer */}} -{{- define "imperative.initcontainers.gitinit" }} -- name: git-init - image: {{ $.Values.clusterGroup.imperative.image }} - imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} - env: - - name: HOME - value: /git/home - volumeMounts: - - name: git - mountPath: "/git" - command: - - 'sh' - - '-c' - - >- - if ! oc get secrets -n openshift-gitops vp-private-repo-credentials &> /dev/null; then - URL="{{ $.Values.global.repoURL }}"; - else - if ! oc get secrets -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.sshPrivateKey | base64decode}}` }}' &>/dev/null; then - U="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.username | base64decode }}` }}')"; - P="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.password | base64decode }}` }}')"; - URL=$(echo {{ $.Values.global.repoURL }} | sed -E "s/(https?:\/\/)/\1${U}:${P}@/"); - else - S="$(oc get secret -n openshift-gitops vp-private-repo-credentials -o go-template='{{ `{{index .data.sshPrivateKey | base64decode }}` }}')"; - mkdir -p --mode 0700 "${HOME}/.ssh"; - echo "${S}" > "${HOME}/.ssh/id_rsa"; - chmod 0600 "${HOME}/.ssh/id_rsa"; - URL=$(echo {{ $.Values.global.repoURL }} | sed -E "s/(https?:\/\/)/\1git@/"); - git config --global core.sshCommand "ssh -i "${HOME}/.ssh/id_rsa" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"; - fi; - fi; - OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpProxy}' 2>/dev/null)"; - if [ -n "${OUT}" ]; then export HTTP_PROXY="${OUT}"; fi; - OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.httpsProxy}' 2>/dev/null)"; - if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; - OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; - if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; - mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- "${URL}" /git/repo; - chmod 0770 /git/{repo,home}; -{{- end }} - {{/* git-init-ca InitContainer */}} {{- define "imperative.initcontainers.gitinit-ca" }} - name: git-init From e967b159b31dc839d894088c1b67893c1408ed57 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 10 Jun 2024 10:41:26 +0200 Subject: [PATCH 1207/1263] Have unseal work whenever we specifiy HEAD Otherwise if HEAD is specified we get the following: mkdir: cannot create directory '/git/home': File exists Cloning into '/git/repo'... warning: Could not find remote branch HEAD to clone. fatal: Remote branch HEAD not found in upstream origin --- clustergroup/templates/imperative/_helpers.tpl | 3 ++- tests/clustergroup-industrial-edge-factory.expected.yaml | 3 ++- tests/clustergroup-industrial-edge-hub.expected.yaml | 6 ++++-- tests/clustergroup-medical-diagnosis-hub.expected.yaml | 6 ++++-- tests/clustergroup-naked.expected.yaml | 3 ++- tests/clustergroup-normal.expected.yaml | 6 ++++-- 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl index 18735dec..c7f986e4 100644 --- a/clustergroup/templates/imperative/_helpers.tpl +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -66,8 +66,9 @@ if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; + if [ "{{ $.Values.global.targetRevision }}" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch {{ $.Values.global.targetRevision }}"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; {{- end }} {{/* Final done container */}} diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 2242f75d..ccd2a52c 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -473,8 +473,9 @@ spec: if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; + if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 5e64dc60..2486927b 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -634,8 +634,9 @@ spec: if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; + if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -769,8 +770,9 @@ spec: if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; + if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 1ea53dc6..908f526f 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -561,8 +561,9 @@ spec: if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; + if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -696,8 +697,9 @@ spec: if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; + if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index bd89773a..4a417d5d 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -326,8 +326,9 @@ spec: if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; + if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 13daced6..efb98493 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -542,8 +542,9 @@ spec: if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; + if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -677,8 +678,9 @@ spec: if [ -n "${OUT}" ]; then export HTTPS_PROXY="${OUT}"; fi; OUT="$(oc get proxy.config.openshift.io/cluster -o jsonpath='{.spec.noProxy}' 2>/dev/null)"; if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; + if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch --branch main --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest From 85c3a9bec46378570b2e1df8aaa0c073151980af Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Wed, 5 Jun 2024 15:09:06 -0600 Subject: [PATCH 1208/1263] make resourceExclusion configurable --- clustergroup/templates/plumbing/argocd.yaml | 9 +++------ clustergroup/values.schema.json | 4 ++++ clustergroup/values.yaml | 6 ++++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 7d42ec36..68b692ff 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -150,12 +150,9 @@ spec: requests: cpu: 250m memory: 256Mi - resourceExclusions: | - - apiGroups: - - tekton.dev - kinds: - - TaskRun - - PipelineRun +{{- if $.Values.clusterGroup.argoCD.resourceExclusions }} + resourceExclusions: {{- $.Values.clusterGroup.argoCD.resourceExclusions | toYaml | indent 2 }} +{{- end }} {{- if .Values.global.excludeESO }} - apiGroups: - external-secrets.io diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 2955bf1a..e0331497 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -548,6 +548,10 @@ "initContainers": { "type": "array", "description": "A list of initContainers to add to the repo-server if needed" + }, + "resourceExclusions": { + "type": "string", + "description": "ResourceExclusions is used to completely ignore entire classes of resource group/kinds." } } }, diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index bb149567..94a01af9 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -28,6 +28,12 @@ clusterGroup: argoCD: initContainers: [] configManagementPlugins: [] + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun imperative: jobs: [] From 05a876bf60fa5787cec1480394ce875dff3a5207 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 10 Jun 2024 16:57:23 +0200 Subject: [PATCH 1209/1263] Update tests after upgrading resourceExclusions tweak --- tests/clustergroup-industrial-edge-factory.expected.yaml | 8 +++++++- tests/clustergroup-industrial-edge-hub.expected.yaml | 8 +++++++- tests/clustergroup-medical-diagnosis-hub.expected.yaml | 8 +++++++- tests/clustergroup-naked.expected.yaml | 8 +++++++- tests/clustergroup-normal.expected.yaml | 8 +++++++- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index ccd2a52c..d651d450 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -121,6 +121,12 @@ data: --set clusterGroup.name=factory --post-renderer ./kustomize"] initContainers: [] + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role @@ -778,7 +784,7 @@ spec: requests: cpu: 250m memory: 256Mi - resourceExclusions: | + resourceExclusions: | - apiGroups: - tekton.dev kinds: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 2486927b..eec93391 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -242,6 +242,12 @@ data: --set clusterGroup.name=datacenter --post-renderer ./kustomize"] initContainers: [] + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role @@ -1545,7 +1551,7 @@ spec: requests: cpu: 250m memory: 256Mi - resourceExclusions: | + resourceExclusions: | - apiGroups: - tekton.dev kinds: diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 908f526f..17e26914 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -225,6 +225,12 @@ data: argoCD: configManagementPlugins: [] initContainers: [] + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role @@ -1704,7 +1710,7 @@ spec: requests: cpu: 250m memory: 256Mi - resourceExclusions: | + resourceExclusions: | - apiGroups: - tekton.dev kinds: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 4a417d5d..3c8294d1 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -48,6 +48,12 @@ data: argoCD: configManagementPlugins: [] initContainers: [] + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role @@ -498,7 +504,7 @@ spec: requests: cpu: 250m memory: 256Mi - resourceExclusions: | + resourceExclusions: | - apiGroups: - tekton.dev kinds: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index efb98493..4cb54949 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -133,6 +133,12 @@ data: argoCD: configManagementPlugins: [] initContainers: [] + resourceExclusions: | + - apiGroups: + - tekton.dev + kinds: + - TaskRun + - PipelineRun imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role @@ -1268,7 +1274,7 @@ spec: requests: cpu: 250m memory: 256Mi - resourceExclusions: | + resourceExclusions: | - apiGroups: - tekton.dev kinds: From e97b2e7313c4161e382ecbae8ef4bc1e093de000 Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Wed, 5 Jun 2024 14:42:50 -0600 Subject: [PATCH 1210/1263] Make resourcehealthchecks configurable --- clustergroup/values.schema.json | 22 ++++++++++++++++++++++ clustergroup/values.yaml | 20 ++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index e0331497..b05dcb03 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -552,6 +552,13 @@ "resourceExclusions": { "type": "string", "description": "ResourceExclusions is used to completely ignore entire classes of resource group/kinds." + }, + "resourceHealthChecks": { + "type": "array", + "items": { + "$ref": "#/definitions/ArgoCDResourceHealthChecks" + }, + "description": "ResourceHealthChecks customizes resource health check behavior." } } }, @@ -585,6 +592,21 @@ "image" ] }, + "ArgoCDResourceHealthChecks": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "type": "string" + }, + "group": { + "type": "string" + }, + "kind": { + "type": "string" + } + } + }, "IndexImages": { "type": "object", "description": "Details for overriding default catalog sources", diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 94a01af9..cdbf823e 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -28,6 +28,26 @@ clusterGroup: argoCD: initContainers: [] configManagementPlugins: [] + resourceHealthChecks: + - kind: PersistentVolumeClaim + check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs resourceExclusions: | - apiGroups: - tekton.dev From 8d1aaab0a77c9a2c4226b46f8160409ad34fa033 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 10 Jun 2024 17:23:39 +0200 Subject: [PATCH 1211/1263] Update tests after upgrading resourceHealthChecks change --- ...roup-industrial-edge-factory.expected.yaml | 20 +++++++++++++++++++ ...tergroup-industrial-edge-hub.expected.yaml | 20 +++++++++++++++++++ ...rgroup-medical-diagnosis-hub.expected.yaml | 20 +++++++++++++++++++ tests/clustergroup-naked.expected.yaml | 20 +++++++++++++++++++ tests/clustergroup-normal.expected.yaml | 20 +++++++++++++++++++ 5 files changed, 100 insertions(+) diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index d651d450..96cf061c 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -127,6 +127,26 @@ data: kinds: - TaskRun - PipelineRun + resourceHealthChecks: + - check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs + kind: PersistentVolumeClaim imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index eec93391..eaf5b736 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -248,6 +248,26 @@ data: kinds: - TaskRun - PipelineRun + resourceHealthChecks: + - check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs + kind: PersistentVolumeClaim imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 17e26914..e834bd9e 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -231,6 +231,26 @@ data: kinds: - TaskRun - PipelineRun + resourceHealthChecks: + - check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs + kind: PersistentVolumeClaim imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 3c8294d1..04724078 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -54,6 +54,26 @@ data: kinds: - TaskRun - PipelineRun + resourceHealthChecks: + - check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs + kind: PersistentVolumeClaim imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 4cb54949..c28834d5 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -139,6 +139,26 @@ data: kinds: - TaskRun - PipelineRun + resourceHealthChecks: + - check: | + hs = {} + if obj.status ~= nil then + if obj.status.phase ~= nil then + if obj.status.phase == "Pending" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + elseif obj.status.phase == "Bound" then + hs.status = "Healthy" + hs.message = obj.status.phase + return hs + end + end + end + hs.status = "Progressing" + hs.message = "Waiting for PVC" + return hs + kind: PersistentVolumeClaim imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role From 32194d1ad4c3cce0f4d7c8ce3c8877a30b83914a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 10 Jun 2024 18:10:27 +0200 Subject: [PATCH 1212/1263] Do not quote $BRANCH variable Otherwise we might error out with: error: unknown option `branch qe_test-16831' usage: git clone [] [--] [

      ] --- clustergroup/templates/imperative/_helpers.tpl | 2 +- tests/clustergroup-industrial-edge-factory.expected.yaml | 2 +- tests/clustergroup-industrial-edge-hub.expected.yaml | 4 ++-- tests/clustergroup-medical-diagnosis-hub.expected.yaml | 4 ++-- tests/clustergroup-naked.expected.yaml | 2 +- tests/clustergroup-normal.expected.yaml | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl index c7f986e4..93299f91 100644 --- a/clustergroup/templates/imperative/_helpers.tpl +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -68,7 +68,7 @@ if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; if [ "{{ $.Values.global.targetRevision }}" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch {{ $.Values.global.targetRevision }}"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch ${BRANCH} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; {{- end }} {{/* Final done container */}} diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 96cf061c..e795d287 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -501,7 +501,7 @@ spec: if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch ${BRANCH} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index eaf5b736..8c05e007 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -662,7 +662,7 @@ spec: if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch ${BRANCH} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -798,7 +798,7 @@ spec: if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch ${BRANCH} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index e834bd9e..a299e7dc 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -589,7 +589,7 @@ spec: if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch ${BRANCH} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -725,7 +725,7 @@ spec: if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch ${BRANCH} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 04724078..ef518557 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -354,7 +354,7 @@ spec: if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch ${BRANCH} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index c28834d5..c046404b 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -570,7 +570,7 @@ spec: if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch ${BRANCH} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: test image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest @@ -706,7 +706,7 @@ spec: if [ -n "${OUT}" ]; then export NO_PROXY="${OUT}"; fi; if [ "main" = "HEAD" ]; then BRANCH=""; else BRANCH="--branch main"; fi; mkdir /git/{repo,home}; - git clone --recurse-submodules --single-branch "${BRANCH}" --depth 1 -- "${URL}" /git/repo; + git clone --recurse-submodules --single-branch ${BRANCH} --depth 1 -- "${URL}" /git/repo; chmod 0770 /git/{repo,home}; - name: unseal-playbook image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest From 97b2afa01bffe16bf70142165b1a8e2b3ac0e1d6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 10 Jun 2024 18:40:38 +0200 Subject: [PATCH 1213/1263] Fix initcontainer race on spokes initContainers on the clusterwide argo instances on spokes rely on a configmap called "trusted-ca-bundle" that gets created by ACM in the openshift-gitops namespace. Since we have no explicit guarantees about ordering, it might happen that the argocd object gets created by acm before the configmap. This is problematic because the init container will start but just ignore the missing configmap. Solve this by splitting the ocp-gitops-policy in two and making sure that both the openshift-gitops subscription and the trusted-ca-bundle configmap are green before applying the next dependency. --- acm/templates/policies/ocp-gitops-policy.yaml | 99 ++++++++++++++++--- .../acm-industrial-edge-factory.expected.yaml | 94 ++++++++++++++++-- tests/acm-industrial-edge-hub.expected.yaml | 94 ++++++++++++++++-- tests/acm-medical-diagnosis-hub.expected.yaml | 94 ++++++++++++++++-- tests/acm-naked.expected.yaml | 94 ++++++++++++++++-- tests/acm-normal.expected.yaml | 94 ++++++++++++++++-- 6 files changed, 511 insertions(+), 58 deletions(-) diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index 29204254..9210b47f 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -24,15 +24,6 @@ spec: include: - default object-templates: - - complianceType: mustonlyhave - objectDefinition: - kind: ConfigMap - apiVersion: v1 - metadata: - name: trusted-ca-bundle - namespace: openshift-gitops - labels: - config.openshift.io/inject-trusted-cabundle: 'true' - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT @@ -53,6 +44,88 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-ca-bundle + namespace: openshift-gitops + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: openshift-gitops-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: openshift-gitops-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: openshift-gitops-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: openshift-gitops-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift + - key: local-cluster + operator: NotIn + values: + - 'true' +--- +# This policy depends on openshift-gitops-policy and the reason is that we need to be +# certain that the trusted-ca-bundle exists before spawning the clusterwide argocd instance +# because the initcontainer references the trusted-ca-bundle and if it starts without the +# configmap being there we risk running an argo instances that won't trust public CAs +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: openshift-gitops-policy-argocd + annotations: + policy.open-cluster-management.io/standards: NIST-CSF + policy.open-cluster-management.io/categories: PR.DS Data Security + policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + dependencies: + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: openshift-gitops-policy + namespace: open-cluster-management + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: openshift-gitops-config-argocd + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: - complianceType: mustonlyhave objectDefinition: apiVersion: argoproj.io/v1beta1 @@ -217,22 +290,22 @@ spec: apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding metadata: - name: openshift-gitops-placement-binding + name: openshift-gitops-placement-binding-argocd annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true placementRef: - name: openshift-gitops-placement + name: openshift-gitops-placement-argocd kind: PlacementRule apiGroup: apps.open-cluster-management.io subjects: - - name: openshift-gitops-policy + - name: openshift-gitops-policy-argocd kind: Policy apiGroup: policy.open-cluster-management.io --- apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: - name: openshift-gitops-placement + name: openshift-gitops-placement-argocd annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index fa867edd..980d0220 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -42,6 +42,22 @@ subjects: apiGroup: policy.open-cluster-management.io --- # Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: openshift-gitops-placement-binding-argocd + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: openshift-gitops-placement-argocd + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: openshift-gitops-policy-argocd + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: @@ -64,6 +80,28 @@ spec: - 'true' --- # Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: openshift-gitops-placement-argocd + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift + - key: local-cluster + operator: NotIn + values: + - 'true' +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: @@ -90,15 +128,6 @@ spec: include: - default object-templates: - - complianceType: mustonlyhave - objectDefinition: - kind: ConfigMap - apiVersion: v1 - metadata: - name: trusted-ca-bundle - namespace: openshift-gitops - labels: - config.openshift.io/inject-trusted-cabundle: 'true' - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT @@ -119,6 +148,53 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-ca-bundle + namespace: openshift-gitops + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +# This policy depends on openshift-gitops-policy and the reason is that we need to be +# certain that the trusted-ca-bundle exists before spawning the clusterwide argocd instance +# because the initcontainer references the trusted-ca-bundle and if it starts without the +# configmap being there we risk running an argo instances that won't trust public CAs +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: openshift-gitops-policy-argocd + annotations: + policy.open-cluster-management.io/standards: NIST-CSF + policy.open-cluster-management.io/categories: PR.DS Data Security + policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + dependencies: + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: openshift-gitops-policy + namespace: open-cluster-management + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: openshift-gitops-config-argocd + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: - complianceType: mustonlyhave objectDefinition: apiVersion: argoproj.io/v1beta1 diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index d715989d..7e3bd0f3 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -70,6 +70,22 @@ subjects: kind: Policy apiGroup: policy.open-cluster-management.io --- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: openshift-gitops-placement-binding-argocd + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: openshift-gitops-placement-argocd + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: openshift-gitops-policy-argocd + kind: Policy + apiGroup: policy.open-cluster-management.io +--- # Source: acm/templates/policies/acm-hub-ca-policy.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule @@ -136,6 +152,28 @@ spec: values: - 'true' --- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: openshift-gitops-placement-argocd + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift + - key: local-cluster + operator: NotIn + values: + - 'true' +--- # Source: acm/templates/policies/acm-hub-ca-policy.yaml apiVersion: policy.open-cluster-management.io/v1 kind: Policy @@ -298,15 +336,6 @@ spec: include: - default object-templates: - - complianceType: mustonlyhave - objectDefinition: - kind: ConfigMap - apiVersion: v1 - metadata: - name: trusted-ca-bundle - namespace: openshift-gitops - labels: - config.openshift.io/inject-trusted-cabundle: 'true' - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT @@ -327,6 +356,53 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-ca-bundle + namespace: openshift-gitops + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +# This policy depends on openshift-gitops-policy and the reason is that we need to be +# certain that the trusted-ca-bundle exists before spawning the clusterwide argocd instance +# because the initcontainer references the trusted-ca-bundle and if it starts without the +# configmap being there we risk running an argo instances that won't trust public CAs +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: openshift-gitops-policy-argocd + annotations: + policy.open-cluster-management.io/standards: NIST-CSF + policy.open-cluster-management.io/categories: PR.DS Data Security + policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + dependencies: + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: openshift-gitops-policy + namespace: open-cluster-management + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: openshift-gitops-config-argocd + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: - complianceType: mustonlyhave objectDefinition: apiVersion: argoproj.io/v1beta1 diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 2fd25e75..e63e0654 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -70,6 +70,22 @@ subjects: kind: Policy apiGroup: policy.open-cluster-management.io --- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: openshift-gitops-placement-binding-argocd + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: openshift-gitops-placement-argocd + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: openshift-gitops-policy-argocd + kind: Policy + apiGroup: policy.open-cluster-management.io +--- # Source: acm/templates/policies/acm-hub-ca-policy.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule @@ -127,6 +143,28 @@ spec: values: - 'true' --- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: openshift-gitops-placement-argocd + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift + - key: local-cluster + operator: NotIn + values: + - 'true' +--- # Source: acm/templates/policies/acm-hub-ca-policy.yaml apiVersion: policy.open-cluster-management.io/v1 kind: Policy @@ -289,15 +327,6 @@ spec: include: - default object-templates: - - complianceType: mustonlyhave - objectDefinition: - kind: ConfigMap - apiVersion: v1 - metadata: - name: trusted-ca-bundle - namespace: openshift-gitops - labels: - config.openshift.io/inject-trusted-cabundle: 'true' - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT @@ -318,6 +347,53 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-ca-bundle + namespace: openshift-gitops + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +# This policy depends on openshift-gitops-policy and the reason is that we need to be +# certain that the trusted-ca-bundle exists before spawning the clusterwide argocd instance +# because the initcontainer references the trusted-ca-bundle and if it starts without the +# configmap being there we risk running an argo instances that won't trust public CAs +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: openshift-gitops-policy-argocd + annotations: + policy.open-cluster-management.io/standards: NIST-CSF + policy.open-cluster-management.io/categories: PR.DS Data Security + policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + dependencies: + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: openshift-gitops-policy + namespace: open-cluster-management + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: openshift-gitops-config-argocd + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: - complianceType: mustonlyhave objectDefinition: apiVersion: argoproj.io/v1beta1 diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index fa867edd..980d0220 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -42,6 +42,22 @@ subjects: apiGroup: policy.open-cluster-management.io --- # Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: openshift-gitops-placement-binding-argocd + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: openshift-gitops-placement-argocd + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: openshift-gitops-policy-argocd + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: @@ -64,6 +80,28 @@ spec: - 'true' --- # Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: openshift-gitops-placement-argocd + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift + - key: local-cluster + operator: NotIn + values: + - 'true' +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml apiVersion: policy.open-cluster-management.io/v1 kind: Policy metadata: @@ -90,15 +128,6 @@ spec: include: - default object-templates: - - complianceType: mustonlyhave - objectDefinition: - kind: ConfigMap - apiVersion: v1 - metadata: - name: trusted-ca-bundle - namespace: openshift-gitops - labels: - config.openshift.io/inject-trusted-cabundle: 'true' - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT @@ -119,6 +148,53 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-ca-bundle + namespace: openshift-gitops + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +# This policy depends on openshift-gitops-policy and the reason is that we need to be +# certain that the trusted-ca-bundle exists before spawning the clusterwide argocd instance +# because the initcontainer references the trusted-ca-bundle and if it starts without the +# configmap being there we risk running an argo instances that won't trust public CAs +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: openshift-gitops-policy-argocd + annotations: + policy.open-cluster-management.io/standards: NIST-CSF + policy.open-cluster-management.io/categories: PR.DS Data Security + policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + dependencies: + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: openshift-gitops-policy + namespace: open-cluster-management + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: openshift-gitops-config-argocd + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: - complianceType: mustonlyhave objectDefinition: apiVersion: argoproj.io/v1beta1 diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 356d0658..f3ca998a 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -860,6 +860,22 @@ subjects: kind: Policy apiGroup: policy.open-cluster-management.io --- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: openshift-gitops-placement-binding-argocd + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: openshift-gitops-placement-argocd + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: openshift-gitops-policy-argocd + kind: Policy + apiGroup: policy.open-cluster-management.io +--- # Source: acm/templates/policies/acm-hub-ca-policy.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule @@ -945,6 +961,28 @@ spec: values: - 'true' --- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: openshift-gitops-placement-argocd + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: vendor + operator: In + values: + - OpenShift + - key: local-cluster + operator: NotIn + values: + - 'true' +--- # Source: acm/templates/policies/acm-hub-ca-policy.yaml apiVersion: policy.open-cluster-management.io/v1 kind: Policy @@ -1301,15 +1339,6 @@ spec: include: - default object-templates: - - complianceType: mustonlyhave - objectDefinition: - kind: ConfigMap - apiVersion: v1 - metadata: - name: trusted-ca-bundle - namespace: openshift-gitops - labels: - config.openshift.io/inject-trusted-cabundle: 'true' - complianceType: mustonlyhave objectDefinition: # This is an auto-generated file. DO NOT EDIT @@ -1330,6 +1359,53 @@ spec: env: - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: "*" + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-ca-bundle + namespace: openshift-gitops + labels: + config.openshift.io/inject-trusted-cabundle: 'true' +--- +# Source: acm/templates/policies/ocp-gitops-policy.yaml +# This policy depends on openshift-gitops-policy and the reason is that we need to be +# certain that the trusted-ca-bundle exists before spawning the clusterwide argocd instance +# because the initcontainer references the trusted-ca-bundle and if it starts without the +# configmap being there we risk running an argo instances that won't trust public CAs +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: openshift-gitops-policy-argocd + annotations: + policy.open-cluster-management.io/standards: NIST-CSF + policy.open-cluster-management.io/categories: PR.DS Data Security + policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + dependencies: + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: openshift-gitops-policy + namespace: open-cluster-management + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: openshift-gitops-config-argocd + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: - complianceType: mustonlyhave objectDefinition: apiVersion: argoproj.io/v1beta1 From ffa35ef5be91d5542e1a468b24f40de6325267b0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 10 Jun 2024 20:50:30 +0200 Subject: [PATCH 1214/1263] Release clustergroup v0.8.8 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 222a2482..5b774788 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.8.7 +version: 0.8.8 From e47dd3f883dac404f56721cd703784f0c61b4b46 Mon Sep 17 00:00:00 2001 From: Trevor Royer Date: Tue, 11 Jun 2024 09:57:54 +0200 Subject: [PATCH 1215/1263] configure annotation based resource tracking --- clustergroup/templates/plumbing/argocd.yaml | 1 + clustergroup/values.schema.json | 9 +++++++++ clustergroup/values.yaml | 2 ++ tests/clustergroup-industrial-edge-factory.expected.yaml | 2 ++ tests/clustergroup-industrial-edge-hub.expected.yaml | 2 ++ tests/clustergroup-medical-diagnosis-hub.expected.yaml | 2 ++ tests/clustergroup-naked.expected.yaml | 2 ++ tests/clustergroup-normal.expected.yaml | 2 ++ 8 files changed, 22 insertions(+) diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index 68b692ff..ee1b7c2d 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -36,6 +36,7 @@ spec: hs.message = "Waiting for PVC" return hs + resourceTrackingMethod: {{ $.Values.clusterGroup.argoCD.resourceTrackingMethod}} applicationInstanceLabelKey: argocd.argoproj.io/instance applicationSet: resources: diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index b05dcb03..a1449ff3 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -549,6 +549,15 @@ "type": "array", "description": "A list of initContainers to add to the repo-server if needed" }, + "resourceTrackingMethod": { + "type": "string", + "description": "ResourceTrackingMethod defines how Argo CD should track resources that it manages", + "enum": [ + "annotation", + "label", + "annotation+label" + ] + }, "resourceExclusions": { "type": "string", "description": "ResourceExclusions is used to completely ignore entire classes of resource group/kinds." diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index cdbf823e..f02175f9 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -28,6 +28,8 @@ clusterGroup: argoCD: initContainers: [] configManagementPlugins: [] + # resource tracking can be set to annotation, label, or annotation+label + resourceTrackingMethod: label resourceHealthChecks: - kind: PersistentVolumeClaim check: | diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index e795d287..ad6cfa3c 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -147,6 +147,7 @@ data: hs.message = "Waiting for PVC" return hs kind: PersistentVolumeClaim + resourceTrackingMethod: label imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role @@ -703,6 +704,7 @@ spec: hs.message = "Waiting for PVC" return hs + resourceTrackingMethod: label applicationInstanceLabelKey: argocd.argoproj.io/instance applicationSet: resources: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 8c05e007..cc30a7ac 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -268,6 +268,7 @@ data: hs.message = "Waiting for PVC" return hs kind: PersistentVolumeClaim + resourceTrackingMethod: label imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role @@ -1470,6 +1471,7 @@ spec: hs.message = "Waiting for PVC" return hs + resourceTrackingMethod: label applicationInstanceLabelKey: argocd.argoproj.io/instance applicationSet: resources: diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index a299e7dc..1888528f 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -251,6 +251,7 @@ data: hs.message = "Waiting for PVC" return hs kind: PersistentVolumeClaim + resourceTrackingMethod: label imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role @@ -1655,6 +1656,7 @@ spec: hs.message = "Waiting for PVC" return hs + resourceTrackingMethod: label applicationInstanceLabelKey: argocd.argoproj.io/instance applicationSet: resources: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index ef518557..56452cca 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -74,6 +74,7 @@ data: hs.message = "Waiting for PVC" return hs kind: PersistentVolumeClaim + resourceTrackingMethod: label imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role @@ -449,6 +450,7 @@ spec: hs.message = "Waiting for PVC" return hs + resourceTrackingMethod: label applicationInstanceLabelKey: argocd.argoproj.io/instance applicationSet: resources: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index c046404b..ba0e704d 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -159,6 +159,7 @@ data: hs.message = "Waiting for PVC" return hs kind: PersistentVolumeClaim + resourceTrackingMethod: label imperative: activeDeadlineSeconds: 3600 adminClusterRoleName: imperative-admin-cluster-role @@ -1219,6 +1220,7 @@ spec: hs.message = "Waiting for PVC" return hs + resourceTrackingMethod: label applicationInstanceLabelKey: argocd.argoproj.io/instance applicationSet: resources: From 5b52f0daaf9cf4a7ef392b1d32394febce6df1ea Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 15 Jun 2024 14:24:48 +0200 Subject: [PATCH 1216/1263] Drop imperative.volumes and imperative.volumemounts With the switch to initcontainers they are not used anywhere anymore --- clustergroup/templates/imperative/_helpers.tpl | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl index 93299f91..f1d99416 100644 --- a/clustergroup/templates/imperative/_helpers.tpl +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -98,22 +98,6 @@ - mountPath: /tmp/ca-bundles name: ca-bundles {{- end }} -{{- define "imperative.volumemounts" }} -- name: git - mountPath: "/git" -- name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml -{{- end }} - -{{/* volumes for all containers */}} -{{- define "imperative.volumes" }} -- name: git - emptyDir: {} -- name: values-volume - configMap: - name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} -{{- end }} {{- define "imperative.volumes_ca" }} - name: git From 28b2d3965ee10a31bc1ca03539ffd0d04f1a8cc9 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 18 Jun 2024 10:00:46 +0200 Subject: [PATCH 1217/1263] Properly quote vault kv command When using a password with a dollar, the oc exec commands run inside the vault are not properly escaped, casing the dollar signed to be interpreted by the shell inside the vault pod. So a password like 'Y$yxn54&qXAxpUd2*yGH' will become 'Y&qXAxpUd2*yGH' in the vault. This is because the command that is being run ends up being: oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret global/mysecret dollar=Y$yxn54&qXAxpUd2*yGH" The `$yxn54` will be interpreted by the shell inside vault. Let's fix this by running a properly escaped command: oc exec -n vault vault-0 -i -- sh -c "vault kv patch -mount=secret global/mysecret dollar='"'Y$yxn54&qXAxpUd2*yGH'"'" Reported-By: Chris Butler --- ansible/plugins/modules/vault_load_parsed_secrets.py | 2 +- ansible/tests/unit/test_vault_load_parsed_secrets.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ansible/plugins/modules/vault_load_parsed_secrets.py b/ansible/plugins/modules/vault_load_parsed_secrets.py index cfcf9732..0a6aa146 100644 --- a/ansible/plugins/modules/vault_load_parsed_secrets.py +++ b/ansible/plugins/modules/vault_load_parsed_secrets.py @@ -215,7 +215,7 @@ def inject_field( for prefix in prefixes: cmd = ( f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " - f"\"vault kv {verb} -mount={mount} {prefix}/{secret_name} {fieldname}='{fieldvalue}'\"" + f"\"vault kv {verb} -mount={mount} {prefix}/{secret_name} {fieldname}='\"'{fieldvalue}'\"'\"" ) self._run_command(cmd, attempts=3) return diff --git a/ansible/tests/unit/test_vault_load_parsed_secrets.py b/ansible/tests/unit/test_vault_load_parsed_secrets.py index ca37de94..1a449739 100644 --- a/ansible/tests/unit/test_vault_load_parsed_secrets.py +++ b/ansible/tests/unit/test_vault_load_parsed_secrets.py @@ -120,7 +120,7 @@ def test_ensure_value_injection_works(self): attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret hub/config-demo secret='value123'\"", + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret hub/config-demo secret='\"'value123'\"'\"", # noqa: E501 attempts=3, ), ] @@ -159,7 +159,7 @@ def test_ensure_b64_value_injection_works(self): attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret hub/config-demo secret='dmFsdWUxMjMK'\"", # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret hub/config-demo secret='\"'dmFsdWUxMjMK'\"'\"", # noqa: E501 attempts=3, ), ] @@ -198,11 +198,11 @@ def test_ensure_file_injection_works(self): attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/region-one/config-demo secret='value123'\"", # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/region-one/config-demo secret='\"'value123'\"'\"", # noqa: E501 attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret='value123'\"", # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret='\"'value123'\"'\"", # noqa: E501 attempts=3, ), call( @@ -249,11 +249,11 @@ def test_ensure_file_b64_injection_works(self): attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/region-one/config-demo secret='value123'\"", # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/region-one/config-demo secret='\"'value123'\"'\"", # noqa: E501 attempts=3, ), call( - "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret='value123'\"", # noqa: E501 + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo secret='\"'value123'\"'\"", # noqa: E501 attempts=3, ), call( From 6a312d732d1cf551f552eefd5444beddac1243c3 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 11 Jun 2024 15:35:49 +0200 Subject: [PATCH 1218/1263] Make HUB work when spokes point to in hub cluster gitea With this change we push the hub CA to the spokes in both the openshift-gitops, the namespaced argo namespace and in the imperative namespace. We do this so that the two argo instances are allowed to clone from an eventual gitea repository on the hub. Same goes for the imperative framework which might need to clone the code from the in-cluster gitea service running on the hub. --- acm/templates/policies/acm-hub-ca-policy.yaml | 158 +++++++++++++++++- acm/templates/policies/ocp-gitops-policy.yaml | 13 +- .../templates/imperative/_helpers.tpl | 10 +- clustergroup/templates/plumbing/argocd.yaml | 8 +- 4 files changed, 184 insertions(+), 5 deletions(-) diff --git a/acm/templates/policies/acm-hub-ca-policy.yaml b/acm/templates/policies/acm-hub-ca-policy.yaml index ef15b136..5759247c 100644 --- a/acm/templates/policies/acm-hub-ca-policy.yaml +++ b/acm/templates/policies/acm-hub-ca-policy.yaml @@ -36,6 +36,18 @@ spec: data: hub-kube-root-ca.crt: '{{ `{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}` }}' hub-openshift-service-ca.crt: '{{ `{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | base64enc hub}}` }}' + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: imperative + data: + hub-kube-root-ca.crt: | + {{ `{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}}` }} + hub-openshift-service-ca.crt: | + {{ `{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}}` }} --- apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding @@ -68,5 +80,147 @@ spec: operator: NotIn values: - 'true' -{{- end }} -{{- end }} +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: hub-argo-ca-openshift-gitops-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: hub-argo-ca-openshift-gitops-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: openshift-gitops + data: + hub-kube-root-ca.crt: | + {{ `{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}}` }} + hub-openshift-service-ca.crt: | + {{ `{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}}` }} +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: hub-argo-ca-openshift-gitops-policy-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: hub-argo-ca-openshift-gitops-policy-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: hub-argo-ca-openshift-gitops-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: hub-argo-ca-openshift-gitops-policy-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' + +{{- end }}{{/* if (eq (((.Values.global).secretStore).backend) "vault") */}} +{{- range .Values.clusterGroup.managedClusterGroups }} +{{- $group := . }} +{{- if not .hostedArgoSites }} +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: hub-argo-ca-{{ .name }}-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: hub-argo-ca-{{ .name }}-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: {{ $.Values.global.pattern }}-{{ .name }} + data: + hub-kube-root-ca.crt: | + {{ `{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}}` }} + hub-openshift-service-ca.crt: | + {{ `{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}}` }} +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: hub-argo-ca-{{ .name }}-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: hub-argo-ca-{{ .name }}-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: hub-argo-ca-{{ .name }}-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: hub-argo-ca-{{ .name }}-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +{{- end }}{{/* if not .hostedArgoSites */}} +{{- end }}{{/* range .Values.clusterGroup.managedClusterGroups */}} +{{- end }}{{/* isHubCluster */}} diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index 9210b47f..cdc0a7e1 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -113,6 +113,11 @@ spec: kind: Policy name: openshift-gitops-policy namespace: open-cluster-management + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: hub-argo-ca-openshift-gitops-policy + namespace: open-cluster-management policy-templates: - objectDefinition: apiVersion: policy.open-cluster-management.io/v1 @@ -209,7 +214,7 @@ spec: - command: - bash - -c - - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true image: registry.redhat.io/ubi9/ubi-minimal:latest name: fetch-ca @@ -219,6 +224,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles resources: @@ -239,6 +246,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - emptyDir: {} name: ca-bundles resourceExclusions: |- diff --git a/clustergroup/templates/imperative/_helpers.tpl b/clustergroup/templates/imperative/_helpers.tpl index f1d99416..88538f88 100644 --- a/clustergroup/templates/imperative/_helpers.tpl +++ b/clustergroup/templates/imperative/_helpers.tpl @@ -16,13 +16,15 @@ - 'sh' - '-c' - >- - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true; ls -l /tmp/ca-bundles/ volumeMounts: - mountPath: /var/run/kube-root-ca name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles {{- end }} @@ -95,6 +97,8 @@ name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle +- mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles {{- end }} @@ -112,6 +116,10 @@ name: trusted-ca-bundle optional: true name: trusted-ca-bundle +- configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - name: ca-bundles emptyDir: {} {{- end }} diff --git a/clustergroup/templates/plumbing/argocd.yaml b/clustergroup/templates/plumbing/argocd.yaml index ee1b7c2d..d7a8f7e3 100644 --- a/clustergroup/templates/plumbing/argocd.yaml +++ b/clustergroup/templates/plumbing/argocd.yaml @@ -74,7 +74,7 @@ spec: - command: - bash - -c - - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true image: {{ $.Values.clusterGroup.imperative.image }} name: fetch-ca resources: {} @@ -83,6 +83,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles resources: @@ -103,6 +105,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - emptyDir: {} name: ca-bundles {{- if len $.Values.clusterGroup.argoCD.initContainers }} From 591a339f7d8f8077185252d66a32194bf2e2221f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 18 Jun 2024 17:59:54 +0200 Subject: [PATCH 1219/1263] update tests --- .../acm-industrial-edge-factory.expected.yaml | 13 +- tests/acm-industrial-edge-hub.expected.yaml | 167 +++++++++- tests/acm-medical-diagnosis-hub.expected.yaml | 167 +++++++++- tests/acm-naked.expected.yaml | 13 +- tests/acm-normal.expected.yaml | 309 +++++++++++++++++- ...roup-industrial-edge-factory.expected.yaml | 18 +- ...tergroup-industrial-edge-hub.expected.yaml | 28 +- ...rgroup-medical-diagnosis-hub.expected.yaml | 28 +- tests/clustergroup-naked.expected.yaml | 18 +- tests/clustergroup-normal.expected.yaml | 28 +- 10 files changed, 771 insertions(+), 18 deletions(-) diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index 980d0220..39238f91 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -182,6 +182,11 @@ spec: kind: Policy name: openshift-gitops-policy namespace: open-cluster-management + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: hub-argo-ca-openshift-gitops-policy + namespace: open-cluster-management policy-templates: - objectDefinition: apiVersion: policy.open-cluster-management.io/v1 @@ -278,7 +283,7 @@ spec: - command: - bash - -c - - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true image: registry.redhat.io/ubi9/ubi-minimal:latest name: fetch-ca @@ -288,6 +293,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles resources: @@ -308,6 +315,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - emptyDir: {} name: ca-bundles resourceExclusions: |- diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 7e3bd0f3..83a81f06 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -38,6 +38,38 @@ subjects: kind: Policy apiGroup: policy.open-cluster-management.io --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: hub-argo-ca-openshift-gitops-policy-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: hub-argo-ca-openshift-gitops-policy-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: hub-argo-ca-openshift-gitops-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: hub-argo-ca-factory-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: hub-argo-ca-factory-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: hub-argo-ca-factory-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- # Source: acm/templates/policies/application-policies.yaml apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding @@ -104,6 +136,42 @@ spec: values: - 'true' --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: hub-argo-ca-openshift-gitops-policy-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: hub-argo-ca-factory-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +--- # Source: acm/templates/policies/application-policies.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule @@ -209,6 +277,92 @@ spec: data: hub-kube-root-ca.crt: '{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}' hub-openshift-service-ca.crt: '{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | base64enc hub}}' + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: imperative + data: + hub-kube-root-ca.crt: | + {{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}} + hub-openshift-service-ca.crt: | + {{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}} +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: hub-argo-ca-openshift-gitops-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: hub-argo-ca-openshift-gitops-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: openshift-gitops + data: + hub-kube-root-ca.crt: | + {{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}} + hub-openshift-service-ca.crt: | + {{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}} +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: hub-argo-ca-factory-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: hub-argo-ca-factory-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: mypattern-factory + data: + hub-kube-root-ca.crt: | + {{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}} + hub-openshift-service-ca.crt: | + {{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}} --- # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io @@ -390,6 +544,11 @@ spec: kind: Policy name: openshift-gitops-policy namespace: open-cluster-management + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: hub-argo-ca-openshift-gitops-policy + namespace: open-cluster-management policy-templates: - objectDefinition: apiVersion: policy.open-cluster-management.io/v1 @@ -486,7 +645,7 @@ spec: - command: - bash - -c - - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true image: registry.redhat.io/ubi9/ubi-minimal:latest name: fetch-ca @@ -496,6 +655,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles resources: @@ -516,6 +677,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - emptyDir: {} name: ca-bundles resourceExclusions: |- diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index e63e0654..edb33909 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -38,6 +38,38 @@ subjects: kind: Policy apiGroup: policy.open-cluster-management.io --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: hub-argo-ca-openshift-gitops-policy-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: hub-argo-ca-openshift-gitops-policy-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: hub-argo-ca-openshift-gitops-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: hub-argo-ca-region-one-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: hub-argo-ca-region-one-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: hub-argo-ca-region-one-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- # Source: acm/templates/policies/application-policies.yaml apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding @@ -104,6 +136,42 @@ spec: values: - 'true' --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: hub-argo-ca-openshift-gitops-policy-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: hub-argo-ca-region-one-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +--- # Source: acm/templates/policies/application-policies.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule @@ -200,6 +268,92 @@ spec: data: hub-kube-root-ca.crt: '{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}' hub-openshift-service-ca.crt: '{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | base64enc hub}}' + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: imperative + data: + hub-kube-root-ca.crt: | + {{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}} + hub-openshift-service-ca.crt: | + {{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}} +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: hub-argo-ca-openshift-gitops-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: hub-argo-ca-openshift-gitops-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: openshift-gitops + data: + hub-kube-root-ca.crt: | + {{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}} + hub-openshift-service-ca.crt: | + {{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}} +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: hub-argo-ca-region-one-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: hub-argo-ca-region-one-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: mypattern-region-one + data: + hub-kube-root-ca.crt: | + {{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}} + hub-openshift-service-ca.crt: | + {{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}} --- # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io @@ -381,6 +535,11 @@ spec: kind: Policy name: openshift-gitops-policy namespace: open-cluster-management + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: hub-argo-ca-openshift-gitops-policy + namespace: open-cluster-management policy-templates: - objectDefinition: apiVersion: policy.open-cluster-management.io/v1 @@ -477,7 +636,7 @@ spec: - command: - bash - -c - - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true image: registry.redhat.io/ubi9/ubi-minimal:latest name: fetch-ca @@ -487,6 +646,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles resources: @@ -507,6 +668,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - emptyDir: {} name: ca-bundles resourceExclusions: |- diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index 980d0220..39238f91 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -182,6 +182,11 @@ spec: kind: Policy name: openshift-gitops-policy namespace: open-cluster-management + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: hub-argo-ca-openshift-gitops-policy + namespace: open-cluster-management policy-templates: - objectDefinition: apiVersion: policy.open-cluster-management.io/v1 @@ -278,7 +283,7 @@ spec: - command: - bash - -c - - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true image: registry.redhat.io/ubi9/ubi-minimal:latest name: fetch-ca @@ -288,6 +293,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles resources: @@ -308,6 +315,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - emptyDir: {} name: ca-bundles resourceExclusions: |- diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index f3ca998a..6660ae87 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -796,6 +796,70 @@ subjects: kind: Policy apiGroup: policy.open-cluster-management.io --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: hub-argo-ca-openshift-gitops-policy-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: hub-argo-ca-openshift-gitops-policy-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: hub-argo-ca-openshift-gitops-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: hub-argo-ca-acm-edge-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: hub-argo-ca-acm-edge-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: hub-argo-ca-acm-edge-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: hub-argo-ca-acm-provision-edge-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: hub-argo-ca-acm-provision-edge-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: hub-argo-ca-acm-provision-edge-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: hub-argo-ca-acm-provision-on-deploy-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +placementRef: + name: hub-argo-ca-acm-provision-on-deploy-placement + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: hub-argo-ca-acm-provision-on-deploy-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- # Source: acm/templates/policies/application-policies.yaml apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding @@ -894,6 +958,78 @@ spec: values: - 'true' --- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: hub-argo-ca-openshift-gitops-policy-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: hub-argo-ca-acm-edge-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: hub-argo-ca-acm-provision-edge-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: hub-argo-ca-acm-provision-on-deploy-placement + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchExpressions: + - key: local-cluster + operator: NotIn + values: + - 'true' +--- # Source: acm/templates/policies/application-policies.yaml apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule @@ -1018,6 +1154,166 @@ spec: data: hub-kube-root-ca.crt: '{{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | base64enc hub}}' hub-openshift-service-ca.crt: '{{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | base64enc hub}}' + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: imperative + data: + hub-kube-root-ca.crt: | + {{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}} + hub-openshift-service-ca.crt: | + {{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}} +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: hub-argo-ca-openshift-gitops-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: hub-argo-ca-openshift-gitops-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: openshift-gitops + data: + hub-kube-root-ca.crt: | + {{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}} + hub-openshift-service-ca.crt: | + {{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}} +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: hub-argo-ca-acm-edge-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: hub-argo-ca-acm-edge-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: mypattern-acm-edge + data: + hub-kube-root-ca.crt: | + {{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}} + hub-openshift-service-ca.crt: | + {{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}} +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: hub-argo-ca-acm-provision-edge-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: hub-argo-ca-acm-provision-edge-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: mypattern-acm-provision-edge + data: + hub-kube-root-ca.crt: | + {{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}} + hub-openshift-service-ca.crt: | + {{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}} +--- +# Source: acm/templates/policies/acm-hub-ca-policy.yaml +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: hub-argo-ca-acm-provision-on-deploy-policy + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: hub-argo-ca-acm-provision-on-deploy-config + spec: + remediationAction: enforce + severity: medium + namespaceSelector: + include: + - default + object-templates: + - complianceType: mustonlyhave + objectDefinition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: trusted-hub-bundle + namespace: mypattern-acm-provision-on-deploy + data: + hub-kube-root-ca.crt: | + {{hub fromConfigMap "" "kube-root-ca.crt" "ca.crt" | autoindent hub}} + hub-openshift-service-ca.crt: | + {{hub fromConfigMap "" "openshift-service-ca.crt" "service-ca.crt" | autoindent hub}} --- # Source: acm/templates/policies/application-policies.yaml # TODO: Also create a GitOpsCluster.apps.open-cluster-management.io @@ -1393,6 +1689,11 @@ spec: kind: Policy name: openshift-gitops-policy namespace: open-cluster-management + - apiVersion: policy.open-cluster-management.io/v1 + compliance: Compliant + kind: Policy + name: hub-argo-ca-openshift-gitops-policy + namespace: open-cluster-management policy-templates: - objectDefinition: apiVersion: policy.open-cluster-management.io/v1 @@ -1489,7 +1790,7 @@ spec: - command: - bash - -c - - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true image: registry.redhat.io/ubi9/ubi-minimal:latest name: fetch-ca @@ -1499,6 +1800,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles resources: @@ -1519,6 +1822,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - emptyDir: {} name: ca-bundles resourceExclusions: |- diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index ad6cfa3c..49c2c1d6 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -454,13 +454,15 @@ spec: - 'sh' - '-c' - >- - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true; ls -l /tmp/ca-bundles/ volumeMounts: - mountPath: /var/run/kube-root-ca name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles - name: git-init @@ -530,6 +532,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles containers: @@ -555,6 +559,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - name: ca-bundles emptyDir: {} restartPolicy: Never @@ -742,7 +750,7 @@ spec: - command: - bash - -c - - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest name: fetch-ca resources: {} @@ -751,6 +759,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles resources: @@ -771,6 +781,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - emptyDir: {} name: ca-bundles sidecarContainers: diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index cc30a7ac..4730754c 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -615,13 +615,15 @@ spec: - 'sh' - '-c' - >- - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true; ls -l /tmp/ca-bundles/ volumeMounts: - mountPath: /var/run/kube-root-ca name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles - name: git-init @@ -691,6 +693,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles containers: @@ -716,6 +720,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - name: ca-bundles emptyDir: {} restartPolicy: Never @@ -751,13 +759,15 @@ spec: - 'sh' - '-c' - >- - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true; ls -l /tmp/ca-bundles/ volumeMounts: - mountPath: /var/run/kube-root-ca name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles - name: git-init @@ -829,6 +839,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles containers: @@ -854,6 +866,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - name: ca-bundles emptyDir: {} restartPolicy: Never @@ -1509,7 +1525,7 @@ spec: - command: - bash - -c - - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest name: fetch-ca resources: {} @@ -1518,6 +1534,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles resources: @@ -1538,6 +1556,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - emptyDir: {} name: ca-bundles sidecarContainers: diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 1888528f..76083c4e 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -542,13 +542,15 @@ spec: - 'sh' - '-c' - >- - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true; ls -l /tmp/ca-bundles/ volumeMounts: - mountPath: /var/run/kube-root-ca name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles - name: git-init @@ -618,6 +620,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles containers: @@ -643,6 +647,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - name: ca-bundles emptyDir: {} restartPolicy: Never @@ -678,13 +686,15 @@ spec: - 'sh' - '-c' - >- - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true; ls -l /tmp/ca-bundles/ volumeMounts: - mountPath: /var/run/kube-root-ca name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles - name: git-init @@ -756,6 +766,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles containers: @@ -781,6 +793,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - name: ca-bundles emptyDir: {} restartPolicy: Never @@ -1694,7 +1710,7 @@ spec: - command: - bash - -c - - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest name: fetch-ca resources: {} @@ -1703,6 +1719,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles resources: @@ -1723,6 +1741,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - emptyDir: {} name: ca-bundles resources: diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 56452cca..16c6d81d 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -307,13 +307,15 @@ spec: - 'sh' - '-c' - >- - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true; ls -l /tmp/ca-bundles/ volumeMounts: - mountPath: /var/run/kube-root-ca name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles - name: git-init @@ -385,6 +387,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles containers: @@ -410,6 +414,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - name: ca-bundles emptyDir: {} restartPolicy: Never @@ -488,7 +496,7 @@ spec: - command: - bash - -c - - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest name: fetch-ca resources: {} @@ -497,6 +505,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles resources: @@ -517,6 +527,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - emptyDir: {} name: ca-bundles resources: diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index ba0e704d..0ff865e3 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -523,13 +523,15 @@ spec: - 'sh' - '-c' - >- - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true; ls -l /tmp/ca-bundles/ volumeMounts: - mountPath: /var/run/kube-root-ca name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles - name: git-init @@ -599,6 +601,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles containers: @@ -624,6 +628,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - name: ca-bundles emptyDir: {} restartPolicy: Never @@ -659,13 +667,15 @@ spec: - 'sh' - '-c' - >- - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true; + cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true; ls -l /tmp/ca-bundles/ volumeMounts: - mountPath: /var/run/kube-root-ca name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles - name: git-init @@ -737,6 +747,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles containers: @@ -762,6 +774,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - name: ca-bundles emptyDir: {} restartPolicy: Never @@ -1258,7 +1274,7 @@ spec: - command: - bash - -c - - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt > /tmp/ca-bundles/ca-bundle.crt || true + - cat /var/run/kube-root-ca/ca.crt /var/run/trusted-ca/ca-bundle.crt /var/run/trusted-hub/hub-kube-root-ca.crt > /tmp/ca-bundles/ca-bundle.crt || true image: registry.redhat.io/ansible-automation-platform-24/ee-supported-rhel9:latest name: fetch-ca resources: {} @@ -1267,6 +1283,8 @@ spec: name: kube-root-ca - mountPath: /var/run/trusted-ca name: trusted-ca-bundle + - mountPath: /var/run/trusted-hub + name: trusted-hub-bundle - mountPath: /tmp/ca-bundles name: ca-bundles resources: @@ -1287,6 +1305,10 @@ spec: name: trusted-ca-bundle optional: true name: trusted-ca-bundle + - configMap: + name: trusted-hub-bundle + optional: true + name: trusted-hub-bundle - emptyDir: {} name: ca-bundles resources: From de5647f19c931452cda24291b344e0ae516c5ce0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 24 Jun 2024 10:42:09 +0200 Subject: [PATCH 1220/1263] ACM chart version 0.0.2 --- acm/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/Chart.yaml b/acm/Chart.yaml index 3bae9da5..5ab78a75 100644 --- a/acm/Chart.yaml +++ b/acm/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to configure Advanced Cluster Manager for OpenShift. keywords: - pattern name: acm -version: 0.0.1 +version: 0.0.2 From 1fa57464d8bba92c7e7dc171634833df99f946e4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 24 Jun 2024 10:42:36 +0200 Subject: [PATCH 1221/1263] golang-external-secrets chart version 0.0.4 --- golang-external-secrets/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index afffe393..2c214633 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -3,7 +3,7 @@ description: A Helm chart to configure the golang-based external-secrets. keywords: - pattern name: golang-external-secrets -version: 0.0.3 +version: 0.0.4 dependencies: - name: external-secrets version: "0.9.19" From 89b499b3cb114c41b5d2181537430094897dcd0f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 24 Jun 2024 10:42:56 +0200 Subject: [PATCH 1222/1263] hashicorp-vault chart version 0.0.2 --- hashicorp-vault/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index e1577595..0fcbcb48 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -3,7 +3,7 @@ description: A Helm chart to configure Hashicorp's vault. keywords: - pattern name: hashicorp-vault -version: 0.0.1 +version: 0.0.2 dependencies: - name: vault version: "0.28.0" From 89a4cadd511133803ebff74869bb4cbec4a98705 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 24 Jun 2024 11:00:40 +0200 Subject: [PATCH 1223/1263] Implement multi-source --- acm/templates/_helpers.tpl | 44 +++++++++++++ .../policies/application-policies.yaml | 64 ++++++++++++------- clustergroup/templates/_helpers.tpl | 10 ++- 3 files changed, 94 insertions(+), 24 deletions(-) diff --git a/acm/templates/_helpers.tpl b/acm/templates/_helpers.tpl index 910b3970..8302457a 100644 --- a/acm/templates/_helpers.tpl +++ b/acm/templates/_helpers.tpl @@ -11,3 +11,47 @@ Default always defined valueFiles to be included when pushing the cluster wide a # hub's cluster version, whereas we want to include the spoke cluster version - '/values-{{ `{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}` }}.yaml' {{- end }} {{- /*acm.app.policies.valuefiles */}} + +{{- define "acm.app.policies.multisourcevaluefiles" -}} +- "$patternref/values-global.yaml" +- "$patternref/values-{{ .name }}.yaml" +- '$patternref/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}.yaml' +- '$patternref/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}-{{ `{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}` }}.yaml' +- '$patternref/values-{{ `{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}` }}-{{ .name }}.yaml' +# We cannot use $.Values.global.clusterVersion because that gets resolved to the +# hub's cluster version, whereas we want to include the spoke cluster version +- '$patternref/values-{{ `{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}` }}.yaml' +{{- end }} {{- /*acm.app.policies.multisourcevaluefiles */}} + +{{- define "acm.app.policies.helmparameters" -}} +- name: global.repoURL + value: {{ $.Values.global.repoURL }} +- name: global.targetRevision + value: {{ $.Values.global.targetRevision }} +- name: global.namespace + value: $ARGOCD_APP_NAMESPACE +- name: global.pattern + value: {{ $.Values.global.pattern }} +- name: global.hubClusterDomain + value: {{ $.Values.global.hubClusterDomain }} +- name: global.localClusterDomain + value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' +- name: global.clusterDomain + value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}` }}' +- name: global.clusterVersion + value: '{{ `{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}` }}' +- name: global.localClusterName + value: '{{ `{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}` }}' +- name: global.clusterPlatform + value: {{ $.Values.global.clusterPlatform }} +- name: global.multiSourceSupport + value: {{ $.Values.global.multiSourceSupport | quote }} +- name: global.multiSourceRepoUrl + value: {{ $.Values.global.multiSourceRepoUrl }} +- name: global.multiSourceTargetRevision + value: {{ $.Values.global.multiSourceTargetRevision }} +- name: global.privateRepo + value: {{ $.Values.global.privateRepo | quote }} +- name: global.experimentalCapabilities + value: {{ $.Values.global.experimentalCapabilities }} +{{- end }} {{- /*acm.app.policies.helmparameters */}} diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 01073bd4..5f0d132f 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -36,6 +36,46 @@ spec: - resources-finalizer.argocd.argoproj.io/foreground spec: project: default + {{- if $.Values.global.multiSourceSupport }} + sources: + - repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} + targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} + ref: patternref + - repoURL: {{ $.Values.global.multiSourceRepoUrl }} + targetRevision: {{ $.Values.global.multiSourceTargetRevision }} + helm: + ignoreMissingValueFiles: true + values: | + extraParametersNested: + {{- range $k, $v := $.Values.extraParametersNested }} + {{ $k }}: {{ printf "%s" $v | quote }} + {{- end }} + valueFiles: + {{- include "acm.app.policies.multisourcevaluefiles" . | nindent 26 }} + {{- range $valueFile := .extraValueFiles }} + - {{ $valueFile | quote }} + {{- end }} + parameters: + {{- include "acm.app.policies.helmparameters" $ | nindent 26 }} + - name: clusterGroup.name + value: {{ $group.name }} + {{- range $k, $v := $.Values.extraParametersNested }} + - name: {{ $k }} + value: {{ printf "%s" $v | quote }} + {{- end }} + {{- range .helmOverrides }} + - name: {{ .name }} + value: {{ .value | quote }} + {{- end }} + {{- if .fileParameters }} + fileParameters: + {{- range .fileParameters }} + - name: {{ .name }} + path: {{ .path }} + {{- end }} + {{- end }} + + {{- else }} source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} @@ -53,30 +93,9 @@ spec: - {{ $valueFile | quote }} {{- end }} parameters: - - name: global.repoURL - value: {{ $.Values.global.repoURL }} - - name: global.targetRevision - value: {{ $.Values.global.targetRevision }} - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: {{ $.Values.global.pattern }} - - name: global.hubClusterDomain - value: {{ $.Values.global.hubClusterDomain }} - - name: global.localClusterDomain - value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' - - name: global.clusterDomain - value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain | replace "apps." "" }}` }}' - - name: global.clusterVersion - value: '{{ `{{ printf "%d.%d" ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Major) ((semver (index (lookup "config.openshift.io/v1" "ClusterVersion" "" "version").status.history 0).version).Minor) }}` }}' - - name: global.localClusterName - value: '{{ `{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}` }}' - - name: global.clusterPlatform - value: {{ $.Values.global.clusterPlatform }} + {{- include "acm.app.policies.helmparameters" $ | nindent 22 }} - name: clusterGroup.name value: {{ $group.name }} - - name: global.experimentalCapabilities - value: {{ $.Values.global.experimentalCapabilities }} {{- range $k, $v := $.Values.extraParametersNested }} - name: {{ $k }} value: {{ printf "%s" $v | quote }} @@ -92,6 +111,7 @@ spec: path: {{ .path }} {{- end }} {{- end }} + {{- end }}{{/* if $.Values.global.multiSourceSupport */}} destination: server: https://kubernetes.default.svc namespace: {{ $.Values.global.pattern }}-{{ .name }} diff --git a/clustergroup/templates/_helpers.tpl b/clustergroup/templates/_helpers.tpl index c6d14d08..0237e94a 100644 --- a/clustergroup/templates/_helpers.tpl +++ b/clustergroup/templates/_helpers.tpl @@ -3,9 +3,9 @@ Default always defined top-level variables for helm charts */}} {{- define "clustergroup.app.globalvalues.helmparameters" -}} - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: {{ $.Values.global.repoURL }} - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: {{ $.Values.global.targetRevision }} - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -18,6 +18,12 @@ Default always defined top-level variables for helm charts value: "{{ $.Values.global.clusterPlatform }}" - name: global.hubClusterDomain value: {{ $.Values.global.hubClusterDomain }} +- name: global.multiSourceSupport + value: {{ $.Values.global.multiSourceSupport | quote }} +- name: global.multiSourceRepoUrl + value: {{ $.Values.global.multiSourceRepoUrl }} +- name: global.multiSourceTargetRevision + value: {{ $.Values.global.multiSourceTargetRevision }} - name: global.localClusterDomain value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} - name: global.privateRepo From 2e7866966f29dae91c7f3a88e92c18c32ec6d8e6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 29 Jun 2024 17:36:52 +0200 Subject: [PATCH 1224/1263] Add tests for proper multisource support on spokes --- tests/acm-industrial-edge-hub.expected.yaml | 12 +- tests/acm-medical-diagnosis-hub.expected.yaml | 12 +- tests/acm-normal.expected.yaml | 36 ++++- ...roup-industrial-edge-factory.expected.yaml | 10 +- ...tergroup-industrial-edge-hub.expected.yaml | 70 ++++++++-- ...rgroup-medical-diagnosis-hub.expected.yaml | 130 ++++++++++++++---- tests/clustergroup-normal.expected.yaml | 20 ++- 7 files changed, 234 insertions(+), 56 deletions(-) diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 83a81f06..8b18a4da 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -438,10 +438,18 @@ spec: value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform value: aws - - name: clusterGroup.name - value: factory + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: + - name: global.privateRepo + value: - name: global.experimentalCapabilities value: + - name: clusterGroup.name + value: factory - name: clusterGroup.isHubCluster value: "false" destination: diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index edb33909..dffb9eb6 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -429,10 +429,18 @@ spec: value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform value: aws - - name: clusterGroup.name - value: region-one + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: + - name: global.privateRepo + value: - name: global.experimentalCapabilities value: + - name: clusterGroup.name + value: region-one - name: clusterGroup.isHubCluster value: "false" destination: diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 6660ae87..1e2b1573 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -1389,10 +1389,18 @@ spec: value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform value: aws - - name: clusterGroup.name - value: acm-edge + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: + - name: global.privateRepo + value: - name: global.experimentalCapabilities value: + - name: clusterGroup.name + value: acm-edge - name: clusterGroup.isHubCluster value: "false" destination: @@ -1487,10 +1495,18 @@ spec: value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform value: aws - - name: clusterGroup.name - value: acm-provision-edge + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: + - name: global.privateRepo + value: - name: global.experimentalCapabilities value: + - name: clusterGroup.name + value: acm-provision-edge - name: clusterGroup.isHubCluster value: "false" destination: @@ -1585,10 +1601,18 @@ spec: value: '{{ (split "." (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain)._1 }}' - name: global.clusterPlatform value: aws - - name: clusterGroup.name - value: acm-provision-on-deploy + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: + - name: global.privateRepo + value: - name: global.experimentalCapabilities value: + - name: clusterGroup.name + value: acm-provision-on-deploy destination: server: https://kubernetes.default.svc namespace: mypattern-acm-provision-on-deploy diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 49c2c1d6..0d479fe4 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -650,9 +650,9 @@ spec: - "/values-4.12-factory.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -665,6 +665,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 4730754c..626b823a 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -993,9 +993,9 @@ spec: - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1008,6 +1008,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1060,9 +1066,9 @@ spec: - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1075,6 +1081,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1118,9 +1130,9 @@ spec: - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1133,6 +1145,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1176,9 +1194,9 @@ spec: - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1191,6 +1209,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1264,9 +1288,9 @@ spec: - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1279,6 +1303,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1322,9 +1352,9 @@ spec: - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1337,6 +1367,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1407,9 +1443,9 @@ spec: - "/values-4.12-datacenter.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1422,6 +1458,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 76083c4e..0b15ec3a 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -878,9 +878,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -893,6 +893,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -936,9 +942,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -951,6 +957,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -994,9 +1006,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1009,6 +1021,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1052,9 +1070,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1067,6 +1085,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1110,9 +1134,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1125,6 +1149,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1168,9 +1198,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1183,6 +1213,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1226,9 +1262,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1241,6 +1277,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1284,9 +1326,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1299,6 +1341,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1360,9 +1408,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1375,6 +1423,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1418,9 +1472,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1433,6 +1487,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1476,9 +1536,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1491,6 +1551,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1543,9 +1609,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1558,6 +1624,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -1610,9 +1682,9 @@ spec: - "/values-4.12-hub.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -1625,6 +1697,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 0ff865e3..9b035135 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -864,9 +864,9 @@ spec: - "/values/4.12.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -879,6 +879,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo @@ -934,9 +940,9 @@ spec: - "/values/4.12/aws.yaml" parameters: - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL + value: https://github.com/pattern-clone/mypattern - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION + value: main - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern @@ -949,6 +955,12 @@ spec: value: "aws" - name: global.hubClusterDomain value: apps.hub.example.com + - name: global.multiSourceSupport + value: + - name: global.multiSourceRepoUrl + value: + - name: global.multiSourceTargetRevision + value: - name: global.localClusterDomain value: apps.region.example.com - name: global.privateRepo From 9a3aa2b289bbb4bf6c77184254e734f58035617e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 29 Jun 2024 19:13:30 +0200 Subject: [PATCH 1225/1263] Release clustergroup v0.8.9 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 5b774788..ab8abbf5 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.8.8 +version: 0.8.9 From 13f3b23d0cd7d7131b5105e472da362c1f54c282 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 29 Jun 2024 20:11:21 +0200 Subject: [PATCH 1226/1263] Fix multisource indent error --- acm/templates/policies/application-policies.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 5f0d132f..d157e473 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -51,12 +51,12 @@ spec: {{ $k }}: {{ printf "%s" $v | quote }} {{- end }} valueFiles: - {{- include "acm.app.policies.multisourcevaluefiles" . | nindent 26 }} + {{- include "acm.app.policies.multisourcevaluefiles" . | nindent 24 }} {{- range $valueFile := .extraValueFiles }} - {{ $valueFile | quote }} {{- end }} parameters: - {{- include "acm.app.policies.helmparameters" $ | nindent 26 }} + {{- include "acm.app.policies.helmparameters" $ | nindent 24 }} - name: clusterGroup.name value: {{ $group.name }} {{- range $k, $v := $.Values.extraParametersNested }} From ac6640c6d83e931ac4b6c95d742cc92eecc3ebdb Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 29 Jun 2024 20:13:02 +0200 Subject: [PATCH 1227/1263] Release clustergroup v0.8.10 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index ab8abbf5..b2d703a2 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.8.9 +version: 0.8.10 From 5f678b534a5e21c47b830194c3e4db2479e190ca Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 29 Jun 2024 20:18:15 +0200 Subject: [PATCH 1228/1263] Release acm v0.0.3 --- acm/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/Chart.yaml b/acm/Chart.yaml index 5ab78a75..b756d549 100644 --- a/acm/Chart.yaml +++ b/acm/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to configure Advanced Cluster Manager for OpenShift. keywords: - pattern name: acm -version: 0.0.2 +version: 0.0.3 From ca9e3a2831df18ff3137fe338cd48c7f4eaf0aac Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 29 Jun 2024 20:46:23 +0200 Subject: [PATCH 1229/1263] Fix missing chart field --- acm/templates/policies/application-policies.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index d157e473..fd7c2a3f 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -43,6 +43,7 @@ spec: ref: patternref - repoURL: {{ $.Values.global.multiSourceRepoUrl }} targetRevision: {{ $.Values.global.multiSourceTargetRevision }} + chart: clustergroup helm: ignoreMissingValueFiles: true values: | From 5bdeff5e33c49fba525e8fc21439b29bb8b26de8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 9 Jul 2024 17:16:31 +0200 Subject: [PATCH 1230/1263] Release acm v0.0.4 --- acm/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/Chart.yaml b/acm/Chart.yaml index b756d549..327e0ba5 100644 --- a/acm/Chart.yaml +++ b/acm/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to configure Advanced Cluster Manager for OpenShift. keywords: - pattern name: acm -version: 0.0.3 +version: 0.0.4 From bf0c8aac8a2d3b1e0437cd76eb57b940b49447d6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 9 Jul 2024 18:04:18 +0200 Subject: [PATCH 1231/1263] Update chart versions --- acm/Chart.yaml | 2 +- golang-external-secrets/Chart.yaml | 2 +- hashicorp-vault/Chart.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/acm/Chart.yaml b/acm/Chart.yaml index 327e0ba5..31fa54ea 100644 --- a/acm/Chart.yaml +++ b/acm/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to configure Advanced Cluster Manager for OpenShift. keywords: - pattern name: acm -version: 0.0.4 +version: 0.1.0 diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 2c214633..b304d092 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -3,7 +3,7 @@ description: A Helm chart to configure the golang-based external-secrets. keywords: - pattern name: golang-external-secrets -version: 0.0.4 +version: 0.1.0 dependencies: - name: external-secrets version: "0.9.19" diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index 0fcbcb48..ef3c22d2 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -3,7 +3,7 @@ description: A Helm chart to configure Hashicorp's vault. keywords: - pattern name: hashicorp-vault -version: 0.0.2 +version: 0.1.0 dependencies: - name: vault version: "0.28.0" From fc675e51643b2c9ad12e86654c305ced7939f881 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 15 Jul 2024 10:17:31 -0600 Subject: [PATCH 1232/1263] Update vault-helm to v0.28.1 and vault to 1.17.2 --- hashicorp-vault/Chart.yaml | 2 +- hashicorp-vault/charts/vault-0.28.0.tgz | Bin 49079 -> 0 bytes hashicorp-vault/charts/vault-0.28.1.tgz | Bin 0 -> 49807 bytes hashicorp-vault/values.yaml | 2 +- ...ault-industrial-edge-factory.expected.yaml | 20 +++++++++--------- ...rp-vault-industrial-edge-hub.expected.yaml | 20 +++++++++--------- ...-vault-medical-diagnosis-hub.expected.yaml | 20 +++++++++--------- tests/hashicorp-vault-naked.expected.yaml | 20 +++++++++--------- tests/hashicorp-vault-normal.expected.yaml | 20 +++++++++--------- 9 files changed, 52 insertions(+), 52 deletions(-) delete mode 100644 hashicorp-vault/charts/vault-0.28.0.tgz create mode 100644 hashicorp-vault/charts/vault-0.28.1.tgz diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index ef3c22d2..ede5950f 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -6,5 +6,5 @@ name: hashicorp-vault version: 0.1.0 dependencies: - name: vault - version: "0.28.0" + version: "0.28.1" repository: "https://helm.releases.hashicorp.com" diff --git a/hashicorp-vault/charts/vault-0.28.0.tgz b/hashicorp-vault/charts/vault-0.28.0.tgz deleted file mode 100644 index 0e02f376d504c1c78df62986fa658fc8a73e796c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49079 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

      U@r}nz0LDB{;n` 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 z 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 z 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 Date: Mon, 15 Jul 2024 15:02:36 -0600 Subject: [PATCH 1234/1263] Release new hashicorp-vault and golang-external-secrets charts --- golang-external-secrets/Chart.yaml | 2 +- hashicorp-vault/Chart.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 802979b4..0bcc381d 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -3,7 +3,7 @@ description: A Helm chart to configure the golang-based external-secrets. keywords: - pattern name: golang-external-secrets -version: 0.1.0 +version: 0.1.1 dependencies: - name: external-secrets version: "0.9.20" diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index ede5950f..87d3470d 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -3,7 +3,7 @@ description: A Helm chart to configure Hashicorp's vault. keywords: - pattern name: hashicorp-vault -version: 0.1.0 +version: 0.1.1 dependencies: - name: vault version: "0.28.1" From 0983d0d872d29ddf35eeae5bc6e61d1896746a95 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 19 Jul 2024 10:54:41 -0500 Subject: [PATCH 1235/1263] Add Ansible playbook --- scripts/write-token-kubeconfig.yml | 90 ++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100755 scripts/write-token-kubeconfig.yml diff --git a/scripts/write-token-kubeconfig.yml b/scripts/write-token-kubeconfig.yml new file mode 100755 index 00000000..a2edd83e --- /dev/null +++ b/scripts/write-token-kubeconfig.yml @@ -0,0 +1,90 @@ +#!env ansible-playbook +--- +- name: Test k8s authentication methods + hosts: localhost + connection: local + gather_facts: false + become: false + vars: + kubeconfig_file: '~/.kube/config' + k8s_host: '{{ lookup("env", "K8S_AUTH_HOST") }}' + k8s_validate_certs: '{{ lookup("env", "K8S_AUTH_VERIFY_SSL") | default(false) | bool }}' + k8s_username: '{{ lookup("env", "K8S_AUTH_USERNAME") | default("kubeconfig") }}' + k8s_password: '{{ lookup("env", "K8S_AUTH_PASSWORD") | default(omit) }}' + k8s_api_key: '{{ lookup("env", "K8S_AUTH_TOKEN") | default(omit) }}' + k8s_ca_cert_file: '{{ lookup("env", "K8S_AUTH_SSL_CA_CERT") | default(omit) }}' + tasks: + - name: Check for pre-existing kubeconfig + ansible.builtin.stat: + path: '{{ kubeconfig_file }}' + register: kubeconfig_stat + + - name: Exit if kubeconfig found + ansible.builtin.fail: + msg: '{{ kubeconfig_file }} already exists! Exiting' + when: kubeconfig_stat.stat.exists + + - name: Get namespaces to test parameters + kubernetes.core.k8s_info: + host: '{{ k8s_host }}' + validate_certs: '{{ k8s_validate_certs }}' + username: '{{ k8s_username }}' + api_key: '{{ k8s_api_key }}' + ca_cert: '{{ k8s_ca_cert_file | default(omit) }}' + kind: namespace + when: k8s_api_key + + - name: Login explicitly + when: not k8s_api_key + block: + - name: Login explicitly to get token + kubernetes.core.k8s_auth: + host: '{{ k8s_host }}' + validate_certs: '{{ k8s_validate_certs }}' + username: '{{ k8s_username }}' + password: '{{ k8s_password }}' + ca_cert: '{{ k8s_ca_cert_file | default(omit) }}' + register: auth + + - name: Set api_key + ansible.builtin.set_fact: + k8s_api_key: '{{ auth.openshift_auth.api_key }}' + + - name: Update username if needed + ansible.builtin.set_fact: + config_k8s_username: 'kube:admin' + when: k8s_username == 'kubeadmin' + + - name: Determine clustername + ansible.builtin.set_fact: + config_k8s_clustername: "{{ k8s_host | regex_replace('https://', '') | regex_replace('\\.', '-') }}" + + - name: Write config file + ansible.builtin.copy: + content: |- + apiVersion: v1 + clusters: + - cluster: + {% if k8s_validate_certs is false %} + insecure-skip-tls-verify: true + {% endif %} + {% if k8s_ca_cert_file -%} + certificate-authority-data: {{ lookup("file", k8s_ca_cert_file) | b64encode }} + {% endif %} + server: {{ k8s_host }} + name: {{ config_k8s_clustername }} + contexts: + - context: + cluster: {{ config_k8s_clustername }} + namespace: default + user: {{ config_k8s_username | default(k8s_username) }}/{{ config_k8s_clustername }} + name: default/{{ config_k8s_clustername }}/{{ config_k8s_username | default(k8s_username) }} + current-context: default/{{ config_k8s_clustername }}/{{ config_k8s_username | default(k8s_username) }} + kind: Config + preferences: {} + users: + - name: {{ config_k8s_username | default(k8s_username) }}/{{ config_k8s_clustername }} + user: + token: {{ k8s_api_key }} + dest: '{{ kubeconfig_file }}' + mode: '0640' From 4ce4d806702d1853e1e4d117ef00fab4cd69a270 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 19 Jul 2024 11:22:08 -0500 Subject: [PATCH 1236/1263] Put the playbook in a more normal location for us --- Makefile | 3 +++ .../write-token-kubeconfig.yml | 1 - scripts/pattern-util.sh | 6 ++++++ scripts/write-token-kubeconfig.sh | 19 +++++++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) rename {scripts => ansible/playbooks/write-token-kubeconfig}/write-token-kubeconfig.yml (99%) mode change 100755 => 100644 create mode 100755 scripts/write-token-kubeconfig.sh diff --git a/Makefile b/Makefile index f9b00e57..e0f0c11b 100644 --- a/Makefile +++ b/Makefile @@ -119,6 +119,9 @@ load-iib: ## CI target to install Index Image Bundles exit 1; \ fi +.PHONY: token-kubeconfig +token-kubeconfig: ## Create a local ~/.kube/config with password (not usually needed) + common/scripts/write-token-kubeconfig.sh ##@ Validation Tasks diff --git a/scripts/write-token-kubeconfig.yml b/ansible/playbooks/write-token-kubeconfig/write-token-kubeconfig.yml old mode 100755 new mode 100644 similarity index 99% rename from scripts/write-token-kubeconfig.yml rename to ansible/playbooks/write-token-kubeconfig/write-token-kubeconfig.yml index a2edd83e..e1ab10e8 --- a/scripts/write-token-kubeconfig.yml +++ b/ansible/playbooks/write-token-kubeconfig/write-token-kubeconfig.yml @@ -1,4 +1,3 @@ -#!env ansible-playbook --- - name: Test k8s authentication methods hosts: localhost diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index f7be58c2..8ff0c365 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -67,6 +67,12 @@ podman run -it --rm --pull=newer \ -e EXTRA_HELM_OPTS \ -e EXTRA_PLAYBOOK_OPTS \ -e KUBECONFIG \ + -e K8S_AUTH_HOST \ + -e K8S_AUTH_VERIFY_SSL \ + -e K8S_AUTH_SSL_CA_CERT \ + -e K8S_AUTH_USERNAME \ + -e K8S_AUTH_PASSWORD \ + -e K8S_AUTH_TOKEN \ -v "${PKI_HOST_MOUNT}":/etc/pki:ro \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ diff --git a/scripts/write-token-kubeconfig.sh b/scripts/write-token-kubeconfig.sh new file mode 100755 index 00000000..f9b0c8a0 --- /dev/null +++ b/scripts/write-token-kubeconfig.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -eu + +get_abs_filename() { + # $1 : relative filename + echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" +} + +SCRIPT=$(get_abs_filename "$0") +SCRIPTPATH=$(dirname "${SCRIPT}") +COMMONPATH=$(dirname "${SCRIPTPATH}") +PATTERNPATH=$(dirname "${COMMONPATH}") +ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" +PLAYBOOKPATH="${ANSIBLEPATH}/playbooks" +export ANSIBLE_CONFIG="${ANSIBLEPATH}/ansible.cfg" + +PATTERN_NAME=${1:-$(basename "`pwd`")} + +ansible-playbook -e pattern_name="${PATTERN_NAME}" -e pattern_dir="${PATTERNPATH}" "${PLAYBOOKPATH}/write-token-kubeconfig/write-token-kubeconfig.yml" From 41bc42df584b2ff0c6c3f6f1b6f6599e06e8c23a Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 19 Jul 2024 12:58:43 -0500 Subject: [PATCH 1237/1263] Exclude new playbook --- .ansible-lint | 1 + 1 file changed, 1 insertion(+) diff --git a/.ansible-lint b/.ansible-lint index aaffc6b5..0522976e 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -16,5 +16,6 @@ exclude_paths: - ./ansible/playbooks/iib-ci/iib-ci.yaml - ./ansible/playbooks/k8s_secrets/k8s_secrets.yml - ./ansible/playbooks/process_secrets/process_secrets.yml + - ./ansible/playbooks/write-token-kubeconfig/write-token-kubeconfig.yml - ./ansible/playbooks/process_secrets/display_secrets_info.yml - ./ansible/roles/vault_utils/tests/test.yml From 7bcfbd33e6617ca545baf89fd63ab256dac1bdd1 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 26 Jul 2024 12:40:02 -0500 Subject: [PATCH 1238/1263] Allow for choice in where file is written --- .../write-token-kubeconfig/write-token-kubeconfig.yml | 4 ++++ scripts/write-token-kubeconfig.sh | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ansible/playbooks/write-token-kubeconfig/write-token-kubeconfig.yml b/ansible/playbooks/write-token-kubeconfig/write-token-kubeconfig.yml index e1ab10e8..dcb23111 100644 --- a/ansible/playbooks/write-token-kubeconfig/write-token-kubeconfig.yml +++ b/ansible/playbooks/write-token-kubeconfig/write-token-kubeconfig.yml @@ -87,3 +87,7 @@ token: {{ k8s_api_key }} dest: '{{ kubeconfig_file }}' mode: '0640' + + - name: Notify user + ansible.builtin.debug: + msg: "Wrote {{ kubeconfig_file }}" diff --git a/scripts/write-token-kubeconfig.sh b/scripts/write-token-kubeconfig.sh index f9b0c8a0..12a2bb80 100755 --- a/scripts/write-token-kubeconfig.sh +++ b/scripts/write-token-kubeconfig.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash set -eu +OUTPUTFILE=${1:-"~/.kube/config"} + get_abs_filename() { # $1 : relative filename echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" @@ -14,6 +16,4 @@ ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" PLAYBOOKPATH="${ANSIBLEPATH}/playbooks" export ANSIBLE_CONFIG="${ANSIBLEPATH}/ansible.cfg" -PATTERN_NAME=${1:-$(basename "`pwd`")} - -ansible-playbook -e pattern_name="${PATTERN_NAME}" -e pattern_dir="${PATTERNPATH}" "${PLAYBOOKPATH}/write-token-kubeconfig/write-token-kubeconfig.yml" +ansible-playbook -e pattern_dir="${PATTERNPATH}" -e kubeconfig_file="${OUTPUTFILE}" "${PLAYBOOKPATH}/write-token-kubeconfig/write-token-kubeconfig.yml" From 98d43123834d24fafb670e4127244cddc07763d5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 6 Aug 2024 10:03:38 +0200 Subject: [PATCH 1239/1263] Fix pki bind mount when using podman machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we test for the existance of local TLS folder and bind mount them in containers. This does not work correctly when using podman machine, because a the test is running on the host and so we might bind mount the wrong folder. For example: On Mac OSX /etc/pki does not exist on the folder and so we bind mount /etc/ssl even though /etc/pki does exist in the podman machine VM Co-Authored-By: Ákos Erős --- scripts/pattern-util.sh | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 8ff0c365..508e1531 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -49,13 +49,20 @@ if [ -n "$KUBECONFIG" ]; then fi fi -# Use /etc/pki by default and try a couple of fallbacks if it does not exist -if [ -d /etc/pki ]; then - PKI_HOST_MOUNT="/etc/pki" -elif [ -d /etc/ssl ]; then - PKI_HOST_MOUNT="/etc/ssl" +# Detect if we use podman machine. If we do not then we bind mount local host ssl folders +# if we are using podman machine then we do not bind mount anything (for now!) +REMOTE_PODMAN=$(podman system connection list -q | wc -l) +if [ $REMOTE_PODMAN -eq 0 ]; then # If we are not using podman machine we check the hosts folders + # Use /etc/pki by default and try a couple of fallbacks if it does not exist + if [ -d /etc/pki ]; then + PKI_HOST_MOUNT_ARGS="-v /etc/pki:/etc/pki:ro" + elif [ -d /etc/ssl ]; then + PKI_HOST_MOUNT_ARGS="-v /etc/ssl:/etc/ssl:ro" + else + PKI_HOST_MOUNT_ARGS="-v /usr/share/ca-certificates:/usr/share/ca-certificates:ro" + fi else - PKI_HOST_MOUNT="/usr/share/ca-certificates" + PKI_HOST_MOUNT_ARGS="" fi # Copy Kubeconfig from current environment. The utilities will pick up ~/.kube/config if set so it's not mandatory @@ -67,13 +74,7 @@ podman run -it --rm --pull=newer \ -e EXTRA_HELM_OPTS \ -e EXTRA_PLAYBOOK_OPTS \ -e KUBECONFIG \ - -e K8S_AUTH_HOST \ - -e K8S_AUTH_VERIFY_SSL \ - -e K8S_AUTH_SSL_CA_CERT \ - -e K8S_AUTH_USERNAME \ - -e K8S_AUTH_PASSWORD \ - -e K8S_AUTH_TOKEN \ - -v "${PKI_HOST_MOUNT}":/etc/pki:ro \ + ${PKI_HOST_MOUNT_ARGS} \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ ${PODMAN_ARGS} \ From ec340b1ae64004c9f92c14bb9c30bdc408628ca1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 8 Aug 2024 11:55:06 +0200 Subject: [PATCH 1240/1263] Allow originRepo to be set via make install and main.git parameters We introduce the `main.git.repoUpstreamURL` parameter. This can be used to set `originRepo`. When `originRepo` is set, an in-cluster gitea will automatically be spawned. In this case `originRepo` will point to the upstream repository and `targetRepo` will point to the internal in-cluster gitea mirror. --- clustergroup/values.schema.json | 4 ++++ operator-install/templates/pattern.yaml | 3 +++ operator-install/values.yaml | 5 +++++ tests/operator-install-industrial-edge-factory.expected.yaml | 2 +- tests/operator-install-industrial-edge-hub.expected.yaml | 2 +- tests/operator-install-medical-diagnosis-hub.expected.yaml | 2 +- tests/operator-install-naked.expected.yaml | 2 +- tests/operator-install-normal.expected.yaml | 2 +- 8 files changed, 17 insertions(+), 5 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 384f79ef..2f43a6ff 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -81,6 +81,10 @@ "revision" ], "properties": { + "repoUpstreamURL": { + "type": "string", + "description": "Upstream URL of the pattern's git repository. When set an in-cluster gitea instance gets spawned and repoURL is ignored" + }, "repoURL": { "type": "string", "description": "URL of the pattern's git repository" diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index 728726f0..c33d7e8a 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -6,6 +6,9 @@ metadata: spec: clusterGroupName: {{ .Values.main.clusterGroupName }} gitSpec: +{{- if .Values.main.git.repoUpstreamURL }} + originRepo: {{ .Values.main.git.repoUpstreamURL }} +{{- end }} {{/* if .Values.main.git.repoUpstreamURL */}} targetRepo: {{ .Values.main.git.repoURL }} targetRevision: {{ .Values.main.git.revision }} {{- if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace }} diff --git a/operator-install/values.yaml b/operator-install/values.yaml index a46cca2e..6c1b124c 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -3,6 +3,11 @@ global: main: git: + # Uncommenting this will set the `originRepo` with the below value + # when `originRepo` is set, an in-cluster gitea will automatically be spawned. + # In this case `originRepo` will point to the upstream repository and `targetRepo` + # will point to the internal in-cluster gitea mirror + # repoUpstreamURL: https://github.com/validatedpatterns/multicloud-gitops repoURL: https://github.com/pattern-clone/mypattern revision: main diff --git a/tests/operator-install-industrial-edge-factory.expected.yaml b/tests/operator-install-industrial-edge-factory.expected.yaml index 5e4aa02e..6400a5a9 100644 --- a/tests/operator-install-industrial-edge-factory.expected.yaml +++ b/tests/operator-install-industrial-edge-factory.expected.yaml @@ -22,7 +22,7 @@ metadata: namespace: openshift-operators spec: clusterGroupName: example - gitSpec: + gitSpec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main multiSourceConfig: diff --git a/tests/operator-install-industrial-edge-hub.expected.yaml b/tests/operator-install-industrial-edge-hub.expected.yaml index 5e4aa02e..6400a5a9 100644 --- a/tests/operator-install-industrial-edge-hub.expected.yaml +++ b/tests/operator-install-industrial-edge-hub.expected.yaml @@ -22,7 +22,7 @@ metadata: namespace: openshift-operators spec: clusterGroupName: example - gitSpec: + gitSpec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main multiSourceConfig: diff --git a/tests/operator-install-medical-diagnosis-hub.expected.yaml b/tests/operator-install-medical-diagnosis-hub.expected.yaml index 5e4aa02e..6400a5a9 100644 --- a/tests/operator-install-medical-diagnosis-hub.expected.yaml +++ b/tests/operator-install-medical-diagnosis-hub.expected.yaml @@ -22,7 +22,7 @@ metadata: namespace: openshift-operators spec: clusterGroupName: example - gitSpec: + gitSpec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main multiSourceConfig: diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index 3d58b474..b6647318 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -22,7 +22,7 @@ metadata: namespace: openshift-operators spec: clusterGroupName: default - gitSpec: + gitSpec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main multiSourceConfig: diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index 5e4aa02e..6400a5a9 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -22,7 +22,7 @@ metadata: namespace: openshift-operators spec: clusterGroupName: example - gitSpec: + gitSpec: targetRepo: https://github.com/pattern-clone/mypattern targetRevision: main multiSourceConfig: From 73313cbeb8613f792ddc41387f99791851fb5990 Mon Sep 17 00:00:00 2001 From: day0hero Date: Thu, 8 Aug 2024 12:19:56 -0500 Subject: [PATCH 1241/1263] resolves #21 rag-llm-gitops --- scripts/pattern-util.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 508e1531..70357f67 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -38,7 +38,8 @@ else MYNAME=$(id -n -u) MYUID=$(id -u) MYGID=$(id -g) - PODMAN_ARGS="--passwd-entry ${MYNAME}:x:${MYUID}:${MYGID}:/pattern-home:/bin/bash --user ${MYUID}:${MYGID} --userns keep-id:uid=${MYUID},gid=${MYGID}" + PODMAN_ARGS="--passwd-entry ${MYNAME}:x:${MYUID}:${MYGID}::/pattern-home:/bin/bash --user ${MYUID}:${MYGID} --userns keep-id:uid=${MYUID},gid=${MYGID}" + fi if [ -n "$KUBECONFIG" ]; then From 99bc297ef7ea2c2fa5e4a16901265ab5899b0447 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 12 Aug 2024 08:55:13 +0200 Subject: [PATCH 1242/1263] Fix vars that were erroneously dropped In 98d43123834d24fafb670e4127244cddc07763d5 (Fix pki bind mount when using podman machine) we erroneously dropped some K8S_* variables. Reported-by: Andrew Beekhof --- scripts/pattern-util.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index 70357f67..c434ca83 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -75,6 +75,12 @@ podman run -it --rm --pull=newer \ -e EXTRA_HELM_OPTS \ -e EXTRA_PLAYBOOK_OPTS \ -e KUBECONFIG \ + -e K8S_AUTH_HOST \ + -e K8S_AUTH_VERIFY_SSL \ + -e K8S_AUTH_SSL_CA_CERT \ + -e K8S_AUTH_USERNAME \ + -e K8S_AUTH_PASSWORD \ + -e K8S_AUTH_TOKEN \ ${PKI_HOST_MOUNT_ARGS} \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ From 58b6958471e8e56356732f1db2de0a8e2290c670 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 13 Aug 2024 16:34:11 +0200 Subject: [PATCH 1243/1263] Allow more flexibility with multiSourceConfig schema We specifically also add helmRepoUrl as it make it more discoverable, but we stay flexible in what we accept so that things can be extended without having to be in lockstep with the operator. --- clustergroup/values.schema.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 2f43a6ff..38eaeb57 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -107,11 +107,15 @@ }, "multiSourceConfig": { "type": "object", - "additionalProperties": false, + "additionalProperties": true, "properties": { "enabled": { "type": "boolean", - "description": "Enable the experimental support for multi source" + "description": "Enable the experimental support for multi source for the clustergroup chart" + }, + "helmRepoUrl": { + "type": "string", + "description": "The helm repo URL for the clustergroup chart" } } }, From 3c28c4bfce3f02bd19782911e23f26dc352535b5 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 20 Aug 2024 09:23:18 +0200 Subject: [PATCH 1244/1263] Inject VALUES_SECRET env var Since it can be used to point to an alternative file, let's inject it into the container --- scripts/pattern-util.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index c434ca83..8fa4a26f 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -74,6 +74,7 @@ podman run -it --rm --pull=newer \ --security-opt label=disable \ -e EXTRA_HELM_OPTS \ -e EXTRA_PLAYBOOK_OPTS \ + -e VALUES_SECRET \ -e KUBECONFIG \ -e K8S_AUTH_HOST \ -e K8S_AUTH_VERIFY_SSL \ From 7ed4d1e0f0adaaf82c0ed14aa80da091b245119e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 21 Aug 2024 14:36:34 +0200 Subject: [PATCH 1245/1263] Add helmRepoUrl variable --- operator-install/templates/pattern.yaml | 3 +++ operator-install/values.yaml | 1 + 2 files changed, 4 insertions(+) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index c33d7e8a..3eda9482 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -17,6 +17,9 @@ spec: {{- end }} {{/* if and .Values.main.tokenSecret .Values.main.tokenSecretNamespace */}} multiSourceConfig: enabled: {{ .Values.main.multiSourceConfig.enabled }} +{{- if .Values.main.multiSourceConfig.helmRepoUrl }} + helmRepoUrl: {{ .Values.main.multiSourceConfig.helmRepoUrl }} +{{- end }} {{/* if .Values.main.multiSourceConfig.helmRepoUrl */}} {{- if .Values.main.analyticsUUID }} analyticsUUID: {{ .Values.main.analyticsUUID }} {{- end }} {{/* if .Values.main.analyticsUUID */}} diff --git a/operator-install/values.yaml b/operator-install/values.yaml index 6c1b124c..6a77c086 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -17,6 +17,7 @@ main: multiSourceConfig: enabled: false + # helmRepoUrl: registry.internal.network/helm # String to enable certain experimental capabilities in the operator and the # framework. Not needed unless you know exactly what you're doing. From 9ec9af0aefcf77e923caf5ca3a5eb70558a01403 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 22 Aug 2024 16:10:46 +0200 Subject: [PATCH 1246/1263] Update letsencrypt to v0.1.1 --- letsencrypt/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/Chart.yaml b/letsencrypt/Chart.yaml index 899c790e..d8a9810f 100644 --- a/letsencrypt/Chart.yaml +++ b/letsencrypt/Chart.yaml @@ -7,7 +7,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 +version: 0.1.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to From 5ec42ac2cdf9226019053178429ae13de3e57b15 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 23 Aug 2024 06:39:27 +0200 Subject: [PATCH 1247/1263] Allow overriding gitops source on spokes This is needed on spokes when installing in a disconnected environment --- acm/templates/policies/ocp-gitops-policy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index cdc0a7e1..1d54b78d 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -38,7 +38,7 @@ spec: channel: {{ default "gitops-1.12" .Values.main.gitops.channel }} installPlanApproval: Automatic name: openshift-gitops-operator - source: redhat-operators + source: {{ default "redhat-operators" .Values.main.gitops.operatorSource }} sourceNamespace: openshift-marketplace config: env: From e6511e726e12a920e5fa9792cc1fdaa280379142 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 23 Aug 2024 06:40:44 +0200 Subject: [PATCH 1248/1263] Update acm chart to v0.1.1 --- acm/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/Chart.yaml b/acm/Chart.yaml index 31fa54ea..3a7663b4 100644 --- a/acm/Chart.yaml +++ b/acm/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to configure Advanced Cluster Manager for OpenShift. keywords: - pattern name: acm -version: 0.1.0 +version: 0.1.1 From 1a5cce870e9a95a0e17f8831637666266da7596a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 23 Aug 2024 06:41:02 +0200 Subject: [PATCH 1249/1263] Update clustergroup chart to 0.8.11 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index b2d703a2..18fa4dfb 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.8.10 +version: 0.8.11 From 9ccd486a29de051228ab0c9fd0bde8e465503a20 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 23 Aug 2024 08:14:20 +0200 Subject: [PATCH 1250/1263] Extend the schema for disconnected --- clustergroup/values.schema.json | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 38eaeb57..2fbe3421 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -97,11 +97,29 @@ }, "gitops": { "type": "object", - "additionalProperties": false, + "additionalProperties": true, "properties": { "channel": { "type": "string", "description": "The channel from which to install the gitops operator" + }, + "operatorSource": { + "type": "string", + "description": "The catalog source from which to install the gitops operator" + } + } + }, + "patternsOperator": { + "type": "object", + "additionalProperties": true, + "properties": { + "channel": { + "type": "string", + "description": "The channel from which to install the patterns operator" + }, + "source": { + "type": "string", + "description": "The catalog source from which to install the patterns operator" } } }, From b3cef81938b59b35e7a319fd76ab1b42eb39e5b8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 23 Aug 2024 08:18:56 +0200 Subject: [PATCH 1251/1263] Update clustergroup chart to 0.8.12 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 18fa4dfb..2bacbfc5 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.8.11 +version: 0.8.12 From a8d7f944e30b76ba42592732051c6ea7ed08d27e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 26 Aug 2024 09:15:25 +0200 Subject: [PATCH 1252/1263] Update super-linter to v7 --- .github/workflows/superlinter.yml | 5 ++++- Changes.md | 2 +- Makefile | 5 ++++- ansible/roles/iib_ci/README.md | 2 +- ansible/roles/vault_utils/README.md | 2 +- golang-external-secrets/README.md | 2 +- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index 55acbdb0..8c1e8035 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -21,7 +21,7 @@ jobs: # Run Linter against code base # ################################ - name: Lint Code Base - uses: github/super-linter/slim@v6 + uses: github/super-linter/slim@v7 env: VALIDATE_ALL_CODEBASE: true DEFAULT_BRANCH: main @@ -31,10 +31,13 @@ jobs: VALIDATE_BASH: false VALIDATE_CHECKOV: false VALIDATE_JSCPD: false + VALIDATE_JSON_PRETTIER: false + VALIDATE_MARKDOWN_PRETTIER: false VALIDATE_KUBERNETES_KUBECONFORM: false VALIDATE_PYTHON_PYLINT: false VALIDATE_SHELL_SHFMT: false VALIDATE_YAML: false + VALIDATE_YAML_PRETTIER: false # VALIDATE_DOCKERFILE_HADOLINT: false # VALIDATE_MARKDOWN: false # VALIDATE_NATURAL_LANGUAGE: false diff --git a/Changes.md b/Changes.md index ed7d4bf6..8ade8ad6 100644 --- a/Changes.md +++ b/Changes.md @@ -124,7 +124,7 @@ ## October 3, 2022 * Restore the ability to install a non-default site: `make TARGET_SITE=mysite install` -* Revised tests (new output and filenames, requires adding new result files to git) +* Revised tests (new output and filenames, requires adding new result files to Git) * ACM 2.6 required for ACM-based managed sites * Introduced global.clusterDomain template variable (without the `apps.` prefix) * Removed the ability to send specific charts to another cluster, use hosted argo sites instead diff --git a/Makefile b/Makefile index e0f0c11b..785e5307 100644 --- a/Makefile +++ b/Makefile @@ -238,15 +238,18 @@ super-linter: ## Runs super linter locally -e VALIDATE_CHECKOV=false \ -e VALIDATE_DOCKERFILE_HADOLINT=false \ -e VALIDATE_JSCPD=false \ + -e VALIDATE_JSON_PRETTIER=false \ + -e VALIDATE_MARKDOWN_PRETTIER=false \ -e VALIDATE_KUBERNETES_KUBECONFORM=false \ -e VALIDATE_PYTHON_PYLINT=false \ -e VALIDATE_SHELL_SHFMT=false \ -e VALIDATE_TEKTON=false \ -e VALIDATE_YAML=false \ + -e VALIDATE_YAML_PRETTIER=false \ $(DISABLE_LINTERS) \ -v $(PWD):/tmp/lint:rw,z \ -w /tmp/lint \ - ghcr.io/super-linter/super-linter:slim-v6 + ghcr.io/super-linter/super-linter:slim-v7 .PHONY: ansible-lint ansible-lint: ## run ansible lint on ansible/ folder diff --git a/ansible/roles/iib_ci/README.md b/ansible/roles/iib_ci/README.md index 8c654dbb..de4b4107 100644 --- a/ansible/roles/iib_ci/README.md +++ b/ansible/roles/iib_ci/README.md @@ -1,6 +1,6 @@ # IIB Utilities -A set of ansible plays to fetch an IIB (Image Index Bundle, aka a container created by the operator sdk +A set of ansible plays to fetch an IIB (Image Index Bundle, aka a container created by the operator SDK that contains a bunch of references to operators that can be installed in an OpenShift cluster) Run `ansible-playbook common/ansible/playbooks/iib-ci/lookup.yml` to see which IIBs are available (defaults to diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index 7198752c..6b851f2a 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -55,7 +55,7 @@ By default, the first file that will looked up is The paths can be overridden by setting the environment variable `VALUES_SECRET` to the path of the secret file. -The values secret yaml files can be encrypted with `ansible-vault`. If the role detects they are encrypted, the password to +The values secret YAML files can be encrypted with `ansible-vault`. If the role detects they are encrypted, the password to decrypt them will be prompted when needed. ### Version 1.0 diff --git a/golang-external-secrets/README.md b/golang-external-secrets/README.md index 4316d3a1..b6feb981 100644 --- a/golang-external-secrets/README.md +++ b/golang-external-secrets/README.md @@ -11,7 +11,7 @@ we just override the tag with the version + "-ubi" 3. Run `./update-helm-dependency.sh` 4. Tweak `values.yaml` with the new image versions 5. Run `make test` -6. Commit to git +6. Commit to Git ## PRs From 3fbf2e838a468ed0fca3258098b47fdc9fe3d4e0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 26 Aug 2024 09:23:34 +0200 Subject: [PATCH 1253/1263] Fix action path --- .github/workflows/superlinter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index 8c1e8035..3ab8cf27 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -21,7 +21,7 @@ jobs: # Run Linter against code base # ################################ - name: Lint Code Base - uses: github/super-linter/slim@v7 + uses: github/super-linter/super-linter/slim@v7 env: VALIDATE_ALL_CODEBASE: true DEFAULT_BRANCH: main From 97b958939d113a05b156199d651589e0a37b7c70 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 26 Aug 2024 09:25:44 +0200 Subject: [PATCH 1254/1263] Fix action path v2 --- .github/workflows/superlinter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index 3ab8cf27..03b6fff9 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -21,7 +21,7 @@ jobs: # Run Linter against code base # ################################ - name: Lint Code Base - uses: github/super-linter/super-linter/slim@v7 + uses: super-linter/super-linter/slim@v7 env: VALIDATE_ALL_CODEBASE: true DEFAULT_BRANCH: main From 55beff384848b550aa2e8c1aad2ef9187a253e6b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 26 Aug 2024 08:22:37 +0200 Subject: [PATCH 1255/1263] Upgrade ESO to v0.10.0 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.10.0.tgz | Bin 0 -> 82278 bytes .../charts/external-secrets-0.9.20.tgz | Bin 81759 -> 0 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 237 ++++++++++++++---- ...-secrets-industrial-edge-hub.expected.yaml | 237 ++++++++++++++---- ...ecrets-medical-diagnosis-hub.expected.yaml | 237 ++++++++++++++---- ...olang-external-secrets-naked.expected.yaml | 237 ++++++++++++++---- ...lang-external-secrets-normal.expected.yaml | 237 ++++++++++++++---- 9 files changed, 964 insertions(+), 229 deletions(-) create mode 100644 golang-external-secrets/charts/external-secrets-0.10.0.tgz delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.20.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 0bcc381d..22a56453 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.1.1 dependencies: - name: external-secrets - version: "0.9.20" + version: "0.10.0" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.10.0.tgz b/golang-external-secrets/charts/external-secrets-0.10.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..28d7b4a50628a89cf964745ac0dc68cc2f1fdb53 GIT binary patch literal 82278 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 f42Q+8;fuD z{~Dfq>=>``Cj642$hviT9!@D(F9nC21%pqRyj9%*7V&*Cidi@wQyNm0+WIl2VVGmq zD{{Wh-oriu0Ml?JqH^<`5;6qqpC9=Qujx^E^%?IH=5nOVOupwod-H*^n*j*nrSS9N z8#auR)bmHtyhH!;?tA{}uStjgl`qam9sEcBnO;mg8K6@b-(p60m{adBetJ2Be@}x? z@b7$rzhi*o{pJ5bjh}-A?}xGH&yJwoSrTJ3C(n14zjaG$g88H}SS-(-;x<3$G$R1mI-(u+p9$9zww?55et;8~fLg44Xi$oDiF!)J}>Gr9~I{ zrGFarG6|qPEvK}&jPDJSp}0lNff|Vx2@rN)TmxY;8>t~i!RYA!|34k3Wof(Nbn88PC1;20xx@dX1)W_P4<`bR;!y)&O z#tF{mF*ASq-hiYm;dMjzs7<6?dm3+mzjPvRPfI>-(#WD@qtsiF9dUU`VHaxXD@EM@ z7k4ayn5GyJu*c0MPzB=T=9`wzG=qhL*-Pu+A1DKGE3`1>Qy&0r>o;M!dT+F5K zA#AM9!U?RTABvJ15q34JLQ(>GfXXk!iAPvqR%L^gdUuR zL%*$=S)qewT3KCAx+O_)vp!;UA&T_jiuB}Onl-{jdyZ&N$X!>Bc#V>RgkgcM9j@Cc zZ4itGp{ntmk%hh1fqc85RKBN2rNXQUC9~K#NrFA4o^n-{N#D7gR=8?bPa9A0%}(6Y zCQge-WQOMx^Qcx7$pMt^i7)+o;($IAG8ASTOhyYH*a#|OY(E!(1JaR`yr*?TdQ52* zVdaV{A{&(6F0ZBC(&G`-sUj!!nQ)cKpB&t4UNjF*>%>GZJ37i)L(r31<)LVM(A*B$ zfDSXe1Fv4Z88vr%XRljbGov5$wJRjlv?NWbumYGPYGwDmF)d^K{CQnvTlZa&esq zf#voRGk_px?nOzzyj;dGO>45wglnE7M_f_JMwJi7XE5e8b7)Ohq;2#h$Dmw~xW=wu zcaS4Md41EW8mC+n*Qj-J>7AC*JQc;mZmfNB>7N>Fpl(J7rD>rYJ{N*aiPY=15VcaK zbph-8@~sPit7TmmK$gtC?ve$~m94r=3r28k9ZobwB~g12w9i2B)E1yvUILzC0ytbW z;QH(!1?~wL3{oOj=Yb5Tt&ipK01mXj&;$!eqPvf|PJ(96L@q40#f(S76Rkc-5sp~y zXx2pQm;iTrWe%wtX^O+bm?OP!fFjvfLyM$#G=ekSc2qPjU1m`#2;`1r1(dT-om2Ho zJGn^V&yEIGMxoD9G0~OpMVQY+ttXOAQ!pmkiN^xLWFEnFoHXbb9QV;LV-R+NS=6Ri z_w0ffJ^wkuMOrPu@`g^rv+NSjWboic=`; zY#%n_I64bauzMt_-=V%uuNw;*a19mr*lSq9S7DIgya9*Oy{}U$>8#5>w?p@ z)Y);hjj6cFt3nLr%?+w=+zOofDx7@J>#K1b%guAgkdP3I5}8Y;N!ebE6HP_Ja#Ib} z^Tc9Z&RQpAU1hJ!KsB=22{4(}&hAo?;(Oc8b;Woj!GKe`0aRo7Zs*Q0jbDU|A%S>{vzEqgy3Jh~@ z(_Dc8jTdiVwR79;N-TGt9+^d+3vEN4grFJga0~$a@JKyQuh{N zPOG?4{?%0e-Mnf{S8H9fs%oQg+Gw0^P~&tRvbJf(G3cVxlH=51Z)En3%wAV!|JEWq zRlHpi`&|*)H!}A|=3ZLnwq%lz!)eMzvX7ZdDEFe2A5^B+@1rE0J0ZO06x{)WyTlvL zmsetclARX7NdBf(#MK60XeZs_8=F8}}l literal 0 HcmV?d00001 diff --git a/golang-external-secrets/charts/external-secrets-0.9.20.tgz b/golang-external-secrets/charts/external-secrets-0.9.20.tgz deleted file mode 100644 index f73d3475933bb28ccb46503d3ab2ed4875bb8bc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81759 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

      U@r}nz0LDB{;n` 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 z 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 z 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 Date: Mon, 26 Aug 2024 09:58:14 +0200 Subject: [PATCH 1256/1263] Upgrade vault to 1.17.3 --- hashicorp-vault/values.yaml | 2 +- tests/hashicorp-vault-industrial-edge-factory.expected.yaml | 4 ++-- tests/hashicorp-vault-industrial-edge-hub.expected.yaml | 4 ++-- tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml | 4 ++-- tests/hashicorp-vault-naked.expected.yaml | 4 ++-- tests/hashicorp-vault-normal.expected.yaml | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index 6029a2f0..3b16a951 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -48,4 +48,4 @@ vault: termination: "reencrypt" image: repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.17.2-ubi" + tag: "1.17.3-ubi" diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index a17a6974..fb8f1c8e 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.3-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.3-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index a17a6974..fb8f1c8e 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.3-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.3-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index a17a6974..fb8f1c8e 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.3-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.3-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index c3950b6d..49ea96cb 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.3-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.3-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index a17a6974..fb8f1c8e 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.3-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.3-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR From c7105bc3b709b695bae263867f37b9c97833dd06 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 26 Aug 2024 11:31:38 +0200 Subject: [PATCH 1257/1263] Update hashicorp-vault to 0.1.2 --- hashicorp-vault/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index 87d3470d..09d48391 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -3,7 +3,7 @@ description: A Helm chart to configure Hashicorp's vault. keywords: - pattern name: hashicorp-vault -version: 0.1.1 +version: 0.1.2 dependencies: - name: vault version: "0.28.1" From 86ad3420687b372e9ed01efdcf5cd313d5376a85 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 26 Aug 2024 11:32:00 +0200 Subject: [PATCH 1258/1263] Update golang-external-secrets to 0.1.2 --- golang-external-secrets/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 22a56453..3b3a0133 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -3,7 +3,7 @@ description: A Helm chart to configure the golang-based external-secrets. keywords: - pattern name: golang-external-secrets -version: 0.1.1 +version: 0.1.2 dependencies: - name: external-secrets version: "0.10.0" From fc3fb1f70ce326b72e3ea365403143ce43a52342 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 27 Aug 2024 11:22:08 +0200 Subject: [PATCH 1259/1263] Switch to gitops-1.13 It is supported all the way back to OCP 4.12 Tested on sno hub + spoke successfully --- acm/templates/policies/ocp-gitops-policy.yaml | 2 +- acm/values.yaml | 2 +- operator-install/values.yaml | 2 +- reference-output.yaml | 2 +- tests/acm-industrial-edge-factory.expected.yaml | 2 +- tests/acm-industrial-edge-hub.expected.yaml | 2 +- tests/acm-medical-diagnosis-hub.expected.yaml | 2 +- tests/acm-naked.expected.yaml | 2 +- tests/acm-normal.expected.yaml | 2 +- tests/operator-install-industrial-edge-factory.expected.yaml | 2 +- tests/operator-install-industrial-edge-hub.expected.yaml | 2 +- tests/operator-install-medical-diagnosis-hub.expected.yaml | 2 +- tests/operator-install-naked.expected.yaml | 2 +- tests/operator-install-normal.expected.yaml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/acm/templates/policies/ocp-gitops-policy.yaml b/acm/templates/policies/ocp-gitops-policy.yaml index 1d54b78d..753e4447 100644 --- a/acm/templates/policies/ocp-gitops-policy.yaml +++ b/acm/templates/policies/ocp-gitops-policy.yaml @@ -35,7 +35,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: {{ default "gitops-1.12" .Values.main.gitops.channel }} + channel: {{ default "gitops-1.13" .Values.main.gitops.channel }} installPlanApproval: Automatic name: openshift-gitops-operator source: {{ default "redhat-operators" .Values.main.gitops.operatorSource }} diff --git a/acm/values.yaml b/acm/values.yaml index ec3b5746..ff5777da 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -1,6 +1,6 @@ main: gitops: - channel: "gitops-1.12" + channel: "gitops-1.13" global: extraValueFiles: [] diff --git a/operator-install/values.yaml b/operator-install/values.yaml index 6a77c086..62c9943a 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -12,7 +12,7 @@ main: revision: main gitops: - channel: "gitops-1.12" + channel: "gitops-1.13" operatorSource: redhat-operators multiSourceConfig: diff --git a/reference-output.yaml b/reference-output.yaml index 54f4052a..1eef9745 100644 --- a/reference-output.yaml +++ b/reference-output.yaml @@ -112,7 +112,7 @@ metadata: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: "" spec: - channel: gitops-1.12 + channel: gitops-1.13 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-industrial-edge-factory.expected.yaml b/tests/acm-industrial-edge-factory.expected.yaml index 39238f91..94c8254f 100644 --- a/tests/acm-industrial-edge-factory.expected.yaml +++ b/tests/acm-industrial-edge-factory.expected.yaml @@ -139,7 +139,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.12 + channel: gitops-1.13 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 8b18a4da..00cf4e4d 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -509,7 +509,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.12 + channel: gitops-1.13 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index dffb9eb6..5fea58d0 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -500,7 +500,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.12 + channel: gitops-1.13 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-naked.expected.yaml b/tests/acm-naked.expected.yaml index 39238f91..94c8254f 100644 --- a/tests/acm-naked.expected.yaml +++ b/tests/acm-naked.expected.yaml @@ -139,7 +139,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.12 + channel: gitops-1.13 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 1e2b1573..6823a01b 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -1670,7 +1670,7 @@ spec: labels: operators.coreos.com/openshift-gitops-operator.openshift-operators: '' spec: - channel: gitops-1.12 + channel: gitops-1.13 installPlanApproval: Automatic name: openshift-gitops-operator source: redhat-operators diff --git a/tests/operator-install-industrial-edge-factory.expected.yaml b/tests/operator-install-industrial-edge-factory.expected.yaml index 6400a5a9..2bb7854e 100644 --- a/tests/operator-install-industrial-edge-factory.expected.yaml +++ b/tests/operator-install-industrial-edge-factory.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.12 + gitops.channel: gitops-1.13 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan diff --git a/tests/operator-install-industrial-edge-hub.expected.yaml b/tests/operator-install-industrial-edge-hub.expected.yaml index 6400a5a9..2bb7854e 100644 --- a/tests/operator-install-industrial-edge-hub.expected.yaml +++ b/tests/operator-install-industrial-edge-hub.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.12 + gitops.channel: gitops-1.13 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan diff --git a/tests/operator-install-medical-diagnosis-hub.expected.yaml b/tests/operator-install-medical-diagnosis-hub.expected.yaml index 6400a5a9..2bb7854e 100644 --- a/tests/operator-install-medical-diagnosis-hub.expected.yaml +++ b/tests/operator-install-medical-diagnosis-hub.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.12 + gitops.channel: gitops-1.13 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index b6647318..d5f75c80 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.12 + gitops.channel: gitops-1.13 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index 6400a5a9..2bb7854e 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -7,7 +7,7 @@ metadata: namespace: openshift-operators data: gitops.catalogSource: redhat-operators - gitops.channel: gitops-1.12 + gitops.channel: gitops-1.13 # gitops.sourceNamespace: GitOpsDefaultCatalogSourceNamespace # gitops.installApprovalPlan: GitOpsDefaultApprovalPlan From 051cd8c3c60fcecb81ece3f79b760e946dddedf1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 29 Aug 2024 17:43:31 +0200 Subject: [PATCH 1260/1263] Add a pushsecrets policy and vault path for ESO syncing See the README for more details, but TLDR: you can use `secret/pushsecrets` to push secrets from any node to the vault. This secret can then be retrieved from either a different namespace or a different cluster node. Tested this with a pushsecret as follows: ``` apiVersion: external-secrets.io/v1alpha1 kind: PushSecret metadata: name: pushsecret namespace: hello-world spec: data: - conversionStrategy: None match: remoteRef: property: baz remoteKey: pushsecrets/testme secretKey: bar deletionPolicy: Delete refreshInterval: 10s secretStoreRefs: - kind: ClusterSecretStore name: vault-backend selector: secret: name: existing-secret updatePolicy: Replace ``` The above takes the property called `baz` of an existing secret called `existing-secret` in the `hello-world` namespace and pushes it to the `secret/pushsecrets/testme` vault path. Suggested-By: Chris Butler Closes: MBP-641 --- ansible/roles/vault_utils/README.md | 91 +++++++++++-------- ansible/roles/vault_utils/defaults/main.yml | 2 + .../vault_utils/tasks/vault_secrets_init.yaml | 24 ++++- .../vault_utils/tasks/vault_spokes_init.yaml | 32 ++++++- 4 files changed, 106 insertions(+), 43 deletions(-) diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index 6b851f2a..ba26c702 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -40,6 +40,17 @@ unseal_namespace: "imperative" This relies on [kubernetes.core](https://docs.ansible.com/ansible/latest/collections/kubernetes/core/k8s_module.html) +## Vault out of the box configuration + +This role configures four secret paths in vault: + +1. `secret/global` - Any secret under this path is accessible in read-only only to all clusters known to ACM (hub and spokes) +2. `secret/hub` - Any secret under this path is accessible in read-only only to the ACM hub cluster +3. `secret/` - Any secret under this path is accessible in read-only only to the spoke cluster +4. `secret/pushsecrets` - Any secret here can be accessed in read and write mode to all clusters known to ACM. This area can + be used with ESO's `PushSecrets` so you can push an existing secret from one namespace, to the vault under this path and + then it can be retrieved by an `ExternalSecret` either in a different namespace *or* from an entirely different cluster. + ## Values secret file format Currently this role supports two formats: version 1.0 (which is the assumed @@ -58,46 +69,6 @@ secret file. The values secret YAML files can be encrypted with `ansible-vault`. If the role detects they are encrypted, the password to decrypt them will be prompted when needed. -### Version 1.0 - -Here is a well-commented example of a version 1.0 file: - -```yaml ---- -# By default when a top-level 'version: 1.0' is missing it is assumed to be '1.0' -# NEVER COMMIT THESE VALUES TO GIT - -secrets: - # These secrets will be pushed in the vault at secret/hub/test The vault will - # have secret/hub/test with secret1 and secret2 as keys with their associated - # values (secrets) - test: - secret1: foo - secret2: bar - - # This ends up as the s3Secret attribute to the path secret/hub/aws - aws: - s3Secret: test-secret - -# This will create the vault key secret/hub/testfoo which will have two -# properties 'b64content' and 'content' which will be the base64-encoded -# content and the normal content respectively -files: - testfoo: ~/ca.crt -# These secrets will be pushed in the vault at secret/region1/test The vault will -# have secret/region1/test with secret1 and secret2 as keys with their associated -# values (secrets) -secrets.region1: - test: - secret1: foo1 - secret2: bar1 -# This will create the vault key secret/region2/testbar which will have two -# properties 'b64content' and 'content' which will be the base64-encoded -# content and the normal content respectively -files.region2: - testbar: ~/ca.crt -``` - ### Version 2.0 Here is a version 2.0 example file (specifying `version: 2.0` is mandatory in this case): @@ -210,6 +181,46 @@ secrets: ini_key: aws_secret_access_key ``` +### Version 1.0 + +Here is a well-commented example of a version 1.0 file: + +```yaml +--- +# By default when a top-level 'version: 1.0' is missing it is assumed to be '1.0' +# NEVER COMMIT THESE VALUES TO GIT + +secrets: + # These secrets will be pushed in the vault at secret/hub/test The vault will + # have secret/hub/test with secret1 and secret2 as keys with their associated + # values (secrets) + test: + secret1: foo + secret2: bar + + # This ends up as the s3Secret attribute to the path secret/hub/aws + aws: + s3Secret: test-secret + +# This will create the vault key secret/hub/testfoo which will have two +# properties 'b64content' and 'content' which will be the base64-encoded +# content and the normal content respectively +files: + testfoo: ~/ca.crt +# These secrets will be pushed in the vault at secret/region1/test The vault will +# have secret/region1/test with secret1 and secret2 as keys with their associated +# values (secrets) +secrets.region1: + test: + secret1: foo1 + secret2: bar1 +# This will create the vault key secret/region2/testbar which will have two +# properties 'b64content' and 'content' which will be the base64-encoded +# content and the normal content respectively +files.region2: + testbar: ~/ca.crt +``` + Internals --------- diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 4d263223..7759db48 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -17,6 +17,8 @@ vault_spoke_capabilities: '[\\\"read\\\"]' vault_spoke_ttl: "15m" vault_global_policy: global vault_global_capabilities: '[\\\"read\\\"]' +vault_pushsecrets_policy: pushsecrets +vault_pushsecrets_capabilities: '[\\\"create\\\",\\\"read\\\",\\\"update\\\",\\\"delete\\\"]' external_secrets_ns: golang-external-secrets external_secrets_sa: golang-external-secrets external_secrets_secret: golang-external-secrets diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 35327d58..8a098a7c 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -71,6 +71,28 @@ pod: "{{ vault_pod }}" command: "vault policy write {{ vault_global_policy }}-secret /tmp/policy-{{ vault_global_policy }}.hcl" +- name: Configure VP pushsecrets policy template + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: > + bash -e -c "echo \"path \\\"secret/data/{{ vault_pushsecrets_policy }}/*\\\" { + capabilities = {{ vault_pushsecrets_capabilities }} }\" > /tmp/policy-{{ vault_pushsecrets_policy }}.hcl" + +- name: Add metadata path to the pushsecrets policy + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: > + bash -e -c "echo \"path \\\"secret/metadata/{{ vault_pushsecrets_policy }}/*\\\" { + capabilities = {{ vault_pushsecrets_capabilities }} }\" >> /tmp/policy-{{ vault_pushsecrets_policy }}.hcl" + +- name: Configure VP pushsecrets policy + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: "vault policy write {{ vault_pushsecrets_policy }}-secret /tmp/policy-{{ vault_pushsecrets_policy }}.hcl" + - name: Configure policy template for hub kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" @@ -93,4 +115,4 @@ vault write auth/"{{ vault_hub }}"/role/"{{ vault_hub }}"-role bound_service_account_names="{{ external_secrets_sa }}" bound_service_account_namespaces="{{ external_secrets_ns }}" - policies="default,{{ vault_global_policy }}-secret,{{ vault_hub }}-secret" ttl="{{ vault_hub_ttl }}" + policies="default,{{ vault_global_policy }}-secret,{{ vault_pushsecrets_policy }}-secret,{{ vault_hub }}-secret" ttl="{{ vault_hub_ttl }}" diff --git a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml index 060378bc..bafe490b 100644 --- a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml @@ -157,7 +157,7 @@ loop_control: label: "{{ item.key }}" -- name: Configure policy template +- name: Configure spoke policy template kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" @@ -171,6 +171,34 @@ loop_control: label: "{{ item.key }}" +- name: Configure spoke pushsecrets policy template + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: > + bash -e -c "echo \"path \\\"secret/data/{{ vault_pushsecrets_policy }}/*\\\" { + capabilities = {{ vault_pushsecrets_capabilities }} }\" >> /tmp/policy-{{ item.value['vault_path'] }}.hcl" + loop: "{{ clusters_info | dict2items }}" + when: + - item.value['esoToken'] is defined + - item.key != "local-cluster" + loop_control: + label: "{{ item.key }}" + +- name: Configure spoke pushsecrets metadata policy template + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: > + bash -e -c "echo \"path \\\"secret/metadata/{{ vault_pushsecrets_policy }}/*\\\" { + capabilities = {{ vault_pushsecrets_capabilities }} }\" >> /tmp/policy-{{ item.value['vault_path'] }}.hcl" + loop: "{{ clusters_info | dict2items }}" + when: + - item.value['esoToken'] is defined + - item.key != "local-cluster" + loop_control: + label: "{{ item.key }}" + - name: Configure policy for spokes kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" @@ -191,7 +219,7 @@ vault write auth/"{{ item.value['vault_path'] }}"/role/"{{ item.value['vault_path'] }}"-role bound_service_account_names="{{ external_secrets_sa }}" bound_service_account_namespaces="{{ external_secrets_ns }}" - policies="default,{{ vault_global_policy }}-secret,{{ item.value['vault_path'] }}-secret" ttl="{{ vault_spoke_ttl }}" + policies="default,{{ vault_global_policy }}-secret,{{ vault_pushsecrets_policy }}-secret,{{ item.value['vault_path'] }}-secret" ttl="{{ vault_spoke_ttl }}" loop: "{{ clusters_info | dict2items }}" when: - item.value['esoToken'] is defined From 9aef7783fd35c4e6c7f8f1950f6e1674736bf650 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 30 Aug 2024 12:09:41 +0200 Subject: [PATCH 1261/1263] Fix PyInk warnings --- ansible/plugins/filter/parse_acm_secrets.py | 1 + ansible/plugins/module_utils/load_secrets_v1.py | 1 + ansible/plugins/module_utils/load_secrets_v2.py | 1 + ansible/plugins/module_utils/parse_secrets_v2.py | 1 + ansible/plugins/modules/vault_load_parsed_secrets.py | 1 + ansible/tests/unit/test_ini_file.py | 1 + ansible/tests/unit/test_parse_secrets.py | 2 ++ ansible/tests/unit/test_vault_load_parsed_secrets.py | 1 + ansible/tests/unit/test_vault_load_secrets.py | 1 + ansible/tests/unit/test_vault_load_secrets_v2.py | 1 + 10 files changed, 11 insertions(+) diff --git a/ansible/plugins/filter/parse_acm_secrets.py b/ansible/plugins/filter/parse_acm_secrets.py index 0445d96d..1c5148e3 100644 --- a/ansible/plugins/filter/parse_acm_secrets.py +++ b/ansible/plugins/filter/parse_acm_secrets.py @@ -79,5 +79,6 @@ def parse_acm_secrets(secrets): class FilterModule: + def filters(self): return {"parse_acm_secrets": parse_acm_secrets} diff --git a/ansible/plugins/module_utils/load_secrets_v1.py b/ansible/plugins/module_utils/load_secrets_v1.py index 6478ac26..8b89d85a 100644 --- a/ansible/plugins/module_utils/load_secrets_v1.py +++ b/ansible/plugins/module_utils/load_secrets_v1.py @@ -26,6 +26,7 @@ class LoadSecretsV1: + def __init__( self, module, diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 05a5917e..46cdcffa 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -40,6 +40,7 @@ class LoadSecretsV2: + def __init__(self, module, syaml, namespace, pod): self.module = module self.namespace = namespace diff --git a/ansible/plugins/module_utils/parse_secrets_v2.py b/ansible/plugins/module_utils/parse_secrets_v2.py index 512f75ef..f88579b6 100644 --- a/ansible/plugins/module_utils/parse_secrets_v2.py +++ b/ansible/plugins/module_utils/parse_secrets_v2.py @@ -42,6 +42,7 @@ class ParseSecretsV2: + def __init__(self, module, syaml, secrets_backing_store): self.module = module self.syaml = syaml diff --git a/ansible/plugins/modules/vault_load_parsed_secrets.py b/ansible/plugins/modules/vault_load_parsed_secrets.py index 0a6aa146..f5acdc86 100644 --- a/ansible/plugins/modules/vault_load_parsed_secrets.py +++ b/ansible/plugins/modules/vault_load_parsed_secrets.py @@ -82,6 +82,7 @@ class VaultSecretLoader: + def __init__( self, module, diff --git a/ansible/tests/unit/test_ini_file.py b/ansible/tests/unit/test_ini_file.py index e92280cb..6c30fdbb 100644 --- a/ansible/tests/unit/test_ini_file.py +++ b/ansible/tests/unit/test_ini_file.py @@ -29,6 +29,7 @@ class TestMyModule(unittest.TestCase): + def setUp(self): self.testdir_v2 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v2") diff --git a/ansible/tests/unit/test_parse_secrets.py b/ansible/tests/unit/test_parse_secrets.py index 0cfef1b6..2dab5716 100644 --- a/ansible/tests/unit/test_parse_secrets.py +++ b/ansible/tests/unit/test_parse_secrets.py @@ -62,6 +62,7 @@ def set_module_args(args): class BytesEncoder(json.JSONEncoder): + def default(self, o): if isinstance(o, bytes): return base64.b64encode(o).decode("ascii") @@ -113,6 +114,7 @@ def fail_json(*args, **kwargs): @mock.patch("getpass.getpass") class TestMyModule(unittest.TestCase): + def create_inifile(self): self.inifile = open("/tmp/awscredentials", "w") config = configparser.ConfigParser() diff --git a/ansible/tests/unit/test_vault_load_parsed_secrets.py b/ansible/tests/unit/test_vault_load_parsed_secrets.py index 1a449739..66ec6b69 100644 --- a/ansible/tests/unit/test_vault_load_parsed_secrets.py +++ b/ansible/tests/unit/test_vault_load_parsed_secrets.py @@ -70,6 +70,7 @@ def fail_json(*args, **kwargs): class TestMyModule(unittest.TestCase): + def setUp(self): self.mock_module_helper = patch.multiple( basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json diff --git a/ansible/tests/unit/test_vault_load_secrets.py b/ansible/tests/unit/test_vault_load_secrets.py index 12deeb3f..03d25d8c 100644 --- a/ansible/tests/unit/test_vault_load_secrets.py +++ b/ansible/tests/unit/test_vault_load_secrets.py @@ -74,6 +74,7 @@ def fail_json(*args, **kwargs): class TestMyModule(unittest.TestCase): + def setUp(self): self.mock_module_helper = patch.multiple( basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index d0e5881c..7b934320 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -77,6 +77,7 @@ def fail_json(*args, **kwargs): @mock.patch("getpass.getpass") class TestMyModule(unittest.TestCase): + def create_inifile(self): self.inifile = open("/tmp/awscredentials", "w") config = configparser.ConfigParser() From a03f7de814cacc41b842234094c43462591664de Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 5 Sep 2024 15:26:16 +0200 Subject: [PATCH 1262/1263] Removed previous version of common to convert to subtree from https://github.com/validatedpatterns/common.git main --- common/.ansible-lint | 3 - common/.github/linters/.markdown-lint.yml | 6 - common/.github/workflows/ansible-lint.yml | 18 - common/.github/workflows/linter.yml | 64 - common/.github/workflows/superlinter.yml | 38 - common/.gitignore | 9 - common/LICENSE | 202 - common/Makefile | 101 - common/README.md | 18 - common/acm/.helmignore | 1 - common/acm/Chart.yaml | 6 - common/acm/templates/multiclusterhub.yaml | 8 - .../policies/application-policies.yaml | 124 - .../templates/policies/ocp-gitops-policy.yaml | 76 - .../templates/provision/_install-config.tpl | 59 - .../acm/templates/provision/clusterpool.yaml | 83 - .../acm/templates/provision/secrets-aws.yaml | 84 - .../templates/provision/secrets-azure.yaml | 83 - .../templates/provision/secrets-common.yaml | 60 - common/acm/test.yaml | 35 - common/acm/values.yaml | 26 - common/ansible/ansible.cfg | 4 - .../ansible/playbooks/vault/push_secrets.yaml | 10 - common/ansible/playbooks/vault/vault.yaml | 7 - common/ansible/roles/vault_utils/README.md | 79 - .../roles/vault_utils/defaults/main.yml | 26 - .../roles/vault_utils/handlers/main.yml | 2 - .../ansible/roles/vault_utils/meta/main.yml | 31 - .../ansible/roles/vault_utils/tasks/main.yml | 20 - .../roles/vault_utils/tasks/pre_check.yaml | 26 - .../roles/vault_utils/tasks/push_secrets.yaml | 208 - .../roles/vault_utils/tasks/vault_delete.yaml | 23 - .../roles/vault_utils/tasks/vault_init.yaml | 85 - .../vault_utils/tasks/vault_pki_init.yaml | 50 - .../vault_utils/tasks/vault_secrets_init.yaml | 80 - .../roles/vault_utils/tasks/vault_status.yaml | 61 - .../roles/vault_utils/tasks/vault_unseal.yaml | 116 - .../ansible/roles/vault_utils/tests/inventory | 2 - .../ansible/roles/vault_utils/tests/test.yml | 5 - .../ansible/roles/vault_utils/vars/main.yml | 2 - common/clustergroup/.helmignore | 1 - common/clustergroup/Chart.yaml | 6 - .../clustergroup/templates/applications.yaml | 207 - .../templates/argocd-super-role.yaml | 41 - common/clustergroup/templates/argocd.yaml | 123 - .../templates/catalog-sources.yaml | 10 - .../templates/cluster-external-secrets.yaml | 51 - .../templates/gitops-namespace.yaml | 11 - .../templates/imperative/_helpers.tpl | 38 - .../templates/imperative/clusterrole.yaml | 22 - .../templates/imperative/configmap.yaml | 13 - .../templates/imperative/job.yaml | 67 - .../templates/imperative/namespace.yaml | 11 - .../templates/imperative/rbac.yaml | 31 - .../templates/imperative/role.yaml | 21 - .../templates/imperative/serviceaccount.yaml | 11 - .../templates/imperative/unsealjob.yaml | 59 - common/clustergroup/templates/namespaces.yaml | 11 - .../clustergroup/templates/operatorgroup.yaml | 25 - common/clustergroup/templates/projects.yaml | 22 - .../clustergroup/templates/subscriptions.yaml | 67 - common/clustergroup/test.yaml | 104 - common/clustergroup/values.yaml | 86 - common/common | 1 - common/examples/blank/Chart.yaml | 6 - common/examples/blank/templates/manifest.yaml | 4 - common/examples/blank/values.yaml | 2 - common/examples/kustomize-renderer/Chart.yaml | 6 - .../kustomize-renderer/environment.yaml | 34 - .../kustomize-renderer/kustomization.yaml | 5 - common/examples/kustomize-renderer/kustomize | 15 - .../templates/environment.yaml | 34 - .../examples/kustomize-renderer/values.yaml | 12 - common/examples/values-example.yaml | 78 - common/examples/values-secret.yaml | 32 - common/golang-external-secrets/Chart.yaml | 11 - .../charts/external-secrets-0.5.9.tgz | Bin 40987 -> 0 bytes ...ternal-secrets-hub-clusterrolebinding.yaml | 23 - ...lang-external-secrets-hub-secretstore.yaml | 25 - common/golang-external-secrets/values.yaml | 6 - common/hashicorp-vault/Chart.yaml | 10 - common/hashicorp-vault/README.md | 12 - .../hashicorp-vault/charts/vault-0.22.0.tgz | Bin 51863 -> 0 bytes .../hashicorp-vault/patch-server-route.diff | 28 - .../hashicorp-vault/templates/vault-app.yaml | 12 - .../hashicorp-vault/update-helm-dependency.sh | 26 - common/hashicorp-vault/values.yaml | 20 - common/install/.helmignore | 1 - common/install/Chart.yaml | 6 - .../crds/applications.argoproj.io.yaml | 2312 ------- .../install/templates/argocd/application.yaml | 37 - .../install/templates/argocd/namespace.yaml | 6 - .../templates/argocd/subscription.yaml | 20 - common/install/values.yaml | 25 - common/operator-install/Chart.yaml | 6 - ...ops.hybrid-cloud-patterns.io_patterns.yaml | 157 - .../operator-install/templates/pattern.yaml | 12 - .../templates/subscription.yaml | 13 - common/operator-install/values.yaml | 9 - common/reference-output.yaml | 119 - common/scripts/make_common_subtree.sh | 76 - common/scripts/pattern-util.sh | 20 - common/scripts/test.sh | 50 - common/scripts/vault-utils.sh | 30 - common/tests/acm-naked.expected.yaml | 93 - common/tests/acm-normal.expected.yaml | 204 - common/tests/clustergroup-naked.expected.yaml | 182 - .../tests/clustergroup-normal.expected.yaml | 802 --- .../tests/examples-blank-naked.expected.yaml | 6 - .../tests/examples-blank-normal.expected.yaml | 6 - ...les-kustomize-renderer-naked.expected.yaml | 36 - ...es-kustomize-renderer-normal.expected.yaml | 36 - ...olang-external-secrets-naked.expected.yaml | 5911 ----------------- ...lang-external-secrets-normal.expected.yaml | 5911 ----------------- .../tests/hashicorp-vault-naked.expected.yaml | 409 -- .../hashicorp-vault-normal.expected.yaml | 409 -- common/tests/install-naked.expected.yaml | 64 - common/tests/install-normal.expected.yaml | 64 - .../operator-install-naked.expected.yaml | 29 - .../operator-install-normal.expected.yaml | 29 - common/values-global.yaml | 16 - 121 files changed, 20487 deletions(-) delete mode 100644 common/.ansible-lint delete mode 100644 common/.github/linters/.markdown-lint.yml delete mode 100644 common/.github/workflows/ansible-lint.yml delete mode 100644 common/.github/workflows/linter.yml delete mode 100644 common/.github/workflows/superlinter.yml delete mode 100644 common/.gitignore delete mode 100644 common/LICENSE delete mode 100644 common/Makefile delete mode 100644 common/README.md delete mode 100644 common/acm/.helmignore delete mode 100644 common/acm/Chart.yaml delete mode 100644 common/acm/templates/multiclusterhub.yaml delete mode 100644 common/acm/templates/policies/application-policies.yaml delete mode 100644 common/acm/templates/policies/ocp-gitops-policy.yaml delete mode 100644 common/acm/templates/provision/_install-config.tpl delete mode 100644 common/acm/templates/provision/clusterpool.yaml delete mode 100644 common/acm/templates/provision/secrets-aws.yaml delete mode 100644 common/acm/templates/provision/secrets-azure.yaml delete mode 100644 common/acm/templates/provision/secrets-common.yaml delete mode 100644 common/acm/test.yaml delete mode 100644 common/acm/values.yaml delete mode 100644 common/ansible/ansible.cfg delete mode 100644 common/ansible/playbooks/vault/push_secrets.yaml delete mode 100644 common/ansible/playbooks/vault/vault.yaml delete mode 100644 common/ansible/roles/vault_utils/README.md delete mode 100644 common/ansible/roles/vault_utils/defaults/main.yml delete mode 100644 common/ansible/roles/vault_utils/handlers/main.yml delete mode 100644 common/ansible/roles/vault_utils/meta/main.yml delete mode 100644 common/ansible/roles/vault_utils/tasks/main.yml delete mode 100644 common/ansible/roles/vault_utils/tasks/pre_check.yaml delete mode 100644 common/ansible/roles/vault_utils/tasks/push_secrets.yaml delete mode 100644 common/ansible/roles/vault_utils/tasks/vault_delete.yaml delete mode 100644 common/ansible/roles/vault_utils/tasks/vault_init.yaml delete mode 100644 common/ansible/roles/vault_utils/tasks/vault_pki_init.yaml delete mode 100644 common/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml delete mode 100644 common/ansible/roles/vault_utils/tasks/vault_status.yaml delete mode 100644 common/ansible/roles/vault_utils/tasks/vault_unseal.yaml delete mode 100644 common/ansible/roles/vault_utils/tests/inventory delete mode 100644 common/ansible/roles/vault_utils/tests/test.yml delete mode 100644 common/ansible/roles/vault_utils/vars/main.yml delete mode 100644 common/clustergroup/.helmignore delete mode 100644 common/clustergroup/Chart.yaml delete mode 100644 common/clustergroup/templates/applications.yaml delete mode 100644 common/clustergroup/templates/argocd-super-role.yaml delete mode 100644 common/clustergroup/templates/argocd.yaml delete mode 100644 common/clustergroup/templates/catalog-sources.yaml delete mode 100644 common/clustergroup/templates/cluster-external-secrets.yaml delete mode 100644 common/clustergroup/templates/gitops-namespace.yaml delete mode 100644 common/clustergroup/templates/imperative/_helpers.tpl delete mode 100644 common/clustergroup/templates/imperative/clusterrole.yaml delete mode 100644 common/clustergroup/templates/imperative/configmap.yaml delete mode 100644 common/clustergroup/templates/imperative/job.yaml delete mode 100644 common/clustergroup/templates/imperative/namespace.yaml delete mode 100644 common/clustergroup/templates/imperative/rbac.yaml delete mode 100644 common/clustergroup/templates/imperative/role.yaml delete mode 100644 common/clustergroup/templates/imperative/serviceaccount.yaml delete mode 100644 common/clustergroup/templates/imperative/unsealjob.yaml delete mode 100644 common/clustergroup/templates/namespaces.yaml delete mode 100644 common/clustergroup/templates/operatorgroup.yaml delete mode 100644 common/clustergroup/templates/projects.yaml delete mode 100644 common/clustergroup/templates/subscriptions.yaml delete mode 100644 common/clustergroup/test.yaml delete mode 100644 common/clustergroup/values.yaml delete mode 120000 common/common delete mode 100644 common/examples/blank/Chart.yaml delete mode 100644 common/examples/blank/templates/manifest.yaml delete mode 100644 common/examples/blank/values.yaml delete mode 100644 common/examples/kustomize-renderer/Chart.yaml delete mode 100644 common/examples/kustomize-renderer/environment.yaml delete mode 100644 common/examples/kustomize-renderer/kustomization.yaml delete mode 100755 common/examples/kustomize-renderer/kustomize delete mode 100644 common/examples/kustomize-renderer/templates/environment.yaml delete mode 100644 common/examples/kustomize-renderer/values.yaml delete mode 100644 common/examples/values-example.yaml delete mode 100644 common/examples/values-secret.yaml delete mode 100644 common/golang-external-secrets/Chart.yaml delete mode 100644 common/golang-external-secrets/charts/external-secrets-0.5.9.tgz delete mode 100644 common/golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml delete mode 100644 common/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml delete mode 100644 common/golang-external-secrets/values.yaml delete mode 100644 common/hashicorp-vault/Chart.yaml delete mode 100644 common/hashicorp-vault/README.md delete mode 100644 common/hashicorp-vault/charts/vault-0.22.0.tgz delete mode 100644 common/hashicorp-vault/patch-server-route.diff delete mode 100644 common/hashicorp-vault/templates/vault-app.yaml delete mode 100755 common/hashicorp-vault/update-helm-dependency.sh delete mode 100644 common/hashicorp-vault/values.yaml delete mode 100644 common/install/.helmignore delete mode 100644 common/install/Chart.yaml delete mode 100644 common/install/crds/applications.argoproj.io.yaml delete mode 100644 common/install/templates/argocd/application.yaml delete mode 100644 common/install/templates/argocd/namespace.yaml delete mode 100644 common/install/templates/argocd/subscription.yaml delete mode 100644 common/install/values.yaml delete mode 100644 common/operator-install/Chart.yaml delete mode 100644 common/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml delete mode 100644 common/operator-install/templates/pattern.yaml delete mode 100644 common/operator-install/templates/subscription.yaml delete mode 100644 common/operator-install/values.yaml delete mode 100644 common/reference-output.yaml delete mode 100755 common/scripts/make_common_subtree.sh delete mode 100755 common/scripts/pattern-util.sh delete mode 100755 common/scripts/test.sh delete mode 100755 common/scripts/vault-utils.sh delete mode 100644 common/tests/acm-naked.expected.yaml delete mode 100644 common/tests/acm-normal.expected.yaml delete mode 100644 common/tests/clustergroup-naked.expected.yaml delete mode 100644 common/tests/clustergroup-normal.expected.yaml delete mode 100644 common/tests/examples-blank-naked.expected.yaml delete mode 100644 common/tests/examples-blank-normal.expected.yaml delete mode 100644 common/tests/examples-kustomize-renderer-naked.expected.yaml delete mode 100644 common/tests/examples-kustomize-renderer-normal.expected.yaml delete mode 100644 common/tests/golang-external-secrets-naked.expected.yaml delete mode 100644 common/tests/golang-external-secrets-normal.expected.yaml delete mode 100644 common/tests/hashicorp-vault-naked.expected.yaml delete mode 100644 common/tests/hashicorp-vault-normal.expected.yaml delete mode 100644 common/tests/install-naked.expected.yaml delete mode 100644 common/tests/install-normal.expected.yaml delete mode 100644 common/tests/operator-install-naked.expected.yaml delete mode 100644 common/tests/operator-install-normal.expected.yaml delete mode 100644 common/values-global.yaml diff --git a/common/.ansible-lint b/common/.ansible-lint deleted file mode 100644 index 138ae765..00000000 --- a/common/.ansible-lint +++ /dev/null @@ -1,3 +0,0 @@ -# Vim filetype=yaml ---- -offline: false diff --git a/common/.github/linters/.markdown-lint.yml b/common/.github/linters/.markdown-lint.yml deleted file mode 100644 index a0bc47d1..00000000 --- a/common/.github/linters/.markdown-lint.yml +++ /dev/null @@ -1,6 +0,0 @@ -{ - "default": true, - "MD003": false, - "MD013": false, - "MD033": false -} \ No newline at end of file diff --git a/common/.github/workflows/ansible-lint.yml b/common/.github/workflows/ansible-lint.yml deleted file mode 100644 index f0943b53..00000000 --- a/common/.github/workflows/ansible-lint.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Ansible Lint # feel free to pick your own name - -on: [push, pull_request] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - # Important: This sets up your GITHUB_WORKSPACE environment variable - - uses: actions/checkout@v2 - - - name: Lint Ansible Playbook - # Using the latest as of today (2022-06-23) v6.2.1 - uses: ansible/ansible-lint-action@v6.2.1 - # Let's point it to the path - with: - path: "ansible/" diff --git a/common/.github/workflows/linter.yml b/common/.github/workflows/linter.yml deleted file mode 100644 index 81eee65b..00000000 --- a/common/.github/workflows/linter.yml +++ /dev/null @@ -1,64 +0,0 @@ ---- -name: Unit test common - -# -# Documentation: -# https://help.github.com/en/articles/workflow-syntax-for-github-actions -# - -############################# -# Start the job on all push # -############################# -on: [push, pull_request] - -############### -# Set the Job # -############### -jobs: - build: - # Name the Job - name: Unit common/ Code Base - # Set the agent to run on - runs-on: ubuntu-latest - - ################## - # Load all steps # - ################## - steps: - ########################## - # Checkout the code base # - ########################## - - name: Checkout Code - uses: actions/checkout@v2 - with: - # Full git history is needed to get a proper list of changed files within `super-linter` - fetch-depth: 0 - - name: Setup helm - uses: azure/setup-helm@v1 - # with: - # version: '' # default is latest stable - id: install - - ################################ - # Run Linter against code base # - ################################ - # - name: Lint Code Base - # uses: github/super-linter@v4 - # env: - # VALIDATE_ALL_CODEBASE: false - # DEFAULT_BRANCH: main - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Run make test - run: | - make test - - - name: Run make helmlint - run: | - make helmlint - - - name: Run make helm kubeconform - run: | - curl -L -O https://github.com/yannh/kubeconform/releases/download/v0.4.13/kubeconform-linux-amd64.tar.gz - tar xf kubeconform-linux-amd64.tar.gz - sudo mv -v kubeconform /usr/local/bin - make kubeconform diff --git a/common/.github/workflows/superlinter.yml b/common/.github/workflows/superlinter.yml deleted file mode 100644 index 0141598c..00000000 --- a/common/.github/workflows/superlinter.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Super linter - -on: [push, pull_request] - -jobs: - build: - # Name the Job - name: Super linter - # Set the agent to run on - runs-on: ubuntu-latest - - steps: - - name: Checkout Code - uses: actions/checkout@v2 - with: - # Full git history is needed to get a proper list of changed files within `super-linter` - fetch-depth: 0 - - ################################ - # Run Linter against code base # - ################################ - - name: Lint Code Base - uses: github/super-linter/slim@v4 - env: - VALIDATE_ALL_CODEBASE: true - DEFAULT_BRANCH: main - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # These are the validation we disable atm - VALIDATE_BASH: false - VALIDATE_JSCPD: false - VALIDATE_KUBERNETES_KUBEVAL: false - VALIDATE_YAML: false - # VALIDATE_ANSIBLE: false - # VALIDATE_DOCKERFILE_HADOLINT: false - # VALIDATE_MARKDOWN: false - # VALIDATE_NATURAL_LANGUAGE: false - # VALIDATE_TEKTON: false diff --git a/common/.gitignore b/common/.gitignore deleted file mode 100644 index fd2282bc..00000000 --- a/common/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -*~ -*.swp -*.swo -values-secret.yaml -.*.expected.yaml -pattern-vault.init -pattern-vault.init.bak -super-linter.log -golang-external-secrets/Chart.lock diff --git a/common/LICENSE b/common/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/common/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/common/Makefile b/common/Makefile deleted file mode 100644 index 3f371774..00000000 --- a/common/Makefile +++ /dev/null @@ -1,101 +0,0 @@ -NAME=$(shell basename `pwd`) -# This is to ensure that whether we start with a git@ or https:// URL, we end up with an https:// URL -# This is because we expect to use tokens for repo authentication as opposed to SSH keys -TARGET_ORIGIN ?= origin -TARGET_REPO=$(shell git remote show $(TARGET_ORIGIN) | grep Push | sed -e 's/.*URL:[[:space:]]*//' -e 's%^git@%%' -e 's%^https://%%' -e 's%:%/%' -e 's%^%https://%') -# git branch --show-current is also available as of git 2.22, but we will use this for compatibility -TARGET_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) -HUBCLUSTER_APPS_DOMAIN=$(shell oc get ingresses.config/cluster -o jsonpath={.spec.domain}) - -# --set values always take precedence over the contents of -f -HELM_OPTS=-f values-global.yaml --set main.git.repoURL="$(TARGET_REPO)" --set main.git.revision=$(TARGET_BRANCH) \ - --set global.hubClusterDomain=$(HUBCLUSTER_APPS_DOMAIN) -TEST_OPTS= -f common/examples/values-secret.yaml -f values-global.yaml --set global.repoURL="https://github.com/pattern-clone/mypattern" \ - --set main.git.repoURL="https://github.com/pattern-clone/mypattern" --set main.git.revision=main --set global.pattern="mypattern" \ - --set global.namespace="pattern-namespace" --set global.hubClusterDomain=hub.example.com --set global.localClusterDomain=region.example.com \ - --set "clusterGroup.imperative.jobs[0].name"="test" --set "clusterGroup.imperative.jobs[0].playbook"="ansible/test.yml" \ - --set clusterGroup.insecureUnsealVaultInsideCluster=true -PATTERN_OPTS=-f common/examples/values-example.yaml -EXECUTABLES=git helm oc ansible - -.PHONY: help -help: ## This help message - @printf "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)\n" - -# Makefiles in the individual patterns should call these targets explicitly -# e.g. from industrial-edge: make -f common/Makefile show -show: ## show the starting template without installing it - helm template common/install/ --name-template $(NAME) $(HELM_OPTS) - -CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | sed -e 's/.\///') -test: ## run helm tests -# Test that all values used by the chart are in values.yaml with the same defaults as the pattern - @for t in $(CHARTS); do common/scripts/test.sh $$t naked ""; if [ $$? != 0 ]; then exit 1; fi; done -# Test the charts as the pattern would drive them - @for t in $(CHARTS); do common/scripts/test.sh $$t normal "$(TEST_OPTS) $(PATTERN_OPTS)"; if [ $$? != 0 ]; then exit 1; fi; done - -helmlint: ## run helm lint - @for t in $(CHARTS); do helm lint $(TEST_OPTS) $(PATTERN_OPTS) $$t; if [ $$? != 0 ]; then exit 1; fi; done - -API_URL ?= https://raw.githubusercontent.com/hybrid-cloud-patterns/ocp-schemas/main/openshift/4.10/ -KUBECONFORM_SKIP ?= -skip 'CustomResourceDefinition' -# We need to skip 'CustomResourceDefinition' as openapi2jsonschema seems to be unable to generate them ATM -kubeconform: ## run helm kubeconform - @for t in $(CHARTS); do helm template $(TEST_OPTS) $(PATTERN_OPTS) $$t | kubeconform -strict $(KUBECONFORM_SKIP) -verbose -schema-location $(API_URL); if [ $$? != 0 ]; then exit 1; fi; done - -validate-prereq: ## verify pre-requisites - @for t in $(EXECUTABLES); do if ! which $$t > /dev/null 2>&1; then echo "No $$t in PATH"; exit 1; fi; done - @echo "Prerequisites checked '$(EXECUTABLES)': OK" - @ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1 - @echo "Python kubernetes module: OK" - @echo -n "Check for kubernetes.core collection: " - @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi - @echo "OK" - -validate-origin: ## verify the git origin is available - @echo Checking repo $(TARGET_REPO) - branch $(TARGET_BRANCH) - @git ls-remote --exit-code --heads $(TARGET_REPO) $(TARGET_BRANCH) >/dev/null && \ - echo "$(TARGET_REPO) - $(TARGET_BRANCH) exists" || \ - (echo "$(TARGET_BRANCH) not found in $(TARGET_REPO)"; exit 1) - -# Default targets are "deploy" and "upgrade"; they can "move" to whichever install mechanism should be default. -# legacy-deploy and legacy-upgrade should be present so that patterns don't need to depend on "deploy" and "upgrade" -# pointing to one place or another, and don't need to change when they do (provide they use either legacy- or operator- -# targets) -deploy upgrade legacy-deploy legacy-upgrade: validate-prereq validate-origin ## deploys the pattern - helm upgrade --install $(NAME) common/install/ $(HELM_OPTS) - -operator-deploy operator-upgrade: validate-origin ## runs helm install - helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) - -uninstall: ## runs helm uninstall - $(eval CSV := $(shell oc get subscriptions -n openshift-operators openshift-gitops-operator -ojsonpath={.status.currentCSV})) - helm uninstall $(NAME) - @oc delete csv -n openshift-operators $(CSV) - -vault-init: ## inits, unseals and configured the vault - common/scripts/vault-utils.sh vault_init common/pattern-vault.init - common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init - common/scripts/vault-utils.sh vault_secrets_init common/pattern-vault.init - -vault-unseal: ## unseals the vault - common/scripts/vault-utils.sh vault_unseal common/pattern-vault.init - -load-secrets: ## loads the secrets into the vault - common/scripts/vault-utils.sh push_secrets common/pattern-vault.init - -super-linter: ## Runs super linter locally - podman run -e RUN_LOCAL=true -e USE_FIND_ALGORITHM=true \ - -e VALIDATE_BASH=false \ - -e VALIDATE_JSCPD=false \ - -e VALIDATE_KUBERNETES_KUBEVAL=false \ - -e VALIDATE_YAML=false \ - -e VALIDATE_ANSIBLE=false \ - $(DISABLE_LINTERS) \ - -v $(PWD):/tmp/lint:rw,z docker.io/github/super-linter:slim-v4 - -ansible-lint: ## run ansible lint on ansible/ folder - podman run -it -v $(PWD):/workspace:rw,z --workdir /workspace --entrypoint "/usr/local/bin/ansible-lint" quay.io/ansible/creator-ee:latest "-vvv" "ansible/" - -.phony: install test - diff --git a/common/README.md b/common/README.md deleted file mode 100644 index 9c6fa0cb..00000000 --- a/common/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Validated Patterns common/ repository - -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) - -## Start Here - -This repository is never used as standalone. It is usually imported in each pattern as a subtree. -In order to import the common/ the very first time you can use -`https://github.com/hybrid-cloud-patterns/multicloud-gitops/blob/main/common/scripts/make_common_subtree.sh` - -In order to update your common subtree inside your pattern repository you can either use -`https://github.com/hybrid-cloud-patterns/utilities/blob/main/scripts/update-common-everywhere.sh` or -do it manually by doing the following: - -```sh -git remote add -f upstream-common https://github.com/hybrid-cloud-patterns/common.git -git merge -s subtree -Xtheirs -Xsubtree=common upstream-common/ha-vault -``` diff --git a/common/acm/.helmignore b/common/acm/.helmignore deleted file mode 100644 index b25c15b8..00000000 --- a/common/acm/.helmignore +++ /dev/null @@ -1 +0,0 @@ -*~ diff --git a/common/acm/Chart.yaml b/common/acm/Chart.yaml deleted file mode 100644 index 1c3db911..00000000 --- a/common/acm/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -description: A Helm chart to configure Advanced Cluster Manager for OpenShift -keywords: -- pattern -name: acm -version: 0.0.1 diff --git a/common/acm/templates/multiclusterhub.yaml b/common/acm/templates/multiclusterhub.yaml deleted file mode 100644 index f925d5a6..00000000 --- a/common/acm/templates/multiclusterhub.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: operator.open-cluster-management.io/v1 -kind: MultiClusterHub -metadata: - name: multiclusterhub - namespace: open-cluster-management - annotations: - argocd.argoproj.io/sync-wave: "-1" -spec: {} diff --git a/common/acm/templates/policies/application-policies.yaml b/common/acm/templates/policies/application-policies.yaml deleted file mode 100644 index dd9a4658..00000000 --- a/common/acm/templates/policies/application-policies.yaml +++ /dev/null @@ -1,124 +0,0 @@ -# TODO: Also create a GitOpsCluster.apps.open-cluster-management.io -{{- range .Values.clusterGroup.managedClusterGroups }} -{{- $group := . }} -apiVersion: policy.open-cluster-management.io/v1 -kind: Policy -metadata: - name: {{ .name }}-clustergroup-policy - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - argocd.argoproj.io/compare-options: IgnoreExtraneous -spec: - remediationAction: enforce - disabled: false - policy-templates: - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: {{ .name }}-clustergroup-config - spec: - remediationAction: enforce - severity: medium - namespaceSelector: - include: - - default - object-templates: - - complianceType: mustonlyhave - objectDefinition: - apiVersion: argoproj.io/v1alpha1 - kind: Application - metadata: - name: {{ $.Values.global.pattern }}-{{ .name }} - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground - spec: - project: default - source: - repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} - targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} - path: {{ default "common/clustergroup" .path }} - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-{{ .name }}.yaml" - {{- range $valueFile := .extraValueFiles }} - - {{ $valueFile | quote }} - {{- end }} - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: {{ $.Values.global.pattern }} - - name: global.hubClusterDomain - value: {{ $.Values.global.hubClusterDomain }} - - name: global.localClusterDomain - value: '{{ `{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}` }}' - {{- range .helmOverrides }} - - name: {{ .name }} - value: {{ .value | quote }} - {{- end }} - {{- if .fileParameters }} - fileParameters: - {{- range .fileParameters }} - - name: {{ .name }} - path: {{ .path }} - {{- end }} - {{- end }} - destination: - server: https://kubernetes.default.svc - namespace: {{ $.Values.global.pattern }}-{{ .name }} - syncPolicy: - automated: - prune: false - selfHeal: true - ignoreDifferences: - - group: apps - kind: Deployment - jsonPointers: - - /spec/replicas - - group: route.openshift.io - kind: Route - jsonPointers: - - /status ---- -apiVersion: policy.open-cluster-management.io/v1 -kind: PlacementBinding -metadata: - name: {{ .name }}-placement-binding - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -placementRef: - name: {{ .name }}-placement - kind: PlacementRule - apiGroup: apps.open-cluster-management.io -subjects: - - name: {{ .name }}-clustergroup-policy - kind: Policy - apiGroup: policy.open-cluster-management.io ---- -apiVersion: apps.open-cluster-management.io/v1 -kind: PlacementRule -metadata: - name: {{ .name }}-placement -spec: - clusterConditions: - - status: 'True' - type: ManagedClusterConditionAvailable - {{- if .clusterSelector }} - clusterSelector: {{ .clusterSelector | toPrettyJson }} - {{- else }} - clusterSelector: - matchLabels: - {{- range .labels }} - {{ .name }}: {{ .value }} - {{- end }} - {{- end }} ---- -{{- end }} diff --git a/common/acm/templates/policies/ocp-gitops-policy.yaml b/common/acm/templates/policies/ocp-gitops-policy.yaml deleted file mode 100644 index 7ca61b0f..00000000 --- a/common/acm/templates/policies/ocp-gitops-policy.yaml +++ /dev/null @@ -1,76 +0,0 @@ -apiVersion: policy.open-cluster-management.io/v1 -kind: Policy -metadata: - name: openshift-gitops-policy - annotations: - policy.open-cluster-management.io/standards: NIST-CSF - policy.open-cluster-management.io/categories: PR.DS Data Security - policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - argocd.argoproj.io/compare-options: IgnoreExtraneous -spec: - remediationAction: enforce - disabled: false - policy-templates: - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: openshift-gitops-config - spec: - remediationAction: enforce - severity: medium - namespaceSelector: - include: - - default - object-templates: - - complianceType: mustonlyhave - objectDefinition: - # This is an auto-generated file. DO NOT EDIT - apiVersion: operators.coreos.com/v1alpha1 - kind: Subscription - metadata: - name: openshift-gitops-operator - namespace: openshift-operators - labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: '' - spec: - channel: stable - installPlanApproval: Automatic - name: openshift-gitops-operator - source: redhat-operators - sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: "*" ---- -apiVersion: policy.open-cluster-management.io/v1 -kind: PlacementBinding -metadata: - name: openshift-gitops-placement-binding - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -placementRef: - name: openshift-gitops-placement - kind: PlacementRule - apiGroup: apps.open-cluster-management.io -subjects: - - name: openshift-gitops-policy - kind: Policy - apiGroup: policy.open-cluster-management.io ---- -apiVersion: apps.open-cluster-management.io/v1 -kind: PlacementRule -metadata: - name: openshift-gitops-placement -spec: - clusterConditions: - - status: 'True' - type: ManagedClusterConditionAvailable - clusterSelector: - matchExpressions: - - key: vendor - operator: In - values: - - OpenShift diff --git a/common/acm/templates/provision/_install-config.tpl b/common/acm/templates/provision/_install-config.tpl deleted file mode 100644 index 39aa03eb..00000000 --- a/common/acm/templates/provision/_install-config.tpl +++ /dev/null @@ -1,59 +0,0 @@ -{{- define "cluster.install-config" -}} - -{{- $type := "None" }} -{{- $cloud := "None" }} -{{- $region := "None" }} - -{{- if .platform.aws }} -{{- $cloud = "aws" }} -{{- $region = .platform.aws.region }} -{{- $type = "m5.xlarge" }} -{{- else if .platform.azure }} -{{- $cloud = "azure" }} -{{- $region = .platform.azure.region }} -{{- $type = "Standard_D8s_v3" }} -{{- end }} - -apiVersion: v1 -metadata: - name: '{{ .name }}' -baseDomain: {{ .baseDomain }} -controlPlane: - architecture: amd64 - hyperthreading: Enabled - name: controlPlane - {{- if .controlPlane }} - replicas: {{ default 3 .controlPlane.count }} - platform: {{- .controlPlane.platform | toPrettyJson }} - {{- else }} - replicas: 3 - platform: - {{ $cloud }}: - type: {{ $type }} - {{- end }} -compute: -- hyperthreading: Enabled - architecture: amd64 - name: 'worker' - {{- if .workers }} - replicas: {{ default 3 .workers.count }} - platform: {{- .workers.platform | toPrettyJson }} - {{- else }} - replicas: 3 - platform: - {{ $cloud }}: - type: {{ $type }} - {{- end }} -networking: - clusterNetwork: - - cidr: 10.128.0.0/14 - hostPrefix: 23 - machineNetwork: - - cidr: 10.0.0.0/16 - networkType: OpenShiftSDN - serviceNetwork: - - 172.30.0.0/16 -platform: {{ .platform | toPrettyJson }} -pullSecret: "" # skip, hive will inject based on it's secrets -sshKey: "" # skip, hive will inject based on it's secrets -{{- end -}} \ No newline at end of file diff --git a/common/acm/templates/provision/clusterpool.yaml b/common/acm/templates/provision/clusterpool.yaml deleted file mode 100644 index b8cf1ade..00000000 --- a/common/acm/templates/provision/clusterpool.yaml +++ /dev/null @@ -1,83 +0,0 @@ -{{- range .Values.clusterGroup.managedClusterGroups }} -{{- $group := . }} -{{- if .clusterPools }}{{- /* We only create ManagedClusterSets if there are clusterPools defined */}} -apiVersion: cluster.open-cluster-management.io/v1beta1 -kind: ManagedClusterSet -metadata: - annotations: - cluster.open-cluster-management.io/submariner-broker-ns: {{ .name }}-broker - name: {{ .name }} -spec: - clusterSelector: - selectorType: LegacyClusterSetLabel ---- -{{- range .clusterPools }} - -{{- $pool := . }} -{{- $poolName := cat .name $group.name | replace " " "-" }} - -{{- $cloud := "None" }} -{{- $region := "None" }} - -{{- if .platform.aws }} -{{- $cloud = "aws" }} -{{- $region = .platform.aws.region }} -{{- else if .platform.azure }} -{{- $cloud = "azure" }} -{{- $region = .platform.azure.region }} -{{- end }} - -apiVersion: hive.openshift.io/v1 -kind: ClusterPool -metadata: - name: "{{ $poolName }}" - annotations: - argocd.argoproj.io/sync-wave: "10" - labels: - cloud: {{ $cloud }} - region: '{{ $region }}' - vendor: OpenShift - cluster.open-cluster-management.io/clusterset: {{ .name }} -spec: - {{- if .size }} - size: {{ .size }} - {{- else }} - size: {{ len .clusters }} - {{- end }} - runningCount: {{ len .clusters }} - baseDomain: {{ .baseDomain }} - installConfigSecretTemplateRef: - name: {{ $poolName }}-install-config - imageSetRef: - name: img{{ .openshiftVersion }}-x86-64-appsub - pullSecretRef: - name: {{ $poolName }}-pull-secret - sshPrivateKeySecretRef: - name: {{ $poolName }}-ssh-private-key - skipMachinePools: true # Disable MachinePool as using custom install-config - platform: - {{ $cloud }}: - credentialsSecretRef: - name: {{ $poolName }}-creds - region: {{ $region }} ---- -{{- range .clusters }} -apiVersion: hive.openshift.io/v1 -kind: ClusterClaim -metadata: - name: '{{ . }}-{{ $group.name }}' - annotations: - argocd.argoproj.io/sync-wave: "20" - cluster.open-cluster-management.io/createmanagedcluster: "true" - labels: - clusterClaimName: {{ . }}-{{ $group.name }} - {{- range $group.labels }} - {{ .name }}: {{ .value }} - {{- end }} -spec: - clusterPoolName: {{ $pool.name }} ---- -{{- end }}{{- /* range .range clusters */}} -{{- end }}{{- /* range .clusterPools */}} -{{- end }}{{- /* if .clusterPools) */}} -{{- end }}{{- /* range .Values.clusterGroup.managedClusterGroups */}} diff --git a/common/acm/templates/provision/secrets-aws.yaml b/common/acm/templates/provision/secrets-aws.yaml deleted file mode 100644 index 99022df6..00000000 --- a/common/acm/templates/provision/secrets-aws.yaml +++ /dev/null @@ -1,84 +0,0 @@ -{{- range .Values.clusterGroup.managedClusterGroups }} -{{- $group := . }} -{{- range .clusterPools }} -{{- $poolName := cat .name $group.name | replace " " "-" }} -{{- if .platform.aws }} -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: {{ $poolName }}-creds -spec: - dataFrom: - - extract: - # Expects entries called: aws_access_key_id and aws_secret_access_key - key: {{ default "secret/data/hub/aws" .awsKeyPath }} - refreshInterval: 24h0m0s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} - target: - name: {{ $poolName }}-creds - creationPolicy: Owner - template: - type: Opaque ---- -# For use when manually creating clusters with ACM -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: {{ $poolName }}-infra-creds -spec: - data: - - secretKey: openshiftPullSecret - remoteRef: - key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} - property: content - - secretKey: awsKeyId - remoteRef: - key: {{ default "secret/data/hub/aws" .awsKeyPath }} - property: aws_access_key_id - - secretKey: awsAccessKey - remoteRef: - key: {{ default "secret/data/hub/aws" .awsKeyPath }} - property: aws_secret_access_key - - secretKey: sshPublicKey - remoteRef: - key: {{ default "secret/data/hub/publickey" .sshPublicKeyPath }} - property: content - - secretKey: sshPrivateKey - remoteRef: - key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} - property: content - refreshInterval: 24h0m0s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} - target: - name: {{ $poolName }}-infra-creds - creationPolicy: Owner - template: - type: Opaque - metadata: - labels: - cluster.open-cluster-management.io/credentials: "" - cluster.open-cluster-management.io/type: aws - data: - baseDomain: "{{ .baseDomain }}" - pullSecret: |- - {{ "{{ .openshiftPullSecret | toString }}" }} - aws_access_key_id: |- - {{ "{{ .awsKeyId | toString }}" }} - aws_secret_access_key: |- - {{ "{{ .awsAccessKey | toString }}" }} - ssh-privatekey: |- - {{ "{{ .sshPrivateKey | toString }}" }} - ssh-publickey: |- - {{ "{{ .sshPublicKey | toString }}" }} - httpProxy: "" - httpsProxy: "" - noProxy: "" - additionalTrustBundle: "" ---- -{{- end }} -{{- end }} -{{- end }} \ No newline at end of file diff --git a/common/acm/templates/provision/secrets-azure.yaml b/common/acm/templates/provision/secrets-azure.yaml deleted file mode 100644 index 66b470c1..00000000 --- a/common/acm/templates/provision/secrets-azure.yaml +++ /dev/null @@ -1,83 +0,0 @@ -{{- range .Values.clusterGroup.managedClusterGroups }} -{{- $group := . }} -{{- range .clusterPools }} -{{- $poolName := cat .name $group.name | replace " " "-" }} -{{- if .platform.azure }} -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: {{ $poolName }}-creds -spec: - data: - - secretKey: azureOsServicePrincipal - remoteRef: - key: {{ default "secret/data/hub/azureOsServicePrincipal" .azureKeyPath }} - property: content - refreshInterval: 24h0m0s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} - target: - name: {{ $poolName }}-creds - creationPolicy: Owner - template: - type: Opaque - data: - osServicePrincipal.json: |- - {{ "{{ .azureOsServicePrincipal | toString }}" }} ---- -# For use when manually creating clusters with ACM -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: {{ $poolName }}-infra-creds -spec: - data: - - secretKey: openshiftPullSecret - remoteRef: - key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} - property: content - - secretKey: sshPublicKey - remoteRef: - key: {{ default "secret/data/hub/publickey" .sshPublicKeyPath }} - property: content - - secretKey: sshPrivateKey - remoteRef: - key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} - property: content - - secretKey: azureOsServicePrincipal - remoteRef: - key: {{ default "secret/data/hub/azureOsServicePrincipal" .azureKeyPath }} - property: content - refreshInterval: 24h0m0s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} - target: - name: {{ $poolName }}-infra-creds - creationPolicy: Owner - template: - type: Opaque - metadata: - labels: - cluster.open-cluster-management.io/credentials: "" - cluster.open-cluster-management.io/type: aws - data: - cloudName: AzurePublicCloud - osServicePrincipal.json: |- - {{ "{{ .azureOsServicePrincipal | toString }}" }} - baseDomain: "{{ .baseDomain }}" - baseDomainResourceGroupName: "{{ .platform.azure.baseDomainResourceGroupName | toString }}" - pullSecret: |- - {{ "{{ .openshiftPullSecret | toString }}" }} - ssh-privatekey: |- - {{ "{{ .sshPrivateKey | toString }}" }} - ssh-publickey: |- - {{ "{{ .sshPublicKey | toString }}" }} - httpProxy: "" - httpsProxy: "" - noProxy: "" - additionalTrustBundle: "" -{{- end }} -{{- end }} -{{- end }} \ No newline at end of file diff --git a/common/acm/templates/provision/secrets-common.yaml b/common/acm/templates/provision/secrets-common.yaml deleted file mode 100644 index 62641dde..00000000 --- a/common/acm/templates/provision/secrets-common.yaml +++ /dev/null @@ -1,60 +0,0 @@ -{{- range .Values.clusterGroup.managedClusterGroups }} -{{- $group := . }} -{{- range .clusterPools }} -{{- $poolName := cat .name $group.name | replace " " "-" }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ $poolName }}-install-config -data: - # Base64 encoding of install-config yaml - install-config.yaml: {{ include "cluster.install-config" . | b64enc }} -type: Opaque ---- -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: {{ $poolName }}-pull-secret -spec: - data: - - secretKey: openshiftPullSecret - remoteRef: - key: {{ default "secret/data/hub/openshiftPullSecret" .pullSecretKeyPath }} - property: content - refreshInterval: 24h0m0s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} - target: - name: {{ $poolName }}-pull-secret - creationPolicy: Owner - template: - type: kubernetes.io/dockerconfigjson - data: - .dockerconfigjson: |- - {{ "{{ .openshiftPullSecret | toString }}" }} ---- -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: {{ $poolName }}-ssh-private-key -spec: - data: - - secretKey: sshPrivateKey - remoteRef: - key: {{ default "secret/data/hub/privatekey" .sshPrivateKeyPath }} - property: content - refreshInterval: 24h0m0s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} - target: - name: {{ $poolName }}-ssh-private-key - creationPolicy: Owner - template: - type: Opaque - data: - ssh-privatekey: |- - {{ "{{ .sshPrivateKey | toString }}" }} -{{- end }} -{{- end }} \ No newline at end of file diff --git a/common/acm/test.yaml b/common/acm/test.yaml deleted file mode 100644 index 225f4bf8..00000000 --- a/common/acm/test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -clusterGroup: - managedClusterGroups: - exampleRegion: - name: region-one - - # Before enabling cluster provisioning, ensure AWS/Azure credentials and OCP - # pull secrets are defined in Vault. See values-secret.yaml.template - # - clusterPools: - exampleAWSPool: - name: aws-ap - openshiftVersion: 4.10.18 - baseDomain: blueprints.rhecoeng.com - platform: - aws: - region: ap-southeast-2 - clusters: - - One - exampleAzurePool: - name: azure-us - openshiftVersion: 4.10.18 - baseDomain: blueprints.rhecoeng.com - platform: - azure: - baseDomainResourceGroupName: dojo-dns-zones - region: eastus - clusters: - - Two - - Three - labels: - - name: clusterGroup - value: region-one - helmOverrides: - - name: clusterGroup.isHubCluster - value: false diff --git a/common/acm/values.yaml b/common/acm/values.yaml deleted file mode 100644 index e423d531..00000000 --- a/common/acm/values.yaml +++ /dev/null @@ -1,26 +0,0 @@ -global: - pattern: none - repoURL: none - targetRevision: main - - -clusterGroup: - managedClusterGroups: -# testRegion: -# name: region-one -# clusterPools: -# testPool: -# name: spoke -# openshiftVersion: 4.10.18 -# provider: -# region: ap-southeast-2 -# baseDomain: blueprints.rhecoeng.com -# clusters: -# - spoke1 -# labels: -# - name: clusterGroup -# value: region-one - -secretStore: - name: vault-backend - kind: ClusterSecretStore \ No newline at end of file diff --git a/common/ansible/ansible.cfg b/common/ansible/ansible.cfg deleted file mode 100644 index c8bec627..00000000 --- a/common/ansible/ansible.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[defaults] -display_skipped_hosts=False -localhost_warning=False -roles_path=./roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles diff --git a/common/ansible/playbooks/vault/push_secrets.yaml b/common/ansible/playbooks/vault/push_secrets.yaml deleted file mode 100644 index ed3cad61..00000000 --- a/common/ansible/playbooks/vault/push_secrets.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Secret injection of validated-patterns - hosts: localhost - connection: local - gather_facts: false - tasks: - - name: Run push secrets task - include_role: - name: vault_utils - tasks_from: push_secrets diff --git a/common/ansible/playbooks/vault/vault.yaml b/common/ansible/playbooks/vault/vault.yaml deleted file mode 100644 index 64711e47..00000000 --- a/common/ansible/playbooks/vault/vault.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -- name: Vault initialization - hosts: localhost - connection: local - gather_facts: false - roles: - - vault_utils diff --git a/common/ansible/roles/vault_utils/README.md b/common/ansible/roles/vault_utils/README.md deleted file mode 100644 index e83471b9..00000000 --- a/common/ansible/roles/vault_utils/README.md +++ /dev/null @@ -1,79 +0,0 @@ -Role Name -========= - -Bunch of utilities to manage the vault inside k8s imperatively - -Requirements ------------- - -ansible-galaxy collection install kubernetes.core (formerly known as community.kubernetes) - -Role Variables --------------- - -Defaults as to where the values-secret.yaml file is and the two ways to connect to a kubernetes cluster -(KUBERCONFIG and ~/.kube/config respectively): - -```yaml -values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" -kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" -kubeconfig_backup: "{{ lookup('env', 'HOME') }}/.kube/config" -``` - -Default values for vault configuration: - -```yaml -vault_ns: "vault" -vault_pod: "vault-0" -vault_hub: "hub" -vault_hub_kubernetes_host: https://$KUBERNETES_PORT_443_TCP_ADDR:443 -# Needs extra escaping due to how it gets injected via shell in the vault -vault_hub_capabilities: '[\\\"read\\\"]' -vault_base_path: "secret" -vault_path: "{{ vault_base_path }}/{{ vault_hub }}" -vault_hub_ttl: "15m" -vault_pki_max_lease_ttl: "8760h" -external_secrets_ns: golang-external-secrets -external_secrets_sa: golang-external-secrets -``` - -Use the local file system (output_file variable) to store the vault's unseal keys. -If set to false they will be stored inside a secret defined by `unseal_secret` -in the `unseal_namespace` namespace: - -```yaml -file_unseal: true -# token inside a secret in the cluster. -# *Note* that this is fundamentally unsafe -output_file: "common/pattern-vault.init" -unseal_secret: "vaultkeys" -unseal_namespace: "imperative" -``` - -Dependencies ------------- - -This relies on [kubernetes.core](https://docs.ansible.com/ansible/latest/collections/kubernetes/core/k8s_module.html) - -Internals ---------- - -Here is the rough high-level algorithm used to unseal the vault: - -1. Check vault status. If vault is not initialized go to 2. If initialized go to 3. -2. Initialize vault and store unseal keys + login token either on a local file - or inside a secret in k8s (file_unseal var controls this) -3. Check vault status. If vault is unsealed go to 5. else to to 4. -4. Unseal the vault using the secrets read from the file or the secret - (file_unseal controls this) -5. Configure the vault (should be idempotent) - -License -------- - -Apache - -Author Information ------------------- - -Michele Baldessari diff --git a/common/ansible/roles/vault_utils/defaults/main.yml b/common/ansible/roles/vault_utils/defaults/main.yml deleted file mode 100644 index 04fccf7b..00000000 --- a/common/ansible/roles/vault_utils/defaults/main.yml +++ /dev/null @@ -1,26 +0,0 @@ ---- -# defaults file for vault_utils -values_secret: "{{ lookup('env', 'HOME') }}/values-secret.yaml" -kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" -kubeconfig_backup: "{{ lookup('env', 'HOME') }}/.kube/config" -vault_ns: "vault" -vault_pod: "vault-0" -vault_hub: "hub" -vault_pvc: "data-vault-0" -vault_hub_kubernetes_host: https://$KUBERNETES_PORT_443_TCP_ADDR:443 -# Needs extra escaping due to how it gets injected via shell in the vault -vault_hub_capabilities: '[\\\"read\\\"]' -vault_base_path: "secret" -vault_path: "{{ vault_base_path }}/{{ vault_hub }}" -vault_hub_ttl: "15m" -vault_pki_max_lease_ttl: "8760h" -output_file: "common/pattern-vault.init" -external_secrets_ns: golang-external-secrets -external_secrets_sa: golang-external-secrets -external_secrets_secret: golang-external-secrets -# Setting this to false makes the role store the vault unseal keys and root login -# token inside a secret in the cluster. -# *Note* that this is fundamentally unsafe -file_unseal: true -unseal_secret: "vaultkeys" -unseal_namespace: "imperative" diff --git a/common/ansible/roles/vault_utils/handlers/main.yml b/common/ansible/roles/vault_utils/handlers/main.yml deleted file mode 100644 index a983544d..00000000 --- a/common/ansible/roles/vault_utils/handlers/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# handlers file for vault_utils diff --git a/common/ansible/roles/vault_utils/meta/main.yml b/common/ansible/roles/vault_utils/meta/main.yml deleted file mode 100644 index c99eb3a9..00000000 --- a/common/ansible/roles/vault_utils/meta/main.yml +++ /dev/null @@ -1,31 +0,0 @@ -galaxy_info: - author: Validated Patterns Team https://github.com/hybrid-cloud-patterns/ - description: Utilities to manage vault in kubernetes (init, unseal, etc) - - issue_tracker_url: https://github.com/hybrid-cloud-patterns/common/issues - license: Apache-2.0 - min_ansible_version: "2.1" - - # If this a Container Enabled role, provide the minimum Ansible Container version. - # min_ansible_container_version: - - platforms: - - name: Fedora - versions: - - all - - name: Ubuntu - versions: - - all - - name: Debian - versions: - - all - - name: EL - versions: - - "8" - - "9" - - galaxy_tags: [] - -dependencies: [] - # List your role dependencies here, one per line. Be sure to remove the '[]' above, - # if you add dependencies to this list. diff --git a/common/ansible/roles/vault_utils/tasks/main.yml b/common/ansible/roles/vault_utils/tasks/main.yml deleted file mode 100644 index 1da3e440..00000000 --- a/common/ansible/roles/vault_utils/tasks/main.yml +++ /dev/null @@ -1,20 +0,0 @@ ---- -- name: Delete vault - import_tasks: vault_delete.yaml - tags: vault_delete - -- name: Run vault init tasks - import_tasks: vault_init.yaml - tags: vault_init - -- name: Unseal vault - import_tasks: vault_unseal.yaml - tags: vault_unseal - -- name: Vault secrets init - import_tasks: vault_secrets_init.yaml - tags: vault_secrets_init - -- name: Load secrets - import_tasks: push_secrets.yaml - tags: push_secrets diff --git a/common/ansible/roles/vault_utils/tasks/pre_check.yaml b/common/ansible/roles/vault_utils/tasks/pre_check.yaml deleted file mode 100644 index 1dc5f445..00000000 --- a/common/ansible/roles/vault_utils/tasks/pre_check.yaml +++ /dev/null @@ -1,26 +0,0 @@ ---- -- name: Check if the kubernetes python module is usable from ansible - ansible.builtin.command: "{{ ansible_python_interpreter }} -c 'import kubernetes'" - changed_when: false - -- name: Check if KUBECONFIG is correctly set - ansible.builtin.debug: - msg: "KUBECONFIG is not set, falling back to ~/.kube/config" - when: kubeconfig is not defined or kubeconfig | length == 0 - -- name: Check if ~/.kube/config exists - ansible.builtin.stat: - path: "{{ kubeconfig_backup }}" - register: kubeconfig_result - -- name: Check if we're running inside an OCP cluster directly - ansible.builtin.set_fact: - running_in_ocp: "{{ lookup('env', 'KUBERNETES_SERVICE_HOST') | length > 0 | bool }}" - -- name: Fail if both KUBECONFIG and ~/.kube/config do not exist but only when not running in a cluster - ansible.builtin.fail: - msg: "{{ kubeconfig_backup }} not found and KUBECONFIG unset. Bailing out." - failed_when: - - not running_in_ocp - - not kubeconfig_result.stat.exists - - kubeconfig is not defined or kubeconfig | length == 0 diff --git a/common/ansible/roles/vault_utils/tasks/push_secrets.yaml b/common/ansible/roles/vault_utils/tasks/push_secrets.yaml deleted file mode 100644 index 3569d24a..00000000 --- a/common/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ /dev/null @@ -1,208 +0,0 @@ ---- -- include_tasks: pre_check.yaml -- include_tasks: vault_status.yaml - -# Unfortunately we cannot loop vault_status and just check if the vault is unsealed -# https://github.com/ansible/proposals/issues/136 -# So here we keep running the 'vault status' command until sealed is set to false -- name: If the vault is still sealed we need to retry - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault status -format=json - register: vault_status_json - until: "'stdout' in vault_status_json and (not (vault_status_json.stdout | from_json)['sealed'] | bool)" - retries: 20 - delay: 45 - failed_when: "'stdout_lines' not in vault_status_json" - -- name: Check for existence of "{{ values_secret }}" - ansible.builtin.stat: - path: "{{ values_secret }}" - register: result - failed_when: not result.stat.exists - -- name: Parse "{{ values_secret }}" - ansible.builtin.set_fact: - all_values: "{{ lookup('file', values_secret) | from_yaml | default({}, true) }}" - -- name: Check for secrets keys - ansible.builtin.assert: - that: - - "('secrets' in all_values.keys()) or ('files' in all_values.keys())" - fail_msg: "Was not able to parse any secrets from file {{ values_secret }}. Either 'secrets:' or 'files:' top-level keys (or both) must exist" - -- name: Set secrets and file_secrets facts - ansible.builtin.set_fact: - secrets: "{{ all_values['secrets'] | default({}) }}" - file_secrets: "{{ all_values['files'] | default({}) }}" - -- name: Verify we have any secrets at all - ansible.builtin.fail: - msg: "Was not able to parse any secrets from file {{ values_secret }}: {{ all_values }}" - failed_when: - secrets | combine(file_secrets) | length == 0 - -- name: Check the value-secret.yaml file for errors in the secrets section - ansible.builtin.fail: - msg: > - "{{ item }}" is not properly formatted. Each key under 'secrets:' - needs to point to a dictionary of key, value pairs. See values-secret.yaml.template. - when: > - item.key | length == 0 or - item.value is not mapping - loop: - "{{ secrets | dict2items }}" - loop_control: - label: "{{ item.key }}" - -- name: Validate file references in the files section of value-secret.yaml - ansible.builtin.stat: - path: '{{ file_stat.value }}' - register: file_values - loop_control: - loop_var: file_stat - loop: - "{{ file_secrets | dict2items }}" - -- name: debug file_stat - ansible.builtin.debug: - var: file_stat - when: debug | default(False) | bool - -- name: debug file_values - ansible.builtin.debug: - var: file_values - when: debug | default(False) | bool - -- name: Fail if referenced file does not exist - ansible.builtin.fail: - msg: > - "file {{ item.file_stat.key }} {{ item.file_stat.value }}" must exist and be a file - when: > - not item.stat.exists - loop: - "{{ file_values.results }}" - -# Detect here if we have only the following two keys under a password -# s3.accessKey: -# s3.secretKey: -# If we do, then detect it and calculate the b64 s3Secret token -# Note: the vars: line is due to https://github.com/ansible/ansible/issues/40239 -- name: Check if any of the passwords has only s3.[accessKey,secretKey] and generate the combined s3Secret in that case - ansible.builtin.set_fact: - s3keys: "{{ s3keys | default({}) | combine({ item.key: {'s3Secret': s3secret | b64encode } }) }}" - vars: - s3secret: "{{ 's3.accessKey: ' + item.value['s3.accessKey'] + '\ns3.secretKey: ' + item.value['s3.secretKey'] }}" - when: - - '"s3.accessKey" in item.value.keys()' - - '"s3.secretKey" in item.value.keys()' - - '"s3Secret" not in item.value.keys()' - loop: - "{{ secrets | dict2items }}" - loop_control: - label: "{{ item.key }}" - -- name: Merge any s3Secret into the secrets dictionary if we have any - ansible.builtin.set_fact: - secrets: "{{ secrets | combine(s3keys) }}" - when: - s3keys is defined and s3keys | length > 0 - -# The values-secret.yaml file is in the fairly loose form of: -# secrets: -# group1: -# key1: value1 -# key2: value2 -# group2: -# key1: valueA -# key4: valueC -# The above will generate: -# vault kv put 'secret/hub/group1' key1='value1' key2='value2' -# vault kv put 'secret/hub/group2' key1='valueA' key4='valueC' -# Below we loop on the top level keys (group1, group2) and for each -# sub-top-level key (key1, key2) we create a single 'key1=value1 key2=value2' string -# -# Special note is to be given to the regex_replace which is run against each value aka password: -# A. The need for it is to quote the passwords -# B. Since it needs to cater to multiline strings (certs) and ansible offers no way -# to specify re.DOTALL and re.MULTILINE we need to use (?ms) at the beginning -# See https://docs.python.org/3/library/re.html and (?aiLmsux) section -# and https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/filter/core.py#L124 -# C. The \\A and \\Z match the beginning and end of a string (in case of multiline strings -# do not want to match every line) - -- name: Set defaults for vault_cmds and secret_files_vault_cmds to prevent undefined variable errors - ansible.builtin.set_fact: - vault_cmds: "{{ vault_cmds | default({}) }}" - secret_files_vault_cmds: "{{ secret_files_vault_cmds | default({}) }}" - -- name: Set secrets vault commands fact - ansible.builtin.set_fact: - vault_cmds: "{{ vault_cmds | combine({ item.key: vault_cmd}) }}" - vars: - vault_cmd: "vault kv put '{{ vault_path }}/{{ item.key }}' - {{ item.value.keys() | zip(item.value.values() | map('regex_replace', '(?ms)\\A(.*)\\Z', \"'\\1'\")) | map('join', '=') | list | join(' ') }}" - loop: - "{{ secrets | dict2items }}" - loop_control: - label: "{{ item.key }}" - -- name: Set files vault commands fact - ansible.builtin.set_fact: - secret_files_vault_cmds: "{{ secret_files_vault_cmds | combine({ item.key: vault_cmd}) }}" - vars: - vault_cmd: "cat '{{ item.value | expanduser }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'cat - > /tmp/vault_content'; oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'base64 --wrap=0 /tmp/vault_content | vault kv put {{ vault_path }}/{{ item.key }} b64content=- content=@/tmp/vault_content; rm /tmp/vault_content'" # noqa yaml - loop: - "{{ file_secrets | dict2items }}" - loop_control: - label: "{{ item.key }}" - -- name: Debug vault commands - ansible.builtin.debug: - msg: "{{ vault_cmds }}" - when: debug | default(False) | bool - -- name: Debug files vault commands - ansible.builtin.debug: - msg: "{{ secret_files_vault_cmds }}" - when: debug | default(False) | bool - -# This step is not really needed when running make vault-init + load-secrets as -# everything is sequential -# It is needed when the vault is unsealed/configured inside the cluster and load-secrets -# gets run *while* the cronjob configures the vault. I.e. it might be half configured and return -# errors -- name: Make sure that the vault auth policy exists - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: - sh -c "vault list auth/{{ vault_hub }}/role | grep '{{ vault_hub }}-role'" - register: vault_role_cmd - until: vault_role_cmd.rc == 0 - retries: 20 - delay: 45 - changed_when: false - -- name: Add the actual secrets to the vault - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: | - sh -c "{{ item.value }}" - loop: - "{{ vault_cmds | dict2items }}" - loop_control: - label: "{{ item.key }}" - when: not debug | default(False) | bool - - # This has to be shell because of the use of stdin and pipes -- name: Add the file secrets to the vault - ansible.builtin.shell: # noqa: command-instead-of-shell - "{{ item.value }}" - loop: - "{{ secret_files_vault_cmds | dict2items }}" - loop_control: - label: "{{ item.key }}" - when: not debug | default(False) | bool diff --git a/common/ansible/roles/vault_utils/tasks/vault_delete.yaml b/common/ansible/roles/vault_utils/tasks/vault_delete.yaml deleted file mode 100644 index f0e289e7..00000000 --- a/common/ansible/roles/vault_utils/tasks/vault_delete.yaml +++ /dev/null @@ -1,23 +0,0 @@ ---- -- include_tasks: pre_check.yaml -- include_tasks: vault_status.yaml - -# Note: We do not wait on purpose here as the pod will be recreated -# anyways and that would race. We are fine with having it gone once -- name: Delete vault pod - kubernetes.core.k8s: - state: absent - api_version: v1 - kind: Pod - namespace: "{{ vault_ns }}" - name: "{{ item }}" - loop: "{{ vault_pods }}" - -- name: Delete vault pvc - kubernetes.core.k8s: - state: absent - api_version: v1 - kind: PersistentVolumeClaim - namespace: "{{ vault_ns }}" - name: "data-{{ item }}" - loop: "{{ vault_pods }}" diff --git a/common/ansible/roles/vault_utils/tasks/vault_init.yaml b/common/ansible/roles/vault_utils/tasks/vault_init.yaml deleted file mode 100644 index e3ba9cb6..00000000 --- a/common/ansible/roles/vault_utils/tasks/vault_init.yaml +++ /dev/null @@ -1,85 +0,0 @@ ---- -- include_tasks: pre_check.yaml -- include_tasks: vault_status.yaml - -# If the vault is already initialized we skip all the tasks below -- name: Is the vault initialized? - ansible.builtin.set_fact: - vault_initialized: "{{ vault_status['initialized'] | bool }}" - -# Note that the 'realpath' filter explicitely only resolves on the ansible/local box -# which is fine in our case -- name: Set absolute path for output_file - ansible.builtin.set_fact: - output_file_abs: "{{ output_file | realpath }}" - when: - - not vault_initialized - - file_unseal - -- name: Check for existence of "{{ output_file_abs }}" - ansible.builtin.stat: - path: "{{ output_file_abs }}" - register: result - when: - - not vault_initialized - - file_unseal - -- name: Rename "{{ output_file_abs }} if it exists" - ansible.builtin.copy: - src: "{{ output_file_abs }}" - dest: "{{ output_file_abs }}.bak" - mode: '0600' - when: - - not vault_initialized - - file_unseal - - result.stat.exists - -# We need to retry here because the vault service might be starting -# and can return a 500 internal server until its state is fully ready -- name: Init vault operator - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault operator init -format=json - register: vault_init_json_out - until: vault_init_json_out is not failed - retries: 10 - delay: 15 - when: not vault_initialized - -- name: Set vault init output json fact - ansible.builtin.set_fact: - vault_init_json: "{{ vault_init_json_out.stdout | from_json }}" - when: not vault_initialized - -# We store the the operator unseal keys and root token to a file when -# the vault was not already initialized *and* when unseal_from_cluster -# is set to false -- name: Save vault operator output (local file) - ansible.builtin.copy: - follow: true - dest: "{{ output_file_abs }}" - content: "{{ vault_init_json | to_nice_json }}" - mode: '0600' - when: - - not vault_initialized - - file_unseal - -# We store the the operator unseal keys and root token to a secret inside -# the cluster when the vault was not already initialized *and* when -# unseal_from_cluster is set to true -- name: Save vault operator output (into a secret inside the cluster) - kubernetes.core.k8s: - state: present - definition: - apiVersion: v1 - kind: Secret - type: Opaque - metadata: - name: "{{ unseal_secret }}" - namespace: "{{ unseal_namespace }}" - data: - vault_data_json: "{{ vault_init_json | to_nice_json | b64encode }}" - when: - - not vault_initialized - - not file_unseal diff --git a/common/ansible/roles/vault_utils/tasks/vault_pki_init.yaml b/common/ansible/roles/vault_utils/tasks/vault_pki_init.yaml deleted file mode 100644 index ca5fe697..00000000 --- a/common/ansible/roles/vault_utils/tasks/vault_pki_init.yaml +++ /dev/null @@ -1,50 +0,0 @@ -# NOTE: This task is currently not used ---- -- include_tasks: pre_check.yaml - -- name: Fetch Ingress object - kubernetes.core.k8s_info: - kind: Ingress - api_version: config.openshift.io/v1 - register: ingress_domain_raw - -# We split the domain and skip the first two parts -# apps.bandini-dc.blueprints.rhecoeng.com -> blueprints.rhecoeng.com -- name: Set ingress domain - ansible.builtin.set_fact: - ingress_domain_out: "{{ ingress_raw.resources[0].spec.domain.split('.')[2:] | join('.') }}" - -- name: Set ingress domain cleaned up - ansible.builtin.set_fact: - ingress_domain: "{{ ingress_domain_out.stdout }}" - cert_role: "{{ ingress_domain_out.stdout | replace('.', '_') }}" - -- name: Enable vault pki backend - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault secrets enable pki - -- name: Set vault pki max-lease-ttl - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault secrets tune --max-lease-ttl="{{ vault_pki_max_lease_ttl }}" pki - -- name: Set vault pki generate/internal CN - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault write pki/root/generate/internal common_name="{{ ingress_domain }}" ttl="{{ vault_pki_max_lease_ttl }}" - -- name: Set vault pki CRL - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl" - -- name: Set vault pki roles - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault write pki/roles/"{{ cert_role }}" allowed_domains="{{ ingress_domain }}" allow_subdomains=true max_ttl="{{ vault_pki_max_lease_ttl }}" diff --git a/common/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/common/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml deleted file mode 100644 index 9fdb9024..00000000 --- a/common/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ /dev/null @@ -1,80 +0,0 @@ ---- -- include_tasks: pre_check.yaml - -- name: Is secrets backend already enabled - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: > - bash -e -c "vault secrets list | grep -e '^{{ vault_base_path }}'" - register: secrets_enabled - failed_when: false - -- name: Create secrets backend kv-v2 - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault secrets enable -path="{{ vault_base_path }}" kv-v2 - when: secrets_enabled.rc != 0 - -- name: Is kubernetes backend already enabled - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: > - bash -e -c "vault auth list | grep -e '^{{ vault_hub }}'" - register: kubernetes_enabled - failed_when: false - -- name: Enable kubernetes backend on hub - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: "vault auth enable -path={{ vault_hub }} kubernetes" - when: kubernetes_enabled.rc != 0 - -- name: Get token from service account secret {{ external_secrets_ns }}/{{ external_secrets_secret }} - kubernetes.core.k8s_info: - kind: Secret - namespace: "{{ external_secrets_ns }}" - name: "{{ external_secrets_secret }}" - api_version: v1 - register: token_data - failed_when: token_data.resources | length == 0 - -- name: Set sa_token fact - ansible.builtin.set_fact: - sa_token: "{{ token_data.resources[0].data.token | b64decode }}" - -- name: Configure hub kubernetes backend - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: bash -e -c "vault write auth/{{ vault_hub }}/config token_reviewer_jwt={{ sa_token }} - kubernetes_host={{ vault_hub_kubernetes_host }} - kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt - issuer=https://kubernetes.default.svc" - -- name: Configure policy template for hub - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: > - bash -e -c "echo \"path \\\"secret/data/{{ vault_hub }}/*\\\" { - capabilities = {{ vault_hub_capabilities }} }\" > /tmp/policy-{{ vault_hub }}.hcl" - -- name: Configure policy for hub - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: "vault policy write {{ vault_hub }}-secret /tmp/policy-{{ vault_hub }}.hcl" - -- name: Configure kubernetes role for hub - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: > - vault write auth/"{{ vault_hub }}"/role/"{{ vault_hub }}"-role - bound_service_account_names="{{ external_secrets_sa }}" - bound_service_account_namespaces="{{ external_secrets_ns }}" - policies="default,{{ vault_hub }}-secret" ttl="{{ vault_hub_ttl }}" diff --git a/common/ansible/roles/vault_utils/tasks/vault_status.yaml b/common/ansible/roles/vault_utils/tasks/vault_status.yaml deleted file mode 100644 index 9dc3e426..00000000 --- a/common/ansible/roles/vault_utils/tasks/vault_status.yaml +++ /dev/null @@ -1,61 +0,0 @@ ---- -# Registers a variable valled vault_status containing the vault's status json dict -- name: Check for vault namespace - kubernetes.core.k8s_info: - kind: Namespace - name: "{{ vault_ns }}" - register: vault_ns_rc - until: vault_ns_rc.resources | length > 0 - retries: 20 - delay: 45 - -- name: Check if the vault pod is present - kubernetes.core.k8s_info: - kind: Pod - namespace: "{{ vault_ns }}" - name: "{{ vault_pod }}" - register: vault_pod_rc - until: vault_pod_rc.resources | length > 0 - retries: 20 - delay: 45 - -# This needs retrying because during startup we can just get -# Failed to execute on pod vault-0 due to : (0)\nReason: Handshake status 500 Internal Server Error -# In the above case there is no 'rc' in vault_status. So first we wait for 'rc' to show up and ignore -# any errors, and then we bail out if rc is 2 as it means the vault is already initialized -- name: Check for the vault status - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault status -format=json - register: vault_status_json - until: "'rc' in vault_status_json" - retries: 20 - delay: 45 - failed_when: "'stdout_lines' not in vault_status_json" - -- name: Set vault status output json fact - ansible.builtin.set_fact: - vault_status: "{{ vault_status_json.stdout | from_json }}" - when: vault_status_json.stdout_lines | length > 0 - -- name: List Vault pods - kubernetes.core.k8s_info: - namespace: "{{ vault_ns }}" - kind: Pod - label_selectors: - - "component = server" - register: vault_pods_list - -- name: "Get pods" - ansible.builtin.set_fact: - vault_pods: "{{ vault_pods + [item.metadata.name] }}" - loop: "{{ vault_pods_list.resources }}" - loop_control: - label: "{{ item.metadata.name }}" - vars: - vault_pods: [] - -- name: "Followers" - ansible.builtin.set_fact: - followers: "{{ vault_pods | difference(vault_pod) }}" diff --git a/common/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/common/ansible/roles/vault_utils/tasks/vault_unseal.yaml deleted file mode 100644 index 10e63629..00000000 --- a/common/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ /dev/null @@ -1,116 +0,0 @@ ---- -- include_tasks: pre_check.yaml -- include_tasks: vault_status.yaml - -# If the vault is already unsealed we skip all the tasks below -- name: Is the vault sealed? - ansible.builtin.set_fact: - vault_sealed: "{{ vault_status['sealed'] | bool }}" - -# Note that the 'realpath' filter explicitely only resolves on the ansible/local box -# which is fine in our case -- name: Set absolute path for output_file - ansible.builtin.set_fact: - output_file_abs: "{{ output_file | realpath }}" - when: - - vault_sealed - - file_unseal - -- name: Check for existence of "{{ output_file_abs }}" - ansible.builtin.stat: - path: "{{ output_file_abs }}" - register: result - when: - - vault_sealed - - file_unseal - -- name: Fail if "{{ output_file_abs }}" does not exists - ansible.builtin.fail: - msg: "{{ output_file_abs }} does not exist. Stopping here" - failed_when: not result.stat.exists - when: - - vault_sealed - - file_unseal - -# We reparse the json vault init file in case unseal was called without operator init before -# and if file_unseal is true -- name: Parse "{{ output_file_abs }}" - ansible.builtin.set_fact: - vault_init_json: "{{ lookup('file', output_file_abs) | from_json }}" - when: - - vault_sealed - - file_unseal - -# We reparse the json vault init secret in case unseal was called without operator init before -# and if file_unseal is false -- name: Parse "{{ output_file_abs }}" - kubernetes.core.k8s_info: - kind: Secret - namespace: "{{ unseal_namespace }}" - name: "{{ unseal_secret }}" - api_version: v1 - register: vault_init_data - when: - - vault_sealed - - not file_unseal - -- name: Set vault init json - ansible.builtin.set_fact: - vault_init_json: "{{ vault_init_data.resources[0].data.vault_data_json | b64decode | from_json }}" - when: - - vault_sealed - - not file_unseal - -- name: Set root token and unseal_keys - ansible.builtin.set_fact: - root_token: "{{ vault_init_json['root_token'] }}" - unseal_keys: "{{ vault_init_json['unseal_keys_hex'] }}" - when: vault_sealed - -- name: Unseal leader - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault operator unseal "{{ item }}" - loop: "{{ unseal_keys }}" - loop_control: - extended: true - label: "Unsealing with key {{ ansible_loop.index }}" - when: vault_sealed - -- name: Join Raft cluster - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ item }}" - command: vault operator raft join http://{{ vault_pod }}.{{ vault_ns }}-internal:8200 - register: join_raft_cluster_out - until: join_raft_cluster_out is not failed - retries: 10 - delay: 15 - loop: "{{ followers }}" - loop_control: - extended: true - label: "Joining Raft Cluster on http://{{ vault_pod }}.{{ vault_ns }}-internal:8200" - when: - - vault_sealed - - followers | length > 0 - -- name: Unseal followers - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ item.0 }}" - command: vault operator unseal "{{ item.1 }}" - loop: "{{ followers | product(unseal_keys) | list }}" - loop_control: - extended: true - label: "Unsealing {{ item.0 }} with key {{ ansible_loop.index }}" - when: - - vault_sealed - - followers | length > 0 - -- name: Login into vault - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault login "{{ root_token }}" - when: vault_sealed diff --git a/common/ansible/roles/vault_utils/tests/inventory b/common/ansible/roles/vault_utils/tests/inventory deleted file mode 100644 index 878877b0..00000000 --- a/common/ansible/roles/vault_utils/tests/inventory +++ /dev/null @@ -1,2 +0,0 @@ -localhost - diff --git a/common/ansible/roles/vault_utils/tests/test.yml b/common/ansible/roles/vault_utils/tests/test.yml deleted file mode 100644 index 0998beb6..00000000 --- a/common/ansible/roles/vault_utils/tests/test.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -- hosts: localhost - remote_user: root - roles: - - vault_utils diff --git a/common/ansible/roles/vault_utils/vars/main.yml b/common/ansible/roles/vault_utils/vars/main.yml deleted file mode 100644 index f6e02b93..00000000 --- a/common/ansible/roles/vault_utils/vars/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# vars file for vault_utils diff --git a/common/clustergroup/.helmignore b/common/clustergroup/.helmignore deleted file mode 100644 index b25c15b8..00000000 --- a/common/clustergroup/.helmignore +++ /dev/null @@ -1 +0,0 @@ -*~ diff --git a/common/clustergroup/Chart.yaml b/common/clustergroup/Chart.yaml deleted file mode 100644 index 249163ae..00000000 --- a/common/clustergroup/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -description: A Helm chart to create per-clustergroup ArgoCD applications and any required namespaces or subscriptions -keywords: -- pattern -name: pattern-clustergroup -version: 0.0.1 diff --git a/common/clustergroup/templates/applications.yaml b/common/clustergroup/templates/applications.yaml deleted file mode 100644 index 3ffd6a0f..00000000 --- a/common/clustergroup/templates/applications.yaml +++ /dev/null @@ -1,207 +0,0 @@ -{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} -{{- range .Values.clusterGroup.applications }} -{{- if or (.generators) (.generatorFile) (.useGeneratorValues) (.destinationServer) (.destinationNamespace) }} -apiVersion: argoproj.io/v1alpha1 -kind: ApplicationSet -metadata: - name: {{ .name }} - namespace: {{ $namespace }} - labels: - app: {{ .name }} -spec: - {{- if .generators }} - generators: {{ .generators | toPrettyJson }} - {{- else }} - generators: - - git: - repoURL: {{ $.Values.global.repoURL }} - revision: {{ $.Values.global.targetRevision }} - {{- if .generatorFile }} - files: - - path: {{ .generatorFile | quote }} - {{- end }} - {{- end }} - template: - metadata: - name: {{ coalesce .namespace $namespace }} - spec: - project: {{ .project }} - {{- if .syncPolicy }} - syncPolicy: {{ .syncPolicy | toPrettyJson }} - {{- else }} - syncPolicy: - automated: {} - {{- end }} - {{- if .ignoreDifferences }} - ignoreDifferences: {{ .ignoreDifferences | toPrettyJson }} - {{- end }} - source: - repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} - targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} - {{- if .chart }} - chart: {{ .chart }} - {{- end }} - {{- if .path }} - path: {{ .path }} - {{- end }} - {{- if .plugin }} - plugin: {{ .plugin }} - {{- end }} - {{- if not .kustomize }} - helm: - ignoreMissingValueFiles: true - valueFiles: - - "values.yaml" - {{- range .extraValueFiles }} - - {{ . | quote }} - {{- end }} - {{- if .useGeneratorValues }} - values: |- - {{ `{{ values }}` }} - {{- end }} - parameters: - - name: global.hubClusterDomain - value: {{ $.Values.global.hubClusterDomain }} - - name: global.localClusterDomain - value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} - - name: global.repoURL - value: {{ $.Values.global.repoURL }} - - name: global.targetRevision - value: {{ $.Values.global.targetRevision }} - - name: global.namespace - value: {{ $.Values.global.namespace }} - - name: global.pattern - value: {{ $.Values.global.pattern }} - {{- range .extraHubClusterDomainFields }} - - name: {{ . }} - value: {{ $.Values.global.hubClusterDomain }} - {{- end }} - {{- range .extraLocalClusterDomainFields }} - - name: {{ . }} - value: {{ $.Values.global.localClusterDomain }} - {{- end }} - {{- range .extraRepoURLFields }} - - name: {{ . }} - value: {{ $.Values.global.repoURL }} - {{- end }} - {{- range .extraTargetRevisionFields }} - - name: {{ . }} - value: {{ $.Values.global.targetRevision }} - {{- end }} - {{- range .extraNamespaceFields }} - - name: {{ . }} - value: {{ $.Values.global.namespace }} - {{- end }} - {{- range .extraPatternNameFields }} - - name: {{ . }} - value: {{ $.Values.global.pattern }} - {{- end }} - {{- range .overrides }} - - name: {{ .name }} - value: {{ .value | quote }} - {{- if .forceString }} - forceString: true - {{- end }} - {{- end }} - {{- end }} - destination: - server: {{ coalesce .destinationServer "https://kubernetes.default.svc" }} - namespace: {{ coalesce .destinationNamespace .namespace $namespace }} -{{- else }} -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: {{ .name }} - namespace: {{ $namespace }} - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: {{ coalesce .clusterName "in-cluster" }} - namespace: {{ default $namespace .namespace }} - project: {{ .project }} - source: - repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} - targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} - {{- if .chart }} - chart: {{ .chart }} - {{- else }} - path: {{ .path }} - {{- end }} - {{- if .plugin }} - plugin: {{ .plugin | toPrettyJson }} - {{- else if not .kustomize }} - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-{{ $.Values.clusterGroup.name }}.yaml" - {{- range $valueFile := .extraValueFiles }} - - {{ $valueFile | quote }} - {{- end }} - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: {{ $.Values.global.pattern }} - - name: global.hubClusterDomain - value: {{ $.Values.global.hubClusterDomain }} - - name: global.localClusterDomain - value: {{ coalesce $.Values.global.localClusterDomain $.Values.global.hubClusterDomain }} - {{- range .extraHubClusterDomainFields }} - - name: {{ . }} - value: {{ $.Values.global.hubClusterDomain }} - {{- end }} - {{- range .extraLocalClusterDomainFields }} - - name: {{ . }} - value: {{ $.Values.global.localClusterDomain }} - {{- end }} - {{- range .extraRepoURLFields }} - - name: {{ . }} - value: $ARGOCD_APP_SOURCE_REPO_URL - {{- end }} - {{- range .extraTargetRevisionFields }} - - name: {{ . }} - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - {{- end }} - {{- range .extraNamespaceFields }} - - name: {{ . }} - value: $ARGOCD_APP_NAMESPACE - {{- end }} - {{- range .extraPatternNameFields }} - - name: {{ . }} - value: {{ $.Values.global.pattern }} - {{- end }} - {{- range .overrides }} - - name: {{ .name }} - value: {{ .value | quote }} - {{- if .forceString }} - forceString: true - {{- end }} - {{- end }} - {{- if .fileParameters }} - fileParameters: - {{- range .fileParameters }} - - name: {{ .name }} - path: {{ .path }} - {{- end }} - {{- end }} - {{- end }} - {{- if .ignoreDifferences }} - ignoreDifferences: {{ .ignoreDifferences | toPrettyJson }} - {{- end }} - {{- if .syncPolicy }} - syncPolicy: {{ .syncPolicy | toPrettyJson }} - {{- else }} - syncPolicy: - automated: {} - # selfHeal: true - {{- end }} ---- -{{- end }} -{{- end }} diff --git a/common/clustergroup/templates/argocd-super-role.yaml b/common/clustergroup/templates/argocd-super-role.yaml deleted file mode 100644 index 78af462d..00000000 --- a/common/clustergroup/templates/argocd-super-role.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: openshift-gitops-cluster-admin-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: - - kind: ServiceAccount - name: openshift-gitops-argocd-application-controller - namespace: openshift-gitops - # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - - kind: ServiceAccount - name: openshift-gitops-argocd-server - namespace: openshift-gitops ---- -# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }}-cluster-admin-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: - - kind: ServiceAccount - # This is the {ArgoCD.name}-argocd-application-controller - name: {{ .Values.clusterGroup.name }}-gitops-argocd-application-controller - namespace: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} - # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - - kind: ServiceAccount - # This is the {ArgoCD.name}-argocd-server - name: {{ .Values.clusterGroup.name }}-gitops-argocd-server - namespace: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} - # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) - - kind: ServiceAccount - name: {{ .Values.clusterGroup.name }}-gitops-argocd-dex-server - namespace: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} diff --git a/common/clustergroup/templates/argocd.yaml b/common/clustergroup/templates/argocd.yaml deleted file mode 100644 index 0126c0a5..00000000 --- a/common/clustergroup/templates/argocd.yaml +++ /dev/null @@ -1,123 +0,0 @@ -{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} -apiVersion: argoproj.io/v1alpha1 -kind: ArgoCD -metadata: - finalizers: - - argoproj.io/finalizer - # Changing the name affects the ClusterRoleBinding, the generated secret, - # route URL, and argocd.argoproj.io/managed-by annotations - name: {{ .Values.clusterGroup.name }}-gitops - namespace: {{ $namespace }} - annotations: - argocd.argoproj.io/compare-options: IgnoreExtraneous -spec: - applicationInstanceLabelKey: argocd.argoproj.io/instance - # Not the greatest way to pass git/quay info to sub-applications, but it will do until - # we can support helmChart with kustomize - # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - configManagementPlugins: | - - name: kustomize-version - generate: - command: ["sh", "-c"] - args: ["kustomize version 1>&2 && exit 1"] - - name: kustomize-with-helm - generate: - command: ["kustomize"] - args: ["build", "--enable-helm"] - - name: helm-with-kustomize - init: - command: ["/bin/sh", "-c"] - args: ["helm dependency build"] - generate: - command: ["/bin/bash", "-c"] - args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f $(git rev-parse --show-toplevel)/values-global.yaml - -f $(git rev-parse --show-toplevel)/values-{{ .Values.clusterGroup.name }}.yaml - --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL - --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION - --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern={{ .Values.global.pattern }} - --set global.hubClusterDomain={{ .Values.global.hubClusterDomain }} - --set global.localClusterDomain={{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }} - --post-renderer ./kustomize"] - applicationSet: - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 250m - memory: 512Mi - controller: - processors: {} - resources: - limits: - cpu: "4" - memory: 4Gi - requests: - cpu: 500m - memory: 2Gi - dex: - openShiftOAuth: true - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 250m - memory: 128Mi - initialSSHKnownHosts: {} - rbac: - defaultPolicy: role:admin - repo: - resources: - limits: - cpu: "1" - memory: 512Mi - requests: - cpu: 250m - memory: 256Mi - resourceExclusions: | - - apiGroups: - - tekton.dev - kinds: - - TaskRun - - PipelineRun - server: - autoscale: - enabled: false - grpc: - ingress: - enabled: false - ingress: - enabled: false - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 125m - memory: 128Mi - route: - enabled: true - tls: - insecureEdgeTerminationPolicy: Redirect - termination: reencrypt - service: - type: "" - tls: - ca: {} -status: ---- -apiVersion: console.openshift.io/v1 -kind: ConsoleLink -metadata: - name: {{ .Values.clusterGroup.name }}-gitops-link - namespace: {{ $namespace }} -spec: - applicationMenu: - section: OpenShift GitOps - imageURL: data:image/png;base64,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 - href: 'https://{{ .Values.clusterGroup.name }}-gitops-server-{{ $namespace }}.{{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }}' - location: ApplicationMenu - text: '{{ title .Values.clusterGroup.name }} ArgoCD' diff --git a/common/clustergroup/templates/catalog-sources.yaml b/common/clustergroup/templates/catalog-sources.yaml deleted file mode 100644 index 35208b37..00000000 --- a/common/clustergroup/templates/catalog-sources.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- range .Values.clusterGroup.indexImages }} -apiVersion: operators.coreos.com/v1alpha1 -kind: CatalogSource -metadata: - name: {{ .name }} - namespace: openshift-marketplace -spec: - sourceType: grpc - image: {{ .image }}:{{ .version }} -{{- end -}} diff --git a/common/clustergroup/templates/cluster-external-secrets.yaml b/common/clustergroup/templates/cluster-external-secrets.yaml deleted file mode 100644 index b01e3e3d..00000000 --- a/common/clustergroup/templates/cluster-external-secrets.yaml +++ /dev/null @@ -1,51 +0,0 @@ -{{- $namespace := cat $.Values.global.pattern $.Values.clusterGroup.name | replace " " "-" }} -{{ if .Values.clusterGroup.isHubCluster }} -{{- range .Values.clusterGroup.externalClusters }} ---- -apiVersion: "external-secrets.io/v1beta1" -kind: ExternalSecret -metadata: - name: {{ . | kebabcase }}-secret - namespace: {{ $namespace }} - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - argocd.argoproj.io/sync-wave: "100" -spec: - refreshInterval: 15s - secretStoreRef: - name: {{ $.Values.secretStore.name }} - kind: {{ $.Values.secretStore.kind }} - target: - name: {{ . | kebabcase }}-secret - template: - type: Opaque - metadata: - labels: - argocd.argoproj.io/secret-type: cluster - data: - name: {{ . }} - server: |- - {{ "{{ .kubeServer | toString }}" }} - config: | - { - "bearerToken": {{ "{{ .kubeBearer | toString | quote }}" }}, - "tlsClientConfig": { - "insecure": false, - "caData": {{ "{{ .kubeCA | toString | quote }}" }} - } - } - data: - - secretKey: kubeServer - remoteRef: - key: {{ $.Values.secretsBase.key }}/cluster_{{ . }} - property: server - - secretKey: kubeBearer - remoteRef: - key: {{ $.Values.secretsBase.key }}/cluster_{{ . }} - property: bearerToken - - secretKey: kubeCA - remoteRef: - key: {{ $.Values.secretsBase.key }}/cluster_{{ . }}_ca - property: b64content -{{- end }} -{{ end }} diff --git a/common/clustergroup/templates/gitops-namespace.yaml b/common/clustergroup/templates/gitops-namespace.yaml deleted file mode 100644 index 785ef75f..00000000 --- a/common/clustergroup/templates/gitops-namespace.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - name: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} - # The name here needs to be consistent with - # - acm/templates/policies/application-policies.yaml - # - clustergroup/templates/applications.yaml - # - any references to secrets and route URLs in documentation - name: {{ $.Values.global.pattern }}-{{ .Values.clusterGroup.name }} -spec: {} diff --git a/common/clustergroup/templates/imperative/_helpers.tpl b/common/clustergroup/templates/imperative/_helpers.tpl deleted file mode 100644 index 8a946b3c..00000000 --- a/common/clustergroup/templates/imperative/_helpers.tpl +++ /dev/null @@ -1,38 +0,0 @@ -{{/* git-init InitContainer */}} -{{- define "imperative.initcontainers.gitinit" }} -- name: git-init - image: {{ $.Values.clusterGroup.imperative.image }} - imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} - env: - - name: HOME - value: /git/home - command: - - 'sh' - - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch {{ $.Values.global.targetRevision }} --depth 1 -- {{ $.Values.global.repoURL }} /git/repo;chmod 0770 /git/{repo,home}" - volumeMounts: - - name: git - mountPath: "/git" -{{- end }} - -{{/* Final done container */}} -{{- define "imperative.containers.done" }} -- name: "done" - image: {{ $.Values.clusterGroup.imperative.image }} - imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} - command: - - 'sh' - - '-c' - - 'echo' - - 'done' - - '\n' -{{- end }} - -{{/* volume-mounts for all containers */}} -{{- define "imperative.volumemounts" }} -- name: git - mountPath: "/git" -- name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml -{{- end }} diff --git a/common/clustergroup/templates/imperative/clusterrole.yaml b/common/clustergroup/templates/imperative/clusterrole.yaml deleted file mode 100644 index 17e33d8d..00000000 --- a/common/clustergroup/templates/imperative/clusterrole.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{/* Define this if needed (jobs defined or insecure unseal configured) */}} -{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) - (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ $.Values.clusterGroup.imperative.clusterRoleName }} -rules: -{{- if $.Values.clusterGroup.imperative.clusterRoleYaml -}} - {{ toYaml $.Values.clusterGroup.imperative.clusterRoleYaml | nindent 2 }} -{{- else }} - - apiGroups: - - '*' - resources: - - '*' - verbs: - - get - - list - - watch -{{- end }} -{{- end }} diff --git a/common/clustergroup/templates/imperative/configmap.yaml b/common/clustergroup/templates/imperative/configmap.yaml deleted file mode 100644 index 5cde2d37..00000000 --- a/common/clustergroup/templates/imperative/configmap.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{/* Define this if needed (jobs defined or insecure unseal configured) */}} -{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) - (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} -{{- $valuesyaml := toYaml $.Values -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }} - namespace: {{ $.Values.clusterGroup.imperative.namespace}} -data: - values.yaml: | -{{ tpl $valuesyaml . | indent 4 }} -{{ end }} diff --git a/common/clustergroup/templates/imperative/job.yaml b/common/clustergroup/templates/imperative/job.yaml deleted file mode 100644 index b237e11f..00000000 --- a/common/clustergroup/templates/imperative/job.yaml +++ /dev/null @@ -1,67 +0,0 @@ -{{/* Define this if needed (jobs defined */}} -{{- if (gt (len $.Values.clusterGroup.imperative.jobs) 0) -}} ---- -apiVersion: batch/v1 -kind: CronJob -metadata: - name: {{ $.Values.clusterGroup.imperative.cronJobName }} - namespace: {{ $.Values.clusterGroup.imperative.namespace}} -spec: - schedule: {{ $.Values.clusterGroup.imperative.schedule | quote }} - # if previous Job is still running, skip execution of a new Job - concurrencyPolicy: Forbid - jobTemplate: - spec: - activeDeadlineSeconds: {{ $.Values.clusterGroup.imperative.activeDeadlineSeconds }} - template: - metadata: - name: {{ $.Values.clusterGroup.imperative.jobName }} - spec: - serviceAccountName: {{ $.Values.clusterGroup.imperative.serviceAccountName }} - initContainers: - # git init happens in /git/repo so that we can set the folder to 0770 permissions - # reason for that is ansible refuses to create temporary folders in there - {{- include "imperative.initcontainers.gitinit" . | indent 12 }} - {{- range $.Values.clusterGroup.imperative.jobs }} - {{- if ne (.disabled | default "false" | toString | lower ) "true" }} - - name: {{ .name }} - image: {{ .image | default $.Values.clusterGroup.imperative.image }} - imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} - env: - - name: HOME - value: /git/home - workingDir: /git/repo - # We have a default timeout of 600s for each playbook. Can be overridden - # on a per-job basis - command: - - timeout - - {{ .timeout | default "600" | quote }} - - ansible-playbook - {{- if .verbosity }} - - {{ .verbosity }} - {{- end }} - {{- if .tags }} - - -t - - {{ .tags }} - {{- end }} - - -e - - "@/values/values.yaml" - {{- range .extravars }} - - -e - - {{ . | quote }} - {{- end }} - - {{ .playbook }} - volumeMounts: - {{- include "imperative.volumemounts" . | indent 16 }} - {{- end }} - {{- end }} - containers: - {{- include "imperative.containers.done" . | indent 12 }} - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }} - restartPolicy: Never -{{ end }} diff --git a/common/clustergroup/templates/imperative/namespace.yaml b/common/clustergroup/templates/imperative/namespace.yaml deleted file mode 100644 index 827bbee5..00000000 --- a/common/clustergroup/templates/imperative/namespace.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{/* Define this if needed (jobs defined or insecure unseal configured) */}} -{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) - (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} -apiVersion: v1 -kind: Namespace -metadata: - labels: - name: {{ $.Values.clusterGroup.imperative.namespace }} - argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} - name: {{ $.Values.clusterGroup.imperative.namespace }} -{{ end }} diff --git a/common/clustergroup/templates/imperative/rbac.yaml b/common/clustergroup/templates/imperative/rbac.yaml deleted file mode 100644 index f62b23ac..00000000 --- a/common/clustergroup/templates/imperative/rbac.yaml +++ /dev/null @@ -1,31 +0,0 @@ -{{/* Define this if needed (jobs defined or insecure unseal configured) */}} -{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) - (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ $.Values.clusterGroup.imperative.namespace }}-cluster-admin-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ $.Values.clusterGroup.imperative.clusterRoleName }} -subjects: - - kind: ServiceAccount - name: {{ $.Values.clusterGroup.imperative.serviceAccountName }} - namespace: {{ $.Values.clusterGroup.imperative.namespace }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ $.Values.clusterGroup.imperative.namespace }}-admin-rolebinding - namespace: {{ $.Values.clusterGroup.imperative.namespace }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ $.Values.clusterGroup.imperative.roleName }} -subjects: - - kind: ServiceAccount - name: {{ $.Values.clusterGroup.imperative.serviceAccountName }} - namespace: {{ $.Values.clusterGroup.imperative.namespace }} -{{ end }} diff --git a/common/clustergroup/templates/imperative/role.yaml b/common/clustergroup/templates/imperative/role.yaml deleted file mode 100644 index f4909c76..00000000 --- a/common/clustergroup/templates/imperative/role.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{/* Define this if needed (jobs defined or insecure unseal configured) */}} -{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) - (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ $.Values.clusterGroup.imperative.roleName }} - namespace: {{ $.Values.clusterGroup.imperative.namespace }} -rules: -{{- if $.Values.clusterGroup.imperative.roleYaml -}} - {{ toYaml $.Values.clusterGroup.imperative.roleYaml | nindent 2 }} -{{- else }} - - apiGroups: - - '*' - resources: - - '*' - verbs: - - '*' -{{- end }} -{{- end }} diff --git a/common/clustergroup/templates/imperative/serviceaccount.yaml b/common/clustergroup/templates/imperative/serviceaccount.yaml deleted file mode 100644 index bb500deb..00000000 --- a/common/clustergroup/templates/imperative/serviceaccount.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{/* Define this if needed (jobs defined or insecure unseal configured) */}} -{{- if or (gt (len $.Values.clusterGroup.imperative.jobs) 0) - (and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster) -}} -{{- if $.Values.clusterGroup.imperative.serviceAccountCreate -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ $.Values.clusterGroup.imperative.serviceAccountName }} - namespace: {{ $.Values.clusterGroup.imperative.namespace }} -{{- end }} -{{- end }} diff --git a/common/clustergroup/templates/imperative/unsealjob.yaml b/common/clustergroup/templates/imperative/unsealjob.yaml deleted file mode 100644 index 0fae9071..00000000 --- a/common/clustergroup/templates/imperative/unsealjob.yaml +++ /dev/null @@ -1,59 +0,0 @@ -{{/* Only define this if the values.insecureUnsealVaultInsideCluster is set to tre and we're on the cluster */}} -{{- if and $.Values.clusterGroup.insecureUnsealVaultInsideCluster $.Values.clusterGroup.isHubCluster }} ---- -apiVersion: batch/v1 -kind: CronJob -metadata: - name: unsealvault-cronjob - namespace: {{ $.Values.clusterGroup.imperative.namespace}} -spec: - schedule: {{ $.Values.clusterGroup.imperative.insecureUnsealVaultInsideClusterSchedule | quote }} - # if previous Job is still running, skip execution of a new Job - concurrencyPolicy: Forbid - jobTemplate: - spec: - activeDeadlineSeconds: {{ $.Values.clusterGroup.imperative.activeDeadlineSeconds }} - template: - metadata: - name: unsealvault-job - spec: - serviceAccountName: {{ $.Values.clusterGroup.imperative.serviceAccountName }} - initContainers: - # git init happens in /git/repo so that we can set the folder to 0770 permissions - # reason for that is ansible refuses to create temporary folders in there - {{- include "imperative.initcontainers.gitinit" . | indent 12 }} - - name: unseal-playbook - image: {{ $.Values.clusterGroup.imperative.image }} - imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} - env: - - name: HOME - value: /git/home - workingDir: /git/repo - # We have a default timeout of 600s for each playbook. Can be overridden - # on a per-job basis - command: - - timeout - - {{ .timeout | default "600" | quote }} - - ansible-playbook - {{- if $.Values.clusterGroup.imperative.verbosity }} - - {{ $.Values.clusterGroup.imperative.verbosity }} - {{- end }} - - -e - - "@/values/values.yaml" - - -e - - '{"file_unseal": false}' - - -t - - 'vault_init,vault_unseal,vault_secrets_init' - - "common/ansible/playbooks/vault/vault.yaml" - volumeMounts: - {{- include "imperative.volumemounts" . | indent 16 }} - containers: - {{- include "imperative.containers.done" . | indent 12 }} - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }} - restartPolicy: Never -{{ end }} diff --git a/common/clustergroup/templates/namespaces.yaml b/common/clustergroup/templates/namespaces.yaml deleted file mode 100644 index b3bc86a5..00000000 --- a/common/clustergroup/templates/namespaces.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{- range .Values.clusterGroup.namespaces }} -apiVersion: v1 -kind: Namespace -metadata: - labels: - name: {{ default "pattern" $.Release.name }} - argocd.argoproj.io/managed-by: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} - name: {{ . }} -spec: ---- -{{- end }} diff --git a/common/clustergroup/templates/operatorgroup.yaml b/common/clustergroup/templates/operatorgroup.yaml deleted file mode 100644 index 0180a912..00000000 --- a/common/clustergroup/templates/operatorgroup.yaml +++ /dev/null @@ -1,25 +0,0 @@ -{{- range .Values.clusterGroup.namespaces }} - -{{- if empty $.Values.clusterGroup.operatorgroupExcludes }} -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup -metadata: - name: {{ . }}-operator-group - namespace: {{ . }} -spec: - targetNamespaces: - - {{ . }} ---- -{{- else if not (has . $.Values.clusterGroup.operatorgroupExcludes) }} -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup -metadata: - name: {{ . }}-operator-group - namespace: {{ . }} -spec: - targetNamespaces: - - {{ . }} ---- -{{- end }} - -{{- end }} diff --git a/common/clustergroup/templates/projects.yaml b/common/clustergroup/templates/projects.yaml deleted file mode 100644 index d74e2cba..00000000 --- a/common/clustergroup/templates/projects.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{- range .Values.clusterGroup.projects }} -apiVersion: argoproj.io/v1alpha1 -kind: AppProject -metadata: - name: {{ . }} - namespace: {{ $.Values.global.pattern }}-{{ $.Values.clusterGroup.name }} -spec: - description: "Pattern {{ . }}" - destinations: - - namespace: '*' - server: '*' - clusterResourceWhitelist: - - group: '*' - kind: '*' - namespaceResourceWhitelist: - - group: '*' - kind: '*' - sourceRepos: - - '*' -status: {} ---- -{{- end }} diff --git a/common/clustergroup/templates/subscriptions.yaml b/common/clustergroup/templates/subscriptions.yaml deleted file mode 100644 index 3fcd2d1a..00000000 --- a/common/clustergroup/templates/subscriptions.yaml +++ /dev/null @@ -1,67 +0,0 @@ -{{- range .Values.clusterGroup.subscriptions }} -{{- $subs := . }} -{{- $installPlanValue := .installPlanApproval }} - -{{- if $subs.namespaces }} -{{- if not $subs.disabled }} -{{- range .namespaces }} -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: {{ $subs.name }} - namespace: {{ . }} -spec: - name: {{ $subs.name }} - source: {{ default "redhat-operators" $subs.source }} - sourceNamespace: {{ default "openshift-marketplace" $subs.sourceNamespace }} - channel: {{ default "stable" $subs.channel }} - installPlanApproval: {{ coalesce $installPlanValue $.Values.global.options.installPlanApproval }} - {{- if $subs.config }} - {{- if $subs.config.env }} - config: - env: - {{- range $subs.config.env }} - - name: {{ .name }} - value: {{ .value }} - {{- end }} - {{- end }} - {{- end }} - {{- if $.Values.global.options.useCSV }} - startingCSV: {{ $subs.csv }} - {{- else if $subs.csv }} - startingCSV: {{ $subs.csv }} - {{- end }} ---- -{{- end }} -{{- end }} -{{- else if not $subs.disabled }} -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: {{ $subs.name }} - namespace: {{ default "openshift-operators" $subs.namespace }} -spec: - name: {{ $subs.name }} - source: {{ default "redhat-operators" $subs.source }} - sourceNamespace: {{ default "openshift-marketplace" $subs.sourceNamespace }} - channel: {{ default "stable" $subs.channel }} - installPlanApproval: {{ coalesce $installPlanValue $.Values.global.options.installPlanApproval }} - {{- if $subs.config }} - {{- if $subs.config.env }} - config: - env: - {{- range $subs.config.env }} - - name: {{ .name }} - value: {{ .value }} - {{- end }} - {{- end }} - {{- end }} - {{- if $.Values.global.options.useCSV }} - startingCSV: {{ $subs.csv }} - {{- else if $subs.csv }} - startingCSV: {{ $subs.csv }} - {{- end }} ---- -{{- end }} -{{- end }} ---- diff --git a/common/clustergroup/test.yaml b/common/clustergroup/test.yaml deleted file mode 100644 index 0ab52838..00000000 --- a/common/clustergroup/test.yaml +++ /dev/null @@ -1,104 +0,0 @@ -clusterGroup: - name: hub - isHubCluster: true - # Note: setting this to true stores the vault unseal keys inside a cluster secret and - # is fundamentally insecure - insecureUnsealVaultInsideCluster: false - - namespaces: - - open-cluster-management - - vault - - golang-external-secrets - - config-demo - - indexImages: - - name: snr - image: quay.io/mshitrit/self-node-remediation-manager-index - version: 0.0.104 - - subscriptions: - acm: - name: advanced-cluster-management - namespace: open-cluster-management - channel: release-2.5 - csv: advanced-cluster-management.v2.5.0 - - projects: - - hub - - config-demo - - applications: - acm: - name: acm - namespace: open-cluster-management - project: hub - path: common/acm - ignoreDifferences: - - group: internal.open-cluster-management.io - kind: ManagedClusterInfo - jsonPointers: - - /spec/loggingCA - - vault: - name: vault - namespace: vault - project: hub - chart: vault - repoURL: https://helm.releases.hashicorp.com - targetRevision: v0.21.0 - overrides: - - name: global.openshift - value: "true" - - name: injector.enabled - value: "false" - - name: ui.enabled - value: "true" - - name: ui.serviceType - value: LoadBalancer - - name: server.route.enabled - value: "true" - - name: server.route.host - value: null - - name: server.route.tls.termination - value: edge - - name: server.image.repository - value: "registry.connect.redhat.com/hashicorp/vault" - - name: server.image.tag - value: "1.11.2-ubi" - - golang-external-secrets: - name: golang-external-secrets - namespace: golang-external-secrets - project: hub - path: common/golang-external-secrets - - config-demo: - name: config-demo - namespace: config-demo - project: config-demo - path: charts/all/config-demo - - imperative: - # NOTE: We *must* use lists and not hashes. As hashes lose ordering once parsed by helm - # The default schedule is every 10 minutes: imperative.schedule - # Total timeout of all jobs is 1h: imperative.activeDeadlineSeconds - # imagePullPolicy is set to always: imperative.imagePullPolicy - # For additional overrides that apply to the jobs, please refer to - # https://hybrid-cloud-patterns.io/imperative-actions/#additional-job-customizations - jobs: - - name: regional-ca - # ansible playbook to be run - playbook: ansible/playbooks/on-hub-get-regional-ca.yml - # per playbook timeout in seconds - timeout: 234 - # verbosity: "-v" - - managedClusterGroups: - region-one: - name: region-one - labels: - - name: clusterGroup - value: region-one - helmOverrides: - - name: clusterGroup.isHubCluster - value: false diff --git a/common/clustergroup/values.yaml b/common/clustergroup/values.yaml deleted file mode 100644 index b6c802e6..00000000 --- a/common/clustergroup/values.yaml +++ /dev/null @@ -1,86 +0,0 @@ -global: - pattern: common - options: - useCSV: True - syncPolicy: Automatic - installPlanApproval: Automatic - -# Note that sometimes changing helm values might require a hard refresh (https://github.com/helm/helm/issues/3486) -clusterGroup: - name: example - isHubCluster: true - # Note: setting this to true stores the vault unseal keys inside a cluster secret and - # is fundamentally insecure - insecureUnsealVaultInsideCluster: false - - imperative: - jobs: [] - # This image contains ansible + kubernetes.core by default and is used to run the jobs - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - namespace: "imperative" - # configmap name in the namespace that will contain all helm values - valuesConfigMap: "helm-values-configmap" - cronJobName: "imperative-cronjob" - jobName: "imperative-job" - imagePullPolicy: Always - # This is the maximum timeout of all the jobs (1h) - activeDeadlineSeconds: 3600 - # By default we run this every 10minutes - schedule: "*/10 * * * *" - # Schedule used to trigger the vault unsealing (if explicitely enabled) - # Set to run every 5 minutes in order for load-secrets to succeed within - # a reasonable amount of time (it waits up to 15 mins) - insecureUnsealVaultInsideClusterSchedule: "*/5 * * * *" - # Increase ansible verbosity with '-v' or '-vv..' - verbosity: "" - serviceAccountCreate: true - # service account to be used to run the cron pods - serviceAccountName: imperative-sa - clusterRoleName: imperative-cluster-role - clusterRoleYaml: "" - roleName: imperative-role - roleYaml: "" - -secretStore: - name: vault-backend - kind: ClusterSecretStore - -# Depends on the value of 'vault_hub' ansible variable used -# during the installation -secretsBase: - key: secret/data/hub - -# managedClusterGroups: -# - name: factory -# # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git -# # Location of values-global.yaml, values-{name}.yaml, values-{app}.yaml -# targetRevision: main -# path: applications/factory -# helmOverrides: -# - name: clusterGroup.isHubCluster -# value: false -# clusterSelector: -# matchExpressions: -# - key: vendor -# operator: In -# values: -# - OpenShift -# -# namespaces: -# - open-cluster-management -# -# subscriptions: -# - name: advanced-cluster-management -# namespace: open-cluster-management -# source: redhat-operators -# channel: release-2.3 -# csv: v2.3.2 -# -# projects: -# - datacenter -# -# applications: -# - name: acm -# namespace: default -# project: datacenter -# path: applications/acm diff --git a/common/common b/common/common deleted file mode 120000 index 945c9b46..00000000 --- a/common/common +++ /dev/null @@ -1 +0,0 @@ -. \ No newline at end of file diff --git a/common/examples/blank/Chart.yaml b/common/examples/blank/Chart.yaml deleted file mode 100644 index c552610d..00000000 --- a/common/examples/blank/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -description: An empty Helm chart -keywords: -- pattern -name: blank -version: 0.0.1 diff --git a/common/examples/blank/templates/manifest.yaml b/common/examples/blank/templates/manifest.yaml deleted file mode 100644 index 3f160b02..00000000 --- a/common/examples/blank/templates/manifest.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: example diff --git a/common/examples/blank/values.yaml b/common/examples/blank/values.yaml deleted file mode 100644 index 35e4a6f4..00000000 --- a/common/examples/blank/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -tree: - of: "values" diff --git a/common/examples/kustomize-renderer/Chart.yaml b/common/examples/kustomize-renderer/Chart.yaml deleted file mode 100644 index 88a786c9..00000000 --- a/common/examples/kustomize-renderer/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -description: A Helm chart to demonstrate how to use with kustomize -keywords: -- pattern -name: example -version: 0.0.1 diff --git a/common/examples/kustomize-renderer/environment.yaml b/common/examples/kustomize-renderer/environment.yaml deleted file mode 100644 index de4c48a9..00000000 --- a/common/examples/kustomize-renderer/environment.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: environment -data: - IMAGE_PROVIDER: {{ .Values.global.imageregistry.hostname }} - IMAGE_ACCOUNT: {{ .Values.global.imageregistry.account }} - GIT_EMAIL: {{ .Values.global.git.email }} - GIT_DEV_REPO_URL: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }}/manuela-dev.git - GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} - GIT_OPS_REPO_TEST_URL: {{ .Values.global.repoURL }} - GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.targetRevision }} - GIT_OPS_REPO_PROD_URL: {{ .Values.global.repoURL }} - GIT_OPS_REPO_PROD_REVISION: {{ .Values.global.targetRevision }} - IOT_CONSUMER_IMAGE: iot-consumer - IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag - IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml - IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml - IOT_FRONTEND_IMAGE: iot-frontend - IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag - IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml - IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml - IOT_SWSENSOR_IMAGE: iot-software-sensor - IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag - IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml - IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml - IOT_ANOMALY_IMAGE: iot-anomaly-detection - IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag - IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml - IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/common/examples/kustomize-renderer/kustomization.yaml b/common/examples/kustomize-renderer/kustomization.yaml deleted file mode 100644 index 8d8bcd10..00000000 --- a/common/examples/kustomize-renderer/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -resources: - - environment.yaml - -patches: -- helm.patch.yaml diff --git a/common/examples/kustomize-renderer/kustomize b/common/examples/kustomize-renderer/kustomize deleted file mode 100755 index 5f62b40c..00000000 --- a/common/examples/kustomize-renderer/kustomize +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -x - -BASE=`dirname $0` -if [ $BASE = $PWD ]; then - BASE=./ -fi - -cat <&0 > "$BASE/helm.patch.yaml" - -# Including at least one log to stderr allows us to see the full -x output -echo $HOME $PWD 1>&2 -ls -al 1>&2 - -kubectl kustomize "$BASE" && rm "$BASE/helm.patch.yaml" -#kubectl kustomize "$BASE" > "$BASE/result.yaml" diff --git a/common/examples/kustomize-renderer/templates/environment.yaml b/common/examples/kustomize-renderer/templates/environment.yaml deleted file mode 100644 index de4c48a9..00000000 --- a/common/examples/kustomize-renderer/templates/environment.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: environment -data: - IMAGE_PROVIDER: {{ .Values.global.imageregistry.hostname }} - IMAGE_ACCOUNT: {{ .Values.global.imageregistry.account }} - GIT_EMAIL: {{ .Values.global.git.email }} - GIT_DEV_REPO_URL: https://{{ .Values.global.git.hostname }}/{{ .Values.global.git.account }}/manuela-dev.git - GIT_DEV_REPO_REVISION: {{ .Values.global.git.dev_revision }} - GIT_OPS_REPO_TEST_URL: {{ .Values.global.repoURL }} - GIT_OPS_REPO_TEST_REVISION: {{ .Values.global.targetRevision }} - GIT_OPS_REPO_PROD_URL: {{ .Values.global.repoURL }} - GIT_OPS_REPO_PROD_REVISION: {{ .Values.global.targetRevision }} - IOT_CONSUMER_IMAGE: iot-consumer - IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag - IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml - IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml - IOT_FRONTEND_IMAGE: iot-frontend - IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag - IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml - IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml - IOT_SWSENSOR_IMAGE: iot-software-sensor - IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag - IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml - IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml - IOT_ANOMALY_IMAGE: iot-anomaly-detection - IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag - IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml - IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/common/examples/kustomize-renderer/values.yaml b/common/examples/kustomize-renderer/values.yaml deleted file mode 100644 index cb80a03a..00000000 --- a/common/examples/kustomize-renderer/values.yaml +++ /dev/null @@ -1,12 +0,0 @@ -global: - git: - provider: github.com - account: PLAINTEXT - username: PLAINTEXT - email: SOMEWHERE@EXAMPLE.COM - dev_revision: main - - imageregistry: - provider: quay.io - account: PLAINTEXT - diff --git a/common/examples/values-example.yaml b/common/examples/values-example.yaml deleted file mode 100644 index 7b3237fc..00000000 --- a/common/examples/values-example.yaml +++ /dev/null @@ -1,78 +0,0 @@ -global: - options: - useCSV: False - syncPolicy: Automatic - installPlanApproval: Automatic - -clusterGroup: - name: example - - namespaces: - - open-cluster-management - - application-ci - - subscriptions: - acm: - name: advanced-cluster-management - namespace: open-cluster-management - channel: release-2.4 - csv: advanced-cluster-management.v2.4.1 - - odh: - name: opendatahub-operator - source: community-operators - csv: opendatahub-operator.v1.1.0 - disabled: true - - pipelines: - name: openshift-pipelines-operator-rh - csv: redhat-openshift-pipelines.v1.5.2 - - projects: - - datacenter - - applications: - acm: - name: acm - namespace: open-cluster-management - project: datacenter - path: common/acm - ignoreDifferences: - - group: internal.open-cluster-management.io - kind: ManagedClusterInfo - jsonPointers: - - /spec/loggingCA - pipe: - name: pipelines - namespace: application-ci - project: datacenter - path: charts/datacenter/pipelines - external: - name: external-app - namespace: demo - project: datacenter - clusterName: example - - managedClusterGroups: - - name: edge - # Optional - Point to a different repo - # repoURL: https://github.com/dagger-refuse-cool/mySite.git - # Must contain values-{clustergroupname}.yaml at the top level - targetRevision: main - helmOverrides: - # Values must be strings! - - name: clusterGroup.isHubCluster - value: "false" - clusterSelector: -# matchLabels: -# clusterGroup: factory - matchExpressions: - - key: vendor - operator: In - values: - - OpenShift - - # Create an ExternalSecret with a label that ArgoCD - # will detect and register as a new Cluster - externalClusters: - - example # Will read the key: cluster_example diff --git a/common/examples/values-secret.yaml b/common/examples/values-secret.yaml deleted file mode 100644 index 1b6fec08..00000000 --- a/common/examples/values-secret.yaml +++ /dev/null @@ -1,32 +0,0 @@ -main: - git: - repoURL: https://github.com/example/common - -secrets: - # NEVER COMMIT THESE VALUES TO GIT - imageregistry: - # Quay -> Robot Accounts -> Robot Login - account: test-account - token: test-quay-token - - git: - # Go to: https://github.com/settings/tokens - username: test-user - token: test-git-token - - aws: - s3Secret: test-secret - - # The cluster_xxxx pattern is used for creating externalSecrets that - # will be used by ArgoCD to push manifests to other clusters. - # - # Create a service account with enough permissions and extract the token - # - # CLUSTER_TOKEN=$(oc describe secret -n default argocd-external-token | grep 'token:' | awk '{print$2}') - # CLUSTER_CA=$(oc extract -n openshift-config cm/kube-root-ca.crt --to=- --keys=ca.crt | base64 | awk '{print}' ORS='') - cluster_example: - server: https://api.example.openshiftapps.com:6443 - bearerToken: - -files: - cluster_example_ca: /path/to/ca.file diff --git a/common/golang-external-secrets/Chart.yaml b/common/golang-external-secrets/Chart.yaml deleted file mode 100644 index 8fbec047..00000000 --- a/common/golang-external-secrets/Chart.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v2 -description: A Helm chart to configure the golang-based external-secrets -keywords: -- pattern -name: golang-external-secrets -version: 0.0.1 -dependencies: - - name: external-secrets - version: "0.5.9" - repository: "https://charts.external-secrets.io" - #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/common/golang-external-secrets/charts/external-secrets-0.5.9.tgz b/common/golang-external-secrets/charts/external-secrets-0.5.9.tgz deleted file mode 100644 index 3d5b93f969793a05cbfc12ed95d1547568084e9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40987 zcmZ^qQ*fq1yRKu~nb_9Ewr$&-BwuV>6K7)Enb@{%+xEBTU$tx1uG(w$!F$#xU2pgE z^mSiN6b*v`@}B{u0iiRNQeifglIM`~pY-(BP2Qw6igGF!NMX z{>3k4Zf6g2`E}up*OE#dcQfM!_n840fBqDgX~;HRF$R_CCF{U}W=VZ?S!Z6tMD*hpb! z(hyjA%H+#G@^1+8`Br@U`}pX60`HIozhBdmO5^=~obL}mMidpkoqg^f&*Bwx?p$o zE&ja-$YdC?kR2&Wx`;8hgwPT@fGe^<_Q#$P0FLpv zw!GCWf{YXii@xDhZspBP(d` zbXN$`l?56h-EoJ49LGMEI+Qgh~zIfgQtLaV^4cq9`)efa*128CW28al85 z6skMA?fnS_0zudqDsk|NUZz!uzA$jbW*UutYmu$)=E?Q}ai`Cf%8`LuwD|Xv?9+5m z4YOy4*s<*=#^;JZoz1;PEY1Ll%IO~}WwrM^Vq|3}nm&R!QWP?dQaeae2+={1C?(=E zlynlOaFn?+D$spr3e-JV(Ez2o0t5md=s`3zAy}DAP#z}#h~Ws~K&}8VA>vy&d8Ege z>7x^cK70xMqh&q11VA`Nb$NsGpAd9q}+zzS`}T82kCR^#%)lE#mqm%BS1wz zks5NRNk=`cCsG8Falw+s@OqVmqyxw>z~l~Fkh;NH3T^QVlu&&M2S5mJ1BR)6?15K4 zd+Pm==_H=?B8ZOpS4BC>?CM$Uma8E_s$WJ%%^fYG}EI*F4>xTkHL zjovv3>ZW%z7cOs?HtSW1I^vWAPPDEoDbQpGLCA-!&4og!<4z?aWmwHL7_;zC1-H{R z*bgSw63o~|9L1jf-@OV5K2S`60OCtVTHF!1C%pL@G!ijl-q8FM^jiC%F*8 zyA)aZqoye2x);OO#ED0vrX5o5*H;=L}9mNK89bz&n)IO@nw=x{{g^M?xmS=5zqj9HAbvJ*YT8s9C2M4B}aFv;m%weqf#(}?4H9=r-W3P5h znSI|>zNSzRb`q=r;^nW}iNT22R!3Q)sR=ax@{x8*pKlD3ml#>pO7p&{V12Vd@`z}R z0AdBRgOs-uxa#D=*TXwHA7YwQnd^OW^jZ-NR*bUP3h8SE&@c@b`?jsS!;!JOd_J#d zvF-w~@qC#GAtif9vBP0Rh6m8*@=v*1A~50-xvZcbLf{iuaeRsXKz<6e@B$1>%|ozm zGHpFmf`|n1;qPuaCrAw`X8hZGKGh#T!nCg0Z~~?5S)^$fb~g1bc>>@`QFI5SM25kk zOb>k^Gt)|U$5<3UHg_=#A`|$Q*dRg4kwYYkjBu*|CN%ciieZRKOD574belkNL&o=< zKto$(EsR6*Y64PnH)h2c;K|jD0maItr!>XCvFHeV%mImu#F?6y-;*N-8MxX7Oq$$y zrA@cig3u2%J)=n7Hkyap&>>-`I?~gLeG`TdvE*s9^5nij58RYmcK9X$X;BnbISLAp zKnzjLnFN!_Z{^66T2+ojH#inc>8j#Kay??uVVhd%4Ezu&A^b7V!o~i?41uCJ?D+s9 z={As|Afhd0H#}vW`@;0FPinkOBI&4S`mYzobJbFHOF-zD3!)Rbf2v8C*b$tcmu%k0 zMIXT*2AVGWWFEOp9^(p~eP(b-i3Alqb;4N+IER!$YHb)3nLE7!m~dzO!NCkW$KN@z zb1#+3QwgZWEsl{!>4dDVt*Gb?zsbRE^yoRD^ES1rb5&Xa6|GSFxE7_mQdRq8Z%huL zyA+3IOhV!^2&uFo;PP+BD3Y+E8nQs#eHbMu#^hUXkc#azY{;2~r3h;)L5ldRMCZF7 zux+k~bs@AglW*)BKg$0adwUP$Fd$`2^$F>!HUFhfTc`ppQ)?w~WNXtdzzB^J6;Fqk zmOcwC7N8o5?-5M(F7(H(chMN{2iENSpP_G*fvC&$YS;*+kj`pjuEzh|EdvKhUV{Xo z2m=+$F)varH^&26WJW4I=&DC*>!Hh#g#0KoAzq>vQ<%f#@=k)d0GWVg`SPo#_D>d{ z>4MKi+*eHj(P0Ymg3ZS?u@Vfj`b!NLKo+A>dnf=gZq0Af|g z+l27%mynmGzVrAwa7)%nJILeXq;zee_1LW492l4%#$hFon4*sS#6l`0QT z=8-D5S2-sxK{EOG@w+lPUm-0;3OY`svciJdA4$07{Bz=y+=`TqC*40oopBKQeoFY+ zBLh}s{`z_Pl$rT(5&25)_9YPwycxS&ND2b(AX5qABY*q9+$Cq#rzQmBiVR+gV1VU7 zfbQ!>g=7ME`uZS2lSBXb&&-0MlW`pDE5GLNOUN`3!_fR76`v#5*c5m&4s3UqH5*^L)O zLh=LZ|1@RxEbSWmY5dD1ofZdpHxh{1OCI8$fGUSo3Joj?`SgUq-ZgF8k))}y49W~@ zrS7mUWfhvG0>z0!S^Uvg{0?7rD-!jtqn@C}Cg-8v`S$(g9v}tt{dxfSHM_TVu^S-Np2F-5{RVpIMFC&$UPl7IcP1V_ZX|)uj^8w?MPeA2@WaOMhH?L+3F}ILo ziV?b=!r4zppHRPGghV*a0Y@=oU^vknIFs=BzlXE~mnjnqd^>-B{C` z@dSa%iX01hDm1ft^j;El_{t66|TDV!XF zsG4O({NsnSshW?q0GpFrQpOkC(`>?x5x(L*RyUgzE&6m8BM<_Zq!b9ZN*!Ndy3v1U zR|ws}JF!|ZzFIZ1zS`<}N#K1J-YrcJ6%py=w$Wb&Vtt@^tStimqktqBn~ zgkvMsYpjue(zCNu@}p!7c9e3yfZYI}bi3^8+isqEPZ!<8>zJZ24HwU-e^(f{t!;bp zs=YAF_Gyf46QY*NLB3!~gjGqBbPYyxACD_D4kyYGlrR8ii$?d|n^^-IcvLwU1kbG3 z#;y-v#L46aUc(k2nVyBNcjEOm<1qU89CG`(hq&X^#=Oi_cO%WjDF%O))`5?#{S_Oz z1hIe4rBci}z7mWl0={}g0DIC(Gu+Gv$EVQ24$_IL6_FfRU=_FEhcVHZL%?!M?qXx- zBL|`@$uSZj8Pdejo*L;G!O51-qq*1_e$@-e488e-1xZIlfG9rYITIftDF^3|#=V+>t>+xxUss-;t z{S2eY;$!^Pu*o-%=m;nA)Fl2l-OR7_-^N$i(1C9S0n+P40VIAaqlU?j@c*@colIdD z#2v-j6ra*!kUX-9>GQy^bF|BH99f3G_$4pKaZ;NJY{H+h+o|7#^}pzUtg_Dc;~F~q z)|dj)?j{(XA;-9|{h~h9FioIGc{OaY0>FHyjhBNd05!rl7Heq*0A~ezi)>?&l_3*EPQ&>tD{Y_&DV9`*PI|esp4q{0qL+AE%u&A)thm3VX_}5rN z>aXoAAvVE9AG8nrsMlWh2pk;!CNR$VI#5XLc{G&97YUsAAAUBcsM>=Az*39CPeKAT zOOcN0`;zOi)~I1UauZgB!=-Rk<-{oP+kSNjZP`DSvsb*u3BYv75891y&v6sRyK`j= zo;iNi?kCK{=;ycLFPL;0H$#IYODhur4#qAuFg#i8yg@pfl={pI8&RJMK7dh36N($k5t0S zp6qx>Yt%cn)&6yrNs&z$q&in3`)q@cQijtpaR^m~QItGX12j0Wsd*Jpcd4A}+~0{k zmUL?c(XwzKfRysb%sF!af@n`GpQFP?vu-%cDwaIvsWnkmL~XxR##xsSuU04eIH1}QB)9~=++W%N%by*Xbb_Kf*J+8jwA`=} z&l>T*d>PH*M*(O(ZDflNeQ*k^djNjty+Bl-YDM?=Un-%Gl0YIs{-%zE79D6wBKUI&&7M`YvVP{Zr&JJMkYO0e&nyIRlE=)t}4V4CvD<{vtj9*PrQK-aQ%cNt!i_}*{=I2AHQO^eb zq_RK5!RS8NoK!DR>xyT_W;);K9Mo%xE)0?O(&49&XGu$j4Ntyc#(h9a5=Rr(U=5%> zl^_^90a@LYq%j;_y`WGXe2Uj=F?NFd zhm~o0-Y;FM>YB>Fs7H3G7gUXJ!Ih+8qg%eF&*89eLe}}=J12c~fy{!#{*4q|SR-Vk zw*W7kl~cEj>BVQpxPXJD3@js02&nk@j6_RBmA$}(NUlOfuef~(+TEF-Yn|G!S}mU? zMGpL3^V7G?>Ke`gMATU?0mTwRHR4B6o1Ef;*)u#nJqzNgO$rJBxuM-bq6Vj z!QQ2}C;8M9^?ZF(F57WdGg&d?pPJ&;EYJW6O&bh4t3OjA#e5Sl8fPOMkXnQ;frD@dz#p zXCnCWw*^nv7U?}v*Kmx}KUfNim}km1svv*0t_swg^^b@Y;PL)MEIiJ(>0e}2o`@uu zI_d|Wjnj-$NSEuANILt$VHUFItNB)&0Mtb85#Z8w_XE&WsE?TZ-Be6|H26ky@US=d z_?E-3u|DnnxiJaUVBbyc9X~LoBJL9%dZaK)0s^8rfX`(=&i8AcB%A$kbd-(1CeCwH ztqseLFr^lr)QMHcl0o1sHD#2#3V@MlON$`}jse!hOw zLvyLw^49Zeq3YExT{D{kU^=Fk97ycbljaj?XtI#3+B9Wwn~0y3DlUv54K++7A{geS<;kr{Xzx40Oi9vcHjN%*yNK$Y(Sk1Y2bkr zEm+VP;r->t{_0-72DiR+7)hOo-EY*vrg}!(`fRm>k;o-Te!)}@c}ML=et}i?J{*;#6sy? zCUX5nNL@*7*(X3?2%%g`P~bKskSgKFgf4i1pefpYRIG)a-4qtwthFtu>nGN!>g7TM zra~07T{!_umzBc3$I_ zrmUsIDff56iNm#oeBLneiN12arION&2#h+e06-v)7O`=yh{l?0n01CiRhujm4g*DoK#Vq>lb=>P6H`Dyl zuIl7ip1iA7Msi%^SIr%Mm3?|pym;5$$fzWhEYZWPx}REQl88Q@whg#PXfMK4dCjbK z%>?F^DNGm$BQYhex+`6DiPtVK&Yn_yG77!R4eDN@D6`Bf3||pK%=ZrPs8BN8s7Xbz z5?FQUpGtNM=Rj_}C#AfwgHjlWIYHCP1VSM!oWZBPY=63pnB|^+*U+qg(q6==;4X_# z4``L%W7<*Y*v@`a8lW8s)QLzz4xD?hnCNfs(MV13O%{EXJeeASqV7DE%tXn~9Jf6o zY`3B909{b%bvch5-dYv{{ZsBcKyeaFd3kN`@V3t3AMD>KhM-*@mS|OYnUN|=4E1LV z`SPFdBzwtf-M-|n+6Gma#iZy`*Hfam0!1kCWuJL5S!^pG-$-> z;;<@zC0SqOL^I)gIU*0bE0mLN5`CLld0p!LUdIlqa_m5netGDlo#aka zl6|=}{#`BoO09bo(03-^Myvr<|(%$(YVb_k%so5WPRgUxwtzusk(DIKy5A8lDEal;yh9 zsEH;FusiM@#nDgzb~)2%OV4DhTb~ry8pW*n?8IKk78)S_E)%YHCfjg{ZT_0m?HAV1 z1KJ}iHPWYTzqdbB%2h7<9Pw52S&F;@S~7%`W>IBRS@1+j|irY}(-dj(w?P4>2oG;OQT zZ=c9}V0%^CNmwO$6lfz3-(|g)=6(}SmscHnjy{EX+Md)j06i#wkJ8|4zW?DDjI=@^f~ryVNI z{Bs>R@^gwK!xDo+uTSfGeutt@74Hc7YKk9J6*HbFf0Oqh>H>*OvKN1pU26d5?EEfZYT#2&&6K}MVGzV-;4*3c_^)5eQ0oJ+)* zQmZ9rI9DC|gDf1DTBJXnIXUg`F=Vf>28+QQ zj5wQ1PJ#{GTDM~3qlq7>S85~t10Ng!r&E^{3}SxqNZuh=vh0nF7a!xOrYI@dg^M9gy{kfDGW))*Z0a{iDzj_(wVR!eqmJOw z4?XqID($g<*f`8+(wi&x}rUe|j0B{E)#*jF0<*TgN1xN)MN%}12fs>YW@NJjL*isNzby9Qi@BIq*qCfrV?3Fo z(tL|2CwO6abs!`I7wPBmwKxP58mJtz=eeWpmXF~Mq$g(aS;W9Fz($1;G8*7~509gc z3h&-==dTdYp2J+c`Aatd(*W;8{!%AN@btZ?YMZ@ak+LohtDlrWKPSJdADl!{6qGHc zAW4v-dgBo1K+Au*^Uq|-aRHsv5N@Rga8xbN*a{(js+=9`3O%=w{j5edK6ohqm1(fG!(jzi+=E2Q8XRUB1bxN;y5y z3ls3Ssp5aI%%R?VIHWMULqwL!&PpaP;1DQH%8|`w+k5yXBemLJHH0CqyQ1(lR4Rn@ z%_~_=PS(N=*(!05UM@Mf@PvK{48J{~-Olc=XjHQ|jnx#hXxxZ?s z1v}r)h=>?-0y#Bz-`0-8&o(TvPikkTk9y?OuupnM{kuFq7Q!1jYyG=DzW1IUm)?qK zWC|u+j=+OKoc!*p0A_dc=SO_(qD8pn$8Rb9P2994yt8?kCt9%Ay2&M8!eVu?U4-M43oT;L!~+ zW?_8mX9%|3YV&XB%r1_}DiFQmA&)ep@swtWm}MDMx!mt!*%MNGRozo!JWu2&8+u;% z;s?dISUo7q?mQEpE;PpDgMrsu`9hhnphV9np+?J&8red~!j^&$;ZRGk$3aEiF{mKi z6e$VZK_18v*{Vz837#dL<(FAVuitova1xQ){`hh-CJiCjArAg+rQn zTx6$?qU&qm&D(FspAv<`j3I-Gs=@o{3W{V`-N~C^QSp%|VJ*-Z6Kst<=)&@uck>fG z!Fdzg3UD_$;itk)BW+Z=CKPNU;5h9oXS|zbFvUBz{_nP+XYmMlZ-g@ehs2>4+EK-! z4$uTJ!R->c@YW=wwY$(Iq}Qrc%+OMB`p!0*jDo4o`@pKu{5C9)Lwb@%?`@LW%*pe# zW{fPGKq$Y+I&HJBF5C3nPV4TuF3IccX&$^tkwF@|)|d6ndqiKU2T9^Kr@N97P2i(X zp!wzTvaeh^dMF|fI)jP3FC`@hO1OP~skaV3sEqb_bP-00K-@4KgN8ZMi#p!PCGz7I zw(|hJHK<3qfOhiX){U>!t}{>a0{v=;WRBT@oJ&j+>mZ$en6ERNeu(y60dI}to)bEt zL|4=h33#Dz8x}Pm>OisnNXV?PnaEiFSQNBf@QlBuKZyDeTK?V3F|Rfj;+c&%PglFb zkr3=x93qnrZM`>fwA3vy}tE0PZ=w8<(6}OKvi;Fptr463Qd=hjEX705A zr=pVYn#a2%@Ay?b6Q7+ZhVz=W%%OTFulwm1Jn{8m>muDr)6cW!=5g#y&yFu@Y(M2CHb)T6UT%a~|)!xpB zl6}c{SHkbt>G~X}+5`Y$IMdIL&E62+TpNGoKjx?ojqRdeq$V6NilZIetVq2^lHRjXvNjQvcG-9qXV*1KpCSmy40t9#hB@OoVPjjJT2dqr z^@7WDW13jMu)2KsGrZ+*X?X(oBUW1_Y;3Xk92H|;Z=l(cB+sv?@t)3L?c!>QG|3|Nh>McPD*r;E=Gtvmp z>fUtT;+Yx7T0_@{NP$upR1$PN7hGYMQoED|?;i}z zMy>eUCWMBWr(G0(p7+$;HI?4p$wi=indF}bwvWJBzinr(dZt#>Vq`GhDd^501N-DX z9MQkMq-IXs(XY`e)rk4N`3*h-_!)DzyxFQp1Tf0A41`|leB}v1bB3)eKASpp*f*Bo z=}EaB7MRo)9N@yP?~#5YSfcLdN@h@V=~|(T&=u~ewdxANzRGgRnokPY)okzkxhp>4 zc|~_}c@lDJ??7D{OI5IzA#W%e@9r5)2}-IEOXXlxwF65>p`URFzRZtYewE>+{q7e~Zdl-G@RKaXQe-OktYOmpRCpd3wj^Zu)| zC&i>2R>@h{G1+>5GR{F?92X4O6W0%jKw*^D9zJ{t@EhlP2YUI$wLc94mH(P)7X|vBc!Ce=Y%&^|-2W%U`|C>-X&QA+;IAky~Z!kjs6bjSGKB z2G`v&z~K=W%79l}W#0+WeLAwuM*aG&;WGN;aC^x$F+1707m@q!w(`bqWL2Pgb(fx* z3*M)G zD-eGfsnx8rGKk$ZHTU^L4=C>Uy)&HO0b$e-ma68I$u8Rr0|sBG9^tCY8%-Qf+by7- zcGTy(yyehsgM*l^AMwj0Nu^0iWMe~d-IaDm=N*dEC0HQ>bZOU#up$oR1WjSEBgQiP z^LA>q3HnN7fk>bfbHfl9CiM_x@`aKWzd5H`XT*?7c-owDL6nlIULcE|TgT>{V9(4Q zaMi3YxA!nN)OwTiXwy$Og{hXDtvXwKpuh~%o$*uU0j?83)W&Td0>`tu>v7%GwlMm4w)U|}@ zI&UiEb+ZQune@-IC5!LK1GK$oGU|M?^itN#Q8(vh_AT6X9ZDasDgSn?LAN)qf%1eF z>|ou_#2+E9@qT#P%gd79_0o6Aqu9C!Ks8a$J`s|d>j2Vg@gBdH#q9@mAcxye`~1Uw zB6~<7U-kbiHRclkz!}kGNP!-oy%10*wz*}qNtwqUA-0FtpwI$sIF=NKd4mExaitQw{=(6=#zaZ5+Kb5klWo z)c6@CZ)_hU1_7dw10Z-xOFF8&0a!>Yp)B1yra-99+T@T-$x0&2Z*ZopKQ?7QW#&5F zQo6FQq*3I(`Q=!-l(pC=M1e3OMvSR19J^9597mbaze7u@?_VoW6C_P}JSVr{9v|js;+ysZ zR^Qaq`dG4X$yQ7$T&SI` z-r$pdJ}EE}A-QOW1|fN{(z8byq-Wa|eel~!lP266Z4btrm}#aMMruNXZ`dC5-k+KnO?n>ylqbvFm7iJZUfH>V&zz5pkuo#$$p>W zaF6;g&v?#{8z3L7K={;TvrFO|sE=?_E)o+TT3AS4ZsX(Aof6|`^iC1#y zj4z%{CVH?gBu@BH)Xrrq+AVCL@J}w%$@yN4+4uJ0mmzOev@AGH!Vv`KDY96c)9V)o zBsz|;PlbVl(FssPVsKxlwRBRA7==ocDpjuJ`c?rsDfh!mv#}u11J`Xv)-5Z|M)GQ` z9Av~6aRFxBdv(sIvt3}-_e1iUq+mQzB*n1T{M)E@?qDq_@-l!AouMQKqw>Yt#&VbbNQH@(09)T6cj5yH5Q?5v`Z#V#Aw3KgN{ic9nT7CT?^J-Q~;%6qDRTD9YTd`$0$q_#Q1Sd1OY*o>zzsHAi2rYcA$(2_PvbkHl z4Z@h8=S9%s(g)9ef9}OKC}FO+#ZWNi$}_$AX|Am?QlmChCMvVDB0q`vwfhh48)O^K z|80Izgu|qrtQrz@0$n}8h1jimcujJgQ~;}^xXCUb*IiKF;WK*_{?d>phfChu zW+Z;z#doM*#{SSi>u^Uc{@j~%wYNA!aC%e7P+6qp0Kh(c*foxXb6rOHQzJ3+80`_40}B$HDsS1`k;D141@ z{uAVll8*}%T~dbT<;>zU(^jqn5L@r%bk6>+`;F1cmU74sSyG(XWym=T)P~x;7X53| z@65`kHTY=-C*_p~uj&GtU|G+aq69Qv~#R#9Kh8otsuvyo}Q%O*obDGi-r=8UmIo7&c$O-8Loi4d&Sg>)iT)+9pfN`@gM zzsN{OYCB1-O``fYgvv=SBY}$67^d@ImNY_nF*%4+4x-%B@+62SV0E8*-YlZ08* zJ$qbdxt*Lk^oAPmADTUO<=UtU!d&JD6js`~SM&_^NGwIWmxm%}&?YQbl_ENlWm_A4 zX>qW>F#HFF3)w5lH0ZGwl9kuRJX(7E%so()p;u~~JX&*9&9LOQv--g<)c^Re*s+l2 zr*0H+nY(Zwx#nz}Od!j<4Oj5J_soWLrJZyuk5U(TICg-FwXT<>FGx9Up^5KVK^B)( z93*0D{+5DeK|cKa z_u9;IG`n1KMD7teX49b=KuHK**7#9!UrJ}PUTsHJPRS*lyGt84OP*J`&?6uqKzP+1 zI;nh1i&W=#$$RF09FQShO4WRi!&7Au2GXyx2VM9yCKX9_F;XbalT#^E!HEd+SC-DS z&Y|M8{0E!7CveHQn#!GF!4^~Ode%<*tXi|A{H5J~q`w|s3}qlNDrN3*z^CyzNkC^V zCM=d#8R;_DYSpho*LC#k^aTntIIQR3d^0#~r%o+6?uIWoPVn?KIPMo=^>27{w3UPij>n78wZ?NtKk%;jPDfH{R8@3 zMao_pX?C?RvHpmXwJ$r*+E7-N49@$SSPUxablD-yB(;Ep#NOK~pEPwl)6rO|yd?O_ zWfh4GszEC@y*v7;T9XJ|XjtVz>&RZPT2yP^G{XARZ_aE#Y_DRv`bUje=1sa91 zJk&axmf+G60^)3~*>j2p#~Frd@oDXnRAjN&uy!i~KZ8eJH?_99(v(D5xTCGmDpY8f z9gqnN#!SEndvO%O-rNd)vyw<<3HZJr_7&>8>%52^ll(C?X&`QEwh00oe74u3C=s@BHN*Ar6WMrIbFx5GJ--m3DtGgP>xTnT}c&Cdvq6 z;i(hRcxdSjS5y|-G^zn6JlQlBST-X`e6Y&CQB(!izrkdh*1DYoe@rG{RgnehZ8Dcd z?WmBtp1mg-xd)nUnwFQWT-;tsdQz~bi4UxFE?SIcZi@4Tl^L8Wh_V@G{6(6T2ponQ zUSIjEDQPfRK8~ZhP`_{4HKrJ7bOsr`_;u7D#2w@DZu+wke^j_xSm5<59-JTZ#Y;v7 z%}t`v?8xE6$&C(-l8!tNLy{6yboSbiad*6HN41kkE;l#Lg7n4dsrN^nwA=Of#SYQ; zh}iS{#*Tz0OjzbzAu#9kO7nfu)v1lPr59_5?h5>i|Ec@GMG*su{}M%*WlhEYi6A#Q zde{cZ=jPO?y(H!VBnO&h1PUqM)bHddWX$uL7wP9kP0j!=#+YzQ+eW7r=lKPZBdr2G zO+i}QdCWg}<*aEwWpVv|I6almloPyzW4M;zdNgkw+eV3gbaqAic0B6*R6NiL>0~S{ z0<8GA6!I=;@$4olCJ3iN)TZHt3@K@g>~TtRs(Ui}8U(b+3N&b}MOo-*`!RWc97mk) z*jxqlj9A*f=yHv`FlLu%Nwn5L-R zeUj8)^}TeW6@33bUScJoAx<2;LoTD<&bn~A188LIvrhgwB(X_}as^wh)@FDNqRnC$ zlmOTI@L1KVWz!g8MNgjd1_Zv0>bl(;&3A|_VN!AZ^3lQkiWBOkmj4(exQA9ZWvMtv zrjY{vX1e2=Vjf*W=!LzyS&_;KyoeD&R2Tax=)n9BCjx6S6Pp`(j={zu1O<)f&9xoik9+>{Nb)w9wBtjV@AX zSqLDk+Z1fRDVP1Y(tFXUC^O)wS*9)LNq}OZwla@Ze?rm?B? z1EEAqFmjPZOOk76&s!n?@R-ZYL4OYqR5yoAGabBxA`JE);6{kAcp?lIe3}jJqy0SH z`w&oy2{iH8v&zHSAsxE4JI5>CKe$+1$+i)maj-eB@QiHPR zS`zPOb~b=AHzup(zjgiqW?~|DC&8UcTX2vJd`otRo|khqoTru}!&gBn37%URx()80 zKoA;t`gh0;vHl{;pD!X0%(ehWjC$+keqjj>1bDIEklU6TO!8;@HHt#PF)Ib|%Ngf^ z7)*K3%otKoxSVMavlteuApDN`pj&+LfKSNZ9BXGMp z=+e=;=e!i()OOHN-_$bPdHs6iUlj z(rv?=Eu=XE6uw-aIEj+H)OTC2r}aCY<;?D~2it1x6^I>7jlxIIVB4m`MYKHQzvJbw zIT_~ChAE{@7s6Y&B)MV}lv`QU>Fmhh z5RN_GP0vBbTkuA(imgJ^2cJmc9%l_h=o|tPvd2}jhm)Ba|EFg469e?u;ly$1|HHa# zV{I(A=;@9*#)j~#5~ZjOltmgBu_w|6An^W_`5-9lvQv!aPx6;X`?-gxN0}}!(3l)K zk3RYVV8xYl|7FdI2Wcg%de;9z-R_Kva~tCOInio9_LqBp@O&Qs6XcdkpYfOr_PYob z8Uk#)NmW(-|A3pu&{x zOuT>mihxo4gyjpN{6#&o_;{K`GrygoV<}wo1DnD^vftKJ%xsN;5o%vXu3!EiH&ter zH1`B!u4oh>-?&)cnn*C00q$2x{}z}`uVKG7t?9Ed;0f!c!~4(9tnT5^*hA~sJBJmr zs7ki5@2VdhIP#0lH+SOstJv3d_S>5GPb4D>vPJc0M7qtj>z(4%{f)}BA@~?D)dYFU z-HgV-Ec#8r(A2brL&aXu+zpdP!yPx}8BwnN?Sxa~BV)2&ip~W0{o?0D3R%r) zk%phpTo|OGm0?ezPLO%4|0ewi?~47!_$;Ce3@p~w+BM}%-+_K>Ba{sO{G>|F?fQdj zs)|j7W~!FrOtE}lbQB`9tR`TrQqhp%geW4fq~t|l>pOP&_R(-Ov0)a5BV3TvaOC*- z52s77hqT3c1^ZD1St7X*0TwK=cu_B~cc4PY)^>FA%*vL zp~odf_z;rEM$Zigk9=uE5pZ8_3zgz(=!04Z(QyGyG*3U(0f+pQw zC)4X@1H^zs&7mfHF<9@}T9?G%?zs4sVXx7qm zU4P!!${(#Q`~JeT@?*wYq#vxZ03oZq`W0^2&>|5@)pNUb7=+r{dRynftOd%4m*kyN zvYVnPjj^cxvW?agnL3ig;n)BZr}hz#f;rooa8BvW_ryBNKB{0Gx1xNutr1R5wpzLl zA|d#9n3oIn%L?_D5m_#7(@AVyyzBoV?VZ9a`Sy10I33$F9ou%twr$()*tTukw(X8> zo1N_Z*V3=JHj#i)c;h}pb8bO6=muBe_#$tZl zpod2d_p;wz!|BGQ^(OCcF#3vX+>|q_e*(v!QtVY?$s0DE(0FX>#J2uRV=3zmi$o~? z!Lo_Q1JSN|V(QBGAg6#wGni*-0EXEik+nPHcsh+qAz(dPWnI=n#dNk4a?10a0ye!x z_5M?Ewmejf7kQ`)ql?k;mFqAn1={_P-DErlW-`}ETbgMQT}vrsn;3{R`EdBjIf7=cwCO_oXkyk zLEe%|(Me#~7L4_Uc>>d#1lAZba4cQqruj_UiDib{%{xkP{nIGsmWQvFOa+euzIt5Y zp9$sZ4?-~jT4%(pP7N#?Xy~`X0o)=BY#TiW!VuPU6|KQ*>Q!Z1#FTlc{+<44qqL;( z+0Gy4_E?t!f+ft94dD;O5o(<`j6P&l*^=B`8q`w*dH1c@_m094+EeNx*}3~jV(7qw z-<<|kKJkGDRlc#<>M%IA^Nd->YC4@ruXHGcs4D5qBxMmROU44}X@?sx*_*ODYKF@EEjwLyXsY#5RVS1iS{kjz3;3OhVSRvZ6RIkWV9mRdUuoY2 zz_{2RcD`9J$e)BbwFbTcis3OiP{mg@!npGaP*ViLb)gs@rte8hv@gpb+8(cxds=nH7lCa0B_mKmVwisx)@ zo|11=;|gHg^ebmsNPCdV9kL$%0-(0tH~*!rGY)2w}S zq-L|RLGE<(x2w6?neNphSgREPEvLEUzYqo-pvymCRfGd>lqcVrRJDcOiEi5&9y4`; zCp;&P+B9geo~3M)rzn79g7FWzWi*UuoTHILibYLOo%PYOv$tOE?JryiY~IDh8jYx>W>vBwp^zM*PdXpQ{bZ zJZy3aZEQq@XOSHBnRaR%PA58kDzAIRR7k+ML$UK<&pKal{~N%lA_bf%V8A;qm9(pt zo@)$LF^Of_rh0eXrr(&qAbWBwYg|f-Z+06?BVVoG+DMW}=}7CmI$aw@ zqErOexl?ggYwKam8C%ui{N3>5m)1LM_{TSaM0Kw@%GqBVd7rpcM!8T_<>?!dkRv!X z$4=(({__YWnXyKlU4sI9BG!Pa+IcZv$<%o{-cTWbI@Zoa-;D_Wi^lCIhBHQ2xN35P zV|L{%nA9U36SeZRKa8BsN~!=lpiE8^Wlow1pvUWV)1B63|4N>hZyp^#pc)JtFFY-M zAYkDej-$s`ugRJhHoG@S@KOrDCW@n>&suvX4OJh=eCb`v)QtpYLaCa?@f+Dmq~J$g zCWBrq9>lBu_dakJpz3%{HADj|=d4sgMwM8|sLaZnqKB)}OdqHy2^CLxWEFdvSmz<7 zeMd&(Ar26Q2UDS&@REoJ0ecoW_23@n!I=x*??hmBW|4Qto_H!WE_A3X2~pR@7Dw%G zO`yGF46I8*kGC5nmWi<%7ef@B1Q?D@m@Z6oZ7aJp!$<^Plp85cO$v6!=3MF4wW=Pc zI8|N#O%MnQceS-2yfQTOBJId2sw^K}2k=RE;KEo%glGOmEY5V5Cp@qlmP@?v2cDig^k|$Nuo~p@E$6&aRds22oev?E@)uB=+ z`($Q4*JwWsvRpp{Mk$Aw)^K#P60U^ za6?>`Fpe9_XjWf>KGtRYswYPwb1U|kLax;BYMf}k$khBhV!q@cdGH!;s?c8bKmkEH z4){6Zs&r>pS2lUtDTV@MV-%^F?`RqkPIwJ!EK%^@C^^w~A9=nj-XwrQ{kNLoi+>ZRoeAdPnKL_?fU%(xR!ts|MZSwV zlDeVD78JFOd z6c4A0^>4kx-wjzz`q+Hh>y$|<+J1{R#$4hoeNb;;s`i1Tz}i`#kGHqCX3sQ6(f+C~ zRgZ?-;yp}|lTw^Kbx9+$`YcRfi0G1oy=GpF3VUO3w`TFt$CTM;(x7~o^W&J6vTNV8 z;?>(h<+eCpwaS0=U(F$pSdOaPVmI+|mD1yL zi2!oJx5r#>_7)OX*Rs%)Vtpw*d?Wj%1Hx$K^@NMhmeP_WSk+oJB>N2ZI^OTE1PfcF z2=o4UiqzLQ#jSnSOv%rk#6oOJK|+0@g$wWI&Xs}t21nDo1TR5BEs@~fqX{|}j$mnJ5 z74+qjiwLeWx0Q4PW#zn15XkPjMEd>bdu9`l}yQc4yY1hIwDR*5cW*Uz+6&) zF-Co(j6a8680vz|b4zC>DHvtOc-%q>Ltxy#4B23JmqG=$0K1U_A;o8da9NvUNv@pGu1>F2qc>W`@*Q^am(SbDWU1$|> z!C!CV36Z=Xmg0B#d!O1gwi0s81K$xe?R|J`!<*!Mc;(-6=sy)baA!^()ZJIjWkWvs#y(fvG5zCWw;-1}zXP zsivn1qA6h}n&k{ zU7O$vpO`Qkb{MDpaViSHn#{ka&e7rTNUt4Z2+A?qzjcS7ZdIU-Nb_}mbx+P!Wa$`D zRqg9lr)swiTL&e4wTrsq^D1q%ZQJo_hXwG}a94fTK7dl5>3`$e|Uixh9CKzRvaZGTh+5MI7ZM#K?G2tbJb-Ac!bo;JoEM!0GP zM*7`vFl?OjC_S@EW-)4HWqL7xmfB9qB?(ioBbPfe(s(sK7M`oL-l|8eAmJ4kLk)}|N6Wg8%wyx zi6;E@^;xFj7 z(b2CLBODFKMF5hM2rB3hBe2UN3fj*kszY|0F-xqxD`b4s1xE>MhME~nqGHZL-H3U> zE^Q$pmEUna%!6V4ZT^RfC_|O`=EdKDIzh_nt;Waa1rKHQ+SUh0n*h^BB<9mIzbGUe z3a!<8==r-9s}Kay!Ab_9_LrZ0XJGQ>jb4}vy&zK*QksrVr>BFqn|v(Yea8sIz^P1G zSZQlk#8TrK7~gRr+gK|CajyD<7UgF86v>b#Tnvi_y%C5?Q&!eAFtLb1YU&RXS~mB5 zlu8L&NKEK&7?sfclV&L#%@A3G_Zxgw_k~dExPY_y5}-lHBbo*_ky@#zo|t2wwe|MT z*ZEefK=m0o7K$NfllU`wqvo@ON3-ih62K&@de2WlyT8*?q^+CG`1ADMzyh748uJm&k1X6lD=6|10_AUbJ~4XdJivfoFikLY2%1 z`yY`RUMRG+Ub1*ir`rp|xCZ(PnSCbLmjftsbO#IB^+%jRvzMT#&0KH=N+OI@JlmV*?g~|qdL-$(%u4!cRr3jnj+Z#=q-t0uD?=pRu$qEM zGux)kuw)$4H9;}gpkFk}egU(_R6V@Hx7q-ZNAyoeBFD)oVwy$(l$;99&>2~?pHk1~ zc~3@XXD7Xe=}l#hPqqz?nNFJi_sDZ(X@qiu)$B>HUHsjSjYb=TmHD!hu21LV04Q_^ z_B;ICsWY`vwF*`!+MI;;aUg}sd@QM@zqkEoyM?WmqHqmqi>4uL{BOT|-=MDC~$9==r z;_Er>te9jqzoz9xCHh+WbIM&)Rv5HO4$sZIa*fKxPX6lG@J@aw9>(FKuT~45!&>yh za(ZwQmTI|yxr@QMWD+(n{z|Y6YBBAPVOnDi%#hUZn)eIAc%iuZl~!7#!_^gaS2kk4 zyvJTEx%hjLt#X|m^t@EiK0Q0jDuTP&G+VX5EjWRHUt(wU2f5he5Tc`bxHxSdyO0@b znS$Ds&e3uj;1ijMA-=}?eCE9vUS#1v6RJj#VLuZBz6!m%dA`OF!I=k*=InpfP?)Qf zWRMy5yp=eBH=)+PPPc+6Ibvm2e39(<wi5FsnpK)3*%>!sbMEYN&eg&bS@H`Skz3sf0fG;4r1G%QpD^14S{ z2z2eYliqwtbe`Iid8XPEQgj~M8=orvh>LiWA6fjbR3sL7pyNP-lxl$Qe5jz7us=d$ z`@@`Vuu6;W%@IuCKN|m(;Ra1cLiFqQ3{xXt4)=$RhMwi+wU_y&JKBbdZF}Zrtb(Gk z#yk~pT5WeDLG0+fj`ft;<6~yv(pZ1JDUK@9*JYt zm!VC-9t@N@Djzn%tN3X#*c%ToiL}3J^Eu+a2_-GloJ?=olXBvXJ+gXsJr$Z9%mCuf z+HTt(`cp>0aQ4LEnzho&v<#x2tmF^+buIz2O=utv;cl>4>-W#=IJ+s~;*lu1if3w_ zWudZDh#ExAMW#Ia1du--zXVY8;Hm6_zy_UbkEGYz{47)WytYlD9#UGgLBXX_;MeUo z*l!8Y9YTWu1_YMHal-=3Bxg?$);}l#{?H-0{o|%TD5-e`<&^JRx0U3U5S7$Cg8y!F zU$HfPeLe6830~|Va!`B7q+Fb0&`5aAD@G^1dJeya18G5ULk2h0(E~tDRpYD%r z^SW!4emQ>Z@8bo(@jl=F)_nBI+M-*%8r5wq0e(*a@cJQ&*FWb8FX|f6(mb4RtJ}0>*P*g>#!s-!59!79 zZ*^1!$D2Gkbt4-@3LVydtbgfihH$j)05T>K)Fcn(sJOSa_iRnc-Ify=O-9ifjymQ5 zAS!UxdT+VH54dLzEgWadmA{O{Dc@^ZbgQZ(xn{J%HdQ>pZrk8Y7XQofSS3@4AM5N4|ZXUU<)Ai7O zD$V~r68Vm2_iCb>YZP*59kEVD>H4z-kBV@ab_9i~oe8d{hUETmBuu95P7;ROh!L43d=MsUa3$74|(0z0%Rm)YR}w%}gJB*?6~cf7x+#rFV0J zFMR#>bpG6(lKpzIyM^s+1NqjGc%`;~b+G$*xzK)Dw|BCC34fjst>^s8>~jBmKXi{! zQ&KZc=1j}0T|$M22^M-EVVbp>VyoclTZTmlx0-dVTE5oW2s4|zz9jpPG&;OOK=@}k zm>>PO@=v=Pc}5nZ<1|OY5_aGfry$rNctqJDhRxYo&ixa=e!*8@%H()8Lw#M%j*1vI zwm(Nt<6mzjAfnRIQcT2QS@F?nd!U|K{;cuVOfi$mCZzN9pmufmN}o~3Dp#r$WrgJE zW<48q3TOS|yW1{ViWKRIycHwsDCwM=glC{W3l0U!VbkWth+}1(^JCv#)b6<_brHAmu%$TujiR z(&0HihHl~T-POezKeNnJ!>ThgNkoVB<6#n>jbIZ84T$bZaPXLC~#G9B?0G1*I0?`7(rU>X@xu>NL#Qnx() zlkAv%MumXhS=xvIaT@EF|F1b1T;++{#R|35$fbDG^Ayp!+0N@MhblCpk5wR9u>pV$a)I-07 z_eUh1GL62*QLq`J*%pT5L{*|R&^;Eto7y~2vo_DPcjFhVo2KUf!yC2krQGfR@sfIC z76>B8W_f!>9(93n8tpUKZ*PNdVN0Lp!Lh~tytU)W9-nR z!y*Na)-Qac;C{A`s0K}-)lid=I?Mv$+}i;~MCUMRsnqUiq(x+h5%d_>iJ zp=)#s+uTxmw-iC~E~Zk7aM+~ETAS|Zbp*?hdAV!#{Nr09waj=bkR2WP-k#!)3TY%T zHm~N1xyvxE@6?a0r(v;e=S-@;{dqysVjX^!)1Ch+kS4i7M*J&O!v`ioaX_CW3hyX% z69>?WSlE3=0P#y-8Svi?-feZ&tbb7f%+_<8|O%~nXU~@ z6}_AQO4o4z<3>36&G4yw}MUdRLi-6^E+`qy`USYJ)YPunIJ zk|HF~f8)`d-+FgQxm%%pcnXj2JD^;{lqnH^e*LWR>iwBAbH$UsY={@0xSme|*WlHc zMM*inz%HGX~K9Yy&hxt zO^TFTsbH(LGwtx1?>qQ$@>@tV71@yc4-zDQ?swP)pZ*8Pb4tj7*^V~ta#WEx zqo!ir^+xhYY!8Jf)|iD=yIcc1sRChFK|MU{6SdE()H7=ufkMZdA3{9VoDXqjqf^mkVIINr51k=cX$nF$QbKvSm! zUSJL%ReA5*9__}qcw1EfsVe2G@lqj=I$-g6(YUByBm)GhkhIGq$#iTDJ1CQ{t|zLs zMmkdm{`aU0@a(^$E*uN;P`wRhSA}D@unrFc3k-z7aPmKtSBCTxhot4B9vaN$_mnPwNUjbOKG?83ojew?X?Ck6dav?bq^OUpz7BEUl= zO>i1#sd0g%_;lWKyxG%^#GxDT&KUgSb|-Td0a3)cn& zC({N{Curdwnw=cH37mS@r~9?^WT8Djb&m}E&@--?Z4ht^{6~a zFR>%)amg-UF0P(in_W{>ZGNtc7n@F{VtMNP^K4%Zidn{YG6BMjl%@bmP8DEyxPWQ2rRnzcz50eHh!%-Saah>GC8hW* zUQl?iKhpgX{^CJ%c2J)hX@q9({HD5+Nh*71FQK#KV-Wk|Tz#Yxg>aQUD@1FXY{0`{k=?kRf)?+j$#E(YtM+i?F_lZP@9Y$V5kyZ80 z&F7#z(XQsR`!7F7Fvia=Opn@XN4-gFm#9mF@U1h|ro4}C#bd-hq~s}TTY2wy=(gqMfERhGI1#lhTBg}G2ww;%~-zaY?E)wgA1u$zm+(Sr^Zb3eq24`Rmz&$5 zFj9LzSTz2o8C>)*gy{g9*AS%$7ZbUb6O;UL1Ii2AppfHSC-)8sBN7E+t`F^!sI1lq zk!IuJD5&lBKcn@Ua9Tku71Y)Zjb&B9)Vc7)W`_}w8^3|ks&rF7TOHL+ zFVExk`qy}=|Jq|%AIX~+k&zVLB2&e68aK65?b5C&x?7*Fjv7#+0PD=ExU08y(C6H( z$}s-sf!X4HfAIe}lRN=;FF8ZFz1Zh=aD3oaEYCk_GlP;~=Ls567(i&?_1={`pK@gJ z2d1%4E)2)84s(WXaW;41AjcJV<&3fo^v63UBs(z1Xo^S)u6O)N7YiA^L}y4XG@S`0 zceHro8?1FGxe_hK+>3HSieQuR$1))Xg#2uoocwoLQRkR`2B!eOFkJ2BEOGfPyTZjF zZEGCH<**v+87Q@;qCYI5bow==s;Yl)>M-#b7IpEo86tkNJjBC?;0$kNcVM=UUHR2V zVXI9u4UDC8VGy!fvDC3CQRU`5Hnu$_Ex+DiBz!#>FEce93ZmTzT$;)DcrlP34lc)|ht50w6XKvj00Zzs+{qb})6 zb0SHNObi|Cr^>1Z9XU{ab*y|dBjl|B>}Ho+yA5x+1{tQSB*WrQqGW5}bXr66_CdGFVMGp3xJtWd zL$r%Iy>$}US~~iws>ha1eC`|8FuYJj3QUIF20U}w?ep^pD*I<5&OATJ>LFujf&ds) ziZ74K7U|j$eISN7>v&TCBw{ndfkfZ?vUwbQw6~b;`yVtw1xBXL9M(!xvDNIWz<9h?d=kf!$NmCrxPkRcMp=la z&wgZC$0x{k4wikSn;_DhkZDc`^sDx6F(Tfz47r6UQ>h9rkZC?Lq=|NuL`rzUv^d_? zf6#@Rt0zn?XdH8i^PH8x{DLV^G`rn{f(7w-majodSREk=#yfU^i+X1)X6CEqbP8D*nmNnZQe&3@ zCl%+sRrnQ7g(S(lErS$kL<|e3)`Vb69Iqc_bRvQY+$Psh6-xbmEcJ6*M*bN>U>w&f z&mg$t3=ncFEtq!G2orw!>;?|U%(;_-Uz*olYiumRASBxQ(`Ojs1T1!y{n$FZJ&u?IWXOI zK?u|B*0gbH#@<*qm#5rT%}=~^MQ%^Lw3CN5K3xfJk4Q+h+6|&b=ZJZ$I#VQ7L+qcqQ-*2p!#s2X}#g5k=GuL6}ygG$?MnD=3J zS(!E(S=Bq{a-+Ag?%%a3Z-5ibLL=So7dxKg{aF)#IWa^&xMVXlcC^GIqhE1+`Su3~ zNe3UhLvhPe*+<)BJJ!4+aUoP4Ke`_bqVI@>DV(6J46&hHo%w2-hxpeU>g?EMs<(r} z%m=Mzrvi-zg}IWXUb+hs7Wbg3#`#rekOlgdIG>?zu>ymK=MCtc$xk zB63bn?%z)jBhIoK?|etU(hc$p@K@U{{7FL{N5dJ8c3`17lR#Y{zmNRi5-Qo9ZvH!( z^o-LFi>0W-iHsT7?ToafUuSh5B+AL=&TqT zT1BnLR5@mLO8?`)e?~m;53&lhCR|A_ME;T{M`b)CrRP3Uc)j$F@be#1aTfR*KX(D-(*D1tODMg03D3H6;RIZe_WLRubg{?maxW64jP^u2i z0GsY%P^^{-O94mNRP0Jx1iO=b_gqzbsSl&&MJ^O5+#`UBSM?Vz2SEv=v6S#Ye9jU9 zF9?$4Pt3pzI#zTk{oVw;{kl9p^jemizT+A$3yP=v#9rcyK#1G^Urz%M%3y9VsQi7= zZYaUI96j9rFz;Tn^WJtJ>+1R>dw>+zO46gE=PTW>3D zdG*XmE_o-4ftY@N9*QGM!Ubq0{q@=#OS|b!SxIOq=n@gg<_#W1OJXTz<*QRti+Plb z5P~1w8U={3n zOoUB<@|HmP_-nSf!enHXyCe(MbQ@@$R08uPM3f3h^&n032+^>mU0Bhr%bASdHOv!)<7%95u|h>20LvA)}U5 z)L2#YWx6^CuaNdO??g#H3jjCWOm%gYni0sw+Aks=Gm2X9T>7+e44%p={Uo3Q7Wer< z`9s5Nt(+)nP^3XU{H z>CD$uL}|vBK2fc2-3_II6SP23gXOMG7OtRzaMX-Td3bR9`@$C|YP>&A-&JlhnS$A$ zAHHNkM%xg5y%Pzws^gebkOV9Ole}`H9ZZNlE6Pzn2a_`Q@R7-`E}$dxQh$=1O`fuB zr>J)?BY)P4G|cxttv`vc*~B1AUgjH{>sTN*_~uX#Ajp~6xAzv7NuX##+0J?Sx{+um zDc7HjSN%ka7vZGVhK%{qKC=A_5c0BlNaYIGl{RjRcOcl$g-)(jh=M$+xDQ@L8|U&d znv%3E<3?qqNOq`V=%IATAAlKYX6~TWFiorEFue@HkzQ3|XGMn%=;40dDTksu@7Ca6n8WORux{b<#ZUtzVyM@u3q;X};t3;QMsow^JYyI2mOcWZXGGw`{78;t&@;Lx?y)30T=OqKmYi}(^o_Z%B%Q|5FWUT^(9R7~H znUlkb1Br%Cf_P!SCQq08oal%mc|uX5_^8t9V$i)Ycez4VI0D22@uwHr19nHL>%RBH zSW6(ogah@Syv-xFvvdXSXWJyY)_S12K(NOb4_QMK(OOVI z-eUMb3CdL;*Hs_*M*^~~00z<3Hhf7UoT*dehN=nx1a?3@(kl@Ra?ptN;IoP?2E7lZ z$fyIOM;)cqlf}v9Z%;*+75EQ4RX^!0zj^B2tZ>D1Qz6PydMd`thgCo9lzB%OxWnBB zP!3gX`SrWq@}Od2IaCfjKS3Wb^*|+077JuC@Nm z=Rk2|Jp;fwk>Ig>ZZ51E@~M#T9Q0tURIj5(@^huFrA#eoR=OUKI{IyEi*&B z2E;GY(J;21ZQqgF2}z6MNH#uc-ldl+W%mPgwVHvj(oqTeG}UQ~+#g*S9m;fJL(j}H zOn_xQm(bnOEA@c&9@48ZtbATH!wt+z;(3mAGE0T4Wv}a3UHi$ki%$zz=J!HX;X))E zI`QY=I_`jO%n{>qYu;$kc|b%Gg6!F;xX&%(Dn*|R<`}Ti$q(2*eBLzR z7k=4?5uzKJR(0 z+8{r%xiIjgfDW~Q2gb&N+OT0QpoW4U-Q-G4%gK|78U?VBtnx&eAwYVa;fQ&7IM%^I zh~-h%Fp2h50dwVj*@>FPLu|v&HDpMHus(IMBg6`e?A#5vQr>5-!p#OKd@Cm;N94u1 z%?Ev5hVqR+>a;|XS1*HYKIOd5k7t-rQ$A!2YDsFL$k6|mP(9i5mK()B28l3t!|)G@ zsM7kdEt2YA2wYB?ul*7LbN>1X_$S>XEA&ooE;d;_MUO@r6a%<-hfSHjo;NR=N;8no z=tx_K`Qa#CXCztD0y<;#CN`(GWEiS7nx{fO1y1PrmpD#Hy=D@tdwEs2%gn>?mk0Tv z5M!&AMOjE1U+f>4EcCfP03ChYn5ah`B&iJg@{WnH-`a!(aNVk$Z~Dg{epg>n`?tD9 z*Dy()e`wU?D}X(icDJNnZgaCk*idZtD-s2{uV}Vfr99O-J2aK5LTE85GEi0<>8<;+ zCKsnsc%`%Moq1RU8r=K}fTA}KVAPwkeDl&fLtqE&eP0HCpl}$%ung@AOt6t0q032q zpaICZO)xlBO>B&Cs|bJzXBna%`z zAW5h^iJBVm-8GLdR&msA?e1}2y@OEy(mg-Lb0Bs3R#5h2n+#1i2sNHB7$CQcVomO^ zrYsZrMW5VQK`Qy*bzKJ2CTQ&zn^zoz?B^Da1i^IV} zRLbDGVQ9SOcz2loZ_>v-3J$qTc zZy673f6ZgGDc+snyifb47-k6Kzv0pD3*3tYqpseal5mF1Jfj%YHURU*gK+ANK za)d;irFJ#6IUiLP-CK9v5MAFXvs1%xn}G+*gu>0C5{1h7Q!rWw0FRU=hxfX3phh_g zmfhm`obsJgasV5Cj#AE=xdhW$G_tAIT3nENFi#-xDV^~PIW=oL#`Sn7;vW)`m7uy9 zz|S1;x<2?Ll{fLwcpmsJ_e8wMZu~rA^qe!F`of_gjKv-&ddmXaGlh@KdEdzz1x!C4 z=x+ItpzXd_!7#Ui^+03dp+BQk#FIjC4NTJ4h1;geIV|}q!YD-+F0yFkq3fA8M=35p z1bwJ*vbY9b-n#6@2pJpTm;R`ynpwl|&x-$;PgJ)&KR-$2-?`Rzh zl3QH|U2n)9W9wxe&$MR7Vud)-2It~6-zq%6`(=TEiktMhk(mk$8Zsnb3>aa1H6tHH zW!X^hrC@5QA48a&*W`f#H(oZ(gO#9u8o(zSKVuTSU9rgsJr44)r!s-srM@^>V`$t8 z5RhPTDVA&+tE|aj+b&+xm)f#JtS7q;daUS+uJ6=CHN-viX?`3_47*#MpWv#t66#J% zpoaaep>i~YO!#LqdLn`Qhd|C{&bhHd0+6DKIw$E{wHe(MrA$JhN}Jdg-*OtO>Z%&s znV8RC_Ovd>6>hd$N?D_~C}DtjV2VZe z%sC^Y8lxzV`Xt1=^Upt$w*BFl*plwSUJjsHYZ|zd86EqP}OpEcj*mFiHu%dD5@i(fg(_c3e-)>=;LbQhTdRH-ybzD*DNJn zI?wmie)yhTJI{UJF)P9_ghz7HyRISOHg8j`=6dVHcJln1;nc&A6;yg-Xn@G;qB&o7 zA_vCX>}%YQz`DRlwO6p91Q;&dtLh9$^C?%2@k1->m(1UgJm8qb^4#J zmLs8m@2e9L6v_Jyx{;-)d3m+Y)vzc)UXMf0=hp8R5~3P3%GR}5FD5LIShj-4X#t5& zCP8s@_H_*dM^Y3_tD0qFJTSSzaUW)l`fT;blFEk=0NqSm0YFGyOcBPCgesk%_#_2Mh22#WBE_|HUz`X8uo( z;g*NDW!$s9^>lUZ2Tp1qi0$<$#W06J|1XRYYoZ#{SeZBjrzCVG9T(bZh)##0UI@@| zqYJ6f(qBLi^G&uA)Az%f`GQdC@T00F4Qk?1c`_1?ZJL0BTssH?wLUC+!a7?!va{qV zTBeio*=Vb5cybn}*T2L|{%nn4y)SK?Pl%s)3riB)s@K>|vP(HH>ui3oGGa&y1EMpl z=&an(PL+MNBE#y=2EWen^5FM=Bzf@fRD29^d$Rx8#$oACp}dNs)d)tCncc5nwi~jF z*JD@uc+`g7=bOwfsW24LJjfZe-pWuUzK|j-l{JUYM_mAW@*0xu zqvi$0^R=V2`6_M?4z7f@d?p4%QY)1K5_)nMt zg#{%LnpQ;Z?(Ffq?zUxo*@GWA);TD$K8Yg~!{Fo!JfaG_huG74D~Fi8u*Wlizy$*m z^Z~Y3@DyvO`TQk9Zc+udmjAk{Zk%gVu8X_yh5u6}oY)~%-x2{4haR3y?mYarXjT7_ z37u)NIghw$XoRgo5@E=N_J$|M^O^ZG?NyR;#~AT`{4Oz$)a3V$c;0`o3(^mY zVxcp%!Y!aOSj>-XQ8Qr7BCKl^czi1LFf;qVXX9735Gv6Wnwc)^G|G~@%cNx_u*aD; z2{Np^jq)sl|E;mJ3W@{R8Z?6rFt`V2f(O^&4grE&aCi6M?k>UI2^JuD7zpmcHMkSp zZDzT*{;J)5+1mH+emY%U=X|HX)3I%i;`c&zq?3D|jlRF;j`mF})#G=@hT;mrL0NmL z-m-2rK`S@&B%{{a?00uEg4$$Xd&P=vDU?`d_v}wr3DEG4Zdz# zOX~A|5cMI}mPXQnU0>h#{eNd(wPGV;|U_L{maVDcO0&67r@6vv0Iy*ZPvY8>{WD?sq5x6=EON?Xg z#ut+Tg$EPL4#U0@%tT1>z-%m2?I3L)O+t%a>b82t4`Ph%kB=dNuiv5iElFN8 zHOs{I`yPeD`!fl=v!a=urH}&edk32}+_Z70G*68RRkx1Z&jS6p2898z^lXvk^&X?( zL-N|*E_Qo~(= zOi%jRcWfB1vUrpT+w_U->>!*=f(-ghHkQY?k(Ij>dZ67}3{98YzzU9MsxyBUg%ot4 z2_Hg~^H)WVis1JPNO8ZX;nCW)zH%s3(*$jsi4-Zk&s1fhf;&MQ^J^~FgU8Mt$IzXT zomWCCO<>Wb!5vWHY(U%le1`XWmQBBW?CS7un$vDaePZuqCzo>;u6j)iwbhoZbe|zd zdKAg$5Kli0a7E|ta&z6gzHa;qF0Btycdy+GJ}KTfK`$rDTU(m3vrf+DfCtLD_4gRJ zizfXWxia3QeO(Tzcv0gjvUIcnetZ5*aQv++FH^INbx#uvc8wf#lXuG7R`XZ!(336? zF^&M5deaEo;Y0tJX@QPtB;%`$n8hs0Uzu-noIFbU+esXPkfrOfblkjJ3b87et5N2F z3=v#WWeEh&sCe1QrMIr0>4i*uO11^@g$McMjoKJ(-;sStAQ_kGQXR?72J!y~$3Q;( zAC7VKUyec3VDx`;48@bCB9#9>j&Z#HKOEyDiTVGVV@!G6d#Sb_Kz<|WUPai1-0J<8 zW7uh-e=q(I#~>ki;~0(QJZ~JMXK1Y}?{CB^>Vh&R&T&cgo+Tw~ud_@h70xdFwvu;V_lf z;y7tVDLZ?^!i>3l*N!}~GyYh{!&J5ku{?*BCO+~(@nkQL@5{un`tori+a%tE$OGUj z7nV>s^&Q>z_llka9DH(e3K8F@oenFPoaoc(Q`RDb&h_Qv9vyUg`GpS^*JDh6RPjNHf@1vnLi_huap5!OYlgD3fnF02Tv+?=h<0 zxz49B_IZb8nfb{1Z*_RuOl-!C5w>q7x0pALuoj6`1vW}qv_;OvCVW7L#$OGGasu_2 zFX^A{+(0hvn;ixS6Orxv#u{&$p7aKdWzs}re4-xJt0Y$PmOAs(xMuO86uRfp1mRdz zg}km~-cMwoY>+fM#Cf!^Ch*yVggPvfoR?udb&~h{_&}%`@m6-L97HvCeJC3l53 z|L{r86nO{3#-WzMoZcpn=btdSNS}oC0%n%!Txa{`RmcC$NB*TFIuelPJhD)b3fT7Q z_EGpjJHS)kgE>L6AQ@}V0-^O$BRAwDn-YcQ{zE0bCp|MAttSETy#BXJg9bYX=~K!P zpg&-l4*tNA5u|f?&q-o-m@&2wbtM22sfxjvD#_V5GCa z;CiE4t(j@mxVgsSys3gnI2@z7$5k-HSB7}pzKZ0;-$Cnah>~ttM7_sm|4vU&fAI^Y z!8TS`$6f;ioz6FxH}0^o0(@Fv{~mjrULmKau$z|(t3U)_1J>s1exZsfx$UY%E_L`@s_yTs$&P~iiiYdfS?~k|l z$MafQ?}AHEExZiR4crr}E+^hcr^rU;iL1d^QyJNA7sHtr~rnaz+*W zB&d(5X<&y>Wo|t!Ra?b*ky3Z?qZWtR;ru=m5s0DzAgZTlO zrr77^m|xyci@9uY;_YiT)#kZO)ZAmmfGx}O?T>MYb-Z+;?;Uyf_>2Eh_`UjQJdpL0 zJfz4~_@ob+Sm#ne0?2n|abyodkiA_C#Hp(x?2kIR-1%t14=A%@NJ#v6_lH9_+GSgE ze`;@O&D}%z`2cKT_!^J5ij;yL{k67u@^l<#QA?u5$~d7z_Je`rO79wL%=1cA7T_CC zBaHbqs8J)TCsj<*6-U?zwe{LF+D%-s88Oq7YK&@5D;+fXKD-aGpkLl#3ltWq()TX@ zf&0vDE(b?}67|X%4v@WdaFr{m`>Fq_$L&OcP|_OFc5>>esXnNz53y0U*@ce9Sgi^2 z*@|UeArCR>db?%@?ae-8LfO7Dm{4*u_C=rp`i-=)S$fYJltKazV+?(TRa_B#d_uy< z79VgtCJZ0(hkocpG=jv8q|a9iR4p&q*_&<+B{g;_OtP1`5~WF#+>mp2h)t6Xphn-B z-z2V;ifym@BGxIhx^CdN>la3Cq1J;_aQ|4DXPr_cTX^CBV7Dxm7+zqW{@~8h>`}94 zx-m;-ShyH+H1gTl#(zYelq+;JUw8ZiA9I0o*|?nDdr=-mu*317o@QSkd^o(MOjo6$ zuoH*tJ~sAo2{YcZNR=h5gOKP8F_uzA=H_t6+J1)PP_%WBjv;qrfW9xc9ZVvk%G?y` z+mvg6xbjCb_2obBu9K^3pJr66-s}`}_Z(I~`~*pcprG1HV9g@z^px_S=+rR`^RD2W z^6m-ZkS6r42x`-K0I3${b6IyC70a4E7STj8#Lua#AR>!^K2W=d%Jl8PWh2oIDMTRv zsCc?x6$mi|d8SkV8K^zV=fLsL)Yp2~@8vs&70qVlN8B4CyFr1yYN4=YVx;cuKPe2}{oAa1*1hELTT^In+rBmEs(V4T(3`V(p zZ7yDSBwaxHT<2x*qbhT+JS408cE~ShW=f%d@FvM@lk|pLtdaJ)P1DY@7_YQTGpo3gTRbOoUwD!<**M(>mTMJ zsP8XT7NAH!sz9F>QFD5hra2+Le92p7=@A^_|MpcGyPC zwR-{88ke~vY3aDE%qQ7cg7KQ;$Neu`;&$>w0ls>5ZeuIb&4NB?DmVf^Qb4Z($YU`()=z7UkUK#xG zL-V^giBeKY_b)q|!MVoM;58{uEq1tY&e?q|2eC!tp77t6~3joeOQbN-6zj7^~szJFg7 zgpB&ih2hHCZka~KB(r~8{vAI4VS*}Ba{`gvJ&9w@K8J5LWEqO+$C^HA7S(G_59#htI<|)pOueWP#Yp)t=}6Xvut?u2^gGxy zity`({0Oyk(`@$T)DY}~C^J@+B-@1KUxgwqROBO{0NS7Eyz5_K+70=Zk#t&?2}0>Y zIEi%bIBFqs?rFOP^RNS1!F^3TV}Y9*)vg$#gPB%hxE_dnK_!gE>f7%~%HTkZZ_j4< zz5cJs5z)+$Dy-@!>!#daZ#*OuvcEP#TV)^`&J5Kc-2U^} zBcf2+J$TxmSkBFkI<(|uBlZ&OMcG&!xL4_lG98G<>zhZcl$@s+~3z|Aoz)+_d+tc4`)Cl5iW#2txv);`ekZ6tx(RuRX- zpLGuXSloRMj0u~$SBDreXAGSz!a)o7rJq-qLKoHX0D;-ULq(X%-SmWsz?U9SU=Is2P`!0bhdO1dd?`C)$Z@ z^zANmQPPr>O^sR6P8FF1^n6(*Bd32V=f7v?N9i7X{OjujCoktIcEpkkKFnyvm893S zjdVJ+9$;`Pzn6V?T5Dn4y6fl2*^{$_uSHhxjYAS5LKk(7E;H1A@p#RE7AP5><~|a7 z6aWoPiIdK_547wy2?2vb)$x0trQC&=#^z9Q`cgiWhSV~qM?F#(Z_m6kGxON_B9WK^@hb;~urGp``l+w5 ztzHu)*tI?EQM?|uEW>zdIDQl)zx%_9UP|9phL414Qrds+LJdJJpqi#jpWIt7EX!{3 zvq4+?O8!cCp53Zu^3TkkVvxk@1+LxIyd-yuC`o>)&xR)OK~v**11GSqm*rlDUQ*dc7*4H0^sO$ya(*$tgL4W9 zx)<~;e(Q8{Y_$35CMWg#hQ&ecbz-FE?@L-qa<5g`b*tOelMkt0xaB1=mxQ zD-&e|P2Y;lmx@Z0|e_L6BDkdX5eVb@n*j7g=b`d^f9D|d_*Q!*lpZc1^Z z4mLzHr8fu0ciiIOjk?R?ibN&Bs)5#;l%||z_5gyh$3Sm)%0i=ZKwQL2pxN-B#7$9X zy7QLry4(3d1i*c-sAhHPc~YrS7%jjUfSOwrz6q#E3(mMC<_Mwb_wvja0xZ@;3uedi za&mGwcCX3BM*))5NPq6p&-Hu8-8dsZY+EF3Rih|+BZd|(iSJm(W(|MM`_78^F?t^; z2T94Z%XVi8NSjre6MIQzbkuBY+oapLo>28nKbg8j<&XXB+p0x8#48GK1p!2W@yuO}mqP4cY=U;@S&I1+g3_en9bmq^Uw!TADd%@68>&G!%X%Xd41VIuZ&Zfs33Az1a1NeWMBAPU`(F&m6rXdhbv@1)~9SyP)FDSmh8 zYf+N!n@wnC8yG+LH=%MbL2tq9*mI}ls$Xv#9mt-M4!b>H8U1+jqE);0j`+*#DK1BZ z{|`1H)9pq-0AHxMW>W}?Zu#=#4MzOibcZM@3zyZ7eqtrv6e_G4-RTz-h}yU@(o?y4@CDhhO|VomNy zME&>YOWTFa6G2iOK5`k$)?7fv-FJcO&tH`lcYmJsd{AAGA7VK23P;FvX?E@m_$X?t zP3A2A8nb@o*}BY%;!}?->l*8c(o%J?X>EkbD4VNbMZ}`?_`-%=$vkrCkb;*cFA@ zaR_Kj)M6GD{)xz+g(XTv(8ch}5t_wue_BZMoO&KvT}^ML{mz^^4h-4TGqS->>qN~V zS)Zi;Cm~IOk!1e9pVBN{o7N*0!B1(Y$Emm^s||HT;{JO}P7y3#H4fKlaokeWNW<{N zUDt7l>73+jrTQv(k$@Sfn6>HrH?dEpHnTt5tI8|!7GRJwMPZnbW(>-s{ z(T-isr2uTJh33X4KSG`-nUR5X4a8*~AI9>O+GoYS@^}5YN z9-}HgLGa}AWzcA4K;U3}4ggn|lvKnr{83yiR|7fr7zxjRvi-`Sx*2lpp@{xi4%2OO zU5vN@F_(}{+-N6$F{BZqLS<`Q^evDK+~^lyOLt-QPZ#p61CJUM?rECR&eHVd{85NI zEP}ksv6$`eVwa?NZm69Zu^~H>y`n)9WMsVLqbDyNp+U&>N`w z8>RGOPaofC`OEo;;WKY+V%^m?6TImGu1Ow{Au8nsv)@ZHq`#g{4Sm0bhAw(>yUc)3 zKK7ed^$VIa{wbXgoA}MP^I&0py(G64cm721oWRJ}%3jTs)+VvM5cv2{bz0)dbjb=) zOKFYaXGod)F2-@if?-kFm^eZ@QUDquOCWZ2KMtdST1bjKyc=PJs&-8jfndfrN*$py zMGDV$RaV^dY??E0x(#W#j#rh30|(E4DlA@QKbQ11U7B%6OY5qRQ4g_yeHGXb=K6sE zN4nSUW0|mTt;(Cq#JOe)&y?2G*Pw?pj$Y!Ri?5B~DEr`;(n310YwBaA4aMU`WI#Ow zaOu~Zj}2S`NeoOWAFIOCc)pGI=r_*Ro3TEA&m@&Q*xx765!VC^Qy?wzM@1KEsCjB^ z%#Z+=w!`6w5<+}~z2E0gk?Gd$j$Fl-sEa`H6JlUWq+hS+%Y(0Dvzsv-f ziXd0D31G3Q&movq#Xs6dyJd88iIh(_L|xjQRcWmJkCg#Whtro^3+ERzLQWlX zNJhBCsSbWU=t9poA_nJUEZ*2wj457pB?}(X*Z%j_jWWmj#|~E+>nS&SCrMo~__>JF zb}j|_rQ0!LD-LBl*-;9K;_|E!e;tuCO6GE-36f%%eNm?F$6vKpg=JCIQYA9m3PL^> zE;}v|{I&~0us=&~V-)KtH;hd6uGC9bRMumoi;vLr&K6cX0e#@jWub>5Nm`@LEtBg!8bC_^Ax#WN!*RQ81 a3Pw*e84!s_2mlTa1_RN#9>Xaj!~HKK%^wf| diff --git a/common/golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml b/common/golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml deleted file mode 100644 index a8ab9e78..00000000 --- a/common/golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml +++ /dev/null @@ -1,23 +0,0 @@ ---- -apiVersion: v1 -kind: Secret -metadata: - name: golang-external-secrets - namespace: golang-external-secrets - annotations: - kubernetes.io/service-account.name: golang-external-secrets -type: kubernetes.io/service-account-token ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: role-tokenreview-binding - namespace: default -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:auth-delegator -subjects: -- kind: ServiceAccount - name: golang-external-secrets - namespace: golang-external-secrets diff --git a/common/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml b/common/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml deleted file mode 100644 index 6a95745d..00000000 --- a/common/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: external-secrets.io/v1beta1 -kind: ClusterSecretStore -metadata: - name: vault-backend - namespace: golang-external-secrets -spec: - provider: - vault: - server: https://vault-vault.{{ .Values.global.hubClusterDomain }} - path: secret - # Version of KV backend - version: v2 - caProvider: - type: ConfigMap - name: kube-root-ca.crt - key: ca.crt - namespace: golang-external-secrets - auth: - kubernetes: - mountPath: {{ .Values.mountPath }} - role: {{ .Values.mountRole }} - secretRef: - name: golang-external-secrets - namespace: golang-external-secrets - key: "token" diff --git a/common/golang-external-secrets/values.yaml b/common/golang-external-secrets/values.yaml deleted file mode 100644 index b4656f3b..00000000 --- a/common/golang-external-secrets/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -mountPath: "hub" -mountRole: "hub-role" - -global: - hubClusterDomain: hub.example.com diff --git a/common/hashicorp-vault/Chart.yaml b/common/hashicorp-vault/Chart.yaml deleted file mode 100644 index 51ae676f..00000000 --- a/common/hashicorp-vault/Chart.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v2 -description: A Helm chart to configure Hashicorp's vault -keywords: -- pattern -name: hashicorp-vault -version: 0.0.1 -dependencies: - - name: vault - version: "0.22.0" - repository: "https://helm.releases.hashicorp.com" diff --git a/common/hashicorp-vault/README.md b/common/hashicorp-vault/README.md deleted file mode 100644 index 153b516a..00000000 --- a/common/hashicorp-vault/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# VP hashicorp-vault - -**IMPORTANT**: Due to the fact that 'null' values do not work in helm charts ([GH#9136](https://github.com/helm/helm/issues/9136)), -we need to patch the chart to skip setting the host. - -Make sure to run "./update-helm-dependency.sh" - -We can drop this local patch when any one the two conditions is true: - -- [1] is fixed in helm and we can require the version that for installs -- [PR#779](https://github.com/hashicorp/vault-helm/pull/779) is merged in vault-helm *and* our minimum supported OCP version - is OCP 4.11 (route subdomain is broken in OCP < 4.11 due to missing [commit](https://github.com/openshift/router/commit/6f730c7cae966f0ed8def50c81d1bf10fe9eb77b) diff --git a/common/hashicorp-vault/charts/vault-0.22.0.tgz b/common/hashicorp-vault/charts/vault-0.22.0.tgz deleted file mode 100644 index e6111e1f7482f02b422e1341eca5d369e2f3cc98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51863 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 Ly%<5xTYojN( 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 bYunSyd(&rT3oiTbU%q^$4TourIX}iX0ap0H diff --git a/common/hashicorp-vault/patch-server-route.diff b/common/hashicorp-vault/patch-server-route.diff deleted file mode 100644 index edc22c57..00000000 --- a/common/hashicorp-vault/patch-server-route.diff +++ /dev/null @@ -1,28 +0,0 @@ -diff -up vault/values.yaml.orig vault/values.yaml ---- vault/values.yaml.orig 2022-09-05 20:42:02.468428184 +0200 -+++ vault/values.yaml 2022-09-05 20:42:05.218435871 +0200 -@@ -406,7 +406,8 @@ server: - - labels: {} - annotations: {} -- host: chart-example.local -+ #host: chart-example.local -+ host: null - # tls will be passed directly to the route's TLS config, which - # can be used to configure other termination methods that terminate - # TLS at the router -diff -up vault/values.schema.json.orig vault/values.schema.json ---- vault/values.schema.json.orig 2022-09-11 21:00:34.834334961 +0200 -+++ vault/values.schema.json 2022-09-11 21:00:57.190368032 +0200 -@@ -838,7 +838,10 @@ - "type": "boolean" - }, - "host": { -- "type": "string" -+ "type": [ -+ "null", -+ "string" -+ ] - }, - "labels": { - "type": "object" diff --git a/common/hashicorp-vault/templates/vault-app.yaml b/common/hashicorp-vault/templates/vault-app.yaml deleted file mode 100644 index bbe16e14..00000000 --- a/common/hashicorp-vault/templates/vault-app.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: console.openshift.io/v1 -kind: ConsoleLink -metadata: - name: vault-link - namespace: vault -spec: - applicationMenu: - section: HashiCorp Vault - imageURL: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAf0AAAHhCAQAAADO0a/jAAAcYElEQVR42u2dB5hU5fWHzy5NmoBd7LH3WIIFFVS6goJRFBVjwYqKIhgbCFgQO2LBBoKKBQWFYIt/E2NHo4SABERAaui9LOzm/41DCLA7M7ffr7zv++Qxj48c7j3n++3Mztz7XZGh8h9EdMyhIodKGY1AdMoylXvFAFqB6JQD5Ff2lhKageiMJSrzG7mHdiA64z2yiZ1kFQ1BdMJVKu+b0Y2WIDphN9mCbWURTUG03kUq61txJW1BtN4rpRzVZBaNQbTaWSrnFdCe1iBabXupkEoymeYgWutklfEcNKc9iNbaXPLwHQ1CtNLvJC9NaRGilTaVAoykSYjWOVIKchxtQrTO48QDL9EoRKt8STxxiJTSLERrLFWZ9kh/2oVojf3FM3vKOhqGaIXrVJ590IeWIVphH/HFjrKSpiEa70qVZZ90pW2IxttVfFNbFtI4RKNdqHIcgCtoHaLRXiGBqCYzaR6isc6seGMOL5xL+xCN9VwJTLFMooGIRjpJ5TcEzWghopE2k5CMpYmIxjlWQnM6bUQ0ztMlAkbQSESjHCGR8DtaiWiUv5OIGEwzEY1xsETGQWzegWiIpSqvEfIYLUU0wsckUvZg8w5EA1ynshoxvWgrovb2ksjZQVbQWEStXaFyGgM301pErb1ZYqGWLKC5iNq6QGU0Ji6nvYjaernERlWZQYMRtXSGymeMnEOLEbX0HImVYplIkxG1c2K4jTm80IQ2I2pnE0mAr2k0olZ+LYlwKq1G1MpTJSHeotmI2viWJMYxtBtRG4+RBHmRhiNq4YuSKAfIBpqOmLobVBYT5lHajpi6j0ri7C5raTxiqq5VOUyBnrQeMVV7SipsJ8tpPmJqLlcZTIkutB8xNbtIatSU+QwAMRXnq/ylyKWMADEVL5VUqSLTGQJi4k5X2UuZdowBMXHbSeoUywQGgZioE+LfmMMLZzEKxEQ9SzThI4aBmJgfiTY0YhyIidlINGI4A0FMxOGiFUcxEsREPEo043mGghi7z4t27M/mHYgxu0HlTEMeZjSIsfqwaEl9WcNwEGNzjcqYpvRgPIix2UO0pZ4sY0CIsbhM5UtjbmREiLF4o2hNDZnHkBAjd57KluZcwpgQI/cS0Z4q8jODQozUn9PfmMMLZzMqxEg9W4ygSMYzLMTIHK8yZQitGRdiZLYWg/iAgSFG4gdiFCczMsRIPFkM4w2GhhjaN8Q4jpQyBocYyjKVIwN5jtEhhvI5MZJ9ZT3DQwzsepUhQ3mQ8SEG9kExll1lNQNEDORqlR+DuZMRIgbyTjGaurKEISL6donKjuFczxgRfXu9GE91mcsgEX05V+XGAjoySkRfdhQrqCxTGSaiZ6eqzFhCG8aJ6Nk2Yg1FMo6BInpynDkbc3ihFSNF9GQrsYz3GSpiQd8X62jIWBEL2lAs5DUGi5jX18RKjmDzDsQ8lqmMWMpAxouY04FiLb9h8w7EHJaofFhMP0aMWKH9xGp2kVUMGbGcq1Q2LOcOxoxYzjvEeurIYgaNuIWLVS4c4DpGjbiF14kTVJc5DBtxk3Ps2JjDCxcx7kAXfCyUf8rH8lf5lyzTuGbWNTJNvpAP5QeZKxuYXl4vEmeoLJMZuA+XyMtygdTbood7yNUyStZpVTPrFLlfGmxx4+k20kqelJlMskIn27MxhxdaM3KPrpA+eT4C2lNeDPCaGkfNrAvkKinOUbeK+sEym4mWs7U4RZF8ydA9+L7sVLCXh8qPqdfM+sFW7yIq+pynP1Pdwi/t2pjDCy0Ye0EflkqeermtjE61ZtZX1eu6Fy4P/SuFTbYQBxnD4PPa00cvi2VkajWzjvLx6tWGezg3Okac5ARGn8fhPt8I1pZ/plIz63TZzlfl25nwr54gjjKM4edwmtT03c39C7yRjqPmfz3Jd+X3mLJa/85yGG/8chjsEQz9E6+Z9a0AdQ+XUuev0zhMHOZpYl6BE3J+QZafnWVlojWzrpcDA1V+xfEpPy1Os7eUEPVyBn/U4kuJ1sz6ZsC6Jzo94xK19h2nL1EvZ/DdWs5NtGbW5gHrFstCh2fcV5xnp4JvKF3zxxDdrJNjC7Q4amadEfAXiQwvOzvjlR4uq3KA24j7VpfGhGFyYjWzPhKibldnZ3wbsc9eN7aIwEcUJpFPE6uZtVmIuhc6OuFFas3Dr1xL4Dfz1lC9fCOxmhlXyzYh6p7u6ISvJfL/parMIvKbvCFUL/8UQ82X8nxlGAY3H8Y2S6132EQHIr/Je0N9JLcu8pr5HpW6MFTddk7OtwNx35xKbN6xyRdC9LFjDDVFfshzRVqVEHVd/EVvssc7Jx3iDEK/0Y9DdPGjGGoWy5I8R7tfiMouPpDlDKJenu+I/a+uDXCbTZZjY6gpclzeow2zl+w452b7HTGviGbEfqNnBezg2zHUFOmT91iD33G+h4OTbUbMK2Y0sQ9xAU6DvPdBBr2op7jAVl1rZEcu6PHoaCIe7K2lO5bKob57V0XGR14zQ+GN0x8NVLeW/Nu5uR5HxHPzCsH/1RG+O9czhpqZHyhTPXw2sUeAync6N9NXiHc+DnF+A4dg3/628LR9tv9vlB/2dKx/9r2T/FHOPXG5VK1tyMtTxH7j79DHeu7Z4bI88pr5rhIo7+O+6u4kM5yb51NEuxC7s1XzRmer10YvHOHjwRZea2Zoq97Kez/aOz3X3UXGOjfLdWpdQ0HuJ/YbXSXnefhKdHnkNTOPSOnpe9/E16SGp2sPXLxf435i7YUdZAWx3+TQPPvr1JIH826jEaRmhuPlL4GOdZK0y1u3nvRVv3S4N8MVak2DJ24l8ptZIs/IyeWu/K4jV4Z4gGXFNUW9bp8p74Q62rFykWxf4a8lvWWpoxO8lUh7pbbMIfJbuVhel/vkBvV2/Q9ytwyP5NXzfzUvlK7ST8ZE9JpcKp/Lo2rBXyLnyDXSSwY5/ZTdOWo9g2euJuxoiVcTZz9U42nsaIUz1VoGX1zAskELvIAo+6WSTGLhoOFOYmOOILRi6aDhtiLGwRjL4kGDHUuEg9KE5YMG24QIB2cUCwgNdRTxDcORLKGNrpe/ySDpK13kFnlIXs6zS266NbP+LK/L43KbXC995Dl537lbdDMeQXzDMZTYy7vSXuqW68zucqV8o1XNjNOkewV3pm8jLdSPAJceqD6U6IblYE+bUNjrxwW2dWorE7SomXGedM77ZJl9ZIgjW7FsUOsWQjPA2dhvkBs99KeKDEy5Ztb/q/C2na1pkXdXf1scQGyjYE8nb/T8j4qI90+Ir/X43iiOmlmf9LxJ1wHWP2lpTaDdCqEC7nXyFd/fV0NdU6qZ1d9W3/vIQqtndy+RjYodfO5EY4M3+u7SkFRqZvxWqvus2zjQFiNmuJyNOaKku3Mf7vmnRoFdDuKomX0vcWCAyvY+b687cY2SWrLAqegHe1jDNYnXzPh8oLrbWbpnzwK1ViFSrnLqe/xgVJHpidbMWBL4I62eVs7uSqIaNdUc2ru9feAu9Uu0ZtBfI7LsZ+HkZrAxRxy0d+aS3bqBe9QowZpZbw4x0X/xQxu8UFzggZK2+LcQPaqc47uQOGpmDXPV2mOWTW68WqMQCy2ciP6gUD36R2I1s1YNUbezZZNrQUTj42sHoh/uSS0fJVYz4+JQdX9v1dy+Jp5xcoID0e8SqkNDE6uZcWKouidbNbcTiGe8vGN99G8J1Z/hMdR8LeexTglV9zSLpvYO0YybY62P/kOh+vNtDDU/yXmsK0LV7WDR1I4hmvEzxPLovxyqOwtiqPljnqOtGaJuV2tmNoRYJsEBlm/e8UOI3uwbQ82asjbP0TYIUXmwNXdZHkAsk+EJy1/3dwvcmd4x1GyT91h7Ba5bLPMtmdcTRDIpds/7OuTudeBFMi2Ga8sHFrhh1/Vva9aq9QiJ0cfq6H8TsCsXx1CzjizOe6xlcrTjb/f7EMck2V6WWR3+toF6siDymiL3FDzW9wLVPdSSDTqXedqRECLkFqujP0Gq+O7I0Bhq1peVHo721ADzG81VGBCMWtZ8SFSxA332o0cMNavK556Odbbs6rNyN0umNJ+NOdKgk+Wf81/roxedY6gp8oLnY/3K133qZ1izG38nYpgGVQvsH2P+t8VdPfWhkvSVsohrZrr7gq+j/avs6LFyB1ltyYSmh7pvEUJwnvUX9Q6RGgV/G/9L5DUzVT8PEITCn/VXtmo7zvOIYFoUB3xAlEnOkWtyfjy3rfTx9DGcn5qZr/PuCVQ1855ikOyV56qDDvKTVR/FsjFHijRz4CbezCMs+0mjLZ5uUyynSP9QD7IoXzNzyW4bGVjge/xCrpPX5eKtdqIvkt9JL5lo2VSaEb90+cSJ8GdcLv+Qj2SovClfy6KIaw5TffwxwmskS9Xr+2cyXAbLGPk+sqPVyU+IXto0dib6qJONiV76jGAhYsKOIHY6cDRLERP2aGKnB4NZjJigg4mcLuxv+eYdqNelVvsTOX14nCWJCfk4cdOJnWWN1cttvXwi3eUcaSj7ykHSSNpLH/m7hjWzTpX+cpE0kcNkb2kgbaSzjAx4kZB+rlFrDbSit7Wx/0U6SZ0Kz7m+9Ai4b0EcNbM/TgbmeBRXNTlLvrNgGr2Jmm5sF/IqND1dKV0K3BO3vTzi+ead+GpmfVv2y1u3SM5VP3RMnscitc5AO2628ALeIzydeZu8D8SMv2b2+r3unuruJH8zeCI3EzMdqSmzrAr+t1tdCZ+PQ2ReajUzlsiZnutWlWGGTmRWqKcOQIxcbtUde/V9nXtDWZdKzaxX+6pbTb40ciaXEzFdqSJTLQl+iRzv++w7pVAz6zO+6+6ifgyZNpOpAfY2hMSw5bHNAwKd/ZeJ18y4ULYNUPdS42bye+KlM8Uy3oLgr1KvikE4NeGaWYPtSVsp75P89HM8G3PoTlOnrxf7LNGaGRdL9YB1Oxo1k6ZES38+Nz76jQOf+y2J1sw4LHDdegbdefE5sTKBUwwP/tKtts3yw0EJ1sx6YYhJfWrMTE4hVmbwttHRHx3q3OcmVjNr/RB1exkykbeJlCn8NtBlqLr4TKhzH5tYzewNrGE+/DLjISplaj2BMQwyOPp3hzrzUYnVzDg3VN3WRsxjEHEyib1kvbHR7xzqzAfHUHNQnq+8wnCCEbdL70WczOJRY6N/Z6jzHhNDzRF5bv4NQ0sDpvEoUTKNXY19stsToc57Qgw1c1/Rty5UXf2v6Fvt+xnCoAF3Gxr94SHOuSjHbjjDQ3VyWp6j3T5E3Tss/9wFUqKeLDEy+rNDnHOjGGrWz3u0rSP/5UQfl6g1BEZyk6Gv+0cFPuMXY6jZKaavImtG+KCveLyJCJlKDfVqZ2L0ewQ831p5dtbpEbiL7+Q91pnql4xgtNP+3VcNImQulxkZ/XkBd4N5OIaaBxe80v6CgLP5SvMpXEZ8TMbUzTtuC3CuRxcI6W2BOvhWwWOdEuj+AN1f89mYw3jaGRn9JbKHz/OsLeMir+nlbv2MXX3XrS2TNZ9AO6JjOkUFI6Gn3/m6D76afBx5zcw1kQs8XvF2mq+6xfKu5t0fF/gTDNCIZoZ+zv+6VPIc/JGR1xSpKz94PtaFcqCPiTykfe+bERs7+MjQ8P/Z0wMf9vH1VJs/e3yIxEHyL1/HuthjXKrLq9r3/SMiYwvHGXs9/09ycoFfZy70feFSoZrZqkt9H+sGuUO2KVD5SCMevnUckbGH4QbfxDtGjs5xVi0CPx4zd02RM+T7wMc6Uzrl/GT8ABlmxD4Kw4mLTRxh0D5wFfmj+v34dNlr46vqTnKS9JOfI61ZS/aTs+W5CC6CWipvSEc5eONFsFVkN2kovSN6hm/8bvD4YDIwhhcM37Hvf8FaE0PNeB6BvVYWGbdf0gtExTZ+IyWWhB/js0StE7COR1jaWMBHiImN7CKrWNyYx6BPKALt6cHyxhjumATtqSuLWeCY86KkukTEXm5kiWMObyQeNlPdwOe6YxLOCfzYUDCEP7DME/te3yT/QDRsp5JMIeyxXs1nolN83NEIxtKW2Oe5hr8o1DX8ptqWWLhAUZ7HR7pgXHfumetYNuZwhZYOBz+u+/VNtiWRcIcPHQ1+XLv0mOyHxMElTnQy+HHtzWe2JxIHt3jTueDHtyOvyb5JFFzjMCl1LPpx7cNvsqVqHYBzPOdU8ON7+o7JPkcMXGQvpzbviOuZeyZbotYAOMlDDkU/riftmuxDRMBVdnbm2vXZIbpU39KerFTzB2e5y5Hoh9tgepqVPbmL5e8ydWSRE9F/IlSXvrSwI4vU7MFpbnAi+neE6tEICztyA0vfdbaR6Q5Ev3OoHg22rh/TCz4iDBygowPRvztUh0Zb14+OLHsQqSyTrY/+M6E6ZNtNzpPVzAEUZ1kf/dGh+jPXsm6cxZKHLEVGPPo5jEtDvM4dZN39i2zMAZtobv3rfuPAvbnFsk40Z7nD5rxvefQfD9yZz6zqw/ssddiS4y2PftDnydl2x/7xLHXYmtctD/+AQF2x60q+11nmUJ6DLd+8oyTAK55dd+2VqhkDVMCzlr/uz5H6vvrRUNZZdf7PssShYna3bKmX91vZwXM3DpF5Vp37OjVfgBz0s/5LvmlyhKdOtJHllp15P5Y35GZ7WWF9+FdKF6lWoAuPSJllZ71CnRVAHu5w4ibeX6RTjvvV60sPWcZty+Ae28q/Hdm5Z718It2lnTSUfeVAaSTtpY/83dJz/beaK0ABOjsSfZfszLKGwmwjMwmLVc5kYw7wxsXExSovZkmDNyrJJAJjjZN8PF0YnKc1kbHG1ixn8MNYQmOFY1nK4I8mxMYKm7CUwS/vERzjfY9lDP5pQHSMtwHLGIIwjPAY7TCWMATjYNlAgIx1AxtzQHCeIULG+gzLF4Kzp6wlREa6Vs0OIAQPECMjfYClC+HYwbrdalxwuY+NyABycDtRMs7bWbYQntqygDAZ5QI1M4AIuJY4GeW1LFmIhmoyg0AZ44wCm44C+OBCImWMF7JcIToqyURCZYQT2ZgDouVMYmWEZ7JUIWq+IVja+w3LFKLnNKKlvaexTCEO/kS4tPZPLFGIh2OJl9YeyxKFuHiVgGnrqyxPiI8D2bxD2405DmR5Qpw8Rcy09CmWJsTLHmzeoeXGHLuzNCFu7idq2nk/yxLiZ3tZRti0cpmaCUAC/JG4aeUfWZKQDLVkPoHTxvlqHgAJcQ2R08ZrWI6QHFVlOqHTwulqFgAJcgGx08ILWIqQLMUygeCl7gQ1B4CEaUX0UrcVyxDS4CvCl6pfsQQhHRoTv1RtzBKEtBhFAFNzFMsP0uNoKSOEqVimeg+QIi8Tw1R8maUH6XKArCeIibte9R0gZZ4kion7JMsO0mc3WUMYE3WN6jmABtxHHBP1PpYc6MF2spRAJuZS1W8ATehOJBOzO8sN9KGmzCOUiThP9RpAI64ilol4FUsN9KKq/EwwY/dnNuYA/TifaMbu+Swz0I9iGU84Y3U8G3OAnrQknrHakiUGuvIFAY3NL1heoC+nENHYPIXlBTrzLiGNxXdZWqA3v2Xzjlg25vgtSwt0ZwhRjdwhLCvQn/3YvCPyjTn2Y1mBCTxBXCP1CZYUmEF9WU1gI3O16ieAIdxDZCPzHpYTmEM9WUJoI3GJ6iWAQXQjtpHYjaUEZlFD5hLc0M5VfQQwjE5EN7SdWEZgHlVkKuEN5VTVQwADOY/4hvI8lhCYSZGMI8CBHaf6B2AozYlwYJuzfMBkPiPEgfyMpQNmcxIxDuRJLB0wnZEE2bcjWTZgPkeyeYfvjTmOZNmADbxEnH35EksG7GBfKSHQni1R/QKwhP5E2rP9WS5gD7vKKkLtyVWyC8sFbKI3sfZkb5YK2EVdWUywC7pY9QnAMroS7YJ2ZZmAfVSXOYQ7r3NUjwAs5ArindcrWCJgJ5VlCgHP6RTVHwBLOZeI5/RclgfYS5F8T8gr9Hs25gC7aUrMK7QpSwNs51OCXs5PWRZgPycS9XKeyLIAF3ibsG/h2ywJcIPDpZTAb7JU9QPAEQYR+U0OYjmAO+zD5h2bNubYh+UALvEYsf/Vx1gK4BY7y0qCr3qwM0sBXKMX0Vc9AHCOOrLI8eAvUj0AcJCbHI/+TSwBcJPqMsvh4M9iYw5wl8scjv5ljB/cpbJMdjT4k9mYA9zmHEejfw6jB7cpkr87GPzv2JgDoImD0W/C2AFEPnEs+J8wcoAMxzsW/eMZOUCWtxwK/luMG+C/HOrM5h2l6lwBYBMvOhL9Fxk1wObsLescCP46dZ4AsAWPOBD9RxgzwNbsJCssD/4KdY4AUI6elke/JyMGqIhtZaHFwV+ozg8AKqSLxdHvwngBcrGNzLQ0+DPVuQFATi61NPqXMlqAfFSSSRYGf5I6LwDIS1sLo9+WsQIUoki+tSz437IxB4AXmloW/aaMFMAbH1oU/A8ZJ4BXGlgU/QaME8A7b1oS/DcZJYAfDpYNFgR/gzoPAPDF8xZE/3nGCOCXPWWt4cFfq84BAHzzsOHRf5gRAgRhR1lucPCXq+MHgEDcZXD072J8AEGpLQsMDf4CdewAEJgbDI3+DYwOIAzV5BcDg/+LOm4ACMUlBkb/EsYGEJZK8qNhwZ/IxhwAUXC2YdE/m5EBRMM3BgX/G8YFEBWnGxT90xkXQHR8YEjwP2BUAFFyrCHRP5ZRAUTLGwYE/w3GBBA1B2m/eccGdYwAEDnPah79ZxkRQBzsofXmHWvV8QFALDyocfQfZDwAcbGDLNM0+MvUsQFAbNypafTvZDQAcVJL5msY/PnquAAgVq7XMPrXMxaAuKkq0zUL/nR1TAAQOxdrFv2LGQlAEhTLBI2CP0EdDwAkQhuNot+GcQAkx1eaBP8rRgGQJKdqEv1TGQVAsrynQfDfYwwASXOMlKUc/DJ1DACQOK+lHP3XGAFAGhwg61MM/nr19wNAKgxMMfoDaT9AWuwma1IK/hr1dwNAajyQUvQfoPUAabKdLE0h+EvV3wsAqXJ7CtG/nbYDpE1NmZdw8OepvxMAUue6hKN/HS0H0IGqMi3B4E9jYw4AXbgowehfRLsBdKFYxicU/PFszAGgE2cmFP0zaTWAXnyRQPC/oM0AutEogeg3os0A+jEm5uCPocUAOnJUrJt3lKn6AKAlr8YY/VdpL4Cu7B/b5h3rVW0A0JanY4r+07QWQGfqy+oYgr9a1QUArekbQ/T70lYA3aknSyIO/hJVEwC057aIo38bLQUwgRoyN8Lgz1X1AMAIrokw+tfQTgBTqCJTIwr+VFULAIyhQ0TR70ArAUyiWMZFEPxxbMwBYBpnRBD9M2gjgHl8FjL4n9FCABM5OWT0T6aFAGYyOkTwR9M+AFM5MvDmHWXqzwKAsbwSMPqv0DoAk9lXSgIEv0T9OQAwmicDRP9J2gZgOrvKKp/BX6X+DAAYz30+o38fLQOwgbqy2EfwF6v/HgCs4FYf0b+VdgHYQg2Z4zH4c9iYA8AmrvYY/atpFYBNVJGfPAT/JzbmALCN8z1E/3zaBGAbRfJDgeD/oP4bALCOlgWi35IWAdjJp3mC/yntAbCVhnmi35D2ANjLuzmC/y6tAbCZw6W0guCXqn8PAFYztILoD6UtALazT7nNO0rUvwMA6xmwVfQH0BIAF9hFVm4W/JWyMy0BcIN7N4v+vbQDwBXqyKKNwV+k/j8AOEO3jdHvRisAXKK6zFbBn63+CQBOcaWK/pW0AcA1Ksto9T8AcA525HGY/wcAxiEwaH/ifAAAAABJRU5ErkJggg== - href: 'https://vault-vault.{{ coalesce .Values.global.localClusterDomain .Values.global.hubClusterDomain }}' - location: ApplicationMenu - text: 'Vault' diff --git a/common/hashicorp-vault/update-helm-dependency.sh b/common/hashicorp-vault/update-helm-dependency.sh deleted file mode 100755 index afd6d522..00000000 --- a/common/hashicorp-vault/update-helm-dependency.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -set -eu - -# Get the version of the dependency and then unquote it -TMPVER=$(sed -e '1,/^version:/ d' "Chart.yaml" | grep "version:" | awk '{ print $2 }') -VERSION=$(eval echo "${TMPVER}") - -# Chart format is vault-0.21.0.tgz -NAME="vault" -TAR="${NAME}-${VERSION}.tgz" -CHARTDIR="charts" - -if [ ! -f "${CHARTDIR}/${TAR}" ]; then - echo "Charts $TAR not found" - exit 1 -fi - -pushd "${CHARTDIR}" -rm -rf "${NAME}" -tar xfz "${TAR}" -pushd "${NAME}" -patch -p1 < ../../patch-server-route.diff -popd -tar cvfz "${TAR}" "${NAME}" -rm -rf "${NAME}" -popd diff --git a/common/hashicorp-vault/values.yaml b/common/hashicorp-vault/values.yaml deleted file mode 100644 index e16cc0e3..00000000 --- a/common/hashicorp-vault/values.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -global: - openshift: true - localClusterDomain: apps.foo.cluster.com - -vault: - injector: - enabled: false - ui: - enabled: true - serviceType: "LoadBalancer" - server: - route: - host: null - enabled: true - tls: - termination: "edge" - image: - repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.11.3-ubi" diff --git a/common/install/.helmignore b/common/install/.helmignore deleted file mode 100644 index b25c15b8..00000000 --- a/common/install/.helmignore +++ /dev/null @@ -1 +0,0 @@ -*~ diff --git a/common/install/Chart.yaml b/common/install/Chart.yaml deleted file mode 100644 index d1c80ab3..00000000 --- a/common/install/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -description: A Helm chart to build and deploy a Cloud Pattern -keywords: -- pattern -name: pattern-install -version: 0.0.1 diff --git a/common/install/crds/applications.argoproj.io.yaml b/common/install/crds/applications.argoproj.io.yaml deleted file mode 100644 index c9866d38..00000000 --- a/common/install/crds/applications.argoproj.io.yaml +++ /dev/null @@ -1,2312 +0,0 @@ -# oc get crd/applications.argoproj.io -o yaml > applications.argoproj.io.yaml -# Remove annotation, timestamps and other cluster-specific cruft -# Also remove any status leftovers at the end -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - generation: 1 - labels: - app.kubernetes.io/name: applications.argoproj.io - app.kubernetes.io/part-of: argocd - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" - name: applications.argoproj.io -spec: - conversion: - strategy: None - group: argoproj.io - names: - kind: Application - listKind: ApplicationList - plural: applications - shortNames: - - app - - apps - singular: application - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.sync.status - name: Sync Status - type: string - - jsonPath: .status.health.status - name: Health Status - type: string - - jsonPath: .status.sync.revision - name: Revision - priority: 10 - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: Application is a definition of Application resource. - 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 - operation: - description: Operation contains information about a requested or running - operation - properties: - info: - description: Info is a list of informational items for this operation - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - initiatedBy: - description: InitiatedBy contains information about who initiated - the operations - properties: - automated: - description: Automated is set to true if operation was initiated - automatically by the application controller. - type: boolean - username: - description: Username contains the name of a user who started - operation - type: string - type: object - retry: - description: Retry controls the strategy to apply if a sync fails - properties: - backoff: - description: Backoff controls how to backoff on subsequent retries - of failed syncs - properties: - duration: - description: Duration is the amount to back off. Default unit - is seconds, but could also be a duration (e.g. "2m", "1h") - type: string - factor: - description: Factor is a factor to multiply the base duration - after each failed retry - format: int64 - type: integer - maxDuration: - description: MaxDuration is the maximum amount of time allowed - for the backoff strategy - type: string - type: object - limit: - description: Limit is the maximum number of attempts for retrying - a failed sync. If set to 0, no retries will be performed. - format: int64 - type: integer - type: object - sync: - description: Sync contains parameters for the operation - properties: - dryRun: - description: DryRun specifies to perform a `kubectl apply --dry-run` - without actually performing the sync - type: boolean - manifests: - description: Manifests is an optional field that overrides sync - source with a local directory for development - items: - type: string - type: array - prune: - description: Prune specifies to delete resources from the cluster - that are no longer tracked in git - type: boolean - resources: - description: Resources describes which resources shall be part - of the sync - items: - description: SyncOperationResource contains resources to sync. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - type: array - revision: - description: Revision is the revision (Git) or chart version (Helm) - which to sync the application to If omitted, will use the revision - specified in app spec. - type: string - source: - description: Source overrides the source definition set in the - application. This is typically set in a Rollback operation and - is nil during a Sync operation - properties: - chart: - description: Chart is a Helm chart name, and must be specified - for applications sourced from a Helm repo. - type: string - directory: - description: Directory holds path/directory specific options - properties: - exclude: - description: Exclude contains a glob pattern to match - paths against that should be explicitly excluded from - being used during manifest generation - type: string - include: - description: Include contains a glob pattern to match - paths against that should be explicitly included during - manifest generation - type: string - jsonnet: - description: Jsonnet holds options specific to Jsonnet - properties: - extVars: - description: ExtVars is a list of Jsonnet External - Variables - items: - description: JsonnetVar represents a variable to - be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - description: Additional library search dirs - items: - type: string - type: array - tlas: - description: TLAS is a list of Jsonnet Top-level Arguments - items: - description: JsonnetVar represents a variable to - be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - description: Recurse specifies whether to scan a directory - recursively for manifests - type: boolean - type: object - helm: - description: Helm holds helm specific options - properties: - fileParameters: - description: FileParameters are file parameters to the - helm template - items: - description: HelmFileParameter is a file parameter that's - passed to helm template during manifest generation - properties: - name: - description: Name is the name of the Helm parameter - type: string - path: - description: Path is the path to the file containing - the values for the Helm parameter - type: string - type: object - type: array - ignoreMissingValueFiles: - description: IgnoreMissingValueFiles prevents helm template - from failing when valueFiles do not exist locally by - not appending them to helm template --values - type: boolean - parameters: - description: Parameters is a list of Helm parameters which - are passed to the helm template command upon manifest - generation - items: - description: HelmParameter is a parameter that's passed - to helm template during manifest generation - properties: - forceString: - description: ForceString determines whether to tell - Helm to interpret booleans and numbers as strings - type: boolean - name: - description: Name is the name of the Helm parameter - type: string - value: - description: Value is the value for the Helm parameter - type: string - type: object - type: array - passCredentials: - description: PassCredentials pass credentials to all domains - (Helm's --pass-credentials) - type: boolean - releaseName: - description: ReleaseName is the Helm release name to use. - If omitted it will use the application name - type: string - skipCrds: - description: SkipCrds skips custom resource definition - installation step (Helm's --skip-crds) - type: boolean - valueFiles: - description: ValuesFiles is a list of Helm value files - to use when generating a template - items: - type: string - type: array - values: - description: Values specifies Helm values to be passed - to helm template, typically defined as a block - type: string - version: - description: Version is the Helm version to use for templating - (either "2" or "3") - type: string - type: object - ksonnet: - description: Ksonnet holds ksonnet specific options - properties: - environment: - description: Environment is a ksonnet application environment - name - type: string - parameters: - description: Parameters are a list of ksonnet component - parameter override values - items: - description: KsonnetParameter is a ksonnet component - parameter - properties: - component: - type: string - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - kustomize: - description: Kustomize holds kustomize specific options - properties: - commonAnnotations: - additionalProperties: - type: string - description: CommonAnnotations is a list of additional - annotations to add to rendered manifests - type: object - commonLabels: - additionalProperties: - type: string - description: CommonLabels is a list of additional labels - to add to rendered manifests - type: object - forceCommonAnnotations: - description: ForceCommonAnnotations specifies whether - to force applying common annotations to resources for - Kustomize apps - type: boolean - forceCommonLabels: - description: ForceCommonLabels specifies whether to force - applying common labels to resources for Kustomize apps - type: boolean - images: - description: Images is a list of Kustomize image override - specifications - items: - description: KustomizeImage represents a Kustomize image - definition in the format [old_image_name=]: - type: string - type: array - namePrefix: - description: NamePrefix is a prefix appended to resources - for Kustomize apps - type: string - nameSuffix: - description: NameSuffix is a suffix appended to resources - for Kustomize apps - type: string - version: - description: Version controls which version of Kustomize - to use for rendering manifests - type: string - type: object - path: - description: Path is a directory path within the Git repository, - and is only valid for applications sourced from Git. - type: string - plugin: - description: ConfigManagementPlugin holds config management - plugin specific options - properties: - env: - description: Env is a list of environment variable entries - items: - description: EnvEntry represents an entry in the application's - environment - properties: - name: - description: Name is the name of the variable, usually - expressed in uppercase - type: string - value: - description: Value is the value of the variable - type: string - required: - - name - - value - type: object - type: array - name: - type: string - type: object - repoURL: - description: RepoURL is the URL to the repository (Git or - Helm) that contains the application manifests - type: string - targetRevision: - description: TargetRevision defines the revision of the source - to sync the application to. In case of Git, this can be - commit, tag, or branch. If omitted, will equal to HEAD. - In case of Helm, this is a semver tag for the Chart's version. - type: string - required: - - repoURL - type: object - syncOptions: - description: SyncOptions provide per-sync sync-options, e.g. Validate=false - items: - type: string - type: array - syncStrategy: - description: SyncStrategy describes how to perform the sync - properties: - apply: - description: Apply will perform a `kubectl apply` to perform - the sync. - properties: - force: - description: Force indicates whether or not to supply - the --force flag to `kubectl apply`. The --force flag - deletes and re-create the resource, when PATCH encounters - conflict and has retried for 5 times. - type: boolean - type: object - hook: - description: Hook will submit any referenced resources to - perform the sync. This is the default strategy - properties: - force: - description: Force indicates whether or not to supply - the --force flag to `kubectl apply`. The --force flag - deletes and re-create the resource, when PATCH encounters - conflict and has retried for 5 times. - type: boolean - type: object - type: object - type: object - type: object - spec: - description: ApplicationSpec represents desired application state. Contains - link to repository with application definition and additional parameters - link definition revision. - properties: - destination: - description: Destination is a reference to the target Kubernetes server - and namespace - properties: - name: - description: Name is an alternate way of specifying the target - cluster by its symbolic name - type: string - namespace: - description: Namespace specifies the target namespace for the - application's resources. The namespace will only be set for - namespace-scoped resources that have not set a value for .metadata.namespace - type: string - server: - description: Server specifies the URL of the target cluster and - must be set to the Kubernetes control plane API - type: string - type: object - ignoreDifferences: - description: IgnoreDifferences is a list of resources and their fields - which should be ignored during comparison - items: - description: ResourceIgnoreDifferences contains resource filter - and list of json paths which should be ignored during comparison - with live state. - properties: - group: - type: string - jqPathExpressions: - items: - type: string - type: array - jsonPointers: - items: - type: string - type: array - kind: - type: string - managedFieldsManagers: - description: ManagedFieldsManagers is a list of trusted managers. - Fields mutated by those managers will take precedence over - the desired state defined in the SCM and won't be displayed - in diffs - items: - type: string - type: array - name: - type: string - namespace: - type: string - required: - - kind - type: object - type: array - info: - description: Info contains a list of information (URLs, email addresses, - and plain text) that relates to the application - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - project: - description: Project is a reference to the project this application - belongs to. The empty string means that application belongs to the - 'default' project. - type: string - revisionHistoryLimit: - description: RevisionHistoryLimit limits the number of items kept - in the application's revision history, which is used for informational - purposes as well as for rollbacks to previous versions. This should - only be changed in exceptional circumstances. Setting to zero will - store no history. This will reduce storage used. Increasing will - increase the space used to store the history, so we do not recommend - increasing it. Default is 10. - format: int64 - type: integer - source: - description: Source is a reference to the location of the application's - manifests or chart - properties: - chart: - description: Chart is a Helm chart name, and must be specified - for applications sourced from a Helm repo. - type: string - directory: - description: Directory holds path/directory specific options - properties: - exclude: - description: Exclude contains a glob pattern to match paths - against that should be explicitly excluded from being used - during manifest generation - type: string - include: - description: Include contains a glob pattern to match paths - against that should be explicitly included during manifest - generation - type: string - jsonnet: - description: Jsonnet holds options specific to Jsonnet - properties: - extVars: - description: ExtVars is a list of Jsonnet External Variables - items: - description: JsonnetVar represents a variable to be - passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - description: Additional library search dirs - items: - type: string - type: array - tlas: - description: TLAS is a list of Jsonnet Top-level Arguments - items: - description: JsonnetVar represents a variable to be - passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - description: Recurse specifies whether to scan a directory - recursively for manifests - type: boolean - type: object - helm: - description: Helm holds helm specific options - properties: - fileParameters: - description: FileParameters are file parameters to the helm - template - items: - description: HelmFileParameter is a file parameter that's - passed to helm template during manifest generation - properties: - name: - description: Name is the name of the Helm parameter - type: string - path: - description: Path is the path to the file containing - the values for the Helm parameter - type: string - type: object - type: array - ignoreMissingValueFiles: - description: IgnoreMissingValueFiles prevents helm template - from failing when valueFiles do not exist locally by not - appending them to helm template --values - type: boolean - parameters: - description: Parameters is a list of Helm parameters which - are passed to the helm template command upon manifest generation - items: - description: HelmParameter is a parameter that's passed - to helm template during manifest generation - properties: - forceString: - description: ForceString determines whether to tell - Helm to interpret booleans and numbers as strings - type: boolean - name: - description: Name is the name of the Helm parameter - type: string - value: - description: Value is the value for the Helm parameter - type: string - type: object - type: array - passCredentials: - description: PassCredentials pass credentials to all domains - (Helm's --pass-credentials) - type: boolean - releaseName: - description: ReleaseName is the Helm release name to use. - If omitted it will use the application name - type: string - skipCrds: - description: SkipCrds skips custom resource definition installation - step (Helm's --skip-crds) - type: boolean - valueFiles: - description: ValuesFiles is a list of Helm value files to - use when generating a template - items: - type: string - type: array - values: - description: Values specifies Helm values to be passed to - helm template, typically defined as a block - type: string - version: - description: Version is the Helm version to use for templating - (either "2" or "3") - type: string - type: object - ksonnet: - description: Ksonnet holds ksonnet specific options - properties: - environment: - description: Environment is a ksonnet application environment - name - type: string - parameters: - description: Parameters are a list of ksonnet component parameter - override values - items: - description: KsonnetParameter is a ksonnet component parameter - properties: - component: - type: string - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - kustomize: - description: Kustomize holds kustomize specific options - properties: - commonAnnotations: - additionalProperties: - type: string - description: CommonAnnotations is a list of additional annotations - to add to rendered manifests - type: object - commonLabels: - additionalProperties: - type: string - description: CommonLabels is a list of additional labels to - add to rendered manifests - type: object - forceCommonAnnotations: - description: ForceCommonAnnotations specifies whether to force - applying common annotations to resources for Kustomize apps - type: boolean - forceCommonLabels: - description: ForceCommonLabels specifies whether to force - applying common labels to resources for Kustomize apps - type: boolean - images: - description: Images is a list of Kustomize image override - specifications - items: - description: KustomizeImage represents a Kustomize image - definition in the format [old_image_name=]: - type: string - type: array - namePrefix: - description: NamePrefix is a prefix appended to resources - for Kustomize apps - type: string - nameSuffix: - description: NameSuffix is a suffix appended to resources - for Kustomize apps - type: string - version: - description: Version controls which version of Kustomize to - use for rendering manifests - type: string - type: object - path: - description: Path is a directory path within the Git repository, - and is only valid for applications sourced from Git. - type: string - plugin: - description: ConfigManagementPlugin holds config management plugin - specific options - properties: - env: - description: Env is a list of environment variable entries - items: - description: EnvEntry represents an entry in the application's - environment - properties: - name: - description: Name is the name of the variable, usually - expressed in uppercase - type: string - value: - description: Value is the value of the variable - type: string - required: - - name - - value - type: object - type: array - name: - type: string - type: object - repoURL: - description: RepoURL is the URL to the repository (Git or Helm) - that contains the application manifests - type: string - targetRevision: - description: TargetRevision defines the revision of the source - to sync the application to. In case of Git, this can be commit, - tag, or branch. If omitted, will equal to HEAD. In case of Helm, - this is a semver tag for the Chart's version. - type: string - required: - - repoURL - type: object - syncPolicy: - description: SyncPolicy controls when and how a sync will be performed - properties: - automated: - description: Automated will keep an application synced to the - target revision - properties: - allowEmpty: - description: 'AllowEmpty allows apps have zero live resources - (default: false)' - type: boolean - prune: - description: 'Prune specifies whether to delete resources - from the cluster that are not found in the sources anymore - as part of automated sync (default: false)' - type: boolean - selfHeal: - description: 'SelfHeal specifes whether to revert resources - back to their desired state upon modification in the cluster - (default: false)' - type: boolean - type: object - retry: - description: Retry controls failed sync retry behavior - properties: - backoff: - description: Backoff controls how to backoff on subsequent - retries of failed syncs - properties: - duration: - description: Duration is the amount to back off. Default - unit is seconds, but could also be a duration (e.g. - "2m", "1h") - type: string - factor: - description: Factor is a factor to multiply the base duration - after each failed retry - format: int64 - type: integer - maxDuration: - description: MaxDuration is the maximum amount of time - allowed for the backoff strategy - type: string - type: object - limit: - description: Limit is the maximum number of attempts for retrying - a failed sync. If set to 0, no retries will be performed. - format: int64 - type: integer - type: object - syncOptions: - description: Options allow you to specify whole app sync-options - items: - type: string - type: array - type: object - required: - - destination - - project - - source - type: object - status: - description: ApplicationStatus contains status information for the application - properties: - conditions: - description: Conditions is a list of currently observed application - conditions - items: - description: ApplicationCondition contains details about an application - condition, which is usally an error or warning - properties: - lastTransitionTime: - description: LastTransitionTime is the time the condition was - last observed - format: date-time - type: string - message: - description: Message contains human-readable message indicating - details about condition - type: string - type: - description: Type is an application condition type - type: string - required: - - message - - type - type: object - type: array - health: - description: Health contains information about the application's current - health status - properties: - message: - description: Message is a human-readable informational message - describing the health status - type: string - status: - description: Status holds the status code of the application or - resource - type: string - type: object - history: - description: History contains information about the application's - sync history - items: - description: RevisionHistory contains history information about - a previous sync - properties: - deployStartedAt: - description: DeployStartedAt holds the time the sync operation - started - format: date-time - type: string - deployedAt: - description: DeployedAt holds the time the sync operation completed - format: date-time - type: string - id: - description: ID is an auto incrementing identifier of the RevisionHistory - format: int64 - type: integer - revision: - description: Revision holds the revision the sync was performed - against - type: string - source: - description: Source is a reference to the application source - used for the sync operation - properties: - chart: - description: Chart is a Helm chart name, and must be specified - for applications sourced from a Helm repo. - type: string - directory: - description: Directory holds path/directory specific options - properties: - exclude: - description: Exclude contains a glob pattern to match - paths against that should be explicitly excluded from - being used during manifest generation - type: string - include: - description: Include contains a glob pattern to match - paths against that should be explicitly included during - manifest generation - type: string - jsonnet: - description: Jsonnet holds options specific to Jsonnet - properties: - extVars: - description: ExtVars is a list of Jsonnet External - Variables - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - description: Additional library search dirs - items: - type: string - type: array - tlas: - description: TLAS is a list of Jsonnet Top-level - Arguments - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - description: Recurse specifies whether to scan a directory - recursively for manifests - type: boolean - type: object - helm: - description: Helm holds helm specific options - properties: - fileParameters: - description: FileParameters are file parameters to the - helm template - items: - description: HelmFileParameter is a file parameter - that's passed to helm template during manifest generation - properties: - name: - description: Name is the name of the Helm parameter - type: string - path: - description: Path is the path to the file containing - the values for the Helm parameter - type: string - type: object - type: array - ignoreMissingValueFiles: - description: IgnoreMissingValueFiles prevents helm template - from failing when valueFiles do not exist locally - by not appending them to helm template --values - type: boolean - parameters: - description: Parameters is a list of Helm parameters - which are passed to the helm template command upon - manifest generation - items: - description: HelmParameter is a parameter that's passed - to helm template during manifest generation - properties: - forceString: - description: ForceString determines whether to - tell Helm to interpret booleans and numbers - as strings - type: boolean - name: - description: Name is the name of the Helm parameter - type: string - value: - description: Value is the value for the Helm parameter - type: string - type: object - type: array - passCredentials: - description: PassCredentials pass credentials to all - domains (Helm's --pass-credentials) - type: boolean - releaseName: - description: ReleaseName is the Helm release name to - use. If omitted it will use the application name - type: string - skipCrds: - description: SkipCrds skips custom resource definition - installation step (Helm's --skip-crds) - type: boolean - valueFiles: - description: ValuesFiles is a list of Helm value files - to use when generating a template - items: - type: string - type: array - values: - description: Values specifies Helm values to be passed - to helm template, typically defined as a block - type: string - version: - description: Version is the Helm version to use for - templating (either "2" or "3") - type: string - type: object - ksonnet: - description: Ksonnet holds ksonnet specific options - properties: - environment: - description: Environment is a ksonnet application environment - name - type: string - parameters: - description: Parameters are a list of ksonnet component - parameter override values - items: - description: KsonnetParameter is a ksonnet component - parameter - properties: - component: - type: string - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - kustomize: - description: Kustomize holds kustomize specific options - properties: - commonAnnotations: - additionalProperties: - type: string - description: CommonAnnotations is a list of additional - annotations to add to rendered manifests - type: object - commonLabels: - additionalProperties: - type: string - description: CommonLabels is a list of additional labels - to add to rendered manifests - type: object - forceCommonAnnotations: - description: ForceCommonAnnotations specifies whether - to force applying common annotations to resources - for Kustomize apps - type: boolean - forceCommonLabels: - description: ForceCommonLabels specifies whether to - force applying common labels to resources for Kustomize - apps - type: boolean - images: - description: Images is a list of Kustomize image override - specifications - items: - description: KustomizeImage represents a Kustomize - image definition in the format [old_image_name=]: - type: string - type: array - namePrefix: - description: NamePrefix is a prefix appended to resources - for Kustomize apps - type: string - nameSuffix: - description: NameSuffix is a suffix appended to resources - for Kustomize apps - type: string - version: - description: Version controls which version of Kustomize - to use for rendering manifests - type: string - type: object - path: - description: Path is a directory path within the Git repository, - and is only valid for applications sourced from Git. - type: string - plugin: - description: ConfigManagementPlugin holds config management - plugin specific options - properties: - env: - description: Env is a list of environment variable entries - items: - description: EnvEntry represents an entry in the application's - environment - properties: - name: - description: Name is the name of the variable, - usually expressed in uppercase - type: string - value: - description: Value is the value of the variable - type: string - required: - - name - - value - type: object - type: array - name: - type: string - type: object - repoURL: - description: RepoURL is the URL to the repository (Git or - Helm) that contains the application manifests - type: string - targetRevision: - description: TargetRevision defines the revision of the - source to sync the application to. In case of Git, this - can be commit, tag, or branch. If omitted, will equal - to HEAD. In case of Helm, this is a semver tag for the - Chart's version. - type: string - required: - - repoURL - type: object - required: - - deployedAt - - id - - revision - type: object - type: array - observedAt: - description: 'ObservedAt indicates when the application state was - updated without querying latest git state Deprecated: controller - no longer updates ObservedAt field' - format: date-time - type: string - operationState: - description: OperationState contains information about any ongoing - operations, such as a sync - properties: - finishedAt: - description: FinishedAt contains time of operation completion - format: date-time - type: string - message: - description: Message holds any pertinent messages when attempting - to perform operation (typically errors). - type: string - operation: - description: Operation is the original requested operation - properties: - info: - description: Info is a list of informational items for this - operation - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - initiatedBy: - description: InitiatedBy contains information about who initiated - the operations - properties: - automated: - description: Automated is set to true if operation was - initiated automatically by the application controller. - type: boolean - username: - description: Username contains the name of a user who - started operation - type: string - type: object - retry: - description: Retry controls the strategy to apply if a sync - fails - properties: - backoff: - description: Backoff controls how to backoff on subsequent - retries of failed syncs - properties: - duration: - description: Duration is the amount to back off. Default - unit is seconds, but could also be a duration (e.g. - "2m", "1h") - type: string - factor: - description: Factor is a factor to multiply the base - duration after each failed retry - format: int64 - type: integer - maxDuration: - description: MaxDuration is the maximum amount of - time allowed for the backoff strategy - type: string - type: object - limit: - description: Limit is the maximum number of attempts for - retrying a failed sync. If set to 0, no retries will - be performed. - format: int64 - type: integer - type: object - sync: - description: Sync contains parameters for the operation - properties: - dryRun: - description: DryRun specifies to perform a `kubectl apply - --dry-run` without actually performing the sync - type: boolean - manifests: - description: Manifests is an optional field that overrides - sync source with a local directory for development - items: - type: string - type: array - prune: - description: Prune specifies to delete resources from - the cluster that are no longer tracked in git - type: boolean - resources: - description: Resources describes which resources shall - be part of the sync - items: - description: SyncOperationResource contains resources - to sync. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - type: array - revision: - description: Revision is the revision (Git) or chart version - (Helm) which to sync the application to If omitted, - will use the revision specified in app spec. - type: string - source: - description: Source overrides the source definition set - in the application. This is typically set in a Rollback - operation and is nil during a Sync operation - properties: - chart: - description: Chart is a Helm chart name, and must - be specified for applications sourced from a Helm - repo. - type: string - directory: - description: Directory holds path/directory specific - options - properties: - exclude: - description: Exclude contains a glob pattern to - match paths against that should be explicitly - excluded from being used during manifest generation - type: string - include: - description: Include contains a glob pattern to - match paths against that should be explicitly - included during manifest generation - type: string - jsonnet: - description: Jsonnet holds options specific to - Jsonnet - properties: - extVars: - description: ExtVars is a list of Jsonnet - External Variables - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest - generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - description: Additional library search dirs - items: - type: string - type: array - tlas: - description: TLAS is a list of Jsonnet Top-level - Arguments - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest - generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - description: Recurse specifies whether to scan - a directory recursively for manifests - type: boolean - type: object - helm: - description: Helm holds helm specific options - properties: - fileParameters: - description: FileParameters are file parameters - to the helm template - items: - description: HelmFileParameter is a file parameter - that's passed to helm template during manifest - generation - properties: - name: - description: Name is the name of the Helm - parameter - type: string - path: - description: Path is the path to the file - containing the values for the Helm parameter - type: string - type: object - type: array - ignoreMissingValueFiles: - description: IgnoreMissingValueFiles prevents - helm template from failing when valueFiles do - not exist locally by not appending them to helm - template --values - type: boolean - parameters: - description: Parameters is a list of Helm parameters - which are passed to the helm template command - upon manifest generation - items: - description: HelmParameter is a parameter that's - passed to helm template during manifest generation - properties: - forceString: - description: ForceString determines whether - to tell Helm to interpret booleans and - numbers as strings - type: boolean - name: - description: Name is the name of the Helm - parameter - type: string - value: - description: Value is the value for the - Helm parameter - type: string - type: object - type: array - passCredentials: - description: PassCredentials pass credentials - to all domains (Helm's --pass-credentials) - type: boolean - releaseName: - description: ReleaseName is the Helm release name - to use. If omitted it will use the application - name - type: string - skipCrds: - description: SkipCrds skips custom resource definition - installation step (Helm's --skip-crds) - type: boolean - valueFiles: - description: ValuesFiles is a list of Helm value - files to use when generating a template - items: - type: string - type: array - values: - description: Values specifies Helm values to be - passed to helm template, typically defined as - a block - type: string - version: - description: Version is the Helm version to use - for templating (either "2" or "3") - type: string - type: object - ksonnet: - description: Ksonnet holds ksonnet specific options - properties: - environment: - description: Environment is a ksonnet application - environment name - type: string - parameters: - description: Parameters are a list of ksonnet - component parameter override values - items: - description: KsonnetParameter is a ksonnet component - parameter - properties: - component: - type: string - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - kustomize: - description: Kustomize holds kustomize specific options - properties: - commonAnnotations: - additionalProperties: - type: string - description: CommonAnnotations is a list of additional - annotations to add to rendered manifests - type: object - commonLabels: - additionalProperties: - type: string - description: CommonLabels is a list of additional - labels to add to rendered manifests - type: object - forceCommonAnnotations: - description: ForceCommonAnnotations specifies - whether to force applying common annotations - to resources for Kustomize apps - type: boolean - forceCommonLabels: - description: ForceCommonLabels specifies whether - to force applying common labels to resources - for Kustomize apps - type: boolean - images: - description: Images is a list of Kustomize image - override specifications - items: - description: KustomizeImage represents a Kustomize - image definition in the format [old_image_name=]: - type: string - type: array - namePrefix: - description: NamePrefix is a prefix appended to - resources for Kustomize apps - type: string - nameSuffix: - description: NameSuffix is a suffix appended to - resources for Kustomize apps - type: string - version: - description: Version controls which version of - Kustomize to use for rendering manifests - type: string - type: object - path: - description: Path is a directory path within the Git - repository, and is only valid for applications sourced - from Git. - type: string - plugin: - description: ConfigManagementPlugin holds config management - plugin specific options - properties: - env: - description: Env is a list of environment variable - entries - items: - description: EnvEntry represents an entry in - the application's environment - properties: - name: - description: Name is the name of the variable, - usually expressed in uppercase - type: string - value: - description: Value is the value of the variable - type: string - required: - - name - - value - type: object - type: array - name: - type: string - type: object - repoURL: - description: RepoURL is the URL to the repository - (Git or Helm) that contains the application manifests - type: string - targetRevision: - description: TargetRevision defines the revision of - the source to sync the application to. In case of - Git, this can be commit, tag, or branch. If omitted, - will equal to HEAD. In case of Helm, this is a semver - tag for the Chart's version. - type: string - required: - - repoURL - type: object - syncOptions: - description: SyncOptions provide per-sync sync-options, - e.g. Validate=false - items: - type: string - type: array - syncStrategy: - description: SyncStrategy describes how to perform the - sync - properties: - apply: - description: Apply will perform a `kubectl apply` - to perform the sync. - properties: - force: - description: Force indicates whether or not to - supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, - when PATCH encounters conflict and has retried - for 5 times. - type: boolean - type: object - hook: - description: Hook will submit any referenced resources - to perform the sync. This is the default strategy - properties: - force: - description: Force indicates whether or not to - supply the --force flag to `kubectl apply`. - The --force flag deletes and re-create the resource, - when PATCH encounters conflict and has retried - for 5 times. - type: boolean - type: object - type: object - type: object - type: object - phase: - description: Phase is the current phase of the operation - type: string - retryCount: - description: RetryCount contains time of operation retries - format: int64 - type: integer - startedAt: - description: StartedAt contains time of operation start - format: date-time - type: string - syncResult: - description: SyncResult is the result of a Sync operation - properties: - resources: - description: Resources contains a list of sync result items - for each individual resource in a sync operation - items: - description: ResourceResult holds the operation result details - of a specific resource - properties: - group: - description: Group specifies the API group of the resource - type: string - hookPhase: - description: HookPhase contains the state of any operation - associated with this resource OR hook This can also - contain values for non-hook resources. - type: string - hookType: - description: HookType specifies the type of the hook. - Empty for non-hook resources - type: string - kind: - description: Kind specifies the API kind of the resource - type: string - message: - description: Message contains an informational or error - message for the last sync OR operation - type: string - name: - description: Name specifies the name of the resource - type: string - namespace: - description: Namespace specifies the target namespace - of the resource - type: string - status: - description: Status holds the final result of the sync. - Will be empty if the resources is yet to be applied/pruned - and is always zero-value for hooks - type: string - syncPhase: - description: SyncPhase indicates the particular phase - of the sync that this result was acquired in - type: string - version: - description: Version specifies the API version of the - resource - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - type: array - revision: - description: Revision holds the revision this sync operation - was performed to - type: string - source: - description: Source records the application source information - of the sync, used for comparing auto-sync - properties: - chart: - description: Chart is a Helm chart name, and must be specified - for applications sourced from a Helm repo. - type: string - directory: - description: Directory holds path/directory specific options - properties: - exclude: - description: Exclude contains a glob pattern to match - paths against that should be explicitly excluded - from being used during manifest generation - type: string - include: - description: Include contains a glob pattern to match - paths against that should be explicitly included - during manifest generation - type: string - jsonnet: - description: Jsonnet holds options specific to Jsonnet - properties: - extVars: - description: ExtVars is a list of Jsonnet External - Variables - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - description: Additional library search dirs - items: - type: string - type: array - tlas: - description: TLAS is a list of Jsonnet Top-level - Arguments - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - description: Recurse specifies whether to scan a directory - recursively for manifests - type: boolean - type: object - helm: - description: Helm holds helm specific options - properties: - fileParameters: - description: FileParameters are file parameters to - the helm template - items: - description: HelmFileParameter is a file parameter - that's passed to helm template during manifest - generation - properties: - name: - description: Name is the name of the Helm parameter - type: string - path: - description: Path is the path to the file containing - the values for the Helm parameter - type: string - type: object - type: array - ignoreMissingValueFiles: - description: IgnoreMissingValueFiles prevents helm - template from failing when valueFiles do not exist - locally by not appending them to helm template --values - type: boolean - parameters: - description: Parameters is a list of Helm parameters - which are passed to the helm template command upon - manifest generation - items: - description: HelmParameter is a parameter that's - passed to helm template during manifest generation - properties: - forceString: - description: ForceString determines whether - to tell Helm to interpret booleans and numbers - as strings - type: boolean - name: - description: Name is the name of the Helm parameter - type: string - value: - description: Value is the value for the Helm - parameter - type: string - type: object - type: array - passCredentials: - description: PassCredentials pass credentials to all - domains (Helm's --pass-credentials) - type: boolean - releaseName: - description: ReleaseName is the Helm release name - to use. If omitted it will use the application name - type: string - skipCrds: - description: SkipCrds skips custom resource definition - installation step (Helm's --skip-crds) - type: boolean - valueFiles: - description: ValuesFiles is a list of Helm value files - to use when generating a template - items: - type: string - type: array - values: - description: Values specifies Helm values to be passed - to helm template, typically defined as a block - type: string - version: - description: Version is the Helm version to use for - templating (either "2" or "3") - type: string - type: object - ksonnet: - description: Ksonnet holds ksonnet specific options - properties: - environment: - description: Environment is a ksonnet application - environment name - type: string - parameters: - description: Parameters are a list of ksonnet component - parameter override values - items: - description: KsonnetParameter is a ksonnet component - parameter - properties: - component: - type: string - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - kustomize: - description: Kustomize holds kustomize specific options - properties: - commonAnnotations: - additionalProperties: - type: string - description: CommonAnnotations is a list of additional - annotations to add to rendered manifests - type: object - commonLabels: - additionalProperties: - type: string - description: CommonLabels is a list of additional - labels to add to rendered manifests - type: object - forceCommonAnnotations: - description: ForceCommonAnnotations specifies whether - to force applying common annotations to resources - for Kustomize apps - type: boolean - forceCommonLabels: - description: ForceCommonLabels specifies whether to - force applying common labels to resources for Kustomize - apps - type: boolean - images: - description: Images is a list of Kustomize image override - specifications - items: - description: KustomizeImage represents a Kustomize - image definition in the format [old_image_name=]: - type: string - type: array - namePrefix: - description: NamePrefix is a prefix appended to resources - for Kustomize apps - type: string - nameSuffix: - description: NameSuffix is a suffix appended to resources - for Kustomize apps - type: string - version: - description: Version controls which version of Kustomize - to use for rendering manifests - type: string - type: object - path: - description: Path is a directory path within the Git repository, - and is only valid for applications sourced from Git. - type: string - plugin: - description: ConfigManagementPlugin holds config management - plugin specific options - properties: - env: - description: Env is a list of environment variable - entries - items: - description: EnvEntry represents an entry in the - application's environment - properties: - name: - description: Name is the name of the variable, - usually expressed in uppercase - type: string - value: - description: Value is the value of the variable - type: string - required: - - name - - value - type: object - type: array - name: - type: string - type: object - repoURL: - description: RepoURL is the URL to the repository (Git - or Helm) that contains the application manifests - type: string - targetRevision: - description: TargetRevision defines the revision of the - source to sync the application to. In case of Git, this - can be commit, tag, or branch. If omitted, will equal - to HEAD. In case of Helm, this is a semver tag for the - Chart's version. - type: string - required: - - repoURL - type: object - required: - - revision - type: object - required: - - operation - - phase - - startedAt - type: object - reconciledAt: - description: ReconciledAt indicates when the application state was - reconciled using the latest git version - format: date-time - type: string - resources: - description: Resources is a list of Kubernetes resources managed by - this application - items: - description: 'ResourceStatus holds the current sync and health status - of a resource TODO: describe members of this type' - properties: - group: - type: string - health: - description: HealthStatus contains information about the currently - observed health state of an application or resource - properties: - message: - description: Message is a human-readable informational message - describing the health status - type: string - status: - description: Status holds the status code of the application - or resource - type: string - type: object - hook: - type: boolean - kind: - type: string - name: - type: string - namespace: - type: string - requiresPruning: - type: boolean - status: - description: SyncStatusCode is a type which represents possible - comparison results - type: string - version: - type: string - type: object - type: array - sourceType: - description: SourceType specifies the type of this application - type: string - summary: - description: Summary contains a list of URLs and container images - used by this application - properties: - externalURLs: - description: ExternalURLs holds all external URLs of application - child resources. - items: - type: string - type: array - images: - description: Images holds all images of application child resources. - items: - type: string - type: array - type: object - sync: - description: Sync contains information about the application's current - sync status - properties: - comparedTo: - description: ComparedTo contains information about what has been - compared - properties: - destination: - description: Destination is a reference to the application's - destination used for comparison - properties: - name: - description: Name is an alternate way of specifying the - target cluster by its symbolic name - type: string - namespace: - description: Namespace specifies the target namespace - for the application's resources. The namespace will - only be set for namespace-scoped resources that have - not set a value for .metadata.namespace - type: string - server: - description: Server specifies the URL of the target cluster - and must be set to the Kubernetes control plane API - type: string - type: object - source: - description: Source is a reference to the application's source - used for comparison - properties: - chart: - description: Chart is a Helm chart name, and must be specified - for applications sourced from a Helm repo. - type: string - directory: - description: Directory holds path/directory specific options - properties: - exclude: - description: Exclude contains a glob pattern to match - paths against that should be explicitly excluded - from being used during manifest generation - type: string - include: - description: Include contains a glob pattern to match - paths against that should be explicitly included - during manifest generation - type: string - jsonnet: - description: Jsonnet holds options specific to Jsonnet - properties: - extVars: - description: ExtVars is a list of Jsonnet External - Variables - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - description: Additional library search dirs - items: - type: string - type: array - tlas: - description: TLAS is a list of Jsonnet Top-level - Arguments - items: - description: JsonnetVar represents a variable - to be passed to jsonnet during manifest generation - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - description: Recurse specifies whether to scan a directory - recursively for manifests - type: boolean - type: object - helm: - description: Helm holds helm specific options - properties: - fileParameters: - description: FileParameters are file parameters to - the helm template - items: - description: HelmFileParameter is a file parameter - that's passed to helm template during manifest - generation - properties: - name: - description: Name is the name of the Helm parameter - type: string - path: - description: Path is the path to the file containing - the values for the Helm parameter - type: string - type: object - type: array - ignoreMissingValueFiles: - description: IgnoreMissingValueFiles prevents helm - template from failing when valueFiles do not exist - locally by not appending them to helm template --values - type: boolean - parameters: - description: Parameters is a list of Helm parameters - which are passed to the helm template command upon - manifest generation - items: - description: HelmParameter is a parameter that's - passed to helm template during manifest generation - properties: - forceString: - description: ForceString determines whether - to tell Helm to interpret booleans and numbers - as strings - type: boolean - name: - description: Name is the name of the Helm parameter - type: string - value: - description: Value is the value for the Helm - parameter - type: string - type: object - type: array - passCredentials: - description: PassCredentials pass credentials to all - domains (Helm's --pass-credentials) - type: boolean - releaseName: - description: ReleaseName is the Helm release name - to use. If omitted it will use the application name - type: string - skipCrds: - description: SkipCrds skips custom resource definition - installation step (Helm's --skip-crds) - type: boolean - valueFiles: - description: ValuesFiles is a list of Helm value files - to use when generating a template - items: - type: string - type: array - values: - description: Values specifies Helm values to be passed - to helm template, typically defined as a block - type: string - version: - description: Version is the Helm version to use for - templating (either "2" or "3") - type: string - type: object - ksonnet: - description: Ksonnet holds ksonnet specific options - properties: - environment: - description: Environment is a ksonnet application - environment name - type: string - parameters: - description: Parameters are a list of ksonnet component - parameter override values - items: - description: KsonnetParameter is a ksonnet component - parameter - properties: - component: - type: string - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - kustomize: - description: Kustomize holds kustomize specific options - properties: - commonAnnotations: - additionalProperties: - type: string - description: CommonAnnotations is a list of additional - annotations to add to rendered manifests - type: object - commonLabels: - additionalProperties: - type: string - description: CommonLabels is a list of additional - labels to add to rendered manifests - type: object - forceCommonAnnotations: - description: ForceCommonAnnotations specifies whether - to force applying common annotations to resources - for Kustomize apps - type: boolean - forceCommonLabels: - description: ForceCommonLabels specifies whether to - force applying common labels to resources for Kustomize - apps - type: boolean - images: - description: Images is a list of Kustomize image override - specifications - items: - description: KustomizeImage represents a Kustomize - image definition in the format [old_image_name=]: - type: string - type: array - namePrefix: - description: NamePrefix is a prefix appended to resources - for Kustomize apps - type: string - nameSuffix: - description: NameSuffix is a suffix appended to resources - for Kustomize apps - type: string - version: - description: Version controls which version of Kustomize - to use for rendering manifests - type: string - type: object - path: - description: Path is a directory path within the Git repository, - and is only valid for applications sourced from Git. - type: string - plugin: - description: ConfigManagementPlugin holds config management - plugin specific options - properties: - env: - description: Env is a list of environment variable - entries - items: - description: EnvEntry represents an entry in the - application's environment - properties: - name: - description: Name is the name of the variable, - usually expressed in uppercase - type: string - value: - description: Value is the value of the variable - type: string - required: - - name - - value - type: object - type: array - name: - type: string - type: object - repoURL: - description: RepoURL is the URL to the repository (Git - or Helm) that contains the application manifests - type: string - targetRevision: - description: TargetRevision defines the revision of the - source to sync the application to. In case of Git, this - can be commit, tag, or branch. If omitted, will equal - to HEAD. In case of Helm, this is a semver tag for the - Chart's version. - type: string - required: - - repoURL - type: object - required: - - destination - - source - type: object - revision: - description: Revision contains information about the revision - the comparison has been performed to - type: string - status: - description: Status is the sync state of the comparison - type: string - required: - - status - type: object - type: object - required: - - metadata - - spec - type: object - served: true - storage: true - subresources: {} diff --git a/common/install/templates/argocd/application.yaml b/common/install/templates/argocd/application.yaml deleted file mode 100644 index 324e1f32..00000000 --- a/common/install/templates/argocd/application.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: {{ .Release.Name }}-{{ .Values.main.clusterGroupName }} - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: in-cluster - namespace: {{ .Release.Name }}-{{ .Values.main.clusterGroupName }} - project: default - source: - repoURL: {{ .Values.main.git.repoURL }} - targetRevision: {{ .Values.main.git.revision }} - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-{{ .Values.main.clusterGroupName }}.yaml" - # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: {{ .Release.Name }} - - name: global.hubClusterDomain - value: {{ .Values.global.hubClusterDomain }} -{{- if eq .Values.main.options.syncPolicy "Automatic" }} - syncPolicy: - automated: {} -{{- end }} diff --git a/common/install/templates/argocd/namespace.yaml b/common/install/templates/argocd/namespace.yaml deleted file mode 100644 index 9e9fafb2..00000000 --- a/common/install/templates/argocd/namespace.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# Pre-create so we can create our argo app for keeping subscriptions in sync -# Do it here so that we don't try to sync it in the future -apiVersion: v1 -kind: Namespace -metadata: - name: openshift-gitops diff --git a/common/install/templates/argocd/subscription.yaml b/common/install/templates/argocd/subscription.yaml deleted file mode 100644 index 22837b9a..00000000 --- a/common/install/templates/argocd/subscription.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: openshift-gitops-operator - namespace: openshift-operators - labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" -spec: - channel: {{ .Values.main.gitops.channel }} - installPlanApproval: {{ .Values.main.options.installPlanApproval }} - name: openshift-gitops-operator - source: redhat-operators - sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: {{ .Release.Name }}-{{ .Values.main.clusterGroupName }},openshift-gitops -{{- if .Values.main.options.useCSV }} - startingCSV: openshift-gitops-operator.{{ .Values.main.gitops.csv }} -{{- end }} diff --git a/common/install/values.yaml b/common/install/values.yaml deleted file mode 100644 index 370e6b15..00000000 --- a/common/install/values.yaml +++ /dev/null @@ -1,25 +0,0 @@ -main: - git: - repoURL: https://github.com/pattern-clone/mypattern - revision: main - - options: - syncPolicy: Automatic - installPlanApproval: Automatic - useCSV: False - - gitops: - channel: stable - source: redhat-operators - csv: v1.3.0 - - clusterGroupName: default - -global: - imageregistry: - type: quay - - git: - hostname: github.com - # Account is the user or organization under which the pattern repo lives - account: hybrid-cloud-patterns diff --git a/common/operator-install/Chart.yaml b/common/operator-install/Chart.yaml deleted file mode 100644 index 74adcf8f..00000000 --- a/common/operator-install/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -description: A Helm chart to build and deploy a Cloud Pattern via the patterns operator -keywords: -- pattern -name: pattern-install -version: 0.0.1 diff --git a/common/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml b/common/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml deleted file mode 100644 index 1acd4ad2..00000000 --- a/common/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml +++ /dev/null @@ -1,157 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - name: patterns.gitops.hybrid-cloud-patterns.io -spec: - group: gitops.hybrid-cloud-patterns.io - names: - kind: Pattern - listKind: PatternList - plural: patterns - shortNames: - - patt - singular: pattern - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.lastStep - name: Step - priority: 1 - type: string - - jsonPath: .status.lastError - name: Error - priority: 2 - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: Pattern is the Schema for the patterns 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: PatternSpec defines the desired state of Pattern - properties: - clusterGroupName: - type: string - extraParameters: - description: '.Name is dot separated per the helm --set syntax, such - as: global.something.field' - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - extraValueFiles: - description: URLs to additional Helm parameter files - items: - type: string - type: array - gitOpsSpec: - properties: - manualApproval: - description: 'Require manual confirmation before installing and - upgrading operators. Default: False' - type: boolean - manualSync: - description: 'Require manual intervention before Argo will sync - new content. Default: False' - type: boolean - operatorCSV: - description: Specific version of openshift-gitops to deploy. Requires - UseCSV=True - type: string - operatorChannel: - description: 'Channel to deploy openshift-gitops from. Default: - stable' - type: string - operatorSource: - description: 'Source to deploy openshift-gitops from. Default: - redhat-operators' - type: string - useCSV: - description: 'Dangerous. Force a specific version to be installed. - Default: False' - type: boolean - type: object - gitSpec: - properties: - hostname: - description: Optional. FQDN of the git server if automatic parsing - from TargetRepo is broken - type: string - originRepo: - description: Unused - type: string - targetRepo: - description: Git repo containing the pattern to deploy. Must use - https/http - type: string - targetRevision: - description: 'Branch, tag, or commit to deploy. Does not support - short-sha''s. Default: main' - type: string - valuesDirectoryURL: - description: Optional. Alternate URL to obtain Helm values files - from instead of this pattern. - type: string - required: - - targetRepo - type: object - required: - - clusterGroupName - - gitSpec - type: object - status: - description: PatternStatus defines the observed state of Pattern - properties: - clusterDomain: - type: string - clusterID: - type: string - clusterName: - type: string - clusterPlatform: - type: string - lastError: - description: Last error encountered by the pattern - type: string - lastStep: - description: Last action related to the pattern - type: string - version: - description: Number of updates to the pattern - type: integer - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/common/operator-install/templates/pattern.yaml b/common/operator-install/templates/pattern.yaml deleted file mode 100644 index 2ac7b52a..00000000 --- a/common/operator-install/templates/pattern.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 -kind: Pattern -metadata: - name: {{ .Release.Name }} - namespace: openshift-operators -spec: - clusterGroupName: {{ .Values.main.clusterGroupName }} - gitSpec: - targetRepo: {{ .Values.main.git.repoURL }} - targetRevision: {{ .Values.main.git.revision }} - gitOpsSpec: - operatorChannel: {{ default "stable" .Values.main.gitops.channel }} diff --git a/common/operator-install/templates/subscription.yaml b/common/operator-install/templates/subscription.yaml deleted file mode 100644 index 381e185f..00000000 --- a/common/operator-install/templates/subscription.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: patterns-operator - namespace: openshift-operators - labels: - operators.coreos.com/patterns-operator.openshift-operators: "" -spec: - channel: fast - installPlanApproval: Automatic - name: patterns-operator - source: community-operators - sourceNamespace: openshift-marketplace diff --git a/common/operator-install/values.yaml b/common/operator-install/values.yaml deleted file mode 100644 index b6e67044..00000000 --- a/common/operator-install/values.yaml +++ /dev/null @@ -1,9 +0,0 @@ -main: - git: - repoURL: https://github.com/pattern-clone/mypattern - revision: main - - gitops: - channel: "stable" - - clusterGroupName: default diff --git a/common/reference-output.yaml b/common/reference-output.yaml deleted file mode 100644 index cd59ac24..00000000 --- a/common/reference-output.yaml +++ /dev/null @@ -1,119 +0,0 @@ ---- -# Source: pattern-install/templates/argocd/namespace.yaml -# Pre-create so we can create our argo app for keeping subscriptions in sync -# Do it here so that we don't try to sync it in the future -apiVersion: v1 -kind: Namespace -metadata: - name: openshift-gitops ---- -# Source: pattern-install/templates/namespace.yaml -apiVersion: v1 -kind: Namespace -metadata: - name: manuela-ci - labels: - manuela-role: pipeline - app.kubernetes.io/instance: manuela ---- -# Source: pattern-install/templates/pipeline/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: pipeline - namespace: manuela-ci -secrets: -- name: git-repo-credentials -- name: image-registry-credentials ---- -# Source: pattern-install/templates/secrets/s3-secret.yaml -kind: Secret -apiVersion: v1 -metadata: - name: s3-secret -type: Opaque -data: - # Pre-create as part of the initial 'helm install' chart - # Create a file with the following: - # s3.accessKey: KEY - # s3.secretKey: secret key - #application.properties: base64 encrypted value of the above file - # This should live in the values-secret.yaml file - application.properties: BASE64STRING ---- -# Source: pattern-install/templates/secrets/secret-git-repo-credentials.yaml -apiVersion: v1 -kind: Secret -metadata: - name: git-repo-credentials - namespace: manuela-ci - annotations: - # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ - tekton.dev/git-0: https://github.com/hybrid-cloud-patterns -type: kubernetes.io/basic-auth -stringData: - username: STRING - password: STRING ---- -# Source: pattern-install/templates/secrets/secret-image-registry-credentials.yaml -apiVersion: v1 -kind: Secret -metadata: - name: openshift-registry-credentials - namespace: manuela-ci - annotations: - # Tekton magic, see https://tekton.dev/vault/pipelines-v0.15.2/auth/ - tekton.dev/docker-0: "https://" -type: kubernetes.io/basic-auth -stringData: - username: STRING - password: STRING ---- -# Source: pattern-install/templates/argocd/application.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: common-example - namespace: openshift-gitops -spec: - destination: - name: in-cluster - namespace: common-example - project: default - source: - repoURL: https://github.com/beekhof/common.git - targetRevision: main - path: common/clustergroup - helm: - valueFiles: - - "https://github.com/beekhof/patterns/raw/main/values-global.yaml" - - "https://github.com/beekhof/patterns/raw/main/values-example.yaml" - # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.valuesDirectoryURL - value: https://github.com/beekhof/patterns/raw/main - - name: global.pattern - value: common - syncPolicy: - automated: {} ---- -# Source: pattern-install/templates/argocd/subscription.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: openshift-gitops-operator - namespace: openshift-operators - labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" -spec: - channel: stable - installPlanApproval: Automatic - name: openshift-gitops-operator - source: redhat-operators - sourceNamespace: openshift-marketplace diff --git a/common/scripts/make_common_subtree.sh b/common/scripts/make_common_subtree.sh deleted file mode 100755 index a5e406d8..00000000 --- a/common/scripts/make_common_subtree.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/sh - -if [ "$1" = "-h" ]; then - echo "This script will convert common into a subtree and add a remote to help manage it." - echo "The script takes three positional arguments, as follows:" - echo - echo "$0 " - echo - echo "Run without arguments, the script would run as if these arguments had been passed:" - echo "$0 https://github.com/hybrid-cloud-patterns/common.git main common-subtree" - echo - echo "Please ensure the git subtree command is available. On RHEL/Fedora, the git subtree command" - echo "is in a separate package called git-subtree" - exit 1 -fi - -if [ -f '/etc/redhat-release' ]; then - rpm -qa | grep git-subtree 2>&1 - if [ ! $? = 0 ]; then - echo "you need to install git-subtree" - echo "would you like to install it now?" - select ANS in yes no - do - case $ANS in - yes) - sudo dnf install git-subtree -y - break - ;; - no) - exit - break - ;; - *) - echo "You must enter yes or no" - ;; - esac - done - fi -fi - -if [ "$1" ]; then - subtree_repo=$1 -else - subtree_repo=https://github.com/hybrid-cloud-patterns/common.git -fi - -if [ "$2" ]; then - subtree_branch=$2 -else - subtree_branch=main -fi - -if [ "$3" ]; then - subtree_remote=$3 -else - subtree_remote=common-subtree -fi - -git diff --quiet || (echo "This script must be run on a clean working tree" && exit 1) - -echo "Changing directory to project root" -cd `git rev-parse --show-toplevel` - -echo "Removing existing common and replacing it with subtree from $subtree_repo $subtree_remote" -rm -rf common - -echo "Committing removal of common" -(git add -A :/ && git commit -m "Removed previous version of common to convert to subtree from $subtree_repo $subtree_branch") || exit 1 - -echo "Adding (possibly replacing) subtree remote $subtree_remote" -git remote rm "$subtree_remote" -git remote add -f "$subtree_remote" "$subtree_repo" || exit 1 -git subtree add --prefix=common "$subtree_remote" "$subtree_branch" || exit 1 - -echo "Complete. You may now push these results if you are satisfied" -exit 0 diff --git a/common/scripts/pattern-util.sh b/common/scripts/pattern-util.sh deleted file mode 100755 index d2be2e97..00000000 --- a/common/scripts/pattern-util.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -if [ -z "$PATTERN_UTILITY_CONTAINER" ]; then - PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/hybridcloudpatterns-utility-ee" -fi - -# Copy Kubeconfig from current environment. The utilities will pick up ~/.kube/config if set so it's not mandatory -# /home/runner is the normal homedir -# $HOME is mounted as itself for any files that are referenced with absolute paths -# $HOME is mounted to /root because the UID in the container is 0 and that's where SSH looks for credentials - -podman run -it \ - --security-opt label=disable \ - -e KUBECONFIG="${KUBECONFIG}" \ - -v ${HOME}:/home/runner \ - -v ${HOME}:${HOME} \ - -v ${HOME}:/root \ - -w $(pwd) \ - "$PATTERN_UTILITY_CONTAINER" \ - $@ diff --git a/common/scripts/test.sh b/common/scripts/test.sh deleted file mode 100755 index 5790b5de..00000000 --- a/common/scripts/test.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -# helm template (even with --dry-run) can interact with the cluster -# This won't protect us if a user has ~/.kube -# Also call helm template with a non existing --kubeconfig while we're at it -unset KUBECONFIG - -target=$1 -name=$(echo $1 | sed -e s@/@-@g -e s@charts-@@) -TEST_VARIANT="$2" -CHART_OPTS="$3" - -TESTDIR=tests -REFERENCE=${TESTDIR}/${name}-${TEST_VARIANT}.expected.yaml -OUTPUT=${TESTDIR}/.${name}-${TEST_VARIANT}.expected.yaml -#REFERENCE=${TESTDIR}/${name}.expected.yaml -#OUTPUT=${TESTDIR}/.${name}.expected.yaml - -echo "Testing $1 chart (${TEST_VARIANT})" >&2 -helm template --kubeconfig /tmp/doesnotexistever $target --name-template $name ${CHART_OPTS} > ${OUTPUT} -rc=$? -if [ $rc -ne 0 ]; then - echo "FAIL on helm template $target --name-template $name ${CHART_OPTS}" - exit 1 -fi -#cp ${OUTPUT} ${REFERENCE} -if [ ! -e ${REFERENCE} ]; then - touch ${REFERENCE} -fi -diff -u ${REFERENCE} ${OUTPUT} -rc=$? -if [ $rc = 0 ]; then - rm -f ${OUTPUT} -fi - -if [ $TEST_VARIANT = normal -a $rc = 0 ]; then - # Another method of finding variables missing from values.yaml, eg. - # - name: -datacenter - # + name: pattern-name-datacenter - # Alas we can't make it fatal because there *should* be some differences - diff -u ${TESTDIR}/${name}-naked.expected.yaml ${TESTDIR}/${name}-normal.expected.yaml -fi - -if [ $rc = 0 ]; then - echo "PASS on $target $TEST_VARIANT with opts [$CHART_OPTS]" -else - echo "FAIL on $target $TEST_VARIANT with opts [$CHART_OPTS]" -fi - -exit $rc diff --git a/common/scripts/vault-utils.sh b/common/scripts/vault-utils.sh deleted file mode 100755 index 2f014a45..00000000 --- a/common/scripts/vault-utils.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash -set -eu - -get_abs_filename() { - # $1 : relative filename - echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" -} - -SCRIPT=$(get_abs_filename "$0") -SCRIPTPATH=$(dirname "$SCRIPT") -COMMONPATH=$(dirname "$SCRIPTPATH") -ANSIBLEPATH="$(dirname ${SCRIPTPATH})/ansible" -PLAYBOOKPATH="${ANSIBLEPATH}/playbooks" -export ANSIBLE_CONFIG="${ANSIBLEPATH}/ansible.cfg" - -# Parse arguments -if [ $# -lt 1 ]; then - echo "Specify at least the command ($#): $*" - exit 1 -fi - -TASK="${1}" -OUTFILE=${2:-"$COMMONPATH"/pattern-vault.init} - -if [ -z ${TASK} ]; then - echo "Task is unset" - exit 1 -fi - -ansible-playbook -t "${TASK}" -e output_file="${OUTFILE}" "${PLAYBOOKPATH}/vault/vault.yaml" diff --git a/common/tests/acm-naked.expected.yaml b/common/tests/acm-naked.expected.yaml deleted file mode 100644 index e94c6a51..00000000 --- a/common/tests/acm-naked.expected.yaml +++ /dev/null @@ -1,93 +0,0 @@ ---- -# Source: acm/templates/policies/application-policies.yaml -# TODO: Also create a GitOpsCluster.apps.open-cluster-management.io ---- -# Source: acm/templates/multiclusterhub.yaml -apiVersion: operator.open-cluster-management.io/v1 -kind: MultiClusterHub -metadata: - name: multiclusterhub - namespace: open-cluster-management - annotations: - argocd.argoproj.io/sync-wave: "-1" -spec: {} ---- -# Source: acm/templates/policies/ocp-gitops-policy.yaml -apiVersion: policy.open-cluster-management.io/v1 -kind: PlacementBinding -metadata: - name: openshift-gitops-placement-binding - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -placementRef: - name: openshift-gitops-placement - kind: PlacementRule - apiGroup: apps.open-cluster-management.io -subjects: - - name: openshift-gitops-policy - kind: Policy - apiGroup: policy.open-cluster-management.io ---- -# Source: acm/templates/policies/ocp-gitops-policy.yaml -apiVersion: apps.open-cluster-management.io/v1 -kind: PlacementRule -metadata: - name: openshift-gitops-placement -spec: - clusterConditions: - - status: 'True' - type: ManagedClusterConditionAvailable - clusterSelector: - matchExpressions: - - key: vendor - operator: In - values: - - OpenShift ---- -# Source: acm/templates/policies/ocp-gitops-policy.yaml -apiVersion: policy.open-cluster-management.io/v1 -kind: Policy -metadata: - name: openshift-gitops-policy - annotations: - policy.open-cluster-management.io/standards: NIST-CSF - policy.open-cluster-management.io/categories: PR.DS Data Security - policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - argocd.argoproj.io/compare-options: IgnoreExtraneous -spec: - remediationAction: enforce - disabled: false - policy-templates: - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: openshift-gitops-config - spec: - remediationAction: enforce - severity: medium - namespaceSelector: - include: - - default - object-templates: - - complianceType: mustonlyhave - objectDefinition: - # This is an auto-generated file. DO NOT EDIT - apiVersion: operators.coreos.com/v1alpha1 - kind: Subscription - metadata: - name: openshift-gitops-operator - namespace: openshift-operators - labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: '' - spec: - channel: stable - installPlanApproval: Automatic - name: openshift-gitops-operator - source: redhat-operators - sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: "*" diff --git a/common/tests/acm-normal.expected.yaml b/common/tests/acm-normal.expected.yaml deleted file mode 100644 index 29af29a8..00000000 --- a/common/tests/acm-normal.expected.yaml +++ /dev/null @@ -1,204 +0,0 @@ ---- -# Source: acm/templates/multiclusterhub.yaml -apiVersion: operator.open-cluster-management.io/v1 -kind: MultiClusterHub -metadata: - name: multiclusterhub - namespace: open-cluster-management - annotations: - argocd.argoproj.io/sync-wave: "-1" -spec: {} ---- -# Source: acm/templates/policies/application-policies.yaml -apiVersion: policy.open-cluster-management.io/v1 -kind: PlacementBinding -metadata: - name: edge-placement-binding - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -placementRef: - name: edge-placement - kind: PlacementRule - apiGroup: apps.open-cluster-management.io -subjects: - - name: edge-clustergroup-policy - kind: Policy - apiGroup: policy.open-cluster-management.io ---- -# Source: acm/templates/policies/ocp-gitops-policy.yaml -apiVersion: policy.open-cluster-management.io/v1 -kind: PlacementBinding -metadata: - name: openshift-gitops-placement-binding - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true -placementRef: - name: openshift-gitops-placement - kind: PlacementRule - apiGroup: apps.open-cluster-management.io -subjects: - - name: openshift-gitops-policy - kind: Policy - apiGroup: policy.open-cluster-management.io ---- -# Source: acm/templates/policies/application-policies.yaml -apiVersion: apps.open-cluster-management.io/v1 -kind: PlacementRule -metadata: - name: edge-placement -spec: - clusterConditions: - - status: 'True' - type: ManagedClusterConditionAvailable - clusterSelector: { - "matchExpressions": [ - { - "key": "vendor", - "operator": "In", - "values": [ - "OpenShift" - ] - } - ] -} ---- -# Source: acm/templates/policies/ocp-gitops-policy.yaml -apiVersion: apps.open-cluster-management.io/v1 -kind: PlacementRule -metadata: - name: openshift-gitops-placement -spec: - clusterConditions: - - status: 'True' - type: ManagedClusterConditionAvailable - clusterSelector: - matchExpressions: - - key: vendor - operator: In - values: - - OpenShift ---- -# Source: acm/templates/policies/application-policies.yaml -# TODO: Also create a GitOpsCluster.apps.open-cluster-management.io -apiVersion: policy.open-cluster-management.io/v1 -kind: Policy -metadata: - name: edge-clustergroup-policy - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - argocd.argoproj.io/compare-options: IgnoreExtraneous -spec: - remediationAction: enforce - disabled: false - policy-templates: - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: edge-clustergroup-config - spec: - remediationAction: enforce - severity: medium - namespaceSelector: - include: - - default - object-templates: - - complianceType: mustonlyhave - objectDefinition: - apiVersion: argoproj.io/v1alpha1 - kind: Application - metadata: - name: mypattern-edge - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground - spec: - project: default - source: - repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-edge.yaml" - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: mypattern - - name: global.hubClusterDomain - value: hub.example.com - - name: global.localClusterDomain - value: '{{ (lookup "config.openshift.io/v1" "Ingress" "" "cluster").spec.domain }}' - - name: clusterGroup.isHubCluster - value: "false" - destination: - server: https://kubernetes.default.svc - namespace: mypattern-edge - syncPolicy: - automated: - prune: false - selfHeal: true - ignoreDifferences: - - group: apps - kind: Deployment - jsonPointers: - - /spec/replicas - - group: route.openshift.io - kind: Route - jsonPointers: - - /status ---- -# Source: acm/templates/policies/ocp-gitops-policy.yaml -apiVersion: policy.open-cluster-management.io/v1 -kind: Policy -metadata: - name: openshift-gitops-policy - annotations: - policy.open-cluster-management.io/standards: NIST-CSF - policy.open-cluster-management.io/categories: PR.DS Data Security - policy.open-cluster-management.io/controls: PR.DS-1 Data-at-rest - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - argocd.argoproj.io/compare-options: IgnoreExtraneous -spec: - remediationAction: enforce - disabled: false - policy-templates: - - objectDefinition: - apiVersion: policy.open-cluster-management.io/v1 - kind: ConfigurationPolicy - metadata: - name: openshift-gitops-config - spec: - remediationAction: enforce - severity: medium - namespaceSelector: - include: - - default - object-templates: - - complianceType: mustonlyhave - objectDefinition: - # This is an auto-generated file. DO NOT EDIT - apiVersion: operators.coreos.com/v1alpha1 - kind: Subscription - metadata: - name: openshift-gitops-operator - namespace: openshift-operators - labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: '' - spec: - channel: stable - installPlanApproval: Automatic - name: openshift-gitops-operator - source: redhat-operators - sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: "*" diff --git a/common/tests/clustergroup-naked.expected.yaml b/common/tests/clustergroup-naked.expected.yaml deleted file mode 100644 index 993e6bb5..00000000 --- a/common/tests/clustergroup-naked.expected.yaml +++ /dev/null @@ -1,182 +0,0 @@ ---- -# Source: pattern-clustergroup/templates/gitops-namespace.yaml -apiVersion: v1 -kind: Namespace -metadata: - labels: - name: common-example - # The name here needs to be consistent with - # - acm/templates/policies/application-policies.yaml - # - clustergroup/templates/applications.yaml - # - any references to secrets and route URLs in documentation - name: common-example -spec: {} ---- -# Source: pattern-clustergroup/templates/argocd-super-role.yaml -# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: openshift-gitops-cluster-admin-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: - - kind: ServiceAccount - name: openshift-gitops-argocd-application-controller - namespace: openshift-gitops - # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - - kind: ServiceAccount - name: openshift-gitops-argocd-server - namespace: openshift-gitops ---- -# Source: pattern-clustergroup/templates/argocd-super-role.yaml -# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: common-example-cluster-admin-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: - - kind: ServiceAccount - # This is the {ArgoCD.name}-argocd-application-controller - name: example-gitops-argocd-application-controller - namespace: common-example - # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - - kind: ServiceAccount - # This is the {ArgoCD.name}-argocd-server - name: example-gitops-argocd-server - namespace: common-example - # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) - - kind: ServiceAccount - name: example-gitops-argocd-dex-server - namespace: common-example ---- -# Source: pattern-clustergroup/templates/argocd.yaml -apiVersion: argoproj.io/v1alpha1 -kind: ArgoCD -metadata: - finalizers: - - argoproj.io/finalizer - # Changing the name affects the ClusterRoleBinding, the generated secret, - # route URL, and argocd.argoproj.io/managed-by annotations - name: example-gitops - namespace: common-example - annotations: - argocd.argoproj.io/compare-options: IgnoreExtraneous -spec: - applicationInstanceLabelKey: argocd.argoproj.io/instance - # Not the greatest way to pass git/quay info to sub-applications, but it will do until - # we can support helmChart with kustomize - # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - configManagementPlugins: | - - name: kustomize-version - generate: - command: ["sh", "-c"] - args: ["kustomize version 1>&2 && exit 1"] - - name: kustomize-with-helm - generate: - command: ["kustomize"] - args: ["build", "--enable-helm"] - - name: helm-with-kustomize - init: - command: ["/bin/sh", "-c"] - args: ["helm dependency build"] - generate: - command: ["/bin/bash", "-c"] - args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f $(git rev-parse --show-toplevel)/values-global.yaml - -f $(git rev-parse --show-toplevel)/values-example.yaml - --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL - --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION - --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern=common - --set global.hubClusterDomain= - --set global.localClusterDomain= - --post-renderer ./kustomize"] - applicationSet: - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 250m - memory: 512Mi - controller: - processors: {} - resources: - limits: - cpu: "4" - memory: 4Gi - requests: - cpu: 500m - memory: 2Gi - dex: - openShiftOAuth: true - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 250m - memory: 128Mi - initialSSHKnownHosts: {} - rbac: - defaultPolicy: role:admin - repo: - resources: - limits: - cpu: "1" - memory: 512Mi - requests: - cpu: 250m - memory: 256Mi - resourceExclusions: | - - apiGroups: - - tekton.dev - kinds: - - TaskRun - - PipelineRun - server: - autoscale: - enabled: false - grpc: - ingress: - enabled: false - ingress: - enabled: false - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 125m - memory: 128Mi - route: - enabled: true - tls: - insecureEdgeTerminationPolicy: Redirect - termination: reencrypt - service: - type: "" - tls: - ca: {} -status: ---- -# Source: pattern-clustergroup/templates/argocd.yaml -apiVersion: console.openshift.io/v1 -kind: ConsoleLink -metadata: - name: example-gitops-link - namespace: common-example -spec: - applicationMenu: - section: OpenShift GitOps - imageURL: data:image/png;base64,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 - href: 'https://example-gitops-server-common-example.' - location: ApplicationMenu - text: 'Example ArgoCD' diff --git a/common/tests/clustergroup-normal.expected.yaml b/common/tests/clustergroup-normal.expected.yaml deleted file mode 100644 index 2af41cbb..00000000 --- a/common/tests/clustergroup-normal.expected.yaml +++ /dev/null @@ -1,802 +0,0 @@ ---- -# Source: pattern-clustergroup/templates/gitops-namespace.yaml -apiVersion: v1 -kind: Namespace -metadata: - labels: - name: mypattern-example - # The name here needs to be consistent with - # - acm/templates/policies/application-policies.yaml - # - clustergroup/templates/applications.yaml - # - any references to secrets and route URLs in documentation - name: mypattern-example -spec: {} ---- -# Source: pattern-clustergroup/templates/imperative/namespace.yaml -apiVersion: v1 -kind: Namespace -metadata: - labels: - name: imperative - argocd.argoproj.io/managed-by: mypattern-example - name: imperative ---- -# Source: pattern-clustergroup/templates/namespaces.yaml -apiVersion: v1 -kind: Namespace -metadata: - labels: - name: pattern - argocd.argoproj.io/managed-by: mypattern-example - name: open-cluster-management -spec: ---- -# Source: pattern-clustergroup/templates/namespaces.yaml -apiVersion: v1 -kind: Namespace -metadata: - labels: - name: pattern - argocd.argoproj.io/managed-by: mypattern-example - name: application-ci -spec: ---- -# Source: pattern-clustergroup/templates/imperative/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: imperative-sa - namespace: imperative ---- -# Source: pattern-clustergroup/templates/imperative/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: helm-values-configmap - namespace: imperative -data: - values.yaml: | - clusterGroup: - applications: - acm: - ignoreDifferences: - - group: internal.open-cluster-management.io - jsonPointers: - - /spec/loggingCA - kind: ManagedClusterInfo - name: acm - namespace: open-cluster-management - path: common/acm - project: datacenter - external: - clusterName: example - name: external-app - namespace: demo - project: datacenter - pipe: - name: pipelines - namespace: application-ci - path: charts/datacenter/pipelines - project: datacenter - externalClusters: - - example - imperative: - activeDeadlineSeconds: 3600 - clusterRoleName: imperative-cluster-role - clusterRoleYaml: "" - cronJobName: imperative-cronjob - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - insecureUnsealVaultInsideClusterSchedule: '*/5 * * * *' - jobName: imperative-job - jobs: - - name: test - playbook: ansible/test.yml - namespace: imperative - roleName: imperative-role - roleYaml: "" - schedule: '*/10 * * * *' - serviceAccountCreate: true - serviceAccountName: imperative-sa - valuesConfigMap: helm-values-configmap - verbosity: "" - insecureUnsealVaultInsideCluster: true - isHubCluster: true - managedClusterGroups: - - clusterSelector: - matchExpressions: - - key: vendor - operator: In - values: - - OpenShift - helmOverrides: - - name: clusterGroup.isHubCluster - value: "false" - name: edge - targetRevision: main - name: example - namespaces: - - open-cluster-management - - application-ci - projects: - - datacenter - subscriptions: - acm: - channel: release-2.4 - csv: advanced-cluster-management.v2.4.1 - name: advanced-cluster-management - namespace: open-cluster-management - odh: - csv: opendatahub-operator.v1.1.0 - disabled: true - name: opendatahub-operator - source: community-operators - pipelines: - csv: redhat-openshift-pipelines.v1.5.2 - name: openshift-pipelines-operator-rh - files: - cluster_example_ca: /path/to/ca.file - global: - git: - account: hybrid-cloud-patterns - dev_revision: main - email: someone@somewhere.com - hostname: github.com - hubClusterDomain: hub.example.com - localClusterDomain: region.example.com - namespace: pattern-namespace - options: - installPlanApproval: Automatic - syncPolicy: Automatic - useCSV: false - pattern: mypattern - repoURL: https://github.com/pattern-clone/mypattern - main: - clusterGroupName: example - git: - repoURL: https://github.com/pattern-clone/mypattern - revision: main - secretStore: - kind: ClusterSecretStore - name: vault-backend - secrets: - aws: - s3Secret: test-secret - cluster_example: - bearerToken: - server: https://api.example.openshiftapps.com:6443 - git: - token: test-git-token - username: test-user - imageregistry: - account: test-account - token: test-quay-token - secretsBase: - key: secret/data/hub ---- -# Source: pattern-clustergroup/templates/imperative/clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: imperative-cluster-role -rules: - - apiGroups: - - '*' - resources: - - '*' - verbs: - - get - - list - - watch ---- -# Source: pattern-clustergroup/templates/argocd-super-role.yaml -# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: openshift-gitops-cluster-admin-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: - - kind: ServiceAccount - name: openshift-gitops-argocd-application-controller - namespace: openshift-gitops - # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - - kind: ServiceAccount - name: openshift-gitops-argocd-server - namespace: openshift-gitops ---- -# Source: pattern-clustergroup/templates/argocd-super-role.yaml -# WARNING: ONLY USE THIS FOR MANAGING CLUSTERS NOT FOR REGULAR USERS -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: mypattern-example-cluster-admin-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: - - kind: ServiceAccount - # This is the {ArgoCD.name}-argocd-application-controller - name: example-gitops-argocd-application-controller - namespace: mypattern-example - # NOTE: THIS MUST BE FIXED FOR MULTITENANT SETUP - - kind: ServiceAccount - # This is the {ArgoCD.name}-argocd-server - name: example-gitops-argocd-server - namespace: mypattern-example - # NOTE: This is needed starting with gitops-1.5.0 (see issue common#76) - - kind: ServiceAccount - name: example-gitops-argocd-dex-server - namespace: mypattern-example ---- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: imperative-cluster-admin-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: imperative-cluster-role -subjects: - - kind: ServiceAccount - name: imperative-sa - namespace: imperative ---- -# Source: pattern-clustergroup/templates/imperative/role.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: imperative-role - namespace: imperative -rules: - - apiGroups: - - '*' - resources: - - '*' - verbs: - - '*' ---- -# Source: pattern-clustergroup/templates/imperative/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: imperative-admin-rolebinding - namespace: imperative -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: imperative-role -subjects: - - kind: ServiceAccount - name: imperative-sa - namespace: imperative ---- -# Source: pattern-clustergroup/templates/imperative/job.yaml -apiVersion: batch/v1 -kind: CronJob -metadata: - name: imperative-cronjob - namespace: imperative -spec: - schedule: "*/10 * * * *" - # if previous Job is still running, skip execution of a new Job - concurrencyPolicy: Forbid - jobTemplate: - spec: - activeDeadlineSeconds: 3600 - template: - metadata: - name: imperative-job - spec: - serviceAccountName: imperative-sa - initContainers: - # git init happens in /git/repo so that we can set the folder to 0770 permissions - # reason for that is ansible refuses to create temporary folders in there - - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - env: - - name: HOME - value: /git/home - command: - - 'sh' - - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" - volumeMounts: - - name: git - mountPath: "/git" - - name: test - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - env: - - name: HOME - value: /git/home - workingDir: /git/repo - # We have a default timeout of 600s for each playbook. Can be overridden - # on a per-job basis - command: - - timeout - - "600" - - ansible-playbook - - -e - - "@/values/values.yaml" - - ansible/test.yml - volumeMounts: - - name: git - mountPath: "/git" - - name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml - containers: - - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - command: - - 'sh' - - '-c' - - 'echo' - - 'done' - - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap - restartPolicy: Never ---- -# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml -apiVersion: batch/v1 -kind: CronJob -metadata: - name: unsealvault-cronjob - namespace: imperative -spec: - schedule: "*/5 * * * *" - # if previous Job is still running, skip execution of a new Job - concurrencyPolicy: Forbid - jobTemplate: - spec: - activeDeadlineSeconds: 3600 - template: - metadata: - name: unsealvault-job - spec: - serviceAccountName: imperative-sa - initContainers: - # git init happens in /git/repo so that we can set the folder to 0770 permissions - # reason for that is ansible refuses to create temporary folders in there - - name: git-init - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - env: - - name: HOME - value: /git/home - command: - - 'sh' - - '-c' - - "mkdir /git/{repo,home};git clone --single-branch --branch --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" - volumeMounts: - - name: git - mountPath: "/git" - - name: unseal-playbook - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - env: - - name: HOME - value: /git/home - workingDir: /git/repo - # We have a default timeout of 600s for each playbook. Can be overridden - # on a per-job basis - command: - - timeout - - "600" - - ansible-playbook - - -e - - "@/values/values.yaml" - - -e - - '{"file_unseal": false}' - - -t - - 'vault_init,vault_unseal,vault_secrets_init' - - "common/ansible/playbooks/vault/vault.yaml" - volumeMounts: - - name: git - mountPath: "/git" - - name: values-volume - mountPath: /values/values.yaml - subPath: values.yaml - containers: - - name: "done" - image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest - imagePullPolicy: Always - command: - - 'sh' - - '-c' - - 'echo' - - 'done' - - '\n' - volumes: - - name: git - emptyDir: {} - - name: values-volume - configMap: - name: helm-values-configmap - restartPolicy: Never ---- -# Source: pattern-clustergroup/templates/subscriptions.yaml ---- ---- -# Source: pattern-clustergroup/templates/projects.yaml -apiVersion: argoproj.io/v1alpha1 -kind: AppProject -metadata: - name: datacenter - namespace: mypattern-example -spec: - description: "Pattern datacenter" - destinations: - - namespace: '*' - server: '*' - clusterResourceWhitelist: - - group: '*' - kind: '*' - namespaceResourceWhitelist: - - group: '*' - kind: '*' - sourceRepos: - - '*' -status: {} ---- -# Source: pattern-clustergroup/templates/applications.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: acm - namespace: mypattern-example - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: in-cluster - namespace: open-cluster-management - project: datacenter - source: - repoURL: https://github.com/pattern-clone/mypattern - targetRevision: - path: common/acm - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-example.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: mypattern - - name: global.hubClusterDomain - value: hub.example.com - - name: global.localClusterDomain - value: region.example.com - ignoreDifferences: [ - { - "group": "internal.open-cluster-management.io", - "jsonPointers": [ - "/spec/loggingCA" - ], - "kind": "ManagedClusterInfo" - } -] - syncPolicy: - automated: {} - # selfHeal: true ---- -# Source: pattern-clustergroup/templates/applications.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: external-app - namespace: mypattern-example - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: example - namespace: demo - project: datacenter - source: - repoURL: https://github.com/pattern-clone/mypattern - targetRevision: - path: - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-example.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: mypattern - - name: global.hubClusterDomain - value: hub.example.com - - name: global.localClusterDomain - value: region.example.com - syncPolicy: - automated: {} - # selfHeal: true ---- -# Source: pattern-clustergroup/templates/applications.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: pipelines - namespace: mypattern-example - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: in-cluster - namespace: application-ci - project: datacenter - source: - repoURL: https://github.com/pattern-clone/mypattern - targetRevision: - path: charts/datacenter/pipelines - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-example.yaml" - # Watch the progress of https://issues.redhat.com/browse/GITOPS-891 and update accordingly - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: mypattern - - name: global.hubClusterDomain - value: hub.example.com - - name: global.localClusterDomain - value: region.example.com - syncPolicy: - automated: {} - # selfHeal: true ---- -# Source: pattern-clustergroup/templates/argocd.yaml -apiVersion: argoproj.io/v1alpha1 -kind: ArgoCD -metadata: - finalizers: - - argoproj.io/finalizer - # Changing the name affects the ClusterRoleBinding, the generated secret, - # route URL, and argocd.argoproj.io/managed-by annotations - name: example-gitops - namespace: mypattern-example - annotations: - argocd.argoproj.io/compare-options: IgnoreExtraneous -spec: - applicationInstanceLabelKey: argocd.argoproj.io/instance - # Not the greatest way to pass git/quay info to sub-applications, but it will do until - # we can support helmChart with kustomize - # The other option is to pass them in as environment variables eg. BLUEPRINT_VERSION - configManagementPlugins: | - - name: kustomize-version - generate: - command: ["sh", "-c"] - args: ["kustomize version 1>&2 && exit 1"] - - name: kustomize-with-helm - generate: - command: ["kustomize"] - args: ["build", "--enable-helm"] - - name: helm-with-kustomize - init: - command: ["/bin/sh", "-c"] - args: ["helm dependency build"] - generate: - command: ["/bin/bash", "-c"] - args: ["helm template . --name-template ${ARGOCD_APP_NAME:0:52} - -f $(git rev-parse --show-toplevel)/values-global.yaml - -f $(git rev-parse --show-toplevel)/values-example.yaml - --set global.repoURL=$ARGOCD_APP_SOURCE_REPO_URL - --set global.targetRevision=$ARGOCD_APP_SOURCE_TARGET_REVISION - --set global.namespace=$ARGOCD_APP_NAMESPACE - --set global.pattern=mypattern - --set global.hubClusterDomain=hub.example.com - --set global.localClusterDomain=region.example.com - --post-renderer ./kustomize"] - applicationSet: - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 250m - memory: 512Mi - controller: - processors: {} - resources: - limits: - cpu: "4" - memory: 4Gi - requests: - cpu: 500m - memory: 2Gi - dex: - openShiftOAuth: true - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 250m - memory: 128Mi - initialSSHKnownHosts: {} - rbac: - defaultPolicy: role:admin - repo: - resources: - limits: - cpu: "1" - memory: 512Mi - requests: - cpu: 250m - memory: 256Mi - resourceExclusions: | - - apiGroups: - - tekton.dev - kinds: - - TaskRun - - PipelineRun - server: - autoscale: - enabled: false - grpc: - ingress: - enabled: false - ingress: - enabled: false - resources: - limits: - cpu: 500m - memory: 256Mi - requests: - cpu: 125m - memory: 128Mi - route: - enabled: true - tls: - insecureEdgeTerminationPolicy: Redirect - termination: reencrypt - service: - type: "" - tls: - ca: {} -status: ---- -# Source: pattern-clustergroup/templates/argocd.yaml -apiVersion: console.openshift.io/v1 -kind: ConsoleLink -metadata: - name: example-gitops-link - namespace: mypattern-example -spec: - applicationMenu: - section: OpenShift GitOps - imageURL: data:image/png;base64,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 - href: 'https://example-gitops-server-mypattern-example.region.example.com' - location: ApplicationMenu - text: 'Example ArgoCD' ---- -# Source: pattern-clustergroup/templates/cluster-external-secrets.yaml -apiVersion: "external-secrets.io/v1beta1" -kind: ExternalSecret -metadata: - name: example-secret - namespace: mypattern-example - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - argocd.argoproj.io/sync-wave: "100" -spec: - refreshInterval: 15s - secretStoreRef: - name: vault-backend - kind: ClusterSecretStore - target: - name: example-secret - template: - type: Opaque - metadata: - labels: - argocd.argoproj.io/secret-type: cluster - data: - name: example - server: |- - {{ .kubeServer | toString }} - config: | - { - "bearerToken": {{ .kubeBearer | toString | quote }}, - "tlsClientConfig": { - "insecure": false, - "caData": {{ .kubeCA | toString | quote }} - } - } - data: - - secretKey: kubeServer - remoteRef: - key: secret/data/hub/cluster_example - property: server - - secretKey: kubeBearer - remoteRef: - key: secret/data/hub/cluster_example - property: bearerToken - - secretKey: kubeCA - remoteRef: - key: secret/data/hub/cluster_example_ca - property: b64content ---- -# Source: pattern-clustergroup/templates/operatorgroup.yaml -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup -metadata: - name: open-cluster-management-operator-group - namespace: open-cluster-management -spec: - targetNamespaces: - - open-cluster-management ---- -# Source: pattern-clustergroup/templates/operatorgroup.yaml -apiVersion: operators.coreos.com/v1 -kind: OperatorGroup -metadata: - name: application-ci-operator-group - namespace: application-ci -spec: - targetNamespaces: - - application-ci ---- -# Source: pattern-clustergroup/templates/subscriptions.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: advanced-cluster-management - namespace: open-cluster-management -spec: - name: advanced-cluster-management - source: redhat-operators - sourceNamespace: openshift-marketplace - channel: release-2.4 - installPlanApproval: Automatic - startingCSV: advanced-cluster-management.v2.4.1 ---- -# Source: pattern-clustergroup/templates/subscriptions.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: openshift-pipelines-operator-rh - namespace: openshift-operators -spec: - name: openshift-pipelines-operator-rh - source: redhat-operators - sourceNamespace: openshift-marketplace - channel: stable - installPlanApproval: Automatic - startingCSV: redhat-openshift-pipelines.v1.5.2 diff --git a/common/tests/examples-blank-naked.expected.yaml b/common/tests/examples-blank-naked.expected.yaml deleted file mode 100644 index 51a92e5d..00000000 --- a/common/tests/examples-blank-naked.expected.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# Source: blank/templates/manifest.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: example diff --git a/common/tests/examples-blank-normal.expected.yaml b/common/tests/examples-blank-normal.expected.yaml deleted file mode 100644 index 51a92e5d..00000000 --- a/common/tests/examples-blank-normal.expected.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# Source: blank/templates/manifest.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: example diff --git a/common/tests/examples-kustomize-renderer-naked.expected.yaml b/common/tests/examples-kustomize-renderer-naked.expected.yaml deleted file mode 100644 index 0aa7ee5d..00000000 --- a/common/tests/examples-kustomize-renderer-naked.expected.yaml +++ /dev/null @@ -1,36 +0,0 @@ ---- -# Source: example/templates/environment.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: environment -data: - IMAGE_PROVIDER: - IMAGE_ACCOUNT: PLAINTEXT - GIT_EMAIL: SOMEWHERE@EXAMPLE.COM - GIT_DEV_REPO_URL: https:///PLAINTEXT/manuela-dev.git - GIT_DEV_REPO_REVISION: main - GIT_OPS_REPO_TEST_URL: - GIT_OPS_REPO_TEST_REVISION: - GIT_OPS_REPO_PROD_URL: - GIT_OPS_REPO_PROD_REVISION: - IOT_CONSUMER_IMAGE: iot-consumer - IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag - IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml - IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml - IOT_FRONTEND_IMAGE: iot-frontend - IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag - IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml - IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml - IOT_SWSENSOR_IMAGE: iot-software-sensor - IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag - IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml - IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml - IOT_ANOMALY_IMAGE: iot-anomaly-detection - IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag - IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml - IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/common/tests/examples-kustomize-renderer-normal.expected.yaml b/common/tests/examples-kustomize-renderer-normal.expected.yaml deleted file mode 100644 index 09e04b22..00000000 --- a/common/tests/examples-kustomize-renderer-normal.expected.yaml +++ /dev/null @@ -1,36 +0,0 @@ ---- -# Source: example/templates/environment.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: environment -data: - IMAGE_PROVIDER: - IMAGE_ACCOUNT: PLAINTEXT - GIT_EMAIL: someone@somewhere.com - GIT_DEV_REPO_URL: https://github.com/hybrid-cloud-patterns/manuela-dev.git - GIT_DEV_REPO_REVISION: main - GIT_OPS_REPO_TEST_URL: https://github.com/pattern-clone/mypattern - GIT_OPS_REPO_TEST_REVISION: - GIT_OPS_REPO_PROD_URL: https://github.com/pattern-clone/mypattern - GIT_OPS_REPO_PROD_REVISION: - IOT_CONSUMER_IMAGE: iot-consumer - IOT_CONSUMER_YAML_PATH: images.(name==messaging).newTag - IOT_CONSUMER_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_CONSUMER_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/messaging/kustomization.yaml - IOT_CONSUMER_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/messaging/messaging-is.yaml - IOT_FRONTEND_IMAGE: iot-frontend - IOT_FRONTEND_YAML_PATH: images.(name==line-dashboard).newTag - IOT_FRONTEND_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_FRONTEND_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/line-dashboard/kustomization.yaml - IOT_FRONTEND_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/line-dashboard/line-dashboard-is.yaml - IOT_SWSENSOR_IMAGE: iot-software-sensor - IOT_SWSENSOR_YAML_PATH: images.(name==machine-sensor).newTag - IOT_SWSENSOR_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_SWSENSOR_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/machine-sensor/kustomization.yaml - IOT_SWSENSOR_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/machine-sensor/machine-sensor-is.yaml - IOT_ANOMALY_IMAGE: iot-anomaly-detection - IOT_ANOMALY_YAML_PATH: images.(name==anomaly-detection).newTag - IOT_ANOMALY_TEST_KUSTOMIZATION_PATH: charts/datacenter/manuela-tst/kustomization.yaml - IOT_ANOMALY_PROD_KUSTOMIZATION_PATH: charts/factory/manuela-stormshift/anomaly-detection/kustomization.yaml - IOT_ANOMALY_PROD_IMAGESTREAM_PATH: charts/factory/manuela-stormshift/anomaly-detection/anomaly-detection-is.yaml diff --git a/common/tests/golang-external-secrets-naked.expected.yaml b/common/tests/golang-external-secrets-naked.expected.yaml deleted file mode 100644 index 7d9fa628..00000000 --- a/common/tests/golang-external-secrets-naked.expected.yaml +++ /dev/null @@ -1,5911 +0,0 @@ ---- -# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: external-secrets-cert-controller - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-cert-controller - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm ---- -# Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: golang-external-secrets - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm ---- -# Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: external-secrets-webhook - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm ---- -# Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml -apiVersion: v1 -kind: Secret -metadata: - name: golang-external-secrets-webhook - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm - external-secrets.io/component : webhook ---- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml -apiVersion: v1 -kind: Secret -metadata: - name: golang-external-secrets - namespace: golang-external-secrets - annotations: - kubernetes.io/service-account.name: golang-external-secrets -type: kubernetes.io/service-account-token ---- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: clusterexternalsecrets.external-secrets.io -spec: - group: external-secrets.io - names: - categories: - - externalsecrets - kind: ClusterExternalSecret - listKind: ClusterExternalSecretList - plural: clusterexternalsecrets - shortNames: - - ces - singular: clusterexternalsecret - scope: Cluster - versions: - - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name - name: Store - type: string - - jsonPath: .spec.refreshInterval - name: Refresh Interval - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: ClusterExternalSecret is the Schema for the clusterexternalsecrets 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: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret. - properties: - externalSecretName: - description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret - type: string - externalSecretSpec: - description: The spec for the ExternalSecrets to be created - properties: - data: - description: Data defines the connection between the Kubernetes Secret keys and the Provider data - items: - description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. - properties: - remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - decodingStrategy: - default: None - description: Used to define a decoding Strategy - type: string - key: - description: Key is the key used in the Provider, mandatory - type: string - metadataPolicy: - description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None - type: string - property: - description: Used to select a specific property of the Provider value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider value, if supported - type: string - required: - - key - type: object - secretKey: - type: string - required: - - remoteRef - - secretKey - type: object - type: array - dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order - items: - properties: - extract: - description: Used to extract multiple key/value pairs from one secret - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - decodingStrategy: - default: None - description: Used to define a decoding Strategy - type: string - key: - description: Key is the key used in the Provider, mandatory - type: string - metadataPolicy: - description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None - type: string - property: - description: Used to select a specific property of the Provider value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider value, if supported - type: string - required: - - key - type: object - find: - description: Used to find secrets based on tags or regular expressions - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - decodingStrategy: - default: None - description: Used to define a decoding Strategy - type: string - name: - description: Finds secrets based on the name. - properties: - regexp: - description: Finds secrets base - type: string - type: object - path: - description: A root path to start the find operations. - type: string - tags: - additionalProperties: - type: string - description: Find secrets based on tags. - type: object - type: object - rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) - items: - properties: - regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. - properties: - source: - description: Used to define the regular expression of a re.Compiler. - type: string - target: - description: Used to define the target pattern of a ReplaceAll operation. - type: string - required: - - source - - target - type: object - type: object - type: array - type: object - type: array - refreshInterval: - default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. - type: string - secretStoreRef: - description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. - properties: - kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` - type: string - name: - description: Name of the SecretStore resource - type: string - required: - - name - type: object - target: - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. - properties: - creationPolicy: - default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' - enum: - - Owner - - Orphan - - Merge - - None - type: string - deletionPolicy: - default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' - enum: - - Delete - - Merge - - Retain - type: string - immutable: - description: Immutable defines if the final secret will be immutable - type: boolean - name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource - type: string - template: - description: Template defines a blueprint for the created Secret resource. - properties: - data: - additionalProperties: - type: string - type: object - engineVersion: - default: v2 - type: string - metadata: - description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - templateFrom: - items: - maxProperties: 1 - minProperties: 1 - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - name: - type: string - required: - - items - - name - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - name: - type: string - required: - - items - - name - type: object - type: object - type: array - type: - type: string - type: object - type: object - required: - - secretStoreRef - type: object - namespaceSelector: - description: The labels to select by to find the Namespaces to create the ExternalSecrets in. - 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 - required: - - key - - operator - type: object - type: array - 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 - refreshTime: - description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. - type: string - required: - - externalSecretSpec - - namespaceSelector - type: object - status: - description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret. - properties: - conditions: - items: - properties: - message: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - failedNamespaces: - description: Failed namespaces are the namespaces that failed to apply an ExternalSecret - items: - description: ClusterExternalSecretNamespaceFailure represents a failed namespace deployment and it's reason. - properties: - namespace: - description: Namespace is the namespace that failed when trying to apply an ExternalSecret - type: string - reason: - description: Reason is why the ExternalSecret failed to apply to the namespace - type: string - required: - - namespace - type: object - type: array - provisionedNamespaces: - description: ProvisionedNamespaces are the namespaces where the ClusterExternalSecret has secrets - items: - type: string - type: array - type: object - type: object - served: true - storage: true - subresources: - status: {} - conversion: - strategy: Webhook - webhook: - conversionReviewVersions: - - v1 - clientConfig: - service: - name: golang-external-secrets-webhook - namespace: "default" - path: /convert ---- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/clustersecretstore.yaml -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: clustersecretstores.external-secrets.io -spec: - group: external-secrets.io - names: - categories: - - externalsecrets - kind: ClusterSecretStore - listKind: ClusterSecretStoreList - plural: clustersecretstores - shortNames: - - css - singular: clustersecretstore - scope: Cluster - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - deprecated: true - name: v1alpha1 - schema: - openAPIV3Schema: - description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. - 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: SecretStoreSpec defines the desired state of SecretStore. - properties: - controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' - type: string - provider: - description: Used to configure the provider. Only one provider may be set - maxProperties: 1 - minProperties: 1 - properties: - akeyless: - description: Akeyless configures this store to sync secrets using Akeyless Vault provider - properties: - akeylessGWApiURL: - description: Akeyless GW API Url from which the secrets to be fetched from. - type: string - authSecretRef: - description: Auth configures how the operator authenticates with Akeyless. - properties: - secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' - properties: - accessID: - description: The SecretAccessID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - required: - - akeylessGWApiURL - - authSecretRef - type: object - alibaba: - description: Alibaba configures this store to sync secrets using Alibaba Cloud provider - properties: - auth: - description: AlibabaAuth contains a secretRef for credentials. - properties: - secretRef: - description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessKeySecretSecretRef: - description: The AccessKeySecret is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - accessKeyIDSecretRef - - accessKeySecretSecretRef - type: object - required: - - secretRef - type: object - endpoint: - type: string - regionID: - description: Alibaba Region to be used for the provider - type: string - required: - - auth - - regionID - type: object - aws: - description: AWS configures this store to sync secrets using AWS Secret Manager provider - properties: - auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' - properties: - jwt: - description: Authenticate against AWS using service account tokens. - properties: - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - type: object - secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - region: - description: AWS Region to be used for the provider - type: string - role: - description: Role is a Role ARN which the SecretManager provider will assume - type: string - service: - description: Service defines which service should be used to fetch the secrets - enum: - - SecretsManager - - ParameterStore - type: string - required: - - region - - service - type: object - azurekv: - description: AzureKV configures this store to sync secrets using Azure Key Vault provider - properties: - authSecretRef: - description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. - properties: - clientId: - description: The Azure clientId of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientSecret: - description: The Azure ClientSecret of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - authType: - default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' - enum: - - ServicePrincipal - - ManagedIdentity - - WorkloadIdentity - type: string - identityId: - description: If multiple Managed Identity is assigned to the pod, you can select the one to be used - type: string - serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - tenantId: - description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. - type: string - vaultUrl: - description: Vault Url from which the secrets to be fetched from. - type: string - required: - - vaultUrl - type: object - fake: - description: Fake configures a store with static key/value pairs - properties: - data: - items: - properties: - key: - type: string - value: - type: string - valueMap: - additionalProperties: - type: string - type: object - version: - type: string - required: - - key - type: object - type: array - required: - - data - type: object - gcpsm: - description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider - properties: - auth: - description: Auth defines the information necessary to authenticate against GCP - properties: - secretRef: - properties: - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - workloadIdentity: - properties: - clusterLocation: - type: string - clusterName: - type: string - clusterProjectID: - type: string - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - clusterLocation - - clusterName - - serviceAccountRef - type: object - type: object - projectID: - description: ProjectID project where secret is located - type: string - type: object - gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a GitLab instance. - properties: - SecretRef: - properties: - accessToken: - description: AccessToken is used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - SecretRef - type: object - projectID: - description: ProjectID specifies a project where secrets are located. - type: string - url: - description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. - type: string - required: - - auth - type: object - ibm: - description: IBM configures this store to sync secrets using IBM Cloud provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the IBM secrets manager. - properties: - secretRef: - properties: - secretApiKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - serviceUrl: - description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance - type: string - required: - - auth - type: object - kubernetes: - description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a Kubernetes instance. - maxProperties: 1 - minProperties: 1 - properties: - cert: - description: has both clientCert and clientKey as secretKeySelector - properties: - clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - serviceAccount: - description: points to a service account that should be used for authentication - properties: - serviceAccount: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - type: object - token: - description: use static token to authenticate with - properties: - bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - remoteNamespace: - default: default - description: Remote namespace to fetch the secrets from - type: string - server: - description: configures the Kubernetes server Address. - properties: - caBundle: - description: CABundle is a base64-encoded CA certificate - format: byte - type: string - caProvider: - description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - url: - default: kubernetes.default - description: configures the Kubernetes server Address. - type: string - type: object - required: - - auth - type: object - oracle: - description: Oracle configures this store to sync secrets using Oracle Vault provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. - properties: - secretRef: - description: SecretRef to pass through sensitive information. - properties: - fingerprint: - description: Fingerprint is the fingerprint of the API private key. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - privatekey: - description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - fingerprint - - privatekey - type: object - tenancy: - description: Tenancy is the tenancy OCID where user is located. - type: string - user: - description: User is an access OCID specific to the account. - type: string - required: - - secretRef - - tenancy - - user - type: object - region: - description: Region is the region where vault is located. - type: string - vault: - description: Vault is the vault's OCID of the specific vault where secret is located. - type: string - required: - - region - - vault - type: object - vault: - description: Vault configures this store to sync secrets using Hashi provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Vault server. - properties: - appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. - properties: - path: - default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' - type: string - roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. - type: string - secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - - roleId - - secretRef - type: object - cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method - properties: - clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method - properties: - kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. - properties: - audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. - items: - type: string - type: array - expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. - format: int64 - type: integer - serviceAccountRef: - description: Service account field containing the name of a kubernetes ServiceAccount. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - serviceAccountRef - type: object - path: - default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' - type: string - role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method - type: string - secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - type: object - kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. - properties: - mountPath: - default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' - type: string - role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. - type: string - secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - mountPath - - role - type: object - ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method - properties: - path: - default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' - type: string - secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method - type: string - required: - - path - - username - type: object - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by presenting a token. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate Vault server certificate. - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header - type: boolean - namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' - type: string - path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' - type: string - readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency - type: boolean - server: - description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' - type: string - version: - default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". - enum: - - v1 - - v2 - type: string - required: - - auth - - server - type: object - webhook: - description: Webhook configures this store to sync secrets using a generic templated webhook - properties: - body: - description: Body - type: string - caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate webhook server certificate. - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - headers: - additionalProperties: - type: string - description: Headers - type: object - method: - description: Webhook Method - type: string - result: - description: Result formatting - properties: - jsonPath: - description: Json path of return value - type: string - type: object - secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name - items: - properties: - name: - description: Name of this secret in templates - type: string - secretRef: - description: Secret ref to fill in credentials - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - name - - secretRef - type: object - type: array - timeout: - description: Timeout - type: string - url: - description: Webhook url to call - type: string - required: - - result - - url - type: object - yandexlockbox: - description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate against Yandex Lockbox - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - type: object - retrySettings: - description: Used to configure http retries if failed - properties: - maxRetries: - format: int32 - type: integer - retryInterval: - type: string - type: object - required: - - provider - type: object - status: - description: SecretStoreStatus defines the observed state of the SecretStore. - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. - 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: SecretStoreSpec defines the desired state of SecretStore. - properties: - controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' - type: string - provider: - description: Used to configure the provider. Only one provider may be set - maxProperties: 1 - minProperties: 1 - properties: - akeyless: - description: Akeyless configures this store to sync secrets using Akeyless Vault provider - properties: - akeylessGWApiURL: - description: Akeyless GW API Url from which the secrets to be fetched from. - type: string - authSecretRef: - description: Auth configures how the operator authenticates with Akeyless. - properties: - secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' - properties: - accessID: - description: The SecretAccessID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - required: - - akeylessGWApiURL - - authSecretRef - type: object - alibaba: - description: Alibaba configures this store to sync secrets using Alibaba Cloud provider - properties: - auth: - description: AlibabaAuth contains a secretRef for credentials. - properties: - secretRef: - description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessKeySecretSecretRef: - description: The AccessKeySecret is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - accessKeyIDSecretRef - - accessKeySecretSecretRef - type: object - required: - - secretRef - type: object - endpoint: - type: string - regionID: - description: Alibaba Region to be used for the provider - type: string - required: - - auth - - regionID - type: object - aws: - description: AWS configures this store to sync secrets using AWS Secret Manager provider - properties: - auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' - properties: - jwt: - description: Authenticate against AWS using service account tokens. - properties: - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - type: object - secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - region: - description: AWS Region to be used for the provider - type: string - role: - description: Role is a Role ARN which the SecretManager provider will assume - type: string - service: - description: Service defines which service should be used to fetch the secrets - enum: - - SecretsManager - - ParameterStore - type: string - required: - - region - - service - type: object - azurekv: - description: AzureKV configures this store to sync secrets using Azure Key Vault provider - properties: - authSecretRef: - description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. - properties: - clientId: - description: The Azure clientId of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientSecret: - description: The Azure ClientSecret of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - authType: - default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' - enum: - - ServicePrincipal - - ManagedIdentity - - WorkloadIdentity - type: string - identityId: - description: If multiple Managed Identity is assigned to the pod, you can select the one to be used - type: string - serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - tenantId: - description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. - type: string - vaultUrl: - description: Vault Url from which the secrets to be fetched from. - type: string - required: - - vaultUrl - type: object - fake: - description: Fake configures a store with static key/value pairs - properties: - data: - items: - properties: - key: - type: string - value: - type: string - valueMap: - additionalProperties: - type: string - type: object - version: - type: string - required: - - key - type: object - type: array - required: - - data - type: object - gcpsm: - description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider - properties: - auth: - description: Auth defines the information necessary to authenticate against GCP - properties: - secretRef: - properties: - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - workloadIdentity: - properties: - clusterLocation: - type: string - clusterName: - type: string - clusterProjectID: - type: string - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - clusterLocation - - clusterName - - serviceAccountRef - type: object - type: object - projectID: - description: ProjectID project where secret is located - type: string - type: object - gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a GitLab instance. - properties: - SecretRef: - properties: - accessToken: - description: AccessToken is used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - SecretRef - type: object - projectID: - description: ProjectID specifies a project where secrets are located. - type: string - url: - description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. - type: string - required: - - auth - type: object - ibm: - description: IBM configures this store to sync secrets using IBM Cloud provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the IBM secrets manager. - maxProperties: 1 - minProperties: 1 - properties: - containerAuth: - description: IBM Container-based auth with IAM Trusted Profile. - properties: - iamEndpoint: - type: string - profile: - description: the IBM Trusted Profile - type: string - tokenLocation: - description: Location the token is mounted on the pod - type: string - required: - - profile - type: object - secretRef: - properties: - secretApiKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - serviceUrl: - description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance - type: string - required: - - auth - type: object - kubernetes: - description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a Kubernetes instance. - maxProperties: 1 - minProperties: 1 - properties: - cert: - description: has both clientCert and clientKey as secretKeySelector - properties: - clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - serviceAccount: - description: points to a service account that should be used for authentication - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - token: - description: use static token to authenticate with - properties: - bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - remoteNamespace: - default: default - description: Remote namespace to fetch the secrets from - type: string - server: - description: configures the Kubernetes server Address. - properties: - caBundle: - description: CABundle is a base64-encoded CA certificate - format: byte - type: string - caProvider: - description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' - properties: - key: - description: The key where the CA certificate can be found in the Secret or ConfigMap. - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - url: - default: kubernetes.default - description: configures the Kubernetes server Address. - type: string - type: object - required: - - auth - type: object - onepassword: - description: OnePassword configures this store to sync secrets using the 1Password Cloud provider - properties: - auth: - description: Auth defines the information necessary to authenticate against OnePassword Connect Server - properties: - secretRef: - description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. - properties: - connectTokenSecretRef: - description: The ConnectToken is used for authentication to a 1Password Connect Server. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - connectTokenSecretRef - type: object - required: - - secretRef - type: object - connectHost: - description: ConnectHost defines the OnePassword Connect Server to connect to - type: string - vaults: - additionalProperties: - type: integer - description: Vaults defines which OnePassword vaults to search in which order - type: object - required: - - auth - - connectHost - - vaults - type: object - oracle: - description: Oracle configures this store to sync secrets using Oracle Vault provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. - properties: - secretRef: - description: SecretRef to pass through sensitive information. - properties: - fingerprint: - description: Fingerprint is the fingerprint of the API private key. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - privatekey: - description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - fingerprint - - privatekey - type: object - tenancy: - description: Tenancy is the tenancy OCID where user is located. - type: string - user: - description: User is an access OCID specific to the account. - type: string - required: - - secretRef - - tenancy - - user - type: object - region: - description: Region is the region where vault is located. - type: string - vault: - description: Vault is the vault's OCID of the specific vault where secret is located. - type: string - required: - - region - - vault - type: object - senhasegura: - description: Senhasegura configures this store to sync secrets using senhasegura provider - properties: - auth: - description: Auth defines parameters to authenticate in senhasegura - properties: - clientId: - type: string - clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - clientId - - clientSecretSecretRef - type: object - ignoreSslCertificate: - default: false - description: IgnoreSslCertificate defines if SSL certificate must be ignored - type: boolean - module: - description: Module defines which senhasegura module should be used to get secrets - type: string - url: - description: URL of senhasegura - type: string - required: - - auth - - module - - url - type: object - vault: - description: Vault configures this store to sync secrets using Hashi provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Vault server. - properties: - appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. - properties: - path: - default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' - type: string - roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. - type: string - secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - - roleId - - secretRef - type: object - cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method - properties: - clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method - properties: - kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. - properties: - audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. - items: - type: string - type: array - expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. - format: int64 - type: integer - serviceAccountRef: - description: Service account field containing the name of a kubernetes ServiceAccount. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - serviceAccountRef - type: object - path: - default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' - type: string - role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method - type: string - secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - type: object - kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. - properties: - mountPath: - default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' - type: string - role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. - type: string - secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - mountPath - - role - type: object - ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method - properties: - path: - default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' - type: string - secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method - type: string - required: - - path - - username - type: object - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by presenting a token. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate Vault server certificate. - properties: - key: - description: The key where the CA certificate can be found in the Secret or ConfigMap. - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header - type: boolean - namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' - type: string - path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' - type: string - readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency - type: boolean - server: - description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' - type: string - version: - default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". - enum: - - v1 - - v2 - type: string - required: - - auth - - server - type: object - webhook: - description: Webhook configures this store to sync secrets using a generic templated webhook - properties: - body: - description: Body - type: string - caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate webhook server certificate. - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - headers: - additionalProperties: - type: string - description: Headers - type: object - method: - description: Webhook Method - type: string - result: - description: Result formatting - properties: - jsonPath: - description: Json path of return value - type: string - type: object - secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name - items: - properties: - name: - description: Name of this secret in templates - type: string - secretRef: - description: Secret ref to fill in credentials - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - name - - secretRef - type: object - type: array - timeout: - description: Timeout - type: string - url: - description: Webhook url to call - type: string - required: - - result - - url - type: object - yandexcertificatemanager: - description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate against Yandex Certificate Manager - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - yandexlockbox: - description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate against Yandex Lockbox - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - type: object - refreshInterval: - description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. - type: integer - retrySettings: - description: Used to configure http retries if failed - properties: - maxRetries: - format: int32 - type: integer - retryInterval: - type: string - type: object - required: - - provider - type: object - status: - description: SecretStoreStatus defines the observed state of the SecretStore. - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: true - subresources: - status: {} - conversion: - strategy: Webhook - webhook: - conversionReviewVersions: - - v1 - clientConfig: - service: - name: golang-external-secrets-webhook - namespace: "default" - path: /convert ---- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/externalsecret.yaml -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: externalsecrets.external-secrets.io -spec: - group: external-secrets.io - names: - categories: - - externalsecrets - kind: ExternalSecret - listKind: ExternalSecretList - plural: externalsecrets - shortNames: - - es - singular: externalsecret - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name - name: Store - type: string - - jsonPath: .spec.refreshInterval - name: Refresh Interval - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - deprecated: true - name: v1alpha1 - schema: - openAPIV3Schema: - description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. - properties: - data: - description: Data defines the connection between the Kubernetes Secret keys and the Provider data - items: - description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. - properties: - remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - key: - description: Key is the key used in the Provider, mandatory - type: string - property: - description: Used to select a specific property of the Provider value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider value, if supported - type: string - required: - - key - type: object - secretKey: - type: string - required: - - remoteRef - - secretKey - type: object - type: array - dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order - items: - description: ExternalSecretDataRemoteRef defines Provider data location. - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - key: - description: Key is the key used in the Provider, mandatory - type: string - property: - description: Used to select a specific property of the Provider value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider value, if supported - type: string - required: - - key - type: object - type: array - refreshInterval: - default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. - type: string - secretStoreRef: - description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. - properties: - kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` - type: string - name: - description: Name of the SecretStore resource - type: string - required: - - name - type: object - target: - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. - properties: - creationPolicy: - default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' - type: string - immutable: - description: Immutable defines if the final secret will be immutable - type: boolean - name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource - type: string - template: - description: Template defines a blueprint for the created Secret resource. - properties: - data: - additionalProperties: - type: string - type: object - engineVersion: - default: v1 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. - type: string - metadata: - description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - templateFrom: - items: - maxProperties: 1 - minProperties: 1 - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - name: - type: string - required: - - items - - name - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - name: - type: string - required: - - items - - name - type: object - type: object - type: array - type: - type: string - type: object - type: object - required: - - secretStoreRef - - target - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated - format: date-time - nullable: true - type: string - syncedResourceVersion: - description: SyncedResourceVersion keeps track of the last synced version - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name - name: Store - type: string - - jsonPath: .spec.refreshInterval - name: Refresh Interval - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. - properties: - data: - description: Data defines the connection between the Kubernetes Secret keys and the Provider data - items: - description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. - properties: - remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - decodingStrategy: - default: None - description: Used to define a decoding Strategy - type: string - key: - description: Key is the key used in the Provider, mandatory - type: string - metadataPolicy: - description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None - type: string - property: - description: Used to select a specific property of the Provider value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider value, if supported - type: string - required: - - key - type: object - secretKey: - type: string - required: - - remoteRef - - secretKey - type: object - type: array - dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order - items: - properties: - extract: - description: Used to extract multiple key/value pairs from one secret - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - decodingStrategy: - default: None - description: Used to define a decoding Strategy - type: string - key: - description: Key is the key used in the Provider, mandatory - type: string - metadataPolicy: - description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None - type: string - property: - description: Used to select a specific property of the Provider value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider value, if supported - type: string - required: - - key - type: object - find: - description: Used to find secrets based on tags or regular expressions - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - decodingStrategy: - default: None - description: Used to define a decoding Strategy - type: string - name: - description: Finds secrets based on the name. - properties: - regexp: - description: Finds secrets base - type: string - type: object - path: - description: A root path to start the find operations. - type: string - tags: - additionalProperties: - type: string - description: Find secrets based on tags. - type: object - type: object - rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) - items: - properties: - regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. - properties: - source: - description: Used to define the regular expression of a re.Compiler. - type: string - target: - description: Used to define the target pattern of a ReplaceAll operation. - type: string - required: - - source - - target - type: object - type: object - type: array - type: object - type: array - refreshInterval: - default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. - type: string - secretStoreRef: - description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. - properties: - kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` - type: string - name: - description: Name of the SecretStore resource - type: string - required: - - name - type: object - target: - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. - properties: - creationPolicy: - default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' - enum: - - Owner - - Orphan - - Merge - - None - type: string - deletionPolicy: - default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' - enum: - - Delete - - Merge - - Retain - type: string - immutable: - description: Immutable defines if the final secret will be immutable - type: boolean - name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource - type: string - template: - description: Template defines a blueprint for the created Secret resource. - properties: - data: - additionalProperties: - type: string - type: object - engineVersion: - default: v2 - type: string - metadata: - description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - templateFrom: - items: - maxProperties: 1 - minProperties: 1 - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - name: - type: string - required: - - items - - name - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - name: - type: string - required: - - items - - name - type: object - type: object - type: array - type: - type: string - type: object - type: object - required: - - secretStoreRef - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated - format: date-time - nullable: true - type: string - syncedResourceVersion: - description: SyncedResourceVersion keeps track of the last synced version - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} - conversion: - strategy: Webhook - webhook: - conversionReviewVersions: - - v1 - clientConfig: - service: - name: golang-external-secrets-webhook - namespace: "default" - path: /convert ---- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/secretstore.yaml -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: secretstores.external-secrets.io -spec: - group: external-secrets.io - names: - categories: - - externalsecrets - kind: SecretStore - listKind: SecretStoreList - plural: secretstores - shortNames: - - ss - singular: secretstore - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - deprecated: true - name: v1alpha1 - schema: - openAPIV3Schema: - description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. - 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: SecretStoreSpec defines the desired state of SecretStore. - properties: - controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' - type: string - provider: - description: Used to configure the provider. Only one provider may be set - maxProperties: 1 - minProperties: 1 - properties: - akeyless: - description: Akeyless configures this store to sync secrets using Akeyless Vault provider - properties: - akeylessGWApiURL: - description: Akeyless GW API Url from which the secrets to be fetched from. - type: string - authSecretRef: - description: Auth configures how the operator authenticates with Akeyless. - properties: - secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' - properties: - accessID: - description: The SecretAccessID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - required: - - akeylessGWApiURL - - authSecretRef - type: object - alibaba: - description: Alibaba configures this store to sync secrets using Alibaba Cloud provider - properties: - auth: - description: AlibabaAuth contains a secretRef for credentials. - properties: - secretRef: - description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessKeySecretSecretRef: - description: The AccessKeySecret is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - accessKeyIDSecretRef - - accessKeySecretSecretRef - type: object - required: - - secretRef - type: object - endpoint: - type: string - regionID: - description: Alibaba Region to be used for the provider - type: string - required: - - auth - - regionID - type: object - aws: - description: AWS configures this store to sync secrets using AWS Secret Manager provider - properties: - auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' - properties: - jwt: - description: Authenticate against AWS using service account tokens. - properties: - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - type: object - secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - region: - description: AWS Region to be used for the provider - type: string - role: - description: Role is a Role ARN which the SecretManager provider will assume - type: string - service: - description: Service defines which service should be used to fetch the secrets - enum: - - SecretsManager - - ParameterStore - type: string - required: - - region - - service - type: object - azurekv: - description: AzureKV configures this store to sync secrets using Azure Key Vault provider - properties: - authSecretRef: - description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. - properties: - clientId: - description: The Azure clientId of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientSecret: - description: The Azure ClientSecret of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - authType: - default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' - enum: - - ServicePrincipal - - ManagedIdentity - - WorkloadIdentity - type: string - identityId: - description: If multiple Managed Identity is assigned to the pod, you can select the one to be used - type: string - serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - tenantId: - description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. - type: string - vaultUrl: - description: Vault Url from which the secrets to be fetched from. - type: string - required: - - vaultUrl - type: object - fake: - description: Fake configures a store with static key/value pairs - properties: - data: - items: - properties: - key: - type: string - value: - type: string - valueMap: - additionalProperties: - type: string - type: object - version: - type: string - required: - - key - type: object - type: array - required: - - data - type: object - gcpsm: - description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider - properties: - auth: - description: Auth defines the information necessary to authenticate against GCP - properties: - secretRef: - properties: - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - workloadIdentity: - properties: - clusterLocation: - type: string - clusterName: - type: string - clusterProjectID: - type: string - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - clusterLocation - - clusterName - - serviceAccountRef - type: object - type: object - projectID: - description: ProjectID project where secret is located - type: string - type: object - gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a GitLab instance. - properties: - SecretRef: - properties: - accessToken: - description: AccessToken is used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - SecretRef - type: object - projectID: - description: ProjectID specifies a project where secrets are located. - type: string - url: - description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. - type: string - required: - - auth - type: object - ibm: - description: IBM configures this store to sync secrets using IBM Cloud provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the IBM secrets manager. - properties: - secretRef: - properties: - secretApiKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - serviceUrl: - description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance - type: string - required: - - auth - type: object - kubernetes: - description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a Kubernetes instance. - maxProperties: 1 - minProperties: 1 - properties: - cert: - description: has both clientCert and clientKey as secretKeySelector - properties: - clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - serviceAccount: - description: points to a service account that should be used for authentication - properties: - serviceAccount: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - type: object - token: - description: use static token to authenticate with - properties: - bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - remoteNamespace: - default: default - description: Remote namespace to fetch the secrets from - type: string - server: - description: configures the Kubernetes server Address. - properties: - caBundle: - description: CABundle is a base64-encoded CA certificate - format: byte - type: string - caProvider: - description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - url: - default: kubernetes.default - description: configures the Kubernetes server Address. - type: string - type: object - required: - - auth - type: object - oracle: - description: Oracle configures this store to sync secrets using Oracle Vault provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. - properties: - secretRef: - description: SecretRef to pass through sensitive information. - properties: - fingerprint: - description: Fingerprint is the fingerprint of the API private key. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - privatekey: - description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - fingerprint - - privatekey - type: object - tenancy: - description: Tenancy is the tenancy OCID where user is located. - type: string - user: - description: User is an access OCID specific to the account. - type: string - required: - - secretRef - - tenancy - - user - type: object - region: - description: Region is the region where vault is located. - type: string - vault: - description: Vault is the vault's OCID of the specific vault where secret is located. - type: string - required: - - region - - vault - type: object - vault: - description: Vault configures this store to sync secrets using Hashi provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Vault server. - properties: - appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. - properties: - path: - default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' - type: string - roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. - type: string - secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - - roleId - - secretRef - type: object - cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method - properties: - clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method - properties: - kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. - properties: - audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. - items: - type: string - type: array - expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. - format: int64 - type: integer - serviceAccountRef: - description: Service account field containing the name of a kubernetes ServiceAccount. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - serviceAccountRef - type: object - path: - default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' - type: string - role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method - type: string - secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - type: object - kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. - properties: - mountPath: - default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' - type: string - role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. - type: string - secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - mountPath - - role - type: object - ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method - properties: - path: - default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' - type: string - secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method - type: string - required: - - path - - username - type: object - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by presenting a token. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate Vault server certificate. - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header - type: boolean - namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' - type: string - path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' - type: string - readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency - type: boolean - server: - description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' - type: string - version: - default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". - enum: - - v1 - - v2 - type: string - required: - - auth - - server - type: object - webhook: - description: Webhook configures this store to sync secrets using a generic templated webhook - properties: - body: - description: Body - type: string - caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate webhook server certificate. - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - headers: - additionalProperties: - type: string - description: Headers - type: object - method: - description: Webhook Method - type: string - result: - description: Result formatting - properties: - jsonPath: - description: Json path of return value - type: string - type: object - secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name - items: - properties: - name: - description: Name of this secret in templates - type: string - secretRef: - description: Secret ref to fill in credentials - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - name - - secretRef - type: object - type: array - timeout: - description: Timeout - type: string - url: - description: Webhook url to call - type: string - required: - - result - - url - type: object - yandexlockbox: - description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate against Yandex Lockbox - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - type: object - retrySettings: - description: Used to configure http retries if failed - properties: - maxRetries: - format: int32 - type: integer - retryInterval: - type: string - type: object - required: - - provider - type: object - status: - description: SecretStoreStatus defines the observed state of the SecretStore. - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. - 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: SecretStoreSpec defines the desired state of SecretStore. - properties: - controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' - type: string - provider: - description: Used to configure the provider. Only one provider may be set - maxProperties: 1 - minProperties: 1 - properties: - akeyless: - description: Akeyless configures this store to sync secrets using Akeyless Vault provider - properties: - akeylessGWApiURL: - description: Akeyless GW API Url from which the secrets to be fetched from. - type: string - authSecretRef: - description: Auth configures how the operator authenticates with Akeyless. - properties: - secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' - properties: - accessID: - description: The SecretAccessID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - required: - - akeylessGWApiURL - - authSecretRef - type: object - alibaba: - description: Alibaba configures this store to sync secrets using Alibaba Cloud provider - properties: - auth: - description: AlibabaAuth contains a secretRef for credentials. - properties: - secretRef: - description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessKeySecretSecretRef: - description: The AccessKeySecret is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - accessKeyIDSecretRef - - accessKeySecretSecretRef - type: object - required: - - secretRef - type: object - endpoint: - type: string - regionID: - description: Alibaba Region to be used for the provider - type: string - required: - - auth - - regionID - type: object - aws: - description: AWS configures this store to sync secrets using AWS Secret Manager provider - properties: - auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' - properties: - jwt: - description: Authenticate against AWS using service account tokens. - properties: - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - type: object - secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - region: - description: AWS Region to be used for the provider - type: string - role: - description: Role is a Role ARN which the SecretManager provider will assume - type: string - service: - description: Service defines which service should be used to fetch the secrets - enum: - - SecretsManager - - ParameterStore - type: string - required: - - region - - service - type: object - azurekv: - description: AzureKV configures this store to sync secrets using Azure Key Vault provider - properties: - authSecretRef: - description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. - properties: - clientId: - description: The Azure clientId of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientSecret: - description: The Azure ClientSecret of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - authType: - default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' - enum: - - ServicePrincipal - - ManagedIdentity - - WorkloadIdentity - type: string - identityId: - description: If multiple Managed Identity is assigned to the pod, you can select the one to be used - type: string - serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - tenantId: - description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. - type: string - vaultUrl: - description: Vault Url from which the secrets to be fetched from. - type: string - required: - - vaultUrl - type: object - fake: - description: Fake configures a store with static key/value pairs - properties: - data: - items: - properties: - key: - type: string - value: - type: string - valueMap: - additionalProperties: - type: string - type: object - version: - type: string - required: - - key - type: object - type: array - required: - - data - type: object - gcpsm: - description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider - properties: - auth: - description: Auth defines the information necessary to authenticate against GCP - properties: - secretRef: - properties: - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - workloadIdentity: - properties: - clusterLocation: - type: string - clusterName: - type: string - clusterProjectID: - type: string - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - clusterLocation - - clusterName - - serviceAccountRef - type: object - type: object - projectID: - description: ProjectID project where secret is located - type: string - type: object - gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a GitLab instance. - properties: - SecretRef: - properties: - accessToken: - description: AccessToken is used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - SecretRef - type: object - projectID: - description: ProjectID specifies a project where secrets are located. - type: string - url: - description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. - type: string - required: - - auth - type: object - ibm: - description: IBM configures this store to sync secrets using IBM Cloud provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the IBM secrets manager. - maxProperties: 1 - minProperties: 1 - properties: - containerAuth: - description: IBM Container-based auth with IAM Trusted Profile. - properties: - iamEndpoint: - type: string - profile: - description: the IBM Trusted Profile - type: string - tokenLocation: - description: Location the token is mounted on the pod - type: string - required: - - profile - type: object - secretRef: - properties: - secretApiKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - serviceUrl: - description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance - type: string - required: - - auth - type: object - kubernetes: - description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a Kubernetes instance. - maxProperties: 1 - minProperties: 1 - properties: - cert: - description: has both clientCert and clientKey as secretKeySelector - properties: - clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - serviceAccount: - description: points to a service account that should be used for authentication - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - token: - description: use static token to authenticate with - properties: - bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - remoteNamespace: - default: default - description: Remote namespace to fetch the secrets from - type: string - server: - description: configures the Kubernetes server Address. - properties: - caBundle: - description: CABundle is a base64-encoded CA certificate - format: byte - type: string - caProvider: - description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' - properties: - key: - description: The key where the CA certificate can be found in the Secret or ConfigMap. - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - url: - default: kubernetes.default - description: configures the Kubernetes server Address. - type: string - type: object - required: - - auth - type: object - onepassword: - description: OnePassword configures this store to sync secrets using the 1Password Cloud provider - properties: - auth: - description: Auth defines the information necessary to authenticate against OnePassword Connect Server - properties: - secretRef: - description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. - properties: - connectTokenSecretRef: - description: The ConnectToken is used for authentication to a 1Password Connect Server. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - connectTokenSecretRef - type: object - required: - - secretRef - type: object - connectHost: - description: ConnectHost defines the OnePassword Connect Server to connect to - type: string - vaults: - additionalProperties: - type: integer - description: Vaults defines which OnePassword vaults to search in which order - type: object - required: - - auth - - connectHost - - vaults - type: object - oracle: - description: Oracle configures this store to sync secrets using Oracle Vault provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. - properties: - secretRef: - description: SecretRef to pass through sensitive information. - properties: - fingerprint: - description: Fingerprint is the fingerprint of the API private key. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - privatekey: - description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - fingerprint - - privatekey - type: object - tenancy: - description: Tenancy is the tenancy OCID where user is located. - type: string - user: - description: User is an access OCID specific to the account. - type: string - required: - - secretRef - - tenancy - - user - type: object - region: - description: Region is the region where vault is located. - type: string - vault: - description: Vault is the vault's OCID of the specific vault where secret is located. - type: string - required: - - region - - vault - type: object - senhasegura: - description: Senhasegura configures this store to sync secrets using senhasegura provider - properties: - auth: - description: Auth defines parameters to authenticate in senhasegura - properties: - clientId: - type: string - clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - clientId - - clientSecretSecretRef - type: object - ignoreSslCertificate: - default: false - description: IgnoreSslCertificate defines if SSL certificate must be ignored - type: boolean - module: - description: Module defines which senhasegura module should be used to get secrets - type: string - url: - description: URL of senhasegura - type: string - required: - - auth - - module - - url - type: object - vault: - description: Vault configures this store to sync secrets using Hashi provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Vault server. - properties: - appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. - properties: - path: - default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' - type: string - roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. - type: string - secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - - roleId - - secretRef - type: object - cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method - properties: - clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method - properties: - kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. - properties: - audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. - items: - type: string - type: array - expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. - format: int64 - type: integer - serviceAccountRef: - description: Service account field containing the name of a kubernetes ServiceAccount. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - serviceAccountRef - type: object - path: - default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' - type: string - role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method - type: string - secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - type: object - kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. - properties: - mountPath: - default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' - type: string - role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. - type: string - secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - mountPath - - role - type: object - ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method - properties: - path: - default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' - type: string - secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method - type: string - required: - - path - - username - type: object - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by presenting a token. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate Vault server certificate. - properties: - key: - description: The key where the CA certificate can be found in the Secret or ConfigMap. - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header - type: boolean - namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' - type: string - path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' - type: string - readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency - type: boolean - server: - description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' - type: string - version: - default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". - enum: - - v1 - - v2 - type: string - required: - - auth - - server - type: object - webhook: - description: Webhook configures this store to sync secrets using a generic templated webhook - properties: - body: - description: Body - type: string - caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate webhook server certificate. - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - headers: - additionalProperties: - type: string - description: Headers - type: object - method: - description: Webhook Method - type: string - result: - description: Result formatting - properties: - jsonPath: - description: Json path of return value - type: string - type: object - secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name - items: - properties: - name: - description: Name of this secret in templates - type: string - secretRef: - description: Secret ref to fill in credentials - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - name - - secretRef - type: object - type: array - timeout: - description: Timeout - type: string - url: - description: Webhook url to call - type: string - required: - - result - - url - type: object - yandexcertificatemanager: - description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate against Yandex Certificate Manager - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - yandexlockbox: - description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate against Yandex Lockbox - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - type: object - refreshInterval: - description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. - type: integer - retrySettings: - description: Used to configure http retries if failed - properties: - maxRetries: - format: int32 - type: integer - retryInterval: - type: string - type: object - required: - - provider - type: object - status: - description: SecretStoreStatus defines the observed state of the SecretStore. - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: true - subresources: - status: {} - conversion: - strategy: Webhook - webhook: - conversionReviewVersions: - - v1 - clientConfig: - service: - name: golang-external-secrets-webhook - namespace: "default" - path: /convert ---- -# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: golang-external-secrets-cert-controller - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-cert-controller - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -rules: - - apiGroups: - - "apiextensions.k8s.io" - resources: - - "customresourcedefinitions" - verbs: - - "get" - - "list" - - "watch" - - "update" - - "patch" - - apiGroups: - - "admissionregistration.k8s.io" - resources: - - "validatingwebhookconfigurations" - verbs: - - "get" - - "list" - - "watch" - - "update" - - "patch" - - apiGroups: - - "" - resources: - - "endpoints" - verbs: - - "list" - - "get" - - "watch" - - apiGroups: - - "" - resources: - - "events" - verbs: - - "create" - - "patch" - - apiGroups: - - "" - resources: - - "secrets" - verbs: - - "get" - - "list" - - "watch" - - "update" - - "patch" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: golang-external-secrets-controller - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -rules: - - apiGroups: - - "external-secrets.io" - resources: - - "secretstores" - - "clustersecretstores" - - "externalsecrets" - - "clusterexternalsecrets" - verbs: - - "get" - - "list" - - "watch" - - apiGroups: - - "external-secrets.io" - resources: - - "externalsecrets" - - "externalsecrets/status" - - "externalsecrets/finalizers" - - "secretstores" - - "secretstores/status" - - "secretstores/finalizers" - - "clustersecretstores" - - "clustersecretstores/status" - - "clustersecretstores/finalizers" - - "clusterexternalsecrets" - - "clusterexternalsecrets/status" - - "clusterexternalsecrets/finalizers" - verbs: - - "update" - - "patch" - - apiGroups: - - "" - resources: - - "serviceaccounts" - - "namespaces" - verbs: - - "get" - - "list" - - "watch" - - apiGroups: - - "" - resources: - - "configmaps" - verbs: - - "get" - - "list" - - "watch" - - apiGroups: - - "" - resources: - - "secrets" - verbs: - - "get" - - "list" - - "watch" - - "create" - - "update" - - "delete" - - "patch" - - apiGroups: - - "" - resources: - - "serviceaccounts/token" - verbs: - - "create" - - apiGroups: - - "" - resources: - - "events" - verbs: - - "create" - - "patch" - - apiGroups: - - "external-secrets.io" - resources: - - "externalsecrets" - verbs: - - "create" - - "update" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: golang-external-secrets-view - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm - rbac.authorization.k8s.io/aggregate-to-view: "true" - rbac.authorization.k8s.io/aggregate-to-edit: "true" - rbac.authorization.k8s.io/aggregate-to-admin: "true" -rules: - - apiGroups: - - "external-secrets.io" - resources: - - "externalsecrets" - - "secretstores" - - "clustersecretstores" - verbs: - - "get" - - "watch" - - "list" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: golang-external-secrets-edit - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm - rbac.authorization.k8s.io/aggregate-to-edit: "true" - rbac.authorization.k8s.io/aggregate-to-admin: "true" -rules: - - apiGroups: - - "external-secrets.io" - resources: - - "externalsecrets" - - "secretstores" - - "clustersecretstores" - verbs: - - "create" - - "delete" - - "deletecollection" - - "patch" - - "update" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: golang-external-secrets-cert-controller - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-cert-controller - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: golang-external-secrets-cert-controller -subjects: - - name: external-secrets-cert-controller - namespace: "default" - kind: ServiceAccount ---- -# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: golang-external-secrets-controller - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: golang-external-secrets-controller -subjects: - - name: golang-external-secrets - namespace: "default" - kind: ServiceAccount ---- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: role-tokenreview-binding - namespace: default -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:auth-delegator -subjects: -- kind: ServiceAccount - name: golang-external-secrets - namespace: golang-external-secrets ---- -# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: golang-external-secrets-leaderelection - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -rules: - - apiGroups: - - "" - resources: - - "configmaps" - resourceNames: - - "external-secrets-controller" - verbs: - - "get" - - "update" - - "patch" - - apiGroups: - - "" - resources: - - "configmaps" - verbs: - - "create" - - apiGroups: - - "coordination.k8s.io" - resources: - - "leases" - verbs: - - "get" - - "create" - - "update" - - "patch" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: golang-external-secrets-leaderelection - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: golang-external-secrets-leaderelection -subjects: - - kind: ServiceAccount - name: golang-external-secrets - namespace: "default" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml -apiVersion: v1 -kind: Service -metadata: - name: golang-external-secrets-webhook - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm - external-secrets.io/component : webhook -spec: - type: ClusterIP - ports: - - port: 443 - targetPort: 10250 - protocol: TCP - name: webhook - selector: - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets ---- -# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: golang-external-secrets-cert-controller - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-cert-controller - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: external-secrets-cert-controller - app.kubernetes.io/instance: golang-external-secrets - template: - metadata: - labels: - app.kubernetes.io/name: external-secrets-cert-controller - app.kubernetes.io/instance: golang-external-secrets - spec: - serviceAccountName: external-secrets-cert-controller - containers: - - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.5.9" - imagePullPolicy: IfNotPresent - args: - - certcontroller - - --crd-requeue-interval=5m - - --service-name=golang-external-secrets-webhook - - --service-namespace=default - - --secret-name=golang-external-secrets-webhook - - --secret-namespace=default - ports: - - containerPort: 8080 - protocol: TCP - name: metrics - readinessProbe: - httpGet: - port: 8081 - path: /readyz - initialDelaySeconds: 20 - periodSeconds: 5 ---- -# Source: golang-external-secrets/charts/external-secrets/templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: golang-external-secrets - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - template: - metadata: - labels: - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - spec: - serviceAccountName: golang-external-secrets - containers: - - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.5.9" - imagePullPolicy: IfNotPresent - args: - - --concurrent=1 - ports: - - containerPort: 8080 - protocol: TCP - name: metrics ---- -# Source: golang-external-secrets/charts/external-secrets/templates/webhook-deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: golang-external-secrets-webhook - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets - template: - metadata: - labels: - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets - spec: - hostNetwork: false - serviceAccountName: external-secrets-webhook - containers: - - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.5.9" - imagePullPolicy: IfNotPresent - args: - - webhook - - --port=10250 - - --dns-name=golang-external-secrets-webhook.default.svc - - --cert-dir=/tmp/certs - - --check-interval=5m - ports: - - containerPort: 8080 - protocol: TCP - name: metrics - - containerPort: 10250 - protocol: TCP - name: webhook - readinessProbe: - httpGet: - port: 8081 - path: /readyz - initialDelaySeconds: 20 - periodSeconds: 5 - volumeMounts: - - name: certs - mountPath: /tmp/certs - readOnly: true - volumes: - - name: certs - secret: - secretName: golang-external-secrets-webhook ---- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml -apiVersion: external-secrets.io/v1beta1 -kind: ClusterSecretStore -metadata: - name: vault-backend - namespace: golang-external-secrets -spec: - provider: - vault: - server: https://vault-vault.hub.example.com - path: secret - # Version of KV backend - version: v2 - caProvider: - type: ConfigMap - name: kube-root-ca.crt - key: ca.crt - namespace: golang-external-secrets - auth: - kubernetes: - mountPath: hub - role: hub-role - secretRef: - name: golang-external-secrets - namespace: golang-external-secrets - key: "token" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: secretstore-validate - labels: - external-secrets.io/component: webhook -webhooks: -- name: "validate.secretstore.external-secrets.io" - rules: - - apiGroups: ["external-secrets.io"] - apiVersions: ["v1beta1"] - operations: ["CREATE", "UPDATE", "DELETE"] - resources: ["secretstores"] - scope: "Namespaced" - clientConfig: - service: - namespace: "default" - name: golang-external-secrets-webhook - path: /validate-external-secrets-io-v1beta1-secretstore - admissionReviewVersions: ["v1", "v1beta1"] - sideEffects: None - timeoutSeconds: 5 - -- name: "validate.clustersecretstore.external-secrets.io" - rules: - - apiGroups: ["external-secrets.io"] - apiVersions: ["v1beta1"] - operations: ["CREATE", "UPDATE", "DELETE"] - resources: ["clustersecretstores"] - scope: "Cluster" - clientConfig: - service: - namespace: "default" - name: golang-external-secrets-webhook - path: /validate-external-secrets-io-v1beta1-clustersecretstore - admissionReviewVersions: ["v1", "v1beta1"] - sideEffects: None - timeoutSeconds: 5 ---- -# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: externalsecret-validate - labels: - external-secrets.io/component: webhook -webhooks: -- name: "validate.externalsecret.external-secrets.io" - rules: - - apiGroups: ["external-secrets.io"] - apiVersions: ["v1beta1"] - operations: ["CREATE", "UPDATE", "DELETE"] - resources: ["externalsecrets"] - scope: "Namespaced" - clientConfig: - service: - namespace: "default" - name: golang-external-secrets-webhook - path: /validate-external-secrets-io-v1beta1-externalsecret - admissionReviewVersions: ["v1", "v1beta1"] - sideEffects: None - timeoutSeconds: 5 - failurePolicy: Fail diff --git a/common/tests/golang-external-secrets-normal.expected.yaml b/common/tests/golang-external-secrets-normal.expected.yaml deleted file mode 100644 index 7d9fa628..00000000 --- a/common/tests/golang-external-secrets-normal.expected.yaml +++ /dev/null @@ -1,5911 +0,0 @@ ---- -# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: external-secrets-cert-controller - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-cert-controller - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm ---- -# Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: golang-external-secrets - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm ---- -# Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: external-secrets-webhook - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm ---- -# Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml -apiVersion: v1 -kind: Secret -metadata: - name: golang-external-secrets-webhook - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm - external-secrets.io/component : webhook ---- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml -apiVersion: v1 -kind: Secret -metadata: - name: golang-external-secrets - namespace: golang-external-secrets - annotations: - kubernetes.io/service-account.name: golang-external-secrets -type: kubernetes.io/service-account-token ---- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: clusterexternalsecrets.external-secrets.io -spec: - group: external-secrets.io - names: - categories: - - externalsecrets - kind: ClusterExternalSecret - listKind: ClusterExternalSecretList - plural: clusterexternalsecrets - shortNames: - - ces - singular: clusterexternalsecret - scope: Cluster - versions: - - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name - name: Store - type: string - - jsonPath: .spec.refreshInterval - name: Refresh Interval - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: ClusterExternalSecret is the Schema for the clusterexternalsecrets 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: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret. - properties: - externalSecretName: - description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret - type: string - externalSecretSpec: - description: The spec for the ExternalSecrets to be created - properties: - data: - description: Data defines the connection between the Kubernetes Secret keys and the Provider data - items: - description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. - properties: - remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - decodingStrategy: - default: None - description: Used to define a decoding Strategy - type: string - key: - description: Key is the key used in the Provider, mandatory - type: string - metadataPolicy: - description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None - type: string - property: - description: Used to select a specific property of the Provider value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider value, if supported - type: string - required: - - key - type: object - secretKey: - type: string - required: - - remoteRef - - secretKey - type: object - type: array - dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order - items: - properties: - extract: - description: Used to extract multiple key/value pairs from one secret - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - decodingStrategy: - default: None - description: Used to define a decoding Strategy - type: string - key: - description: Key is the key used in the Provider, mandatory - type: string - metadataPolicy: - description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None - type: string - property: - description: Used to select a specific property of the Provider value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider value, if supported - type: string - required: - - key - type: object - find: - description: Used to find secrets based on tags or regular expressions - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - decodingStrategy: - default: None - description: Used to define a decoding Strategy - type: string - name: - description: Finds secrets based on the name. - properties: - regexp: - description: Finds secrets base - type: string - type: object - path: - description: A root path to start the find operations. - type: string - tags: - additionalProperties: - type: string - description: Find secrets based on tags. - type: object - type: object - rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) - items: - properties: - regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. - properties: - source: - description: Used to define the regular expression of a re.Compiler. - type: string - target: - description: Used to define the target pattern of a ReplaceAll operation. - type: string - required: - - source - - target - type: object - type: object - type: array - type: object - type: array - refreshInterval: - default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. - type: string - secretStoreRef: - description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. - properties: - kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` - type: string - name: - description: Name of the SecretStore resource - type: string - required: - - name - type: object - target: - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. - properties: - creationPolicy: - default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' - enum: - - Owner - - Orphan - - Merge - - None - type: string - deletionPolicy: - default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' - enum: - - Delete - - Merge - - Retain - type: string - immutable: - description: Immutable defines if the final secret will be immutable - type: boolean - name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource - type: string - template: - description: Template defines a blueprint for the created Secret resource. - properties: - data: - additionalProperties: - type: string - type: object - engineVersion: - default: v2 - type: string - metadata: - description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - templateFrom: - items: - maxProperties: 1 - minProperties: 1 - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - name: - type: string - required: - - items - - name - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - name: - type: string - required: - - items - - name - type: object - type: object - type: array - type: - type: string - type: object - type: object - required: - - secretStoreRef - type: object - namespaceSelector: - description: The labels to select by to find the Namespaces to create the ExternalSecrets in. - 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 - required: - - key - - operator - type: object - type: array - 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 - refreshTime: - description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. - type: string - required: - - externalSecretSpec - - namespaceSelector - type: object - status: - description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret. - properties: - conditions: - items: - properties: - message: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - failedNamespaces: - description: Failed namespaces are the namespaces that failed to apply an ExternalSecret - items: - description: ClusterExternalSecretNamespaceFailure represents a failed namespace deployment and it's reason. - properties: - namespace: - description: Namespace is the namespace that failed when trying to apply an ExternalSecret - type: string - reason: - description: Reason is why the ExternalSecret failed to apply to the namespace - type: string - required: - - namespace - type: object - type: array - provisionedNamespaces: - description: ProvisionedNamespaces are the namespaces where the ClusterExternalSecret has secrets - items: - type: string - type: array - type: object - type: object - served: true - storage: true - subresources: - status: {} - conversion: - strategy: Webhook - webhook: - conversionReviewVersions: - - v1 - clientConfig: - service: - name: golang-external-secrets-webhook - namespace: "default" - path: /convert ---- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/clustersecretstore.yaml -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: clustersecretstores.external-secrets.io -spec: - group: external-secrets.io - names: - categories: - - externalsecrets - kind: ClusterSecretStore - listKind: ClusterSecretStoreList - plural: clustersecretstores - shortNames: - - css - singular: clustersecretstore - scope: Cluster - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - deprecated: true - name: v1alpha1 - schema: - openAPIV3Schema: - description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. - 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: SecretStoreSpec defines the desired state of SecretStore. - properties: - controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' - type: string - provider: - description: Used to configure the provider. Only one provider may be set - maxProperties: 1 - minProperties: 1 - properties: - akeyless: - description: Akeyless configures this store to sync secrets using Akeyless Vault provider - properties: - akeylessGWApiURL: - description: Akeyless GW API Url from which the secrets to be fetched from. - type: string - authSecretRef: - description: Auth configures how the operator authenticates with Akeyless. - properties: - secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' - properties: - accessID: - description: The SecretAccessID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - required: - - akeylessGWApiURL - - authSecretRef - type: object - alibaba: - description: Alibaba configures this store to sync secrets using Alibaba Cloud provider - properties: - auth: - description: AlibabaAuth contains a secretRef for credentials. - properties: - secretRef: - description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessKeySecretSecretRef: - description: The AccessKeySecret is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - accessKeyIDSecretRef - - accessKeySecretSecretRef - type: object - required: - - secretRef - type: object - endpoint: - type: string - regionID: - description: Alibaba Region to be used for the provider - type: string - required: - - auth - - regionID - type: object - aws: - description: AWS configures this store to sync secrets using AWS Secret Manager provider - properties: - auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' - properties: - jwt: - description: Authenticate against AWS using service account tokens. - properties: - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - type: object - secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - region: - description: AWS Region to be used for the provider - type: string - role: - description: Role is a Role ARN which the SecretManager provider will assume - type: string - service: - description: Service defines which service should be used to fetch the secrets - enum: - - SecretsManager - - ParameterStore - type: string - required: - - region - - service - type: object - azurekv: - description: AzureKV configures this store to sync secrets using Azure Key Vault provider - properties: - authSecretRef: - description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. - properties: - clientId: - description: The Azure clientId of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientSecret: - description: The Azure ClientSecret of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - authType: - default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' - enum: - - ServicePrincipal - - ManagedIdentity - - WorkloadIdentity - type: string - identityId: - description: If multiple Managed Identity is assigned to the pod, you can select the one to be used - type: string - serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - tenantId: - description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. - type: string - vaultUrl: - description: Vault Url from which the secrets to be fetched from. - type: string - required: - - vaultUrl - type: object - fake: - description: Fake configures a store with static key/value pairs - properties: - data: - items: - properties: - key: - type: string - value: - type: string - valueMap: - additionalProperties: - type: string - type: object - version: - type: string - required: - - key - type: object - type: array - required: - - data - type: object - gcpsm: - description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider - properties: - auth: - description: Auth defines the information necessary to authenticate against GCP - properties: - secretRef: - properties: - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - workloadIdentity: - properties: - clusterLocation: - type: string - clusterName: - type: string - clusterProjectID: - type: string - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - clusterLocation - - clusterName - - serviceAccountRef - type: object - type: object - projectID: - description: ProjectID project where secret is located - type: string - type: object - gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a GitLab instance. - properties: - SecretRef: - properties: - accessToken: - description: AccessToken is used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - SecretRef - type: object - projectID: - description: ProjectID specifies a project where secrets are located. - type: string - url: - description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. - type: string - required: - - auth - type: object - ibm: - description: IBM configures this store to sync secrets using IBM Cloud provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the IBM secrets manager. - properties: - secretRef: - properties: - secretApiKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - serviceUrl: - description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance - type: string - required: - - auth - type: object - kubernetes: - description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a Kubernetes instance. - maxProperties: 1 - minProperties: 1 - properties: - cert: - description: has both clientCert and clientKey as secretKeySelector - properties: - clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - serviceAccount: - description: points to a service account that should be used for authentication - properties: - serviceAccount: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - type: object - token: - description: use static token to authenticate with - properties: - bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - remoteNamespace: - default: default - description: Remote namespace to fetch the secrets from - type: string - server: - description: configures the Kubernetes server Address. - properties: - caBundle: - description: CABundle is a base64-encoded CA certificate - format: byte - type: string - caProvider: - description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - url: - default: kubernetes.default - description: configures the Kubernetes server Address. - type: string - type: object - required: - - auth - type: object - oracle: - description: Oracle configures this store to sync secrets using Oracle Vault provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. - properties: - secretRef: - description: SecretRef to pass through sensitive information. - properties: - fingerprint: - description: Fingerprint is the fingerprint of the API private key. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - privatekey: - description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - fingerprint - - privatekey - type: object - tenancy: - description: Tenancy is the tenancy OCID where user is located. - type: string - user: - description: User is an access OCID specific to the account. - type: string - required: - - secretRef - - tenancy - - user - type: object - region: - description: Region is the region where vault is located. - type: string - vault: - description: Vault is the vault's OCID of the specific vault where secret is located. - type: string - required: - - region - - vault - type: object - vault: - description: Vault configures this store to sync secrets using Hashi provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Vault server. - properties: - appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. - properties: - path: - default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' - type: string - roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. - type: string - secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - - roleId - - secretRef - type: object - cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method - properties: - clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method - properties: - kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. - properties: - audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. - items: - type: string - type: array - expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. - format: int64 - type: integer - serviceAccountRef: - description: Service account field containing the name of a kubernetes ServiceAccount. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - serviceAccountRef - type: object - path: - default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' - type: string - role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method - type: string - secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - type: object - kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. - properties: - mountPath: - default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' - type: string - role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. - type: string - secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - mountPath - - role - type: object - ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method - properties: - path: - default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' - type: string - secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method - type: string - required: - - path - - username - type: object - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by presenting a token. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate Vault server certificate. - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header - type: boolean - namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' - type: string - path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' - type: string - readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency - type: boolean - server: - description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' - type: string - version: - default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". - enum: - - v1 - - v2 - type: string - required: - - auth - - server - type: object - webhook: - description: Webhook configures this store to sync secrets using a generic templated webhook - properties: - body: - description: Body - type: string - caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate webhook server certificate. - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - headers: - additionalProperties: - type: string - description: Headers - type: object - method: - description: Webhook Method - type: string - result: - description: Result formatting - properties: - jsonPath: - description: Json path of return value - type: string - type: object - secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name - items: - properties: - name: - description: Name of this secret in templates - type: string - secretRef: - description: Secret ref to fill in credentials - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - name - - secretRef - type: object - type: array - timeout: - description: Timeout - type: string - url: - description: Webhook url to call - type: string - required: - - result - - url - type: object - yandexlockbox: - description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate against Yandex Lockbox - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - type: object - retrySettings: - description: Used to configure http retries if failed - properties: - maxRetries: - format: int32 - type: integer - retryInterval: - type: string - type: object - required: - - provider - type: object - status: - description: SecretStoreStatus defines the observed state of the SecretStore. - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. - 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: SecretStoreSpec defines the desired state of SecretStore. - properties: - controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' - type: string - provider: - description: Used to configure the provider. Only one provider may be set - maxProperties: 1 - minProperties: 1 - properties: - akeyless: - description: Akeyless configures this store to sync secrets using Akeyless Vault provider - properties: - akeylessGWApiURL: - description: Akeyless GW API Url from which the secrets to be fetched from. - type: string - authSecretRef: - description: Auth configures how the operator authenticates with Akeyless. - properties: - secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' - properties: - accessID: - description: The SecretAccessID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - required: - - akeylessGWApiURL - - authSecretRef - type: object - alibaba: - description: Alibaba configures this store to sync secrets using Alibaba Cloud provider - properties: - auth: - description: AlibabaAuth contains a secretRef for credentials. - properties: - secretRef: - description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessKeySecretSecretRef: - description: The AccessKeySecret is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - accessKeyIDSecretRef - - accessKeySecretSecretRef - type: object - required: - - secretRef - type: object - endpoint: - type: string - regionID: - description: Alibaba Region to be used for the provider - type: string - required: - - auth - - regionID - type: object - aws: - description: AWS configures this store to sync secrets using AWS Secret Manager provider - properties: - auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' - properties: - jwt: - description: Authenticate against AWS using service account tokens. - properties: - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - type: object - secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - region: - description: AWS Region to be used for the provider - type: string - role: - description: Role is a Role ARN which the SecretManager provider will assume - type: string - service: - description: Service defines which service should be used to fetch the secrets - enum: - - SecretsManager - - ParameterStore - type: string - required: - - region - - service - type: object - azurekv: - description: AzureKV configures this store to sync secrets using Azure Key Vault provider - properties: - authSecretRef: - description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. - properties: - clientId: - description: The Azure clientId of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientSecret: - description: The Azure ClientSecret of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - authType: - default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' - enum: - - ServicePrincipal - - ManagedIdentity - - WorkloadIdentity - type: string - identityId: - description: If multiple Managed Identity is assigned to the pod, you can select the one to be used - type: string - serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - tenantId: - description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. - type: string - vaultUrl: - description: Vault Url from which the secrets to be fetched from. - type: string - required: - - vaultUrl - type: object - fake: - description: Fake configures a store with static key/value pairs - properties: - data: - items: - properties: - key: - type: string - value: - type: string - valueMap: - additionalProperties: - type: string - type: object - version: - type: string - required: - - key - type: object - type: array - required: - - data - type: object - gcpsm: - description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider - properties: - auth: - description: Auth defines the information necessary to authenticate against GCP - properties: - secretRef: - properties: - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - workloadIdentity: - properties: - clusterLocation: - type: string - clusterName: - type: string - clusterProjectID: - type: string - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - clusterLocation - - clusterName - - serviceAccountRef - type: object - type: object - projectID: - description: ProjectID project where secret is located - type: string - type: object - gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a GitLab instance. - properties: - SecretRef: - properties: - accessToken: - description: AccessToken is used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - SecretRef - type: object - projectID: - description: ProjectID specifies a project where secrets are located. - type: string - url: - description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. - type: string - required: - - auth - type: object - ibm: - description: IBM configures this store to sync secrets using IBM Cloud provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the IBM secrets manager. - maxProperties: 1 - minProperties: 1 - properties: - containerAuth: - description: IBM Container-based auth with IAM Trusted Profile. - properties: - iamEndpoint: - type: string - profile: - description: the IBM Trusted Profile - type: string - tokenLocation: - description: Location the token is mounted on the pod - type: string - required: - - profile - type: object - secretRef: - properties: - secretApiKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - serviceUrl: - description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance - type: string - required: - - auth - type: object - kubernetes: - description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a Kubernetes instance. - maxProperties: 1 - minProperties: 1 - properties: - cert: - description: has both clientCert and clientKey as secretKeySelector - properties: - clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - serviceAccount: - description: points to a service account that should be used for authentication - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - token: - description: use static token to authenticate with - properties: - bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - remoteNamespace: - default: default - description: Remote namespace to fetch the secrets from - type: string - server: - description: configures the Kubernetes server Address. - properties: - caBundle: - description: CABundle is a base64-encoded CA certificate - format: byte - type: string - caProvider: - description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' - properties: - key: - description: The key where the CA certificate can be found in the Secret or ConfigMap. - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - url: - default: kubernetes.default - description: configures the Kubernetes server Address. - type: string - type: object - required: - - auth - type: object - onepassword: - description: OnePassword configures this store to sync secrets using the 1Password Cloud provider - properties: - auth: - description: Auth defines the information necessary to authenticate against OnePassword Connect Server - properties: - secretRef: - description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. - properties: - connectTokenSecretRef: - description: The ConnectToken is used for authentication to a 1Password Connect Server. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - connectTokenSecretRef - type: object - required: - - secretRef - type: object - connectHost: - description: ConnectHost defines the OnePassword Connect Server to connect to - type: string - vaults: - additionalProperties: - type: integer - description: Vaults defines which OnePassword vaults to search in which order - type: object - required: - - auth - - connectHost - - vaults - type: object - oracle: - description: Oracle configures this store to sync secrets using Oracle Vault provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. - properties: - secretRef: - description: SecretRef to pass through sensitive information. - properties: - fingerprint: - description: Fingerprint is the fingerprint of the API private key. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - privatekey: - description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - fingerprint - - privatekey - type: object - tenancy: - description: Tenancy is the tenancy OCID where user is located. - type: string - user: - description: User is an access OCID specific to the account. - type: string - required: - - secretRef - - tenancy - - user - type: object - region: - description: Region is the region where vault is located. - type: string - vault: - description: Vault is the vault's OCID of the specific vault where secret is located. - type: string - required: - - region - - vault - type: object - senhasegura: - description: Senhasegura configures this store to sync secrets using senhasegura provider - properties: - auth: - description: Auth defines parameters to authenticate in senhasegura - properties: - clientId: - type: string - clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - clientId - - clientSecretSecretRef - type: object - ignoreSslCertificate: - default: false - description: IgnoreSslCertificate defines if SSL certificate must be ignored - type: boolean - module: - description: Module defines which senhasegura module should be used to get secrets - type: string - url: - description: URL of senhasegura - type: string - required: - - auth - - module - - url - type: object - vault: - description: Vault configures this store to sync secrets using Hashi provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Vault server. - properties: - appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. - properties: - path: - default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' - type: string - roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. - type: string - secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - - roleId - - secretRef - type: object - cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method - properties: - clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method - properties: - kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. - properties: - audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. - items: - type: string - type: array - expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. - format: int64 - type: integer - serviceAccountRef: - description: Service account field containing the name of a kubernetes ServiceAccount. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - serviceAccountRef - type: object - path: - default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' - type: string - role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method - type: string - secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - type: object - kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. - properties: - mountPath: - default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' - type: string - role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. - type: string - secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - mountPath - - role - type: object - ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method - properties: - path: - default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' - type: string - secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method - type: string - required: - - path - - username - type: object - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by presenting a token. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate Vault server certificate. - properties: - key: - description: The key where the CA certificate can be found in the Secret or ConfigMap. - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header - type: boolean - namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' - type: string - path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' - type: string - readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency - type: boolean - server: - description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' - type: string - version: - default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". - enum: - - v1 - - v2 - type: string - required: - - auth - - server - type: object - webhook: - description: Webhook configures this store to sync secrets using a generic templated webhook - properties: - body: - description: Body - type: string - caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate webhook server certificate. - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - headers: - additionalProperties: - type: string - description: Headers - type: object - method: - description: Webhook Method - type: string - result: - description: Result formatting - properties: - jsonPath: - description: Json path of return value - type: string - type: object - secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name - items: - properties: - name: - description: Name of this secret in templates - type: string - secretRef: - description: Secret ref to fill in credentials - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - name - - secretRef - type: object - type: array - timeout: - description: Timeout - type: string - url: - description: Webhook url to call - type: string - required: - - result - - url - type: object - yandexcertificatemanager: - description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate against Yandex Certificate Manager - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - yandexlockbox: - description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate against Yandex Lockbox - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - type: object - refreshInterval: - description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. - type: integer - retrySettings: - description: Used to configure http retries if failed - properties: - maxRetries: - format: int32 - type: integer - retryInterval: - type: string - type: object - required: - - provider - type: object - status: - description: SecretStoreStatus defines the observed state of the SecretStore. - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: true - subresources: - status: {} - conversion: - strategy: Webhook - webhook: - conversionReviewVersions: - - v1 - clientConfig: - service: - name: golang-external-secrets-webhook - namespace: "default" - path: /convert ---- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/externalsecret.yaml -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: externalsecrets.external-secrets.io -spec: - group: external-secrets.io - names: - categories: - - externalsecrets - kind: ExternalSecret - listKind: ExternalSecretList - plural: externalsecrets - shortNames: - - es - singular: externalsecret - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name - name: Store - type: string - - jsonPath: .spec.refreshInterval - name: Refresh Interval - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - deprecated: true - name: v1alpha1 - schema: - openAPIV3Schema: - description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. - properties: - data: - description: Data defines the connection between the Kubernetes Secret keys and the Provider data - items: - description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. - properties: - remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - key: - description: Key is the key used in the Provider, mandatory - type: string - property: - description: Used to select a specific property of the Provider value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider value, if supported - type: string - required: - - key - type: object - secretKey: - type: string - required: - - remoteRef - - secretKey - type: object - type: array - dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order - items: - description: ExternalSecretDataRemoteRef defines Provider data location. - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - key: - description: Key is the key used in the Provider, mandatory - type: string - property: - description: Used to select a specific property of the Provider value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider value, if supported - type: string - required: - - key - type: object - type: array - refreshInterval: - default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. - type: string - secretStoreRef: - description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. - properties: - kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` - type: string - name: - description: Name of the SecretStore resource - type: string - required: - - name - type: object - target: - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. - properties: - creationPolicy: - default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' - type: string - immutable: - description: Immutable defines if the final secret will be immutable - type: boolean - name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource - type: string - template: - description: Template defines a blueprint for the created Secret resource. - properties: - data: - additionalProperties: - type: string - type: object - engineVersion: - default: v1 - description: EngineVersion specifies the template engine version that should be used to compile/execute the template specified in .data and .templateFrom[]. - type: string - metadata: - description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - templateFrom: - items: - maxProperties: 1 - minProperties: 1 - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - name: - type: string - required: - - items - - name - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - name: - type: string - required: - - items - - name - type: object - type: object - type: array - type: - type: string - type: object - type: object - required: - - secretStoreRef - - target - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated - format: date-time - nullable: true - type: string - syncedResourceVersion: - description: SyncedResourceVersion keeps track of the last synced version - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.secretStoreRef.name - name: Store - type: string - - jsonPath: .spec.refreshInterval - name: Refresh Interval - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: ExternalSecret is the Schema for the external-secrets 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: ExternalSecretSpec defines the desired state of ExternalSecret. - properties: - data: - description: Data defines the connection between the Kubernetes Secret keys and the Provider data - items: - description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. - properties: - remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - decodingStrategy: - default: None - description: Used to define a decoding Strategy - type: string - key: - description: Key is the key used in the Provider, mandatory - type: string - metadataPolicy: - description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None - type: string - property: - description: Used to select a specific property of the Provider value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider value, if supported - type: string - required: - - key - type: object - secretKey: - type: string - required: - - remoteRef - - secretKey - type: object - type: array - dataFrom: - description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order - items: - properties: - extract: - description: Used to extract multiple key/value pairs from one secret - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - decodingStrategy: - default: None - description: Used to define a decoding Strategy - type: string - key: - description: Key is the key used in the Provider, mandatory - type: string - metadataPolicy: - description: Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None - type: string - property: - description: Used to select a specific property of the Provider value (if a map), if supported - type: string - version: - description: Used to select a specific version of the Provider value, if supported - type: string - required: - - key - type: object - find: - description: Used to find secrets based on tags or regular expressions - properties: - conversionStrategy: - default: Default - description: Used to define a conversion Strategy - type: string - decodingStrategy: - default: None - description: Used to define a decoding Strategy - type: string - name: - description: Finds secrets based on the name. - properties: - regexp: - description: Finds secrets base - type: string - type: object - path: - description: A root path to start the find operations. - type: string - tags: - additionalProperties: - type: string - description: Find secrets based on tags. - type: object - type: object - rewrite: - description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) - items: - properties: - regexp: - description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. - properties: - source: - description: Used to define the regular expression of a re.Compiler. - type: string - target: - description: Used to define the target pattern of a ReplaceAll operation. - type: string - required: - - source - - target - type: object - type: object - type: array - type: object - type: array - refreshInterval: - default: 1h - description: RefreshInterval is the amount of time before the values are read again from the SecretStore provider Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" May be set to zero to fetch and create it once. Defaults to 1h. - type: string - secretStoreRef: - description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. - properties: - kind: - description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` - type: string - name: - description: Name of the SecretStore resource - type: string - required: - - name - type: object - target: - description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret. - properties: - creationPolicy: - default: Owner - description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner' - enum: - - Owner - - Orphan - - Merge - - None - type: string - deletionPolicy: - default: Retain - description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'Retain' - enum: - - Delete - - Merge - - Retain - type: string - immutable: - description: Immutable defines if the final secret will be immutable - type: boolean - name: - description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource - type: string - template: - description: Template defines a blueprint for the created Secret resource. - properties: - data: - additionalProperties: - type: string - type: object - engineVersion: - default: v2 - type: string - metadata: - description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - templateFrom: - items: - maxProperties: 1 - minProperties: 1 - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - name: - type: string - required: - - items - - name - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - name: - type: string - required: - - items - - name - type: object - type: object - type: array - type: - type: string - type: object - type: object - required: - - secretStoreRef - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - refreshTime: - description: refreshTime is the time and date the external secret was fetched and the target secret updated - format: date-time - nullable: true - type: string - syncedResourceVersion: - description: SyncedResourceVersion keeps track of the last synced version - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} - conversion: - strategy: Webhook - webhook: - conversionReviewVersions: - - v1 - clientConfig: - service: - name: golang-external-secrets-webhook - namespace: "default" - path: /convert ---- -# Source: golang-external-secrets/charts/external-secrets/templates/crds/secretstore.yaml -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null - name: secretstores.external-secrets.io -spec: - group: external-secrets.io - names: - categories: - - externalsecrets - kind: SecretStore - listKind: SecretStoreList - plural: secretstores - shortNames: - - ss - singular: secretstore - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - deprecated: true - name: v1alpha1 - schema: - openAPIV3Schema: - description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. - 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: SecretStoreSpec defines the desired state of SecretStore. - properties: - controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' - type: string - provider: - description: Used to configure the provider. Only one provider may be set - maxProperties: 1 - minProperties: 1 - properties: - akeyless: - description: Akeyless configures this store to sync secrets using Akeyless Vault provider - properties: - akeylessGWApiURL: - description: Akeyless GW API Url from which the secrets to be fetched from. - type: string - authSecretRef: - description: Auth configures how the operator authenticates with Akeyless. - properties: - secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' - properties: - accessID: - description: The SecretAccessID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - required: - - akeylessGWApiURL - - authSecretRef - type: object - alibaba: - description: Alibaba configures this store to sync secrets using Alibaba Cloud provider - properties: - auth: - description: AlibabaAuth contains a secretRef for credentials. - properties: - secretRef: - description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessKeySecretSecretRef: - description: The AccessKeySecret is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - accessKeyIDSecretRef - - accessKeySecretSecretRef - type: object - required: - - secretRef - type: object - endpoint: - type: string - regionID: - description: Alibaba Region to be used for the provider - type: string - required: - - auth - - regionID - type: object - aws: - description: AWS configures this store to sync secrets using AWS Secret Manager provider - properties: - auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' - properties: - jwt: - description: Authenticate against AWS using service account tokens. - properties: - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - type: object - secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - region: - description: AWS Region to be used for the provider - type: string - role: - description: Role is a Role ARN which the SecretManager provider will assume - type: string - service: - description: Service defines which service should be used to fetch the secrets - enum: - - SecretsManager - - ParameterStore - type: string - required: - - region - - service - type: object - azurekv: - description: AzureKV configures this store to sync secrets using Azure Key Vault provider - properties: - authSecretRef: - description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. - properties: - clientId: - description: The Azure clientId of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientSecret: - description: The Azure ClientSecret of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - authType: - default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' - enum: - - ServicePrincipal - - ManagedIdentity - - WorkloadIdentity - type: string - identityId: - description: If multiple Managed Identity is assigned to the pod, you can select the one to be used - type: string - serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - tenantId: - description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. - type: string - vaultUrl: - description: Vault Url from which the secrets to be fetched from. - type: string - required: - - vaultUrl - type: object - fake: - description: Fake configures a store with static key/value pairs - properties: - data: - items: - properties: - key: - type: string - value: - type: string - valueMap: - additionalProperties: - type: string - type: object - version: - type: string - required: - - key - type: object - type: array - required: - - data - type: object - gcpsm: - description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider - properties: - auth: - description: Auth defines the information necessary to authenticate against GCP - properties: - secretRef: - properties: - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - workloadIdentity: - properties: - clusterLocation: - type: string - clusterName: - type: string - clusterProjectID: - type: string - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - clusterLocation - - clusterName - - serviceAccountRef - type: object - type: object - projectID: - description: ProjectID project where secret is located - type: string - type: object - gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a GitLab instance. - properties: - SecretRef: - properties: - accessToken: - description: AccessToken is used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - SecretRef - type: object - projectID: - description: ProjectID specifies a project where secrets are located. - type: string - url: - description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. - type: string - required: - - auth - type: object - ibm: - description: IBM configures this store to sync secrets using IBM Cloud provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the IBM secrets manager. - properties: - secretRef: - properties: - secretApiKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - serviceUrl: - description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance - type: string - required: - - auth - type: object - kubernetes: - description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a Kubernetes instance. - maxProperties: 1 - minProperties: 1 - properties: - cert: - description: has both clientCert and clientKey as secretKeySelector - properties: - clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - serviceAccount: - description: points to a service account that should be used for authentication - properties: - serviceAccount: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - type: object - token: - description: use static token to authenticate with - properties: - bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - remoteNamespace: - default: default - description: Remote namespace to fetch the secrets from - type: string - server: - description: configures the Kubernetes server Address. - properties: - caBundle: - description: CABundle is a base64-encoded CA certificate - format: byte - type: string - caProvider: - description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - url: - default: kubernetes.default - description: configures the Kubernetes server Address. - type: string - type: object - required: - - auth - type: object - oracle: - description: Oracle configures this store to sync secrets using Oracle Vault provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. - properties: - secretRef: - description: SecretRef to pass through sensitive information. - properties: - fingerprint: - description: Fingerprint is the fingerprint of the API private key. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - privatekey: - description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - fingerprint - - privatekey - type: object - tenancy: - description: Tenancy is the tenancy OCID where user is located. - type: string - user: - description: User is an access OCID specific to the account. - type: string - required: - - secretRef - - tenancy - - user - type: object - region: - description: Region is the region where vault is located. - type: string - vault: - description: Vault is the vault's OCID of the specific vault where secret is located. - type: string - required: - - region - - vault - type: object - vault: - description: Vault configures this store to sync secrets using Hashi provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Vault server. - properties: - appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. - properties: - path: - default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' - type: string - roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. - type: string - secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - - roleId - - secretRef - type: object - cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method - properties: - clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method - properties: - kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. - properties: - audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. - items: - type: string - type: array - expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. - format: int64 - type: integer - serviceAccountRef: - description: Service account field containing the name of a kubernetes ServiceAccount. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - serviceAccountRef - type: object - path: - default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' - type: string - role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method - type: string - secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - type: object - kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. - properties: - mountPath: - default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' - type: string - role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. - type: string - secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - mountPath - - role - type: object - ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method - properties: - path: - default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' - type: string - secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method - type: string - required: - - path - - username - type: object - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by presenting a token. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate Vault server certificate. - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header - type: boolean - namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' - type: string - path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' - type: string - readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency - type: boolean - server: - description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' - type: string - version: - default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". - enum: - - v1 - - v2 - type: string - required: - - auth - - server - type: object - webhook: - description: Webhook configures this store to sync secrets using a generic templated webhook - properties: - body: - description: Body - type: string - caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate webhook server certificate. - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - headers: - additionalProperties: - type: string - description: Headers - type: object - method: - description: Webhook Method - type: string - result: - description: Result formatting - properties: - jsonPath: - description: Json path of return value - type: string - type: object - secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name - items: - properties: - name: - description: Name of this secret in templates - type: string - secretRef: - description: Secret ref to fill in credentials - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - name - - secretRef - type: object - type: array - timeout: - description: Timeout - type: string - url: - description: Webhook url to call - type: string - required: - - result - - url - type: object - yandexlockbox: - description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate against Yandex Lockbox - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - type: object - retrySettings: - description: Used to configure http retries if failed - properties: - maxRetries: - format: int32 - type: integer - retryInterval: - type: string - type: object - required: - - provider - type: object - status: - description: SecretStoreStatus defines the observed state of the SecretStore. - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].reason - name: Status - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. - 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: SecretStoreSpec defines the desired state of SecretStore. - properties: - controller: - description: 'Used to select the correct KES controller (think: ingress.ingressClassName) The KES controller is instantiated with a specific controller name and filters ES based on this property' - type: string - provider: - description: Used to configure the provider. Only one provider may be set - maxProperties: 1 - minProperties: 1 - properties: - akeyless: - description: Akeyless configures this store to sync secrets using Akeyless Vault provider - properties: - akeylessGWApiURL: - description: Akeyless GW API Url from which the secrets to be fetched from. - type: string - authSecretRef: - description: Auth configures how the operator authenticates with Akeyless. - properties: - secretRef: - description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.' - properties: - accessID: - description: The SecretAccessID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessType: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessTypeParam: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - secretRef - type: object - required: - - akeylessGWApiURL - - authSecretRef - type: object - alibaba: - description: Alibaba configures this store to sync secrets using Alibaba Cloud provider - properties: - auth: - description: AlibabaAuth contains a secretRef for credentials. - properties: - secretRef: - description: AlibabaAuthSecretRef holds secret references for Alibaba credentials. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - accessKeySecretSecretRef: - description: The AccessKeySecret is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - accessKeyIDSecretRef - - accessKeySecretSecretRef - type: object - required: - - secretRef - type: object - endpoint: - type: string - regionID: - description: Alibaba Region to be used for the provider - type: string - required: - - auth - - regionID - type: object - aws: - description: AWS configures this store to sync secrets using AWS Secret Manager provider - properties: - auth: - description: 'Auth defines the information necessary to authenticate against AWS if not set aws sdk will infer credentials from your environment see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' - properties: - jwt: - description: Authenticate against AWS using service account tokens. - properties: - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - type: object - secretRef: - description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. - properties: - accessKeyIDSecretRef: - description: The AccessKeyID is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - region: - description: AWS Region to be used for the provider - type: string - role: - description: Role is a Role ARN which the SecretManager provider will assume - type: string - service: - description: Service defines which service should be used to fetch the secrets - enum: - - SecretsManager - - ParameterStore - type: string - required: - - region - - service - type: object - azurekv: - description: AzureKV configures this store to sync secrets using Azure Key Vault provider - properties: - authSecretRef: - description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. - properties: - clientId: - description: The Azure clientId of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientSecret: - description: The Azure ClientSecret of the service principle used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - authType: - default: ServicePrincipal - description: 'Auth type defines how to authenticate to the keyvault service. Valid values are: - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret) - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)' - enum: - - ServicePrincipal - - ManagedIdentity - - WorkloadIdentity - type: string - identityId: - description: If multiple Managed Identity is assigned to the pod, you can select the one to be used - type: string - serviceAccountRef: - description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - tenantId: - description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. - type: string - vaultUrl: - description: Vault Url from which the secrets to be fetched from. - type: string - required: - - vaultUrl - type: object - fake: - description: Fake configures a store with static key/value pairs - properties: - data: - items: - properties: - key: - type: string - value: - type: string - valueMap: - additionalProperties: - type: string - type: object - version: - type: string - required: - - key - type: object - type: array - required: - - data - type: object - gcpsm: - description: GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider - properties: - auth: - description: Auth defines the information necessary to authenticate against GCP - properties: - secretRef: - properties: - secretAccessKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - workloadIdentity: - properties: - clusterLocation: - type: string - clusterName: - type: string - clusterProjectID: - type: string - serviceAccountRef: - description: A reference to a ServiceAccount resource. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - clusterLocation - - clusterName - - serviceAccountRef - type: object - type: object - projectID: - description: ProjectID project where secret is located - type: string - type: object - gitlab: - description: Gitlab configures this store to sync secrets using Gitlab Variables provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a GitLab instance. - properties: - SecretRef: - properties: - accessToken: - description: AccessToken is used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - SecretRef - type: object - projectID: - description: ProjectID specifies a project where secrets are located. - type: string - url: - description: URL configures the GitLab instance URL. Defaults to https://gitlab.com/. - type: string - required: - - auth - type: object - ibm: - description: IBM configures this store to sync secrets using IBM Cloud provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the IBM secrets manager. - maxProperties: 1 - minProperties: 1 - properties: - containerAuth: - description: IBM Container-based auth with IAM Trusted Profile. - properties: - iamEndpoint: - type: string - profile: - description: the IBM Trusted Profile - type: string - tokenLocation: - description: Location the token is mounted on the pod - type: string - required: - - profile - type: object - secretRef: - properties: - secretApiKeySecretRef: - description: The SecretAccessKey is used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - serviceUrl: - description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance - type: string - required: - - auth - type: object - kubernetes: - description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider - properties: - auth: - description: Auth configures how secret-manager authenticates with a Kubernetes instance. - maxProperties: 1 - minProperties: 1 - properties: - cert: - description: has both clientCert and clientKey as secretKeySelector - properties: - clientCert: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - clientKey: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - serviceAccount: - description: points to a service account that should be used for authentication - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - token: - description: use static token to authenticate with - properties: - bearerToken: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - type: object - remoteNamespace: - default: default - description: Remote namespace to fetch the secrets from - type: string - server: - description: configures the Kubernetes server Address. - properties: - caBundle: - description: CABundle is a base64-encoded CA certificate - format: byte - type: string - caProvider: - description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' - properties: - key: - description: The key where the CA certificate can be found in the Secret or ConfigMap. - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - url: - default: kubernetes.default - description: configures the Kubernetes server Address. - type: string - type: object - required: - - auth - type: object - onepassword: - description: OnePassword configures this store to sync secrets using the 1Password Cloud provider - properties: - auth: - description: Auth defines the information necessary to authenticate against OnePassword Connect Server - properties: - secretRef: - description: OnePasswordAuthSecretRef holds secret references for 1Password credentials. - properties: - connectTokenSecretRef: - description: The ConnectToken is used for authentication to a 1Password Connect Server. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - connectTokenSecretRef - type: object - required: - - secretRef - type: object - connectHost: - description: ConnectHost defines the OnePassword Connect Server to connect to - type: string - vaults: - additionalProperties: - type: integer - description: Vaults defines which OnePassword vaults to search in which order - type: object - required: - - auth - - connectHost - - vaults - type: object - oracle: - description: Oracle configures this store to sync secrets using Oracle Vault provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Oracle Vault. If empty, use the instance principal, otherwise the user credentials specified in Auth. - properties: - secretRef: - description: SecretRef to pass through sensitive information. - properties: - fingerprint: - description: Fingerprint is the fingerprint of the API private key. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - privatekey: - description: PrivateKey is the user's API Signing Key in PEM format, used for authentication. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - fingerprint - - privatekey - type: object - tenancy: - description: Tenancy is the tenancy OCID where user is located. - type: string - user: - description: User is an access OCID specific to the account. - type: string - required: - - secretRef - - tenancy - - user - type: object - region: - description: Region is the region where vault is located. - type: string - vault: - description: Vault is the vault's OCID of the specific vault where secret is located. - type: string - required: - - region - - vault - type: object - senhasegura: - description: Senhasegura configures this store to sync secrets using senhasegura provider - properties: - auth: - description: Auth defines parameters to authenticate in senhasegura - properties: - clientId: - type: string - clientSecretSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - clientId - - clientSecretSecretRef - type: object - ignoreSslCertificate: - default: false - description: IgnoreSslCertificate defines if SSL certificate must be ignored - type: boolean - module: - description: Module defines which senhasegura module should be used to get secrets - type: string - url: - description: URL of senhasegura - type: string - required: - - auth - - module - - url - type: object - vault: - description: Vault configures this store to sync secrets using Hashi provider - properties: - auth: - description: Auth configures how secret-manager authenticates with the Vault server. - properties: - appRole: - description: AppRole authenticates with Vault using the App Role auth mechanism, with the role and secret stored in a Kubernetes Secret resource. - properties: - path: - default: approle - description: 'Path where the App Role authentication backend is mounted in Vault, e.g: "approle"' - type: string - roleId: - description: RoleID configured in the App Role authentication backend when setting up the authentication backend in Vault. - type: string - secretRef: - description: Reference to a key in a Secret that contains the App Role secret used to authenticate with Vault. The `key` field must be specified and denotes which entry within the Secret resource is used as the app role secret. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - - roleId - - secretRef - type: object - cert: - description: Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate Cert authentication method - properties: - clientCert: - description: ClientCert is a certificate to authenticate using the Cert Vault authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - secretRef: - description: SecretRef to a key in a Secret resource containing client private key to authenticate with Vault using the Cert authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - jwt: - description: Jwt authenticates with Vault by passing role and JWT token using the JWT/OIDC authentication method - properties: - kubernetesServiceAccountToken: - description: Optional ServiceAccountToken specifies the Kubernetes service account for which to request a token for with the `TokenRequest` API. - properties: - audiences: - description: Optional audiences field that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to a single audience `vault` it not specified. - items: - type: string - type: array - expirationSeconds: - description: Optional expiration time in seconds that will be used to request a temporary Kubernetes service account token for the service account referenced by `serviceAccountRef`. Defaults to 10 minutes. - format: int64 - type: integer - serviceAccountRef: - description: Service account field containing the name of a kubernetes ServiceAccount. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - serviceAccountRef - type: object - path: - default: jwt - description: 'Path where the JWT authentication backend is mounted in Vault, e.g: "jwt"' - type: string - role: - description: Role is a JWT role to authenticate using the JWT/OIDC Vault authentication method - type: string - secretRef: - description: Optional SecretRef that refers to a key in a Secret resource containing JWT token to authenticate with Vault using the JWT/OIDC authentication method. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - path - type: object - kubernetes: - description: Kubernetes authenticates with Vault by passing the ServiceAccount token stored in the named Secret resource to the Vault server. - properties: - mountPath: - default: kubernetes - description: 'Path where the Kubernetes authentication backend is mounted in Vault, e.g: "kubernetes"' - type: string - role: - description: A required field containing the Vault Role to assume. A Role binds a Kubernetes ServiceAccount with a set of Vault policies. - type: string - secretRef: - description: Optional secret field containing a Kubernetes ServiceAccount JWT used for authenticating with Vault. If a name is specified without a key, `token` is the default. If one is not specified, the one bound to the controller will be used. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - serviceAccountRef: - description: Optional service account field containing the name of a kubernetes ServiceAccount. If the service account is specified, the service account secret token JWT will be used for authenticating with Vault. If the service account selector is not supplied, the secretRef will be used instead. - properties: - name: - description: The name of the ServiceAccount resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - required: - - name - type: object - required: - - mountPath - - role - type: object - ldap: - description: Ldap authenticates with Vault by passing username/password pair using the LDAP authentication method - properties: - path: - default: ldap - description: 'Path where the LDAP authentication backend is mounted in Vault, e.g: "ldap"' - type: string - secretRef: - description: SecretRef to a key in a Secret resource containing password for the LDAP user used to authenticate with Vault using the LDAP authentication method - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - username: - description: Username is a LDAP user name used to authenticate using the LDAP Vault authentication method - type: string - required: - - path - - username - type: object - tokenSecretRef: - description: TokenSecretRef authenticates with Vault by presenting a token. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caBundle: - description: PEM encoded CA bundle used to validate Vault server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate Vault server certificate. - properties: - key: - description: The key where the CA certificate can be found in the Secret or ConfigMap. - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - forwardInconsistent: - description: ForwardInconsistent tells Vault to forward read-after-write requests to the Vault leader instead of simply retrying within a loop. This can increase performance if the option is enabled serverside. https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header - type: boolean - namespace: - description: 'Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows Vault environments to support Secure Multi-tenancy. e.g: "ns1". More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces' - type: string - path: - description: 'Path is the mount path of the Vault KV backend endpoint, e.g: "secret". The v2 KV secret engine version specific "/data" path suffix for fetching secrets from Vault is optional and will be appended if not present in specified path.' - type: string - readYourWrites: - description: ReadYourWrites ensures isolated read-after-write semantics by providing discovered cluster replication states in each request. More information about eventual consistency in Vault can be found here https://www.vaultproject.io/docs/enterprise/consistency - type: boolean - server: - description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".' - type: string - version: - default: v2 - description: Version is the Vault KV secret engine version. This can be either "v1" or "v2". Version defaults to "v2". - enum: - - v1 - - v2 - type: string - required: - - auth - - server - type: object - webhook: - description: Webhook configures this store to sync secrets using a generic templated webhook - properties: - body: - description: Body - type: string - caBundle: - description: PEM encoded CA bundle used to validate webhook server certificate. Only used if the Server URL is using HTTPS protocol. This parameter is ignored for plain HTTP protocol connection. If not set the system root certificates are used to validate the TLS connection. - format: byte - type: string - caProvider: - description: The provider for the CA bundle to use to validate webhook server certificate. - properties: - key: - description: The key the value inside of the provider type to use, only used with "Secret" type - type: string - name: - description: The name of the object located at the provider type. - type: string - namespace: - description: The namespace the Provider type is in. - type: string - type: - description: The type of provider to use such as "Secret", or "ConfigMap". - enum: - - Secret - - ConfigMap - type: string - required: - - name - - type - type: object - headers: - additionalProperties: - type: string - description: Headers - type: object - method: - description: Webhook Method - type: string - result: - description: Result formatting - properties: - jsonPath: - description: Json path of return value - type: string - type: object - secrets: - description: Secrets to fill in templates These secrets will be passed to the templating function as key value pairs under the given name - items: - properties: - name: - description: Name of this secret in templates - type: string - secretRef: - description: Secret ref to fill in credentials - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - required: - - name - - secretRef - type: object - type: array - timeout: - description: Timeout - type: string - url: - description: Webhook url to call - type: string - required: - - result - - url - type: object - yandexcertificatemanager: - description: YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate against Yandex Certificate Manager - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - yandexlockbox: - description: YandexLockbox configures this store to sync secrets using Yandex Lockbox provider - properties: - apiEndpoint: - description: Yandex.Cloud API endpoint (e.g. 'api.cloud.yandex.net:443') - type: string - auth: - description: Auth defines the information necessary to authenticate against Yandex Lockbox - properties: - authorizedKeySecretRef: - description: The authorized key used for authentication - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - caProvider: - description: The provider for the CA bundle to use to validate Yandex.Cloud server certificate. - properties: - certSecretRef: - description: A reference to a specific 'key' within a Secret resource, In some instances, `key` is a required field. - properties: - key: - description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. - type: string - name: - description: The name of the Secret resource being referred to. - type: string - namespace: - description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. - type: string - type: object - type: object - required: - - auth - type: object - type: object - refreshInterval: - description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. - type: integer - retrySettings: - description: Used to configure http retries if failed - properties: - maxRetries: - format: int32 - type: integer - retryInterval: - type: string - type: object - required: - - provider - type: object - status: - description: SecretStoreStatus defines the observed state of the SecretStore. - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: true - subresources: - status: {} - conversion: - strategy: Webhook - webhook: - conversionReviewVersions: - - v1 - clientConfig: - service: - name: golang-external-secrets-webhook - namespace: "default" - path: /convert ---- -# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: golang-external-secrets-cert-controller - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-cert-controller - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -rules: - - apiGroups: - - "apiextensions.k8s.io" - resources: - - "customresourcedefinitions" - verbs: - - "get" - - "list" - - "watch" - - "update" - - "patch" - - apiGroups: - - "admissionregistration.k8s.io" - resources: - - "validatingwebhookconfigurations" - verbs: - - "get" - - "list" - - "watch" - - "update" - - "patch" - - apiGroups: - - "" - resources: - - "endpoints" - verbs: - - "list" - - "get" - - "watch" - - apiGroups: - - "" - resources: - - "events" - verbs: - - "create" - - "patch" - - apiGroups: - - "" - resources: - - "secrets" - verbs: - - "get" - - "list" - - "watch" - - "update" - - "patch" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: golang-external-secrets-controller - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -rules: - - apiGroups: - - "external-secrets.io" - resources: - - "secretstores" - - "clustersecretstores" - - "externalsecrets" - - "clusterexternalsecrets" - verbs: - - "get" - - "list" - - "watch" - - apiGroups: - - "external-secrets.io" - resources: - - "externalsecrets" - - "externalsecrets/status" - - "externalsecrets/finalizers" - - "secretstores" - - "secretstores/status" - - "secretstores/finalizers" - - "clustersecretstores" - - "clustersecretstores/status" - - "clustersecretstores/finalizers" - - "clusterexternalsecrets" - - "clusterexternalsecrets/status" - - "clusterexternalsecrets/finalizers" - verbs: - - "update" - - "patch" - - apiGroups: - - "" - resources: - - "serviceaccounts" - - "namespaces" - verbs: - - "get" - - "list" - - "watch" - - apiGroups: - - "" - resources: - - "configmaps" - verbs: - - "get" - - "list" - - "watch" - - apiGroups: - - "" - resources: - - "secrets" - verbs: - - "get" - - "list" - - "watch" - - "create" - - "update" - - "delete" - - "patch" - - apiGroups: - - "" - resources: - - "serviceaccounts/token" - verbs: - - "create" - - apiGroups: - - "" - resources: - - "events" - verbs: - - "create" - - "patch" - - apiGroups: - - "external-secrets.io" - resources: - - "externalsecrets" - verbs: - - "create" - - "update" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: golang-external-secrets-view - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm - rbac.authorization.k8s.io/aggregate-to-view: "true" - rbac.authorization.k8s.io/aggregate-to-edit: "true" - rbac.authorization.k8s.io/aggregate-to-admin: "true" -rules: - - apiGroups: - - "external-secrets.io" - resources: - - "externalsecrets" - - "secretstores" - - "clustersecretstores" - verbs: - - "get" - - "watch" - - "list" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: golang-external-secrets-edit - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm - rbac.authorization.k8s.io/aggregate-to-edit: "true" - rbac.authorization.k8s.io/aggregate-to-admin: "true" -rules: - - apiGroups: - - "external-secrets.io" - resources: - - "externalsecrets" - - "secretstores" - - "clustersecretstores" - verbs: - - "create" - - "delete" - - "deletecollection" - - "patch" - - "update" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: golang-external-secrets-cert-controller - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-cert-controller - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: golang-external-secrets-cert-controller -subjects: - - name: external-secrets-cert-controller - namespace: "default" - kind: ServiceAccount ---- -# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: golang-external-secrets-controller - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: golang-external-secrets-controller -subjects: - - name: golang-external-secrets - namespace: "default" - kind: ServiceAccount ---- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: role-tokenreview-binding - namespace: default -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:auth-delegator -subjects: -- kind: ServiceAccount - name: golang-external-secrets - namespace: golang-external-secrets ---- -# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: golang-external-secrets-leaderelection - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -rules: - - apiGroups: - - "" - resources: - - "configmaps" - resourceNames: - - "external-secrets-controller" - verbs: - - "get" - - "update" - - "patch" - - apiGroups: - - "" - resources: - - "configmaps" - verbs: - - "create" - - apiGroups: - - "coordination.k8s.io" - resources: - - "leases" - verbs: - - "get" - - "create" - - "update" - - "patch" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/rbac.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: golang-external-secrets-leaderelection - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: golang-external-secrets-leaderelection -subjects: - - kind: ServiceAccount - name: golang-external-secrets - namespace: "default" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/webhook-service.yaml -apiVersion: v1 -kind: Service -metadata: - name: golang-external-secrets-webhook - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm - external-secrets.io/component : webhook -spec: - type: ClusterIP - ports: - - port: 443 - targetPort: 10250 - protocol: TCP - name: webhook - selector: - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets ---- -# Source: golang-external-secrets/charts/external-secrets/templates/cert-controller-deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: golang-external-secrets-cert-controller - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-cert-controller - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: external-secrets-cert-controller - app.kubernetes.io/instance: golang-external-secrets - template: - metadata: - labels: - app.kubernetes.io/name: external-secrets-cert-controller - app.kubernetes.io/instance: golang-external-secrets - spec: - serviceAccountName: external-secrets-cert-controller - containers: - - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.5.9" - imagePullPolicy: IfNotPresent - args: - - certcontroller - - --crd-requeue-interval=5m - - --service-name=golang-external-secrets-webhook - - --service-namespace=default - - --secret-name=golang-external-secrets-webhook - - --secret-namespace=default - ports: - - containerPort: 8080 - protocol: TCP - name: metrics - readinessProbe: - httpGet: - port: 8081 - path: /readyz - initialDelaySeconds: 20 - periodSeconds: 5 ---- -# Source: golang-external-secrets/charts/external-secrets/templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: golang-external-secrets - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - template: - metadata: - labels: - app.kubernetes.io/name: external-secrets - app.kubernetes.io/instance: golang-external-secrets - spec: - serviceAccountName: golang-external-secrets - containers: - - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.5.9" - imagePullPolicy: IfNotPresent - args: - - --concurrent=1 - ports: - - containerPort: 8080 - protocol: TCP - name: metrics ---- -# Source: golang-external-secrets/charts/external-secrets/templates/webhook-deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: golang-external-secrets-webhook - namespace: "default" - labels: - helm.sh/chart: external-secrets-0.5.9 - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.9" - app.kubernetes.io/managed-by: Helm -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets - template: - metadata: - labels: - app.kubernetes.io/name: external-secrets-webhook - app.kubernetes.io/instance: golang-external-secrets - spec: - hostNetwork: false - serviceAccountName: external-secrets-webhook - containers: - - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.5.9" - imagePullPolicy: IfNotPresent - args: - - webhook - - --port=10250 - - --dns-name=golang-external-secrets-webhook.default.svc - - --cert-dir=/tmp/certs - - --check-interval=5m - ports: - - containerPort: 8080 - protocol: TCP - name: metrics - - containerPort: 10250 - protocol: TCP - name: webhook - readinessProbe: - httpGet: - port: 8081 - path: /readyz - initialDelaySeconds: 20 - periodSeconds: 5 - volumeMounts: - - name: certs - mountPath: /tmp/certs - readOnly: true - volumes: - - name: certs - secret: - secretName: golang-external-secrets-webhook ---- -# Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml -apiVersion: external-secrets.io/v1beta1 -kind: ClusterSecretStore -metadata: - name: vault-backend - namespace: golang-external-secrets -spec: - provider: - vault: - server: https://vault-vault.hub.example.com - path: secret - # Version of KV backend - version: v2 - caProvider: - type: ConfigMap - name: kube-root-ca.crt - key: ca.crt - namespace: golang-external-secrets - auth: - kubernetes: - mountPath: hub - role: hub-role - secretRef: - name: golang-external-secrets - namespace: golang-external-secrets - key: "token" ---- -# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: secretstore-validate - labels: - external-secrets.io/component: webhook -webhooks: -- name: "validate.secretstore.external-secrets.io" - rules: - - apiGroups: ["external-secrets.io"] - apiVersions: ["v1beta1"] - operations: ["CREATE", "UPDATE", "DELETE"] - resources: ["secretstores"] - scope: "Namespaced" - clientConfig: - service: - namespace: "default" - name: golang-external-secrets-webhook - path: /validate-external-secrets-io-v1beta1-secretstore - admissionReviewVersions: ["v1", "v1beta1"] - sideEffects: None - timeoutSeconds: 5 - -- name: "validate.clustersecretstore.external-secrets.io" - rules: - - apiGroups: ["external-secrets.io"] - apiVersions: ["v1beta1"] - operations: ["CREATE", "UPDATE", "DELETE"] - resources: ["clustersecretstores"] - scope: "Cluster" - clientConfig: - service: - namespace: "default" - name: golang-external-secrets-webhook - path: /validate-external-secrets-io-v1beta1-clustersecretstore - admissionReviewVersions: ["v1", "v1beta1"] - sideEffects: None - timeoutSeconds: 5 ---- -# Source: golang-external-secrets/charts/external-secrets/templates/validatingwebhook.yaml -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: externalsecret-validate - labels: - external-secrets.io/component: webhook -webhooks: -- name: "validate.externalsecret.external-secrets.io" - rules: - - apiGroups: ["external-secrets.io"] - apiVersions: ["v1beta1"] - operations: ["CREATE", "UPDATE", "DELETE"] - resources: ["externalsecrets"] - scope: "Namespaced" - clientConfig: - service: - namespace: "default" - name: golang-external-secrets-webhook - path: /validate-external-secrets-io-v1beta1-externalsecret - admissionReviewVersions: ["v1", "v1beta1"] - sideEffects: None - timeoutSeconds: 5 - failurePolicy: Fail diff --git a/common/tests/hashicorp-vault-naked.expected.yaml b/common/tests/hashicorp-vault-naked.expected.yaml deleted file mode 100644 index 51800147..00000000 --- a/common/tests/hashicorp-vault-naked.expected.yaml +++ /dev/null @@ -1,409 +0,0 @@ ---- -# Source: hashicorp-vault/charts/vault/templates/server-serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: hashicorp-vault - namespace: default - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm ---- -# Source: hashicorp-vault/charts/vault/templates/server-config-configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: hashicorp-vault-config - namespace: default - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm -data: - extraconfig-from-values.hcl: |- - disable_mlock = true - ui = true - - listener "tcp" { - tls_disable = 1 - address = "[::]:8200" - cluster_address = "[::]:8201" - # Enable unauthenticated metrics access (necessary for Prometheus Operator) - #telemetry { - # unauthenticated_metrics_access = "true" - #} - } - storage "file" { - path = "/vault/data" - } - - # Example configuration for using auto-unseal, using Google Cloud KMS. The - # GKMS keys must already exist, and the cluster must have a service account - # that is authorized to access GCP KMS. - #seal "gcpckms" { - # project = "vault-helm-dev" - # region = "global" - # key_ring = "vault-helm-unseal-kr" - # crypto_key = "vault-helm-unseal-key" - #} - - # Example configuration for enabling Prometheus metrics in your config. - #telemetry { - # prometheus_retention_time = "30s", - # disable_hostname = true - #} ---- -# Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: hashicorp-vault-server-binding - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:auth-delegator -subjects: -- kind: ServiceAccount - name: hashicorp-vault - namespace: default ---- -# Source: hashicorp-vault/charts/vault/templates/server-headless-service.yaml -# Service for Vault cluster -apiVersion: v1 -kind: Service -metadata: - name: hashicorp-vault-internal - namespace: default - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm - vault-internal: "true" - annotations: - -spec: - clusterIP: None - publishNotReadyAddresses: true - ports: - - name: "http" - port: 8200 - targetPort: 8200 - - name: https-internal - port: 8201 - targetPort: 8201 - selector: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - component: server ---- -# Source: hashicorp-vault/charts/vault/templates/server-service.yaml -# Service for Vault cluster -apiVersion: v1 -kind: Service -metadata: - name: hashicorp-vault - namespace: default - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm - annotations: - -spec: - # We want the servers to become available even if they're not ready - # since this DNS is also used for join operations. - publishNotReadyAddresses: true - ports: - - name: http - port: 8200 - targetPort: 8200 - - name: https-internal - port: 8201 - targetPort: 8201 - selector: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - component: server ---- -# Source: hashicorp-vault/charts/vault/templates/ui-service.yaml -apiVersion: v1 -kind: Service -metadata: - name: hashicorp-vault-ui - namespace: default - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault-ui - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm -spec: - selector: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - component: server - publishNotReadyAddresses: true - ports: - - name: http - port: 8200 - targetPort: 8200 - type: LoadBalancer - externalTrafficPolicy: Cluster ---- -# Source: hashicorp-vault/charts/vault/templates/server-statefulset.yaml -# StatefulSet to run the actual vault server cluster. -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: hashicorp-vault - namespace: default - labels: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm -spec: - serviceName: hashicorp-vault-internal - podManagementPolicy: Parallel - replicas: 1 - updateStrategy: - type: OnDelete - selector: - matchLabels: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - component: server - template: - metadata: - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - component: server - spec: - - affinity: - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchLabels: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: "hashicorp-vault" - component: server - topologyKey: kubernetes.io/hostname - - - - - terminationGracePeriodSeconds: 10 - serviceAccountName: hashicorp-vault - - volumes: - - - name: config - configMap: - name: hashicorp-vault-config - - - name: home - emptyDir: {} - containers: - - name: vault - - image: registry.connect.redhat.com/hashicorp/vault:1.11.3-ubi - imagePullPolicy: IfNotPresent - command: - - "/bin/sh" - - "-ec" - args: - - | - cp /vault/config/extraconfig-from-values.hcl /tmp/storageconfig.hcl; - [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /tmp/storageconfig.hcl; - [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /tmp/storageconfig.hcl; - [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /tmp/storageconfig.hcl; - [ -n "${API_ADDR}" ] && sed -Ei "s|API_ADDR|${API_ADDR?}|g" /tmp/storageconfig.hcl; - [ -n "${TRANSIT_ADDR}" ] && sed -Ei "s|TRANSIT_ADDR|${TRANSIT_ADDR?}|g" /tmp/storageconfig.hcl; - [ -n "${RAFT_ADDR}" ] && sed -Ei "s|RAFT_ADDR|${RAFT_ADDR?}|g" /tmp/storageconfig.hcl; - /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl - - env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: POD_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: VAULT_K8S_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: VAULT_K8S_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: VAULT_ADDR - value: "http://127.0.0.1:8200" - - name: VAULT_API_ADDR - value: "http://$(POD_IP):8200" - - name: SKIP_CHOWN - value: "true" - - name: SKIP_SETCAP - value: "true" - - name: HOSTNAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: VAULT_CLUSTER_ADDR - value: "https://$(HOSTNAME).hashicorp-vault-internal:8201" - - name: HOME - value: "/home/vault" - - - - volumeMounts: - - - - - name: data - mountPath: /vault/data - - - - - name: config - mountPath: /vault/config - - - name: home - mountPath: /home/vault - ports: - - containerPort: 8200 - name: http - - containerPort: 8201 - name: https-internal - - containerPort: 8202 - name: http-rep - readinessProbe: - # Check status; unsealed vault servers return 0 - # The exit code reflects the seal status: - # 0 - unsealed - # 1 - error - # 2 - sealed - exec: - command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] - failureThreshold: 2 - initialDelaySeconds: 5 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 3 - lifecycle: - # Vault container doesn't receive SIGTERM from Kubernetes - # and after the grace period ends, Kube sends SIGKILL. This - # causes issues with graceful shutdowns such as deregistering itself - # from Consul (zombie services). - preStop: - exec: - command: [ - "/bin/sh", "-c", - # Adding a sleep here to give the pod eviction a - # chance to propagate, so requests will not be made - # to this pod while it's terminating - "sleep 5 && kill -SIGTERM $(pidof vault)", - ] - - - volumeClaimTemplates: - - metadata: - name: data - - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 10Gi ---- -# Source: hashicorp-vault/templates/vault-app.yaml -apiVersion: console.openshift.io/v1 -kind: ConsoleLink -metadata: - name: vault-link - namespace: vault -spec: - applicationMenu: - section: HashiCorp Vault - imageURL: data:image/png;base64,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 - href: 'https://vault-vault.apps.foo.cluster.com' - location: ApplicationMenu - text: 'Vault' ---- -# Source: hashicorp-vault/charts/vault/templates/server-route.yaml -kind: Route -apiVersion: route.openshift.io/v1 -metadata: - name: hashicorp-vault - namespace: default - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm -spec: - host: - to: - kind: Service - name: hashicorp-vault - weight: 100 - port: - targetPort: 8200 - tls: - termination: edge ---- -# Source: hashicorp-vault/charts/vault/templates/tests/server-test.yaml -apiVersion: v1 -kind: Pod -metadata: - name: "hashicorp-vault-server-test" - namespace: default - annotations: - "helm.sh/hook": test -spec: - - containers: - - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.11.3-ubi - imagePullPolicy: IfNotPresent - env: - - name: VAULT_ADDR - value: http://hashicorp-vault.default.svc:8200 - - command: - - /bin/sh - - -c - - | - echo "Checking for sealed info in 'vault status' output" - ATTEMPTS=10 - n=0 - until [ "$n" -ge $ATTEMPTS ] - do - echo "Attempt" $n... - vault status -format yaml | grep -E '^sealed: (true|false)' && break - n=$((n+1)) - sleep 5 - done - if [ $n -ge $ATTEMPTS ]; then - echo "timed out looking for sealed info in 'vault status' output" - exit 1 - fi - - exit 0 - volumeMounts: - volumes: - restartPolicy: Never diff --git a/common/tests/hashicorp-vault-normal.expected.yaml b/common/tests/hashicorp-vault-normal.expected.yaml deleted file mode 100644 index d2af3d1b..00000000 --- a/common/tests/hashicorp-vault-normal.expected.yaml +++ /dev/null @@ -1,409 +0,0 @@ ---- -# Source: hashicorp-vault/charts/vault/templates/server-serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: hashicorp-vault - namespace: default - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm ---- -# Source: hashicorp-vault/charts/vault/templates/server-config-configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: hashicorp-vault-config - namespace: default - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm -data: - extraconfig-from-values.hcl: |- - disable_mlock = true - ui = true - - listener "tcp" { - tls_disable = 1 - address = "[::]:8200" - cluster_address = "[::]:8201" - # Enable unauthenticated metrics access (necessary for Prometheus Operator) - #telemetry { - # unauthenticated_metrics_access = "true" - #} - } - storage "file" { - path = "/vault/data" - } - - # Example configuration for using auto-unseal, using Google Cloud KMS. The - # GKMS keys must already exist, and the cluster must have a service account - # that is authorized to access GCP KMS. - #seal "gcpckms" { - # project = "vault-helm-dev" - # region = "global" - # key_ring = "vault-helm-unseal-kr" - # crypto_key = "vault-helm-unseal-key" - #} - - # Example configuration for enabling Prometheus metrics in your config. - #telemetry { - # prometheus_retention_time = "30s", - # disable_hostname = true - #} ---- -# Source: hashicorp-vault/charts/vault/templates/server-clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: hashicorp-vault-server-binding - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:auth-delegator -subjects: -- kind: ServiceAccount - name: hashicorp-vault - namespace: default ---- -# Source: hashicorp-vault/charts/vault/templates/server-headless-service.yaml -# Service for Vault cluster -apiVersion: v1 -kind: Service -metadata: - name: hashicorp-vault-internal - namespace: default - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm - vault-internal: "true" - annotations: - -spec: - clusterIP: None - publishNotReadyAddresses: true - ports: - - name: "http" - port: 8200 - targetPort: 8200 - - name: https-internal - port: 8201 - targetPort: 8201 - selector: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - component: server ---- -# Source: hashicorp-vault/charts/vault/templates/server-service.yaml -# Service for Vault cluster -apiVersion: v1 -kind: Service -metadata: - name: hashicorp-vault - namespace: default - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm - annotations: - -spec: - # We want the servers to become available even if they're not ready - # since this DNS is also used for join operations. - publishNotReadyAddresses: true - ports: - - name: http - port: 8200 - targetPort: 8200 - - name: https-internal - port: 8201 - targetPort: 8201 - selector: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - component: server ---- -# Source: hashicorp-vault/charts/vault/templates/ui-service.yaml -apiVersion: v1 -kind: Service -metadata: - name: hashicorp-vault-ui - namespace: default - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault-ui - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm -spec: - selector: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - component: server - publishNotReadyAddresses: true - ports: - - name: http - port: 8200 - targetPort: 8200 - type: LoadBalancer - externalTrafficPolicy: Cluster ---- -# Source: hashicorp-vault/charts/vault/templates/server-statefulset.yaml -# StatefulSet to run the actual vault server cluster. -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: hashicorp-vault - namespace: default - labels: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm -spec: - serviceName: hashicorp-vault-internal - podManagementPolicy: Parallel - replicas: 1 - updateStrategy: - type: OnDelete - selector: - matchLabels: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - component: server - template: - metadata: - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - component: server - spec: - - affinity: - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchLabels: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: "hashicorp-vault" - component: server - topologyKey: kubernetes.io/hostname - - - - - terminationGracePeriodSeconds: 10 - serviceAccountName: hashicorp-vault - - volumes: - - - name: config - configMap: - name: hashicorp-vault-config - - - name: home - emptyDir: {} - containers: - - name: vault - - image: registry.connect.redhat.com/hashicorp/vault:1.11.3-ubi - imagePullPolicy: IfNotPresent - command: - - "/bin/sh" - - "-ec" - args: - - | - cp /vault/config/extraconfig-from-values.hcl /tmp/storageconfig.hcl; - [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /tmp/storageconfig.hcl; - [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /tmp/storageconfig.hcl; - [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /tmp/storageconfig.hcl; - [ -n "${API_ADDR}" ] && sed -Ei "s|API_ADDR|${API_ADDR?}|g" /tmp/storageconfig.hcl; - [ -n "${TRANSIT_ADDR}" ] && sed -Ei "s|TRANSIT_ADDR|${TRANSIT_ADDR?}|g" /tmp/storageconfig.hcl; - [ -n "${RAFT_ADDR}" ] && sed -Ei "s|RAFT_ADDR|${RAFT_ADDR?}|g" /tmp/storageconfig.hcl; - /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl - - env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: POD_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: VAULT_K8S_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: VAULT_K8S_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: VAULT_ADDR - value: "http://127.0.0.1:8200" - - name: VAULT_API_ADDR - value: "http://$(POD_IP):8200" - - name: SKIP_CHOWN - value: "true" - - name: SKIP_SETCAP - value: "true" - - name: HOSTNAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: VAULT_CLUSTER_ADDR - value: "https://$(HOSTNAME).hashicorp-vault-internal:8201" - - name: HOME - value: "/home/vault" - - - - volumeMounts: - - - - - name: data - mountPath: /vault/data - - - - - name: config - mountPath: /vault/config - - - name: home - mountPath: /home/vault - ports: - - containerPort: 8200 - name: http - - containerPort: 8201 - name: https-internal - - containerPort: 8202 - name: http-rep - readinessProbe: - # Check status; unsealed vault servers return 0 - # The exit code reflects the seal status: - # 0 - unsealed - # 1 - error - # 2 - sealed - exec: - command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] - failureThreshold: 2 - initialDelaySeconds: 5 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 3 - lifecycle: - # Vault container doesn't receive SIGTERM from Kubernetes - # and after the grace period ends, Kube sends SIGKILL. This - # causes issues with graceful shutdowns such as deregistering itself - # from Consul (zombie services). - preStop: - exec: - command: [ - "/bin/sh", "-c", - # Adding a sleep here to give the pod eviction a - # chance to propagate, so requests will not be made - # to this pod while it's terminating - "sleep 5 && kill -SIGTERM $(pidof vault)", - ] - - - volumeClaimTemplates: - - metadata: - name: data - - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 10Gi ---- -# Source: hashicorp-vault/templates/vault-app.yaml -apiVersion: console.openshift.io/v1 -kind: ConsoleLink -metadata: - name: vault-link - namespace: vault -spec: - applicationMenu: - section: HashiCorp Vault - imageURL: data:image/png;base64,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 - href: 'https://vault-vault.region.example.com' - location: ApplicationMenu - text: 'Vault' ---- -# Source: hashicorp-vault/charts/vault/templates/server-route.yaml -kind: Route -apiVersion: route.openshift.io/v1 -metadata: - name: hashicorp-vault - namespace: default - labels: - helm.sh/chart: vault-0.22.0 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: hashicorp-vault - app.kubernetes.io/managed-by: Helm -spec: - host: - to: - kind: Service - name: hashicorp-vault - weight: 100 - port: - targetPort: 8200 - tls: - termination: edge ---- -# Source: hashicorp-vault/charts/vault/templates/tests/server-test.yaml -apiVersion: v1 -kind: Pod -metadata: - name: "hashicorp-vault-server-test" - namespace: default - annotations: - "helm.sh/hook": test -spec: - - containers: - - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.11.3-ubi - imagePullPolicy: IfNotPresent - env: - - name: VAULT_ADDR - value: http://hashicorp-vault.default.svc:8200 - - command: - - /bin/sh - - -c - - | - echo "Checking for sealed info in 'vault status' output" - ATTEMPTS=10 - n=0 - until [ "$n" -ge $ATTEMPTS ] - do - echo "Attempt" $n... - vault status -format yaml | grep -E '^sealed: (true|false)' && break - n=$((n+1)) - sleep 5 - done - if [ $n -ge $ATTEMPTS ]; then - echo "timed out looking for sealed info in 'vault status' output" - exit 1 - fi - - exit 0 - volumeMounts: - volumes: - restartPolicy: Never diff --git a/common/tests/install-naked.expected.yaml b/common/tests/install-naked.expected.yaml deleted file mode 100644 index 7272f0eb..00000000 --- a/common/tests/install-naked.expected.yaml +++ /dev/null @@ -1,64 +0,0 @@ ---- -# Source: pattern-install/templates/argocd/namespace.yaml -# Pre-create so we can create our argo app for keeping subscriptions in sync -# Do it here so that we don't try to sync it in the future -apiVersion: v1 -kind: Namespace -metadata: - name: openshift-gitops ---- -# Source: pattern-install/templates/argocd/application.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: install-default - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: in-cluster - namespace: install-default - project: default - source: - repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-default.yaml" - # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: install - - name: global.hubClusterDomain - value: - syncPolicy: - automated: {} ---- -# Source: pattern-install/templates/argocd/subscription.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: openshift-gitops-operator - namespace: openshift-operators - labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" -spec: - channel: stable - installPlanApproval: Automatic - name: openshift-gitops-operator - source: redhat-operators - sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: install-default,openshift-gitops diff --git a/common/tests/install-normal.expected.yaml b/common/tests/install-normal.expected.yaml deleted file mode 100644 index d115d8ae..00000000 --- a/common/tests/install-normal.expected.yaml +++ /dev/null @@ -1,64 +0,0 @@ ---- -# Source: pattern-install/templates/argocd/namespace.yaml -# Pre-create so we can create our argo app for keeping subscriptions in sync -# Do it here so that we don't try to sync it in the future -apiVersion: v1 -kind: Namespace -metadata: - name: openshift-gitops ---- -# Source: pattern-install/templates/argocd/application.yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: install-example - namespace: openshift-gitops - finalizers: - - resources-finalizer.argocd.argoproj.io/foreground -spec: - destination: - name: in-cluster - namespace: install-example - project: default - source: - repoURL: https://github.com/pattern-clone/mypattern - targetRevision: main - path: common/clustergroup - helm: - ignoreMissingValueFiles: true - valueFiles: - - "/values-global.yaml" - - "/values-example.yaml" - # Track the progress of https://github.com/argoproj/argo-cd/pull/6280 - parameters: - - name: global.repoURL - value: $ARGOCD_APP_SOURCE_REPO_URL - - name: global.targetRevision - value: $ARGOCD_APP_SOURCE_TARGET_REVISION - - name: global.namespace - value: $ARGOCD_APP_NAMESPACE - - name: global.pattern - value: install - - name: global.hubClusterDomain - value: hub.example.com - syncPolicy: - automated: {} ---- -# Source: pattern-install/templates/argocd/subscription.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: openshift-gitops-operator - namespace: openshift-operators - labels: - operators.coreos.com/openshift-gitops-operator.openshift-operators: "" -spec: - channel: stable - installPlanApproval: Automatic - name: openshift-gitops-operator - source: redhat-operators - sourceNamespace: openshift-marketplace - config: - env: - - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES - value: install-example,openshift-gitops diff --git a/common/tests/operator-install-naked.expected.yaml b/common/tests/operator-install-naked.expected.yaml deleted file mode 100644 index 6b4cddc7..00000000 --- a/common/tests/operator-install-naked.expected.yaml +++ /dev/null @@ -1,29 +0,0 @@ ---- -# Source: pattern-install/templates/pattern.yaml -apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 -kind: Pattern -metadata: - name: operator-install - namespace: openshift-operators -spec: - clusterGroupName: default - gitSpec: - targetRepo: https://github.com/pattern-clone/mypattern - targetRevision: main - gitOpsSpec: - operatorChannel: stable ---- -# Source: pattern-install/templates/subscription.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: patterns-operator - namespace: openshift-operators - labels: - operators.coreos.com/patterns-operator.openshift-operators: "" -spec: - channel: fast - installPlanApproval: Automatic - name: patterns-operator - source: community-operators - sourceNamespace: openshift-marketplace diff --git a/common/tests/operator-install-normal.expected.yaml b/common/tests/operator-install-normal.expected.yaml deleted file mode 100644 index dc07be36..00000000 --- a/common/tests/operator-install-normal.expected.yaml +++ /dev/null @@ -1,29 +0,0 @@ ---- -# Source: pattern-install/templates/pattern.yaml -apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 -kind: Pattern -metadata: - name: operator-install - namespace: openshift-operators -spec: - clusterGroupName: example - gitSpec: - targetRepo: https://github.com/pattern-clone/mypattern - targetRevision: main - gitOpsSpec: - operatorChannel: stable ---- -# Source: pattern-install/templates/subscription.yaml -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: patterns-operator - namespace: openshift-operators - labels: - operators.coreos.com/patterns-operator.openshift-operators: "" -spec: - channel: fast - installPlanApproval: Automatic - name: patterns-operator - source: community-operators - sourceNamespace: openshift-marketplace diff --git a/common/values-global.yaml b/common/values-global.yaml deleted file mode 100644 index 8a890f3d..00000000 --- a/common/values-global.yaml +++ /dev/null @@ -1,16 +0,0 @@ -global: - options: - useCSV: True - syncPolicy: Manual - installPlanApproval: Automatic - - git: - hostname: github.com - # Account is the user or organization under which the pattern repos lives - account: hybrid-cloud-patterns - email: someone@somewhere.com - dev_revision: main - -main: - clusterGroupName: example - From 75e6b4bb274ecd446a5cb274ea80a8ba3d7b0b00 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 5 Sep 2024 15:32:07 +0200 Subject: [PATCH 1263/1263] Simplify testing workflow for now --- .github/workflows/linter.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 316e1c48..a574e3f9 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -52,17 +52,12 @@ jobs: run: | make common-test - - name: Run make test - run: | - make test + # Disable tests for now, as it needs quite some love + # - name: Run make test + # run: | + # make test - name: Run make helmlint run: | make helmlint - - name: Run make helm kubeconform - run: | - curl -L -O https://github.com/yannh/kubeconform/releases/download/v0.4.13/kubeconform-linux-amd64.tar.gz - tar xf kubeconform-linux-amd64.tar.gz - sudo mv -v kubeconform /usr/local/bin - make kubeconform

      e9?vX&2{P`z*yEQ@&1J8iL1Z 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 Ls1SjK0N4Zo6jePG diff --git a/hashicorp-vault/charts/vault-0.28.1.tgz b/hashicorp-vault/charts/vault-0.28.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..ef0f05f52e28759a71fd4a2f08aa88d239748ec8 GIT binary patch literal 49807 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 z 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 N=){|%&2WQ-0RUykeRBW+ literal 0 HcmV?d00001 diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index 691f7094..6029a2f0 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -48,4 +48,4 @@ vault: termination: "reencrypt" image: repository: "registry.connect.redhat.com/hashicorp/vault" - tag: "1.16.2-ubi" + tag: "1.17.2-ubi" diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index 8156e2af..a17a6974 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -43,7 +43,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -64,7 +64,7 @@ metadata: name: hashicorp-vault-internal namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -96,7 +96,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -127,7 +127,7 @@ metadata: name: hashicorp-vault-ui namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -348,7 +348,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index 8156e2af..a17a6974 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -43,7 +43,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -64,7 +64,7 @@ metadata: name: hashicorp-vault-internal namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -96,7 +96,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -127,7 +127,7 @@ metadata: name: hashicorp-vault-ui namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -348,7 +348,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index 8156e2af..a17a6974 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -43,7 +43,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -64,7 +64,7 @@ metadata: name: hashicorp-vault-internal namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -96,7 +96,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -127,7 +127,7 @@ metadata: name: hashicorp-vault-ui namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -348,7 +348,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index 8727ef0e..c3950b6d 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: default labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -43,7 +43,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -64,7 +64,7 @@ metadata: name: hashicorp-vault-internal namespace: default labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -96,7 +96,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -127,7 +127,7 @@ metadata: name: hashicorp-vault-ui namespace: default labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -348,7 +348,7 @@ metadata: name: hashicorp-vault namespace: default labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index 8156e2af..a17a6974 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -6,7 +6,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -18,7 +18,7 @@ metadata: name: hashicorp-vault-config namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -43,7 +43,7 @@ kind: ClusterRoleBinding metadata: name: hashicorp-vault-server-binding labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -64,7 +64,7 @@ metadata: name: hashicorp-vault-internal namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -96,7 +96,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -127,7 +127,7 @@ metadata: name: hashicorp-vault-ui namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault-ui app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -168,7 +168,7 @@ spec: template: metadata: labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault component: server @@ -206,7 +206,7 @@ spec: containers: - name: vault - image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi imagePullPolicy: IfNotPresent command: - "/bin/sh" @@ -348,7 +348,7 @@ metadata: name: hashicorp-vault namespace: pattern-namespace labels: - helm.sh/chart: vault-0.28.0 + helm.sh/chart: vault-0.28.1 app.kubernetes.io/name: vault app.kubernetes.io/instance: hashicorp-vault app.kubernetes.io/managed-by: Helm @@ -375,7 +375,7 @@ spec: containers: - name: hashicorp-vault-server-test - image: registry.connect.redhat.com/hashicorp/vault:1.16.2-ubi + image: registry.connect.redhat.com/hashicorp/vault:1.17.2-ubi imagePullPolicy: IfNotPresent env: - name: VAULT_ADDR From 0b45eeada9359aeb447301799206859228dc4f2f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 15 Jul 2024 11:20:54 -0600 Subject: [PATCH 1233/1263] Update ESO to 0.9.20 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.9.19.tgz | Bin 76457 -> 0 bytes .../charts/external-secrets-0.9.20.tgz | Bin 0 -> 81759 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 505 ++++++++++++++++-- ...-secrets-industrial-edge-hub.expected.yaml | 505 ++++++++++++++++-- ...ecrets-medical-diagnosis-hub.expected.yaml | 505 ++++++++++++++++-- ...olang-external-secrets-naked.expected.yaml | 505 ++++++++++++++++-- ...lang-external-secrets-normal.expected.yaml | 505 ++++++++++++++++-- 9 files changed, 2279 insertions(+), 254 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.9.19.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.9.20.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index b304d092..802979b4 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.1.0 dependencies: - name: external-secrets - version: "0.9.19" + version: "0.9.20" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.9.19.tgz b/golang-external-secrets/charts/external-secrets-0.9.19.tgz deleted file mode 100644 index 66fffaa0b08d686867fc9e4307204eaae1e22964..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76457 zcmV)gK%~DPiwFP!000001ML0#cH2glFpBrzehM6BX4(0EB565})1TMLH={^STJ4*b zq}Zp|tXXl9ghWIjzyUzXN;;2qUf?|0Z|_|f6z)|dNYS0bn(0_Xp)R|s_Wk02&io`| z;V|{5iJzt8zx4k+h3C6(zs3I^e)sKD{lD`0%i;4E-#mTs^y#w5o%K7M)skLN7OM)zzH_BIA?@Qdfq8~6Xar-!)z&%S%{ z;+yZD;{LxleERG!-qYS@(DMAr_n$3;4}OvcarDx=efEegm-(MhNB?_t_`e^`{B)WG z%Zz?}CC-b-&x^NU5xe#mew2AvapJvRP2jbk`RSv1yzpOo^DJAYFURBSAe*lyqiMVt zSB_lyheyE_hmxNr>~6$AU!{HmuQC|Ttf%pU1<{zLDZNbNVHjV-|FGoRA1$NnM>qcc zU7XC)myd?tjhgzfGNGX-|Anm-gdYE&@4Z6+(dg;$>ElN@RtEnMZ?Qig{tjPyH+~of z(PGMOSC{_@quDEpjkIz|`%e{uMtg#W*Lad_zP|1O^L{(Ezl zdK2G|yqUiY<9mPRrK{=GPt&Va7~Xq9M2IV0vZ?R={<|lSi#I6uG*ayb-pKpokN@k@ zqtnO(Z0jdp7Q-0V(AO#rqH9G|F8G%R-hI6C?t(D%A|ECItpS01tEI<0-sB>S6aNv= zo8u7n&rh^py#(<5DuLNOL4xFeW-vkjz?(=kM-`p9zA;W7CQ(c z_;5kk)`S1c=Ds%zuCDw9i5w#2)Qhk5FH?_2GjAT>;Y@)(T?N-G=m$PrJeslymI}0S z1x;YVFg5QdB4#fUd?U_o{S9a(fM`=>-oyTD_x~3-?@ORIqineuFW7$n51&7O{$26@ zKYRMEyZ^g+e*b;^zaG8%yu=fXCmk;>9&h~jl+Na(|22k_`TOrfzzA2s-89_vfj7*X z;pLKN`yKW;`oO}K&s)9+7A6U1zV}ZrOIFd;d+`nZGgw@#uC9X5-e5TJL|;Bk3;y|t zrg@C_*N59HN8l*we^x9Et^z<5z$eiHMvp%Fyl-ls;Rv`G#B5UrT%?!ch0B>$zHI0t zoWW&=yBh|JAmgYfn+K`)1keY94z3hXG0pvL8&i3sQEdo|0h3vCwy;m>Ib)k_V@rd6<|yP z&;sBS4D&{uilTg!8eSwpGHdo(y=~g)#s)gT3J~Ix>{z+DciMqQY#{^@Jj`^c#SP)D zLN@V3lY>po^VODjQ$J|@-$M9;olF3Z{Q7n8Q7zfc(H(vXsRZg!yEFs0x4LG z()pOCPn0}}rr~PVvcwUeL`%aIHf9P8#TzluNV`X3^+!rR$9I75M!DEJ3gWSn4s+f@ z`{bX1E90!Z-_>5YP(K?^?s-S@#9a8vZIGXrJK$r~*_#qH`P9Wqv-lru5$4ONJzvcL zAKv+sc^uzt9q?)(w*j?q87_RdPSfHISRbG@%jNyQ^akR?!A_Pp^^Y&GS2n6Rs%*LGLl;=OJoj%tcwjNQ=J2osI*t3j9Q@^

      Jg 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≺y@%7)v?JCkQ+ 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 N{(l|wY?%OX4FHLa0%VtrcH1 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

    Jg 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≺y@%7)v?JCkQ+ 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 N{(l|wY?%OX4FHLahz!` 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 P`~qK}8u$WXLIC+c2~34q literal 0 HcmV?d00001 From e7209915c0477923ab6b7f166449872594fb93a1 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 31 Jul 2023 15:44:58 +0200 Subject: [PATCH 0947/1263] Tweak ESO UBI images Tested the ESO upgrade on MCG on both 4.10 and 4.13 --- golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 80 +++++++++---------- ...-secrets-industrial-edge-hub.expected.yaml | 80 +++++++++---------- ...ecrets-medical-diagnosis-hub.expected.yaml | 80 +++++++++---------- ...olang-external-secrets-naked.expected.yaml | 80 +++++++++---------- ...lang-external-secrets-normal.expected.yaml | 80 +++++++++---------- 6 files changed, 188 insertions(+), 218 deletions(-) diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index ea7db53d..0030eda3 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -11,10 +11,10 @@ clusterGroup: external-secrets: image: - tag: v0.8.3-ubi + tag: v0.8.5-ubi webhook: image: - tag: v0.8.3-ubi + tag: v0.8.5-ubi certController: image: - tag: v0.8.3-ubi + tag: v0.8.5-ubi diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index c1a23515..1452df28 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -7771,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8031,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8222,9 +8222,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8252,10 +8250,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8282,9 +8280,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8300,10 +8296,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8330,9 +8326,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index 7ae2a78f..f0314907 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -7771,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8031,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8222,9 +8222,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8252,10 +8250,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8282,9 +8280,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8300,10 +8296,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8330,9 +8326,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index 7ae2a78f..f0314907 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -7771,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8031,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8222,9 +8222,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8252,10 +8250,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8282,9 +8280,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8300,10 +8296,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8330,9 +8326,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 518bda17..063464e7 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -7771,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8031,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8222,9 +8222,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8252,10 +8250,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8282,9 +8280,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8300,10 +8296,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8330,9 +8326,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - webhook diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 7ae2a78f..f0314907 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -7771,10 +7771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7838,10 +7838,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -7947,10 +7947,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -7987,10 +7987,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -8031,10 +8031,10 @@ metadata: name: golang-external-secrets-servicebindings labels: servicebinding.io/controller: "true" - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8052,10 +8052,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8072,10 +8072,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8108,10 +8108,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -8147,10 +8147,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -8168,10 +8168,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -8192,10 +8192,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8222,9 +8222,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -8252,10 +8250,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8282,9 +8280,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -8300,10 +8296,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.8.3 + helm.sh/chart: external-secrets-0.8.5 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.8.3" + app.kubernetes.io/version: "v0.8.5" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -8330,9 +8326,7 @@ spec: - ALL readOnlyRootFilesystem: true runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - image: "ghcr.io/external-secrets/external-secrets:v0.8.3-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.8.5-ubi" imagePullPolicy: IfNotPresent args: - webhook From 4cbef5e8fd0c50cb9ec49df389898eab79f03660 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 31 Jul 2023 20:25:14 +0200 Subject: [PATCH 0948/1263] Upgrade vault-helm to v0.25.0 --- hashicorp-vault/Chart.yaml | 2 +- hashicorp-vault/charts/vault-0.24.1.tgz | Bin 45763 -> 0 bytes hashicorp-vault/charts/vault-0.25.0.tgz | Bin 0 -> 46359 bytes hashicorp-vault/values.yaml | 2 +- ...ault-industrial-edge-factory.expected.yaml | 20 +++++++++--------- ...rp-vault-industrial-edge-hub.expected.yaml | 20 +++++++++--------- ...-vault-medical-diagnosis-hub.expected.yaml | 20 +++++++++--------- tests/hashicorp-vault-naked.expected.yaml | 20 +++++++++--------- tests/hashicorp-vault-normal.expected.yaml | 20 +++++++++--------- 9 files changed, 52 insertions(+), 52 deletions(-) delete mode 100644 hashicorp-vault/charts/vault-0.24.1.tgz create mode 100644 hashicorp-vault/charts/vault-0.25.0.tgz diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index 6df9f5ec..649bd4e2 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -6,5 +6,5 @@ name: hashicorp-vault version: 0.0.1 dependencies: - name: vault - version: "0.24.1" + version: "0.25.0" repository: "https://helm.releases.hashicorp.com" diff --git a/hashicorp-vault/charts/vault-0.24.1.tgz b/hashicorp-vault/charts/vault-0.24.1.tgz deleted file mode 100644 index 94b385007a0493996ce6d1de00b03472a10e8937..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45763 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 diff --git a/hashicorp-vault/charts/vault-0.25.0.tgz b/hashicorp-vault/charts/vault-0.25.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..62e685dceb5a7e34cc94e8b4db992a70f0335332 GIT binary patch literal 46359 zcmV(_K-9k4ANQ8Tef0*$ND-Dq?-R+6LHomUT%Hps`rY2)D|GO%&Emy_S~RL)1Yh_;4^toBtlnJ)Qr-;eP+-^5+pvy1o%{RyHbYFqB>%xOZAC+sDHt!u zT(uX@ZCfVG&VmAz0bqyYvMJqnX8pd>@AhQ{10{Jk`}^SFK*2K5pkD8t^!M9$gM)X6 zxcjc(VQsh>o9a?B*XLwTZhqDz`L1o~^Tr)s-xE`TacLn28va^Xc1+b?!%wWs=9=}6 zzc;aM!LOyHOLM%$I?XOQHd#t;9#x;2l4YBYY&#}y+&Q<{AF|i~cn`zY@3ynmV6(CEdev?B4*(G!8^`OEp7@%v z@zT&F8(R+|32e^)2k>9b|Eoyi{9j&8N0YYw#V)aq9RA-u81w}Ge=va0HUF<5HKq$m zze8){pcyfNN5ODtnySx5M*TmH#wEE&_Cm5*?uE3%NWq3i)>z?L-m(wYf;gIjG(zqW z0agG-AS012M0Dg>0J_0B|2?o1^eb>dQ^$aMNLop%Cf#ZnEwG7?8;uNcphE$68jZ&9 zz#fqa6C+s|sTQKFQ$b(9qV_d^*}jA~=&!%pXi5;gF?F;gCPvaNactH$xAa?Nn`@{Q zp&t?DkO)S?cB~{=L7q9bDaTi5*s>FIK6U0Nu7AqNL4MJJr4xOo&XOOvb0bwa!%;wgk9!rT}iJ zGZmZ1=wf`{>VtguQ->n@*RL();Sp*wylMkBjdZ%Cl_qMV7G$Hj)ESa=1-%J<-a~Kj zpYZ0hq&e84AKDBH+n9ex-;hlvwh7c@4>duC#?3tjFR~5kHSSbhIYuW8buT2NvBb8d zNVY`xHe9NPMJl#(=4cxINZa_=UlCb0vKCwzmLbU;P-L~U*PJ*26~h|aPa#Go+Sa1O zc2=yEs>_*h#hTe5x31z~UuS+MEJ~k#;?)<>@u^&5Y8zT}Mkzu(?us zZ@tmwL)2c#+A;dZz_CW6uk1HX?$mp8JlDvrq_y!E8!D7E#uJ8$VtTEisVTt=Y*|h8 z9yMtL^2;_O^}#gYEP%p@TCm2wF$Knxi!1FkBxqYJ`8d>I3kD3E><^4v+aRVLZ!4?` zx2b42`VYDcUF4kC`3Q(cR53Uqlv>?}EB)&AL%k zi*efbh^{5K&QK#Bn=I9-qLjq{VMzd{nI_S2DLTPC{a+XMh64ZZ_V;W4UqyN({vY}h zW8>znB)28UUJz6LFH@ei@7^(X6fk&>q1S++yR-NZx(mGfnXzKi(YT@zM)MbwI0oxR z3pFY22L|D366rga6G^g69No4$Ho3z(d;ut>Nn3?o=`h3`7$(EnRlZXk2-#s>1} z|NeoX|LMc4gPQ(Vk)A#M|CwWGscgW-`e)7!pd;6KM%(W3gN(6-TyfS~0z8LkHgU)~&z5x*+=|z{D=#I{e?`Je0*k#Yi-2tU-y0nE`-1-eXfUYd|4Pymr2jze ztbo*?GGv@!duIy2mHO_P5&e*!4TY7diZ_Z>XJ$(0tcPsm1O1ateuw2=px8I0!_)vC zpn(s1i2Qri1MmFuQmH_1Jj6=EHBV4R?qVTNN$Rrn!YtC{%7K zefgvZ$4@Q<;9vA0>=Vm-><&~kYw@NfR}oSBSumZlO-x?ZQnKa6c?c8B!o_W{(VI-S zX`zM46qAuwX0}sYuGtOUZsDY@0fQ(h*q^Zi#6kh?zl#8f;y-^QtN{utj{P*Nw*nH2 z1t{eiADs~0hBi@vAs~q;ZqJtFs6hr2W!h*gUdHMubs=M5dJp1zy8#fzd zNi#NXX}NWxU~e@Q$S%!hst)~(WR+o%B~=He6d-mBZMWbO=haupM5^5o1^iaVfT&C0 z6~~cN6AFsGhPn-6sW3WgdeD)W#g^ug6ABmN7$$H`G*w>5(1n*X{0!0dNaj2=l6*8< zEx;jzsJi{W;|*EIJ&7UmT`aePZ=Lyjk>t{s(Yg|s!eS}O6K19-5(^JJ&C%kPd@VYm zVoli)l8&1)Ah!yl5HEX&*YV22>o2w2jCc)U@RRZ;5WFCNK!j9hdhjc@g`PA*;bVR-cHHR{I0K7J*oKFa(K;{h(HYt!i!Q2 zTqLgu$Z}-h(N@vUirGbQwz|Z`v5Peyqf#guoE^22x&< zG>zPkO?9Pec#cPwENK#Va+*mX)I~wT(!SEQH3jtn@>^>Q@H7D^tMijZ)vCR2H~z^z zzkzSzkNHBFV80knVVNfvZ7ET_<(X4+c%4n?Uios^1_IZn;#Psx6MMyhCFly0rQijz zcsHXQjCDL!rRagOPF3@P*g^`r$euxMw7q3m)E37NsKZ`E*Q0G%VnuaeMP0xsnd|fk z%D|c!k4;6Ei2g4N9U}XCpTTkE0b@o1oWn;ws7yNlE6woFO?mbohx?-c&*7lotIz+c zNKf$muYmnWIfj4VrNtlmquKm-BW&*nZ(R3e_JLdY!OJ|2! zg;%TifJXJ7Av*a3dd)2qU(=o6Qne`dSV(3^Ea;Z{by>jgWE)WIcJ23)qY9Mvyvw44 z&iu+uJi{i2>FP*qW7neCsI!0n+o9;$`GIAj+_YtrmOs{8&IIoVysMrKq|yIU4FBAe zYya0f5bZyX4r=?4O49SC|D_rJy>yGeT}=II8^33slIVZbH(+-bkVpUf`!WBY!BK7h zT}gU^^xvoQ0u`z4|F*aPi<;+bW(1f~b_+{D0b1N3s;1{vCXxQD`hS?&R7=LDZMilp zJF_-|d2{`Y#lI{sTF>0#-=hfOI=K=>RB5E0gBdwJVH^MI_5xOHDc;EQYW7cNQ$c|H}PDxY%@Hnac%X|p|ZI? zzsI)=LhksXt>YQu2Rdw5sn3oSyx%ljjsI{@y;=MxgFivTYeD~!QxW>V>sdg)|Gyaj ztv~4ZYx-YB`i|)TE(Zbv8z}KO;89sXl_3B!IBAIo1hL^Pc0^D!nv|4E|9NOv8pr&B z<9~wmf7BKA|3}^W{I`w zkt^i;{`j7vEMk!wSEKFhyPn|_o-jJw`h+KRR5+dRyJopg`@Yja74GkeUESURKC!zY z+tkqckl*i(oZ}NqZRs8F!`je2J}8&u!JVAdCI->Sd_6I!x%vsz1kG1s@<&slnefmCOp z;V+}h>CM^YKSw9ioAa~DbaW}Wc?53GfU&wgL(SvAt(pn=OudoQe0rnRWV;?EjgRkm z^bIL^#)4JDFw97*6LF-%Y@hZ=@+VKCD%;haS^WF+X3w7FPt%V#HZ&+g3)@jD+q?&nGu0qwDF-hqLpv4l#!_S1{?Y zS~^M1=JnHm-P)5q1Y_0zI{Lk|+VAi>vGVkIA1RFjG`dQ~pbeEPvw874Z$4g4rjwFt zhV}M#n?k88xLjzI9&EkZ^IS|*n276O8sZ{UKvyJFDaJ{zN1v~L9o<}A-b_Aznx0<$ zD}`GoQUMdM$Q{1YCkw|`$h{tF2J=SETk7fJRz`~_Bl`czr|Yxn?>8q`m($U2XN=&jtnx&7m&1$EWIQ}cq?R8{vJ~S;AT(34rns8r#Bbb%*V`$#ePznlYx89M z2^Px#eHu*)_SkTF~tLR*%LXVSQf@&vzcLgz9&qRw>ZE5!$3{yG={1 z>O$R0Y|`oI1Cvmvql@wRaJsRO>bJ?~^Th-?w>rvUKAin>Gy3gpO1b1UBkUiBXXl@; zcW(>7D+;zbs(qzj14meY;L&Tg#Ag96y+lrRJtD7Rz_ED-`)U)~S&)&rV=$Qxr)MXW z>q7To2renbKLgHt{drg*+(rqFExs(Gdp)Kf9O*07Bsx{kf0j%uzSYkRz~Bwd`0DhA za;t}BRH3Y?;a^;7RQS~Y$N%<1uO2QWSEBAOVXuo2-)SGCkJITmx(CP%>J*4IrH1lK z0lQOhrxcjm2Y5;WxI>Vq6o`)$-m5y~DLE`y8r*Wwr?dcJX%Ndmpu(ZT5WT+Cv>oF) z<4?E?esa*S?#8Yjeocg%*Gs7j(;mTm=lYuSII9}wjjC!iMzV``R0X;3G|u!{p>|gb#B!D0d4?sTUUOIRCnE*UrEA(m4scO_k!MHhcE2 z390r+F1Z=TECJmQ?&Y*0KkW8`BCHGu+Xhy^n&j>kLjk4V#82E@fO~uGNcPYPYOF{! zquYPR6N^?A${ap19(|Ey+dnR*W@w|?jJZspOWnugXX8YrInN zy|>D!^B*juw}3Sq$(ges@LEj;oZ$%V2>`CKO|U`Q605^U_r(M?o(%UQ3C6MfGfr9h z+0hl@iZ0Qx#LWX^^QS}X;N~yxI_Yf>iQ!p&(qTEd9u21>Iy9fgr}Wpq{g%XI|M~BQ zfepSz{4GH;vD`3#Km&=Ha8B%x%)}MU_5cgA&>*3J+s)XQW4gg~rKSymX04ip7SKa( zzO9YyYEx(eVeUhdkS%Si_Ub7orOJOgH>7Rxz1dDaK)L(>p1A+-9n|t)HR-9!e_lO<)!| z(QPHLEA-X(j|kso(Mr0<#xRLZWTG9T>B%_4a$YnSNu~dWWt0^NKG*-hKM?&t`v*rg z{jVZDBl-^*>DpBmU;qypL|)jpK)be5EpM}DuXdB37((VXmk3{r!D3$1&5P?i#ImT^ zdL0ULPSYQ+9ScWFvhndK{>H2~F&f#+B;;GBT>4)^AoyJQzc2d#_YS)?{jVgwaQa_j z%$V<(4Fq9iwu%^^I8a3b(gbm1D0fJS9JBSltqx`RDx3b7bq7=q{qGNY;{8uY`*r-^ zYSQ;g|9z7ZenjWnl}ua-^%2xRPlTE$%@ZBkcdqvYLnsRUPC{(z{%jB3KH*#vUn z71icr|1wOAzBUQ5+l?YOW=4$iTETsT?z4CGpZjQ-gbqGd+-GiP=RQy`$a~3DhU2q_ z5H!N8jlMh^pG04b&rX9E!F8Mrs^41{jmQs)!20Ou=w1cp<=Cvh!Dv}+|1gOj30wAc z=w4A~S#z%H&KIwS=|Z(!v_+C-vD*e|Z7hrklAvzK0)$6H!x)-NVj@$5&on-gq@&4* z!3x*NY8&d*hSzcoL&MbhLekuQTvF5cJ4v4hhhIOS55&Ax6(lKCPGSvWy_{1pZC~NG zRADj$--o62Y_RDPKVBIp4%0>BhvAI}_icCKn8rl3}?SL-D6sXleH<#o&b> zl3I*IEd;4%kB*_Zr3*C6HP%RKMYK{ws8oUBptU>&K9ouSOS%Lym;Mjp@&D`lf2&C~ z{ok7Ym#8}w*gz0|`Xz9JN^Y$zz;_>3+vpYD;5y4Hm_jQ3_d=+Zum#As{}SW>15c>w ze}%b1V}q&t9_WKpy=s;}YOJ`rjSI&i@bk_4!XF=~>bLU2Xz?nF0PYSwPkuyf2m@ zzOW?Hf5RlOx)#{6TES1ItlORiIVhF5!BG5RL#?dG2Nr&b8zvahu zAhZw+6S+q)0{R@c(NCSo-SR&M5Rc!ZqO%_q3V{NA{W0-_>jN437{2K~lrbCt+~h-Y zjDos@e4LwlJFK~X2;V|!(-=b^!7nu-!bPF=7LO_Pa(6#<%HaR5l;~2%2aNyG-533T z`tVru|0+_c{9ojNOQPUlMJcAde{%P^|hY6aM zTDFMCZD3c+mL+N~q|o>g4nI+v-ZZEFgZ*3D2zR{9>S_>|TsR_+b!L_q54={LqZ+ zfEXCn4LcoCE9YQcWK%Nmlm|iC1W=@40F8$-&1(EN1Xzvz`<2{ys;gedT3g;_ z6YdvNTKc#!WA}%IoR_2MWouS6-U`e%HDbn-JJkU4rp~;un28ZRd;|=6+PS`V^<}wr z66HU>Hp;yHJzxKSa472k`@P!#ubT9r@*hX0p<^#jVd&EVzN{Rkf(v|e?N=&|r{yUT|OT4V!HTS3GT8f8PJKaJroR~z$sF+pH6Lm94 zqyJ3eX!+*04|)E(-#d!Me;U;GpVg%5_W%5*U+ir^yn98Padwbk4jPnG@T%#Be-ktc z-sPX{_C1a0xXtl5ra)*fB>z+Dne&P7gSHLz=HRzFX8sOoyEl65E+Ch-(C|(zS5>f( zVXOD8)$q5ebXp@@)xGd@FvHC~@0`1ZezeyHrYZya_iBdT&<*_;Vb{k&?77n)6y{rF z4g%m)J(0gXR#b~3QJ)+9RjiqQ9&Qa&`6~J0X1ee)n(8)?`~hu zC|Vqr1oSqgnVoDL&2MDf%Wk2+K&ot+Yqp)&Fb1GN>jE`IRD~f}n3vRKsBekQIYw_3 zRkndng^{MoaAq$XCByxqm5fbn+v|T?L=VDc4&oi!z2Xh(jErc)!zMo;oAuS{RjT|4 zNF)>Zu`a_8G&la+VPBO0dbR&&HK}6xFI~cmDW($wK}2tN_cmkhbH_8RH6p^yqx%O#akRRU|RNSZu3?bNd*Gi3EI1}j5P1_ zo8SGSA~;thbaO?=Y_6y{%fFT;_k7Z7mw)EM|Mye@&-DM>$m9R@t+lNu{QnrAKOX-7 zl2yz1)hU-g>nA0}V+Nw;2>xfGlzyUOVf&RZW z4|`BUTi46g_M57?!zrGX6`Zuw+Vz6##MjBlM0qAI67UsJit9d%f=TR#08JD98+*8= z?Z>Eg`9#LMP92Pa|MA_>zwyHeZ@=^aN4?9o+e|O`J|2A24`Kl&GZ+G$B9&GDp7s7K z@V}JeSWE)A8Tfy*(^<>sf7{-AlK&p%v+(-A!`M^+D-`Zw2J_x#Vi)BrSPgPi<14Z@ zVukS%M`F{In7gBVfS5y3M=hYxeb1c z8B2%RmDl{|wDr$v)BUx{gEk$+k{bpSK$M)YN;E-PAoSUySL~S>YAg1ko9Ir6Erotk zKSy@|(frs}jeeWE9Mlm{`b2T>9~XdWaI5Z=kHB?DSJ7*g&iA%~v6xywfW9J)$#h(i zskLN$FW8#W(9Z0DzSEd4*oniFuJpaxqjIy(mzT`#j@-*B#o@8K*tYq5mWj1`kMh6! zk)Nh!r2Mxfp7B#haF(tE5@(*Db2NN{JTv~QctDtO#?<~ogplrv$B&KHg`P+>hxLOt zUU$|Ci_Tx$5+NMBKXG?#02>32WCN_L$u zOKPa9kACY8R?ZDd(_?H!;aeDBJh((+Z4Ggv)AE!+&i|M2r$f8q<}~X}4YuvxRXiIDDBF1_HtWIbblzFZu<-hu>uXi@Hwz6r zl?}_8oE%Ki!g&{g-b7`Fg!0}6F7%IJ6{*1g3#|S*8~?B8{r@{VJ6linpC9G(+1UR{ z+hO!RdG-E0HGm#9k^k?w+}r0?_47&Oc(~6D{J)S205ko6w>o+Le`DQ5N zX$R-#wUKz!r&*l9iOuj7muV52(hN5Go|d~G`Qe~wqkJkf+IT87I!~d|&x;eOp#N|J z*`msS&ZPgHt<6IH@69Lr|0th@+kd9}e%1HxVn{6bcGY*$s^d|`lRlCTLhBHU%pN_^N0cU2N-s^qkcGfdfX$U%mEm14dXwhMg1)s%}kH{DJsi6N0to}5I{P(y z9wGmutID&Sp{snM-Ij~33U%L@$v+Hzgj=a_=vHS(?w0JYqVso8qa0JZ+f=ocBdaat zi!tLWpQ>2he$r<;{>NGm`T7rQTTl6a9_RBJ#DA9kIwLFOGw)OY z^pqntQ8|YC8<4KXe#9B-bAg&}5GLTp)T;hqMtd~b>PsL6rJ+sctft5`mK-vVu}~Eg zV86)Mv^G>DvW9-wJMj}gih_t#Zd|{;bXfJHJ}%A<1adq4_j)3NkMh#b5h%B<@!tOo zt2q>dK|MW{EYkvG{6x-*%UGBEQuDmaeRq1-y_WCRa2q9@N;zvXojob%M_xGnU7m!$ zDQvv>D6pY=7*(CFuEJrMO`H=eX_v*LIEt?)rz2E7@53ChYOJs^yJLjqbaJaIZAyj! zf%#n9G7a4*31hUT{DcRH|z0np0QC_elFDPo*rU7HDX z+pTJq`PWwbTa#pgtXVSA3D9jtl&c0C={D{FfX0FAaAMzDUUT&_9ImGQru%Ix=-S`- z$#prw#~%tuQu}@A%P9;M3~$`1I(26+-<+PEADuYAd0hJHdna&0eSl|&YJeYo&7D5ixl>x@O<0!~l% z_8)i^gpH>xW$)nNZP^NTy=IYaVrrgDte_uC-q z?Dcwy3(MBpO3?U}PQ?;nwYU1!5`*n>$yQ6SrF#aaKOUW&@4tNW({aW0b^)@IuBV4* z`+KD?W@=x2;-3`E_g}p`Jv)3`>*ht;t$+J=$3i@|J?(Tzz{u^CE?gboa(jOH522$o zCv@bDx_8OjKCNFGWXHe=A5BeCUw&_I_r5&Nz2OAb`9IZh~m~DZF~~!}+Vj_lK{laYpsH#bs1OKyj4ssifZQ<$eF=?d!d> zI_uLT*ID2a!f2k6M2H8`2c5G>wPxphn6vGV!DI#`zB<}JJU%@exk84EQyrw!UE z9h6YNt;(A|$od^t)&g-R$LLo`d^k-}`7=$b;~w^f7leuea98*&1(+=m#CeeFFOBn( z)6;FUI|p9NjfxpY@^^(d@Iz3@17ebv%pS3l^y}iSGX}{e7vax*Z7D&?`JI8oja-f0<=5a{o&aiuQoVG;Z@L`bfX}TS>F?4GXW31I7o-j zGr-K<0H*X#kA472_%$UZ@qW}cq}(XTXM^k}<>s$Z;V&nSOG#I#Cm>>6ER6W$(W_T& zj{+ic19kmz8l+yBrm%PR6{XQYZ1vLqIKyz06fdCf`Ki|nlHeL+L{LWb!YmDj1K8?+Xay%IL+cw{&|+s3Y%J#NR18w0s2wW_u(0Y(v78{qT@ zelO4K@+iU*x{mZ|2A8;`(1yElt zjly1h#azR3$r%-iYKaLfPP5a@FJd+QL%rK9{9tFRtC+eV3F4MpA{<#x+ap+#OPh{+_O|k1s4z2iF^sF|DfUOm`D1_b{$FVKNmmy; z2YPw!Y;9Hf|KbPzzqUI&JKJlW&Y#vgTb-@#KY3e=T}Jum5AOd}e*fYHh<_{~2y9mU z=k@Kp|M%L)#*_cwqkKLq|GyGXzf#BVlekynrq?WSA!=EW+-%J2re}HZFez>-Z>fG9 ze`xM{IGlEMt3i}Rg_!pRXc^ttv<(Omd=!KKy^Q^i(R`}8)EK0un-6DNklIz(*-me| zC8yvdC(koE_tHSWaJ=+fcCO_KM{a%7>RNyNYMo8c?Z;k|lw8Q5U&V>2kYIsVG5p7S zZmWZydmxF8#*P7J@9gaG^~u@kw;fymHT>58YYgu>@_zQ3Uk#gH3pmWLR4<41?ZvL< ziWaw*;jIANd^K#hT`9)ZR9&&z1ZfEz)iuDV*E;l`|1aLquD66{wO=V}digmr>6c01 ze{cpIe*4wZ((qq9%ge41O#J_rv(lk0U;tkE8P=EI+P|T2Gc4H?%CtQ^OD}@Qe%F)t zSi5K|%CEvkBlokqhjLp&Im2zYEpdA(Ww)I^bMV@Zx7M^}RD?;EH?^WXaxotCXA&SO z)-J!tfnDR|x2H(L2l?drzj3Hy{~j;@&*pYM{(l?(_lf^|jL+x8|K&T6!-~2o`Um=U zYG~k{Qo4=9xf#Vrj|Jq#eexKPaX2;f!w#2t)G(LwKmprPNRcMo0cBz!#WM&Ln8jte zP-0HTr3JRQgX1#h5U|nsG78iFah$yk{N4mTse&{OTs=BWT-8oSF?Rh{9cz8T^a3-f zN9I<1umk_y_U4yLr+;XrVte42+@?eMCO>Rt2j}<-VL7I!iN$gFb497tHqR0aqv$dR z(X_^y(l?6z-gkb4aFyh{*x??(Q|=>A|IY!9VaBU=Hd+kv|I-Zm-&tGV*xJhJ|F$AifK2#Wvw*a9;{7uABka^etWHR+-S{cM5SL+>+GpPO2AYwQ)VmDG z_~xZC;B{l^qh9O>$w|tha1dsch^|3qqovCU*{cqYPq8fuZb*@q+uqR?$QDE_co0#) zq%>j1-+?fOMykdoW%2vHLDOg*#82EX!= z;9=lD!@=no5ZXtt*|HJ_p)ZWigEW)`Nb0=X`?#mJ<3(7y23V&)2OhuF)wIEHBS@D&g>N z#0t-ICaUf9gDJEsIUUue_7P3*_<-jmO4K<{$*~YroKdb#$q8xK=6dHdt=q$&j+>Kd z{==%szcc0N%6rI>5bIucEVPSSent1A8z+KDQYrm+%Q!3AY zRiehKesczEQOt;I5S?;`Q;%c5(*T)wjuRNqpxooBgnL|{ZdY}A2!xBrFz`QygYiHL zXCl`%Hud3k_7O4)Ve^x?cMzt@c!a93@5a6B0GKmMu`J9N<-}eZuC6b)I^He0*A4NY ziLcXh3YBY?4?Xlio(cWP>!K)w3s9^1wzDEbgB+9g<8jo}ydTWIhP>}> z5LGj}quym3HvZ1+)RAcLn1S^zi5gTZSVre}_L?#kS5Xis4N~moPt2WLjda(JIT@rE z-gryt_|jXVe+|$DbLMW=mc4cOANUA*{4xo=YV4k$SKkY zA|RAdg6}j0>=9l~8XAp6@pag(yffCWtMq7dVjr!~LyA!3`$UmON>S8HE*GCyv5B3r z&A|n{LQBO!y(d`V-q#wn@ zsd)cRT@kI$TB{kOu;y7^oetBU({sj<@*+LvyDohc;A779)eEn#0Oi5g1Ik>_1D1Hvtnd(giLl|9M|(Ik2Ps*{jp#N~EWA#5l+- zS3CsY>@qXG)zHt|S}uFW@{{9Ht!y#;o8@((t5&#P+w)o*^>}uYALH&_<~RL5?|NZY zheypLwvc`fbBVNQmUhP06gOy<1CF1{2Rx#M!IIES7Z;+QY8sBemsC?b~iUEuM z>GCdK!+mp4>kI2;ZLY6*4!*IWX1Yg99eoA0+{wlxxs@wd>Z@*qQYYooXUP{L1AAVIW>W{3_id3u{ zfBMz5sz1I;wNo;_`sr8Gs{Z(AZ}*N*kIt&*uz&v5ZC!sZN<>mIk^b#htKIxltd7%U zHHy1_L@uqpxcebUT7Wc?$tVWts-5=TG^|4DzAYcT3;`jW?;(2N_fg7j5qm!M{nTG5 zaz`_lGn*0fHJ^kI(aL*Ri-@Vq&Ej?{u?wfmC}&oSCy@8KJ~(`T{`SqAv-7hzKOP=e z!rpaG$(}8DNQGm{9oc=PhH zuqU1=CE!PuHghtOR$PH31<>InvT`#IUhk?q*W4Vr>*`+H$z9U8d`jlrKKm*Lz5MRW z+ZC>64qkgaVg;qCVR)vTzEE{Mq=$xBgV2m81R539iIjW<(f{RMV?cgFsJ25^nL4Sq zX^7mlaLdp!3O@v-B6yCp4#@7Xx8n8VTXgJS5&Wn6^00u)dqEl|u5xsSGC)Q0&ZiNW z13Ars=a=m`3HLNjlIN-E>E^?V%i45TB}dGUvpr)J)X>=~IW9Pv3JfCt=pv>sx*=fx{>ZY5GanO_YrzFgRHvoeUG0~ZG?MYN-f}B9iQ_LbO zY54aNB}EZ^IeqwlDaWKiB3_gC2nEfuJ$d{exP!Wi~b!mfan@tHJ>hnkHFT#6pf2KE=? zb(f!`nD?xj{m}!LhN2c2psAOdy54Kmms`9uZA+}Uwt~&^UUxKQl)K$halnP$Q*r2p z-Be{&hdjX+ng6}cG0VLkaP|0$e$mx4-2-r{-gxz7bsqh^;8J)K z4-6Z1=D8>0cm(*EW3C0F7F9Rq0Z6-3l;#fjMEAC#M1N^ZVlz-i$w0G#0FAq4_`wV| z{l?4gdm1({)7-Xqq zY#-oFy49a*KT7O=s#d2rYJWTTvn(I#uJ6(rZIKVt8F=9@bH;T~d7M)&vH+=zxV0IB z-Ti?s{^rVuUijTvE>>Jy&`YJb;FN1qO=ex0YR4;6f2o92Gp@+(JpF2_u0@YwU&*F( zCHd=^b`^R!d$4lV{q!qf2Q+*omU5|5xnS#`N)KF#*QTK*spb=f&4t%Whq|9q!5XnW zmG`ToP_PD_kDZ+FX7uxzc|}HIo_R}UP49e9(+9h+Vqu|oRXXg0+?I-xneWC_SW1nX zE9t^ZE!^Hb2(D_d*|%4Db7f<;i|DHC_NF4fl3}X`o$K~;)$Mesiu3RyEVNU?1X{}~ zsUbP*1`Ay*Ex!9>t#>$e;CsHuvauKVF3X0W`+eF$5^fkqNuAEh0|sGWFFlTj=*N?@ z=F?9bV!dq1!@+2v~kzf~Zz z-V@Lr9e}C4syn_1vomL!`Cdk?*I{O|-$;4YrSd{?bsmeEZRT8ee>XEP#E+u*_9O{! z!YH^74%4n55t`ohC}E}3w(F1l%PF>9@RWZ)Uk9V7xei zatNOT0+t?N_T{>;GWP;3S~S-&)Ca5wTgrW*@Oe(Jt$;AT#4nWodIMN~ zCLp*2l)*y2g&n*BlgPH+R#kur?M)o}3@9QXf)04y+I7)2OXR}3xffqKbe!6)%v7)b zRX`Hsa6Tnnc@E!FHpXpK8@HEruaCxaqud>1=LWjhvyfba`Rsgo>dYaf)6xm$vL=;g zlAM~Z$pTe{6_#>tw^qz~!C+gKxk>HV&FEmfX5}CGDi~A8nwIQAL2xbJrt}E!$k>#x z|308U=ZN)R!|Z|Y{y=7ry*P&E8soL+xnXHKzp1XI!(Feha;Q7sTiJ;BzOb@E7j;u0 zo@a9r6lysCEft#I^^PhAy7LVc$Gi9a6o*{Y?KqLkr}I<)`R#KqPN+KPd63}ZvEPHB zy+^z!DkxCvx~Rt;ZmrO-{^d;_;y!m+9C&e8SseR8ZR!%)6GrK1jw=&OL#}aczIfU& z^>3?Uw7cI?#ej>#-?pW19&BwHk>-C?<(=-VB$zhb-QQQmsP}(e6$9V@2Cc|&`xwcJ zOQ|L};X>72PR&U$M|z0dK1)(GOmF0vEA9vj7319#7OF`^?&>)27ZA-Hr>} z9U`&)(<1=sy=xpV4SPY?PipSk$G&szJ=e1C3FN!iJ_lyaV8=klg8^WJSuLS!|8uTGfNhb5Z5^(iv}I32_P| z-#z_J8PBS(YG%Z;UdUVwYp$hrNqKg4o)=X*(mW5U;+85H)jZ(O|B}kt)VrfVb3wKI z#&9`vdyUz9SfF>4q*s|XAICg2L%xR*UH{6m7P1^9?gDb(4re8_58U!EfH|RL+Gz%x|op$@dfGaQd13$|8 z9(>XLK>A~sHT6nOWkdcYZO#UXaNu7D zC*vsM6HmSCU?_FFMq{!)^Ve8T#tTAtaf!#+23_;9kk}0`vax6W@&@CSQg-9p|41vA zq}>Sd&+#Unm+2;RYcYpG#ddPXQCK-P&VRXULzaEEh;@%rgE`v_3O0B-=L%hDGgz>W z{iO@-^FGaW0H~o#7oH`^sdUADoZ`NG<&N;22wO=rG*pbJb{rgayKxHmBTn!&hr`hr zM@Xb}NW~Zb{ zev3~wEe>q|>OO9HkIu`jeucTO|Mc|9f4IzqQs$6dTJesCT|h@C2miAr1x{K=$mV1D&Sckn zee$Zc-d%+$u(cw7D_Qjc2mVyel0IwEJU%ShPom>`8IZgLRnVxeN`QG@fe<7 z1>u?Y9VBlAcH`z6g&^oQ{89KmNYW4ky>B{=55QCGsvZrycEY?xLU{)1kd8o(V8r_t zAkt5R_Obd}P^o_jw23+5K2|ME`>RyXkb31}!{`Ui8j)LK`@{GWE+@#)#v8BdRpxq{ z1y*znhv=wVw%F=E1Ahq2U9WXHaeJu)myTRUhtFkKZYD2+mz-;B_bxKKIx~ zm8Hn>@i};sVyn;&|5cXfqS#VsR+%0m*GCG(E8?vGoHkFcPeD@Gw>*O{EFYf78I3Qa zFzp}585Y=>?5XOU)7DkhM@9sWNI=?R$8!*=#48Px+;8fv@VjM!@RRExJDJhPv??gt z$dza+YEkTGMR{%;7#!qd*j88dK5Mb=tQE>ObEqUzwv-RHbBHh2*E%KVBwweF3E<9H zWHyFYSVbx2=crh77u1wHpKd%D#X}%|yW&1xjfP6N&&q#(YWiQz|4EF+0vNzq{NL8r zR-XUcS=)T#{~qJ>#Q#-<7S&+@Rn*r5a)2_$6AP#|_z;|4X((PjR_}?^d*bvoAU!s# zmz(y9*Lz4_k21J@S7XP=tzOkL>|28+ap7_7V`20@!KbJpa zzD%g$;(0R*fj%*3j($#^%mZua$|LP<#?4}vjsdez3?Tud+SO6^C+Ju`hRcw|3vm5QYTZA?&L`$ zGgTw=3=`Mf`j~uF5|RS6W0slA0O)Su`8R$Tk-J`S6AUHfbn-k2DC;>ElAX{fDJ%w^ zlfu+HI6n2@e?a1;bf!IeRey`ap%;%-R7?9w8B~>o=5e(^!zcC6qSZgYU+$Fozc!Mi z@Ol_055eHf;Q!XP*E`7nZEkO@?`&_v_npno)|3C&qkNuuCw_(qfG7lm0GANH9EVX4 zJ-~s1`2e&Z5d1Um45>lN{;BW}IFZOhPi=4DXWc%uUI7AvH7;%f=$G}a?*T=^0K?## zonMzmNpKZ@gttu2q5rhp_TCJmiHCKHN4z8t<0GPQ7_=MhgVS@&DH6aC`|)4^1H9iq z^?G5FHrm%=wo3oyv-f{g%KuV zYp1s(`0u6v0sfl};NSl@G=1+UVLVQ~qk}`3e3Zl(ZqaCmy})1PcJNgL$2=ms0B8t) z?uJPhkXXno?T;_{A3%s(-LO3wL=9}W`q)T?-~ay0z2hGaU%mOEJ?Jfdj_L1zdt+yP zEBF34wl{a4-v48Ko;~y44HKq`JpkHg-dcNotG(te{nr>?;`;WAx4yQ%vD|3v_r)Fi zYws1R3Nr6UO=i6t7N`i;(QiEkPUY6?wAXi_ITO)c@1+sP>Bq2 z622=xy7eb1M^A(qre?fAPT<1(`G=RSZ`RiTxN(xHi!B)jkThT@Y?pSwo40 zZxv%;R-wl-EcSxFzmS5>l%28D?rg$|(6F8Dy2Ez1&9JR*8n#q4X>N{tPSH3*ymLcw z!*oger$->7`yT@D!VY1+ptHMBk+K&iCIKY^$xCCBLOf5=pCP4{z1XgMrqBa7ud)j4 zdT^EAMKD5kJ@KPg;ULUpLHf(L!7soG;@2ri7bfSIS|?sVCC$M=8P&;4IpLI*Vh2~e z^UaHzD~2Ap;@5CEgYm%oPy}&W3kv5j7ksDhZ+#6ekaKwg=o8Z!akVF)X-YbTEkzhe zoWWxv@eKcjrzG!2&2b+%_-=en_pe^vX~2S@D&BzWzW`7f1n})ReLIFH*-r4k7qtAL z2ack+cE?-V8zo`nb#^o|QHzB4z(Dc#yS;rl&|k)3!tseW zo%RMFsFboP_bot=`B2w}pst(%v^ab@#s6S+-V#AnG7W z60ncQ1GsaHQAVu^Fsf;KlEjxBa2Ej&Y`)QT2t~i1U*JaV1#Wa))Jmw{ofMamTbZ*7 zqN4eoAwQIjN8Fs&wpIK6Y!TYmj~j4`o8}DejPDC+&54y=fv!`a%p<16>-r=>$YzGY ztskZLf;N8F2(wGaNHFmV*I#6Pdfn^p>n?w+`bl**>z+(!bLwO5$V^q1jwR2 z3B*QxgAII>rCW}$_N(m_&`NG`D!+pKc@U;u0Mewe*vhq9$BsO9PYvKDu{?SD$7MJq zB>GVMsMOur8(>~Zwm}#jFhD%c5Kj=g#2JO#>II`Ho=_qKUUO|i+1V&zkNz*puW+F> zk9}Y1=uzspkVto)aIN-)YZ~3)t6O>l=RnwIyB@X?oUm4V$7|DBgEfT;?l!XC8er1X zV3aW`wK1uUD-cFbG~4-x>;~vN&B`QU@goHZ4(@jP`4@b2~ew=gbEp2O$j1 zi@G=wdf*YCAqVJR`v~+lCkW_v>wY7b!vyeLGVE6EVYl?nb%?heUt=DJ&YBIAqz^jH zpOv)+0G%B;Ho1|y=mUDpORUxwF-7tC*6%m$DKbgK-xF(6dhwfbI-9j`ev@wgAQ&aU zE#ay!bf#Tp52W`&D5{Mw3)@J>jADxwReFL7POvF3&iaI4k5s}x(o{-nGR2Xp4b+CQ z&8=nDK;)RaAz^!#M=##*y?b?bzISl&_9CD6CnQn^>y%t1oSINdu(9s%c*0&7tnTz&p z3+J-4QCkMw_@+cz`g)Aph7(Ywjc>y!0(9j?F)$5@7{Td4C>OR#l!}1LE2%5YKo5%_ zRfJ06HI>Rvlt-)t>)9?FiaQE9O*qvfA{n5!g1S>Qo?Ql4XwO??h&HoR}_SQoa& z7uMOX;1(1SQoKH{{WeZMq`0JB+#TbQ;|jCBkDFW3uvh2`8=@Esz6&Mapw4xYu}P6E zhbYGYi5UaJ*M@tTO$f*({C5mAxrgvscK$NL09>iDPMeD+N#)LLe zm0LpVDoo$yN>M(+5~lnl2`In!_Wf4J6jCXCDO;8>K5Oj_AXT7^e32HL z&Z)@42`bNviVbZYSs}J&Mc=diaA_^y)JD$VtSRcEoeCLtc#o|Jjv`1} zDj_Y(@+3l=xXn#2N6JQl8jgDMkkg2dO5ol>^NeM{qQRD-crG9p0xJa?x?es>QpJA zKoD;^oh@vgRGP$Nk`smEAGddE!_SU^pR-q|MJO>_qWaTvkhQJ3+s2yPdEr=1Iy>?f zJMLS23)go8z-8<9Sba+b%FEF(BjXW6hqv*w=KM7P&b4ajuxvnvqmTI5Y8#ujH@{h)f|u52;iXOyc=^dHPkBbZq<{#!oC)=9 zFoZ=P;nr@DA11}QWjJ+8JTP&r{h;Zx85(vRrS_Hu4DQN3kDr9Kk|VBIkKTuU%1BA8 z*{DsYHa4x*tXZq+q?umLkH>F*I`)oEPv0?oo}t2s&W1#bD?g0*@)1N4Avi2e=bL|J zz*M4q$ZJ>J`nT+LbQyGg04@|7FGjMf3lYxdpj0o~o3kKdtpuIP3sc7lz<{qGmX1Of zxyqaz(b;a*W<|HYar6XAP9r5nX;=a?#}{@wQPF`IZHMBL(cN7T(t2%#w7y-oFu4{( zPGT)3sN-QEt4&EZ)+|B&aF_zG;9a0=Py|mdjR^ z+MISSKMp{QXu*2HP)?|>=LYo9&jM8?mU(9Vn{u381(h4qp>hN9l$yw~18EgOZlcpq zOcdzJU!tcN6DiEcO^-h&PtDX2z_|soXyv)R@#Da1Lf2 zYit=L_JXAFgmfw11+?8)of$W^3kX@*21gNa(T~#%@o`?Jxe5I|(5g-;fh@i8=}=8@ zTw9;9jrt7Fz>Ez5a`Jp^9-ENGb#AM+s}uBCmNnMZk3kRp*0wBz+WJP)tDuKW4GXEw z4Zb4Ikakkr*txZ{t~gMw7ZIiAo~7M&SOc1=wv4#-!qDDL_85*P#*?N~wEq^Fm*8WV zy&1j@vSji-P+>%@vzeBy?b@VlyHpkssIomvskEIb)wIHf2yvEWMGq?IbqEycHAw{L zmDM#eL62oiEa_>zqN8u4xIL1q$tgmO=Ecc1V4c7F7Ub6j-z{U#u-WxwJRB-&?{~X^ z)qRQv1i(zSz0;Sf2NxJad!c+>@4t{dqs2xL2{pao0}*qd(uIeL0qV!&s7Lscmrmvr z#dYwLstr9mYx$LtKsC?0sNqAdstkxPDvBpuFbw&swu7G}{zRs)%Y;D>cvABGH*~;& zBNCR&FFuRFgyRs@ZfsW%T1;(c$Srn;PqS59df8epi`GB_z?2U;C&^Es9CT4nhrhMp_9*v);9*)o zZH4+)XU$xX=xqO%BJ*KgdTsBztqy6U_FNsGUG;E^c5kb;8e(gmUi`ZwWtt@|MuEnZ8hpgk zF<}NQTKXXzsQ?w0z24_h@Mdi-;pPhx%C$ccx%#L;QCbL2g(8O1PVhvvJ+L;}1MA2b z;HO>ui@GNcJ)GhBXxM#S1s5qcE!8()ocNo%4+whT#D`S23$+Nqq=SfwqhAJs1)Nw1 z&GJ}m#Aa>QcypVu!K?ThM5&t~BAg@T)~xvvgnMBEXA(^mIut02;_L6Jz3cD-U%$cu zFXSTXie%8El7+aSkuFG!Z(B$8SrAb0J=CQo&?Eoyk#>sN@h$pJ)3mtP110Yo?-*_W zNA0vUmE2&+cv;Uc)#}@tPT7gXwmj&2rzuoOJw(x?j zmnK3n2X|tj87Lnezc&a|qL0d3hgP#y`y{vYNq&P$vhRb-gl&vNBe^R;IMQBhR{#y1 z%13lqEVB8-ENtllq!>`g%c>oE1U;5lq;Fm6A9^TplHDs04&R@@IyyZ&JSO}2;pwSI z=G+^P(lGHXwGVF7IylQVTLpsUM8UzCLk^{iuc%OB*!5E(+_l%XF@+BKV|bNlE5nN$ zK*w+t1!hLZYdMY5y%th{^#gVC&blKjck3?<>wiJfHz!GW6Gp*xaF}*|j#u7g zzq<<)riZjnuT5*FQ(QuA{%W20t7G8-rJ`QxYY;w>o3sPsc3|5ZwRyUY7n+NH;TQ>( zFG-npkQC&?5+BMi`hCkCDcT@8uJS4@+KSP(q*Z-$oNK5D8~Ga#+eXc z@)3Q%j#}@5^3&hgseK?j+6KB~n`JhwH(w$;i`$OKGW-`k8u9%k)fKMt+#gQP$&QUk zKoncF)Y^fy&||q>?vj;htD#BR;AOy@4A96pL9rf7g(lWTv#fe#Yt%Msn~^ql%6www zjD+QV4IIWrVY%!s`(BQbX0Of0Y%m-1MhA*2IKfLnE&2&L$5A*4zuo6)QI}kBKJDz( z#hTEAqR1px>EuY_m8vl0dTlR@b@sAQE>k@d^d-8pz#9)KDWfSaZHH;l0oP6%vB60r zBskxWD9*})Ku%4N6CdO^bxAPvaJ+b(i^>I-uy~y-mTM{RH)`9KHrTeL4%zIGEFo+- zX17&ffG><_v{73-wXxHg_h3)n@2l&pjWD)1U3&+j{37e{j$MD&YX^z0zu3xIB}S91 zAEV&Nj1O@^Y{Fq9CGDfOmUY9}Cc6H2<6$r2B&3%CV%T3WG#l`t&tw7EpRmWb2I4<* zLPW$lqJS4r+x35gGgaUMG^SAMNp^gfwJ^z6L{hgUJdk-REhR6aHl^CqlxkZW(vQJiKJ4SV55;Z@L`bm0z3 zAS3aD#J*G#&>HKgkaqeu84nen;pGtn=wD?V_(_>*838hNtt~IIJo;Ub`MU-Cozjf) z(6t4NPFXP7DPuL>t;6st$9;G^=rhpvK#GmBu!Ttul~Go8L)CJcp|cW(H38gf8dKXg zv(B~|h5n9C_Rn9v+5Zteuvzv?#mgzZ^+yQPDv*paS6Sj7ho@RZ$_Ab{qXc`Wfpa^G7U(e2Xg=2PTC{0+KM}i(aVJX|D;+)?C zB(u+m(pIApgcZOv?{;0NgB~<*-T|`cP!HF~(iD?{+8cafZ_rjEE8By~rS4rAr_=U_ z7nFI|#DMVgs>>Tek0}S-rGyTSN2$hhFZarvEH*l|2~=mNAhYObI5`9I_!O9}OUR)I z?T|;dVUqHk?Wv&ORd+Y)Mr-@IK@Wb4qAW|%v512hwM`3-mhBaAkRiJs60MgK5ptPp zwQ=RzHyR7%?uJ4$`7nvR_I>pvYje{(Yu0&Qj<0b*K&1GiM8&}z@XheK;xdX+n}(xV zuZ`>0ztOaO%h+qbDU~@jWD|<8pBG#_^Pmh>wHL9@M}2H-)tI&(YOfd>ymt6J^spW! zY88~u;cajoQtXiO+>>pj_nXQQ1j)m7?}x+Vv-6|l|2o`1I~URH-Pz0YlfAQ-7bQM} zM7C-(!O+9nXtg#cim$Itg0h>qtCK#K%i~PLYa{&*->G{P)vIz&z|NgS({I!!1sgUg zFbJMo_H^ z#rc#srxjU1p&upz4J$bq@@o5)Ll1;J<~>A4H3W^}#+>af#sZH>IPZz2fMBmHFfFnYc|Kx*r-OQRQWe9OWN@a8^6CuN z%^AM1j&P2};8>^Vzx5tn0Vj0t7~_E{a}{Iy^ZOrPoVGT$%$;?t_~~J9zWJ2e1^p3Q z=>#6{KxgQr@EL53IuDwyvuS1nEO`)gB??GbpUyXV*5@z>o-@3iaOx7A#@>biMChka zW-`;NjQp0M>enO`JKlW+r`ijhzCnvNVa^_^)wIx|surOhgIvA&h)}fknagC=1;x-z zuQ_<37Y@V$AMnewvy)Z)PpajG+8Q1O=hhFy<}xF_Pan~M{xTz#!KhZdm&AlKJE zdESU#Tj{e5P!)h>Ji1Ph^dhK0Gm54n^#vhj@{f_UW9o`IznH{Y>2W}8i;-8laFxIp z-V%bg`c}+Y7t6Yz&TG4hId3ESC!LrWOVwjQghTNQN|aKskTNowQ08?DPW4um#^eHL z!}1j99PvFqWqJ=6`jIGwBuXzzPYGWeHpk3r+n*~R2j3^+NbgcYDb?lcU$y^V?)$2I z6cTf}w6AJVt$51Ae8sbaHdnkL>)Hw<@*OfW$D<1~Jzat(s(E;YY84|=!1IkwbU#U^qE0PC? zC&FLJ02-lVXGW3TASCQ@di2BD;oH~13||8~km>*vq?pQpD-^8i(XeWqeX|b@(SGkI zHv6G<(j1DQR0Dq6@XATO89$5C1J;igji16?D?F6N-8hnrCMt$;xbNF z1V}En$kz{7o)h66ok&YMQvf>FzKg9|VkIxsB5R7cyjWjbqi11JBE6pi!l7{Yi=yq< zvPba6(?;1)E)h|eSZP;)rtPV;S{#VMJliVX6mR{4_DPEoUa~gII3;M~qEU`a=s5z-+*|V^?0%8T3ZPc%ld2)JyJ4I(LPulndn`WJ|^@c`GByCS+d3vA`C0gY{>6WQ!^CATD|yoc%h{<*8hhZ&9kov z*7{_123*c*f1F__d7M-S4qgZle@vHvbD3K|bpJAD_KF`tIG?(eVS6fSdmQ*Ec$AoB8^W>reF`ALoOW4~HPpT#hq`q9V@& zB=PGLF2VvFez5!X*H~FNLl-V;L_tri^dr$B9w$r=pQAn_r7}#Et{BTDMw52q=*pYK zV_>kNFaWj$|K0a*Pyj}!)2v#aw~GG zQhpkJSLtY3ALbquY$pmo;ObeSI0zEpwpSXNAAMl;`ajVb;t7BZf#8)9hyn99zV2r# zv!0oN4%6OMgU`Pk0Tg&7&3N(UO-O<?w)szKGC#I6k=jZ2n07y zdOXTS#LooFvy3?w?fW29ef-C;t7UbdIg7=Hd};Q z7)PL#xORMeo?gVo0RH!a5@?ei1--DYMB#Wn2GItN4vxng2Dhfl95#xF5tdEYE2liw z-W4<(z?t^(FuzvS5Xch7zHb4DAiZ%{EJ=7u{1U?}y;)sF8=K&^Q0-1rr5uBdcZ&v4 zE%S{@7NB3-o?viQR0~t*uaL$j?Ivh}1W7zg|B1Doh+ZkJEIONyGQuoo9aJ4~;?eVE zGb<^8Cr02w;V1AZ2^N79ZK#dz!%<+IHW_&l_U>Vt@-XgkBtn}Zhk(UUNYI1!>$#sKLxL0!K-*@Vgn#Gv?pw5tYR&kUF%_Y~()HfHYOGvL5gT&ytIWTK zgMb)8`doLNk0#SpE3}-Sbv%i3~jeTI!TmSnpl|WdM*Lf?$M0nmfo6pR~X@-!L~m zdtPFs*GB;+lN^P$3*OZjl@rjE4cb0Qd<*H&qHk{Za#` zV@TvQbNz569?j&sI37x5RzCYuo4eBQZ2M-aEs7g@qN4Y)(pV~&U#_WvFq{PqLQz5x zX#c6Dm@@{=D<3oU1VIn0V|zW$G1SA@{&<80u1yOnWQo!XDqDxdUs(}Zlu)soMHhkv zsHD<6B6$a_t=F#3d;*+1pYs?gOMD89XV>_g6twy(#@AP4YWpM4`V3HXKnqWSvq}*w zN<)c20nDrGQlJj+0X;t+RYNlQ3@* z6A%#xyTbL^spUbHF#~_3UGFv;7Z(>)uDv0q5ANQ&^1k}V$xjErty1-I{%sW(-sXQ( z4)KyEUQ1=;!v(g8ioz6)V&9#H0(NNy z&HTK4ySzI34?YcA%n_F_HslM5G>y&`SHf-)GK9<{p(TzB`5>?1;C^=-glX7^9eG)^ z+op01X(~*JR3)|U9X@LO-)F4_e0U_s z89>beyPyaSeKeOOHDXR1S|(C1RbEhz!JuwQ&$L8YiWR3D;$a|bfJ}3+>~7~jX^!Uq zNR@+^bq4>w>-`V7b1Jb4GGWLL#*&qo`gLJ#|K9ZeVOEd-_z8{9Qi@wUVUqh}$m_=2 z7HUcBQj@C{YbcQWkGa;L4K+AK+rqNmK>+L;ItJ7uG)nwUNv}0gVP~2LBRPktvP2Cd zgTOM1gr;MQp$eQw^RABy&7sFiGKp81%5cc6=Erb}^SxEu+cIe|GaZvUHNcE+PF4N+Dl`D`jj$X7{ z@cV5G8LJELD)O)CJclSI4rpI~k94!v>nHoF044ShGVZC>BrL4Rr3$kRhjHQD#D%^t zHlz=|Q8;4!J(~?_H!dc>T(F-Ba+Q<`Y|KdxSeMLf7r$aoV}+MMDXpbu)^J6Yz9io| z7O7chOdM@!QAxgCLajPOo26v7=J79a32=H#g!OQ&C>UO6{bfFti&ny?h)uYqcFJt6%FGQuHUXh9|Jk%|M(5 z9={zlE5j;f3fyTg4#=5`!03t`m+9gV30^oj^pFJ^K%y_np9=#`F%C=woRp0NgMrj= z!vHsQXh*)Zge+{R@; zFLqd#_CKQt$IJY!>3!=phw%EF-e2kG-&`W9kg*mnfmP_U@?Se3X_bKXY1PPg39F6R zXl_|hAYd&k?a8k4EY@N* zLd~BGAoJ&$@bc$BS3wFdprtBG!+~gskxmFJrtY*X)2T!BytJ`ibX(N;X?qY1Kmz`D zeQW!5ICrP+Xbe5p2C73jIN~oZAj%f`FB9_d^V(mlmsSe7e=Y%A+5*DsXMgw+JeZnC zFI^t=;GJdFKXwN}h?2)cC6$P;gP0BUfd6k}!?C4gsJ>PF|8>m%t!V)C#TVUif!XpC z%_b=h-6+hF)}<7IFOjt|7ZtaT7lu?v)GVJdA z8Nk8y>Vb~**ezp*>X8_)Hj$YEy#-?9)*Aknay=HP%&%V1?Z;lT)oL}DXA_<0EJt|% z?yc3KI5c;W)ZqUTYkJGvX;-c*5OF0B0>YyZ^ytbP%7Mq`6)e6c%_b3fM0kOph7nm~ zQ=msMmw3`x6h%RV<~c;Z_^7D^ zc}vaxS9{Inh7|Q?e-0JgB*Kt(-ay}tSAh?r8a+Rj{BHXb?B|GBZYvGwHt z`52%5cr-~!m$uYh#(07k21BfXblL?(@;d%|7)8E!g5j%Og_%}#*}QeEOxbB;^bUoR zp`N>eRph<@^S}RJ3#a{`|NZ|lli&>kA4qh7+CdEExCwjcvV^%bf@{j0no&p}S!XCY zDKHBfyJ@2Zk3|hwV@*C7@huF!kKT5Gr%&Un>=sB!8yB>mUyusw$uCT3%cWFgR^(>i zc8(*B5o+i_2nUJ-)MlC0WvGAT!R(oQrIkV!ChU@l**Tj3Cw0cB1#h?uE?H@QpR9u` zpM!?nlp)=2`C(7F%*8&s=b&8&4_q|PeNHZi;U@O}`WW<>Wj>QnbvM94VfYJ({)Zp? z;U(~Ud#n;aBnJUfJJa~EYzm#sPfX5mJl5;DhXTH@Prsr=IAp*=t%J}oF6@>V3kikXpn};#%)b`SvxBv$jmPzh;#%*alWP4a(d`(p_wwfLTw9wOm>q#oO*AJc?ALC5w z+!TlN^+1KQqm$mIlg{0J9`;Z(dL=NCu`~1sWg0>uB7fsF@(zvs3QGYc-cQh@Fe;D3 zD21MOQ1W_6AHj2>l_v?&X^(9~B9`!EoI8Z8+sTHTCiQ;KlQOC>6;t(>^e${SIh*3^(!^(BCk=av+$7 z6NmQ{1@mDRQT;=9^|!2Ev;r~`-2@ZDf9i?Bw(Q&QtkJfdcU*S)GOjjv?wW^2upu_& zB96jaP)@Pzf-uy%Mc`o2HzEo$8{V-M5siVhC?j1AGpY&(e@}mgxGZ!Cp1w4#hr;Hi zVA5fddE(Qh?MHq%z!PGmZs8P}**dckcj;o6B~$_%#D_3VzG*1s(*{>00&+#=Ct8v5 z=w?R*=xFw%`5FGr6b34h(h&}}u^sMIM$@8wIJ`nT5n9_3gQ#Rs0%Ca_GZ6qLt+Wh9 zHbE?JO9iXo#;&e7e>$Z%@DZ@(O(4#LB@afF)c|LLS<)*)W-^!<*3kNuqv*LW4THe9 z!~8=5wHX*zd`&3WD)4?de&_uV3`y;nXP@31_Gv{oeuaLwi3~TpXUFv1!!wC1vYXyk2(AO)i= zT`nWr&6TckAfxiT3a{Y6MsORX!q{5-=Vbv6HPSdEZ7OAV>iY?sdM^Qr0XUEnIRjfO zL?aQrNTnzF0kH|k1np}TBenpX%D)K&kxzodpaCL=O z_)w5w2(?gN`}ENyBghd9ck`NdRFMol;8fDFj*{mgEP}oA50H)Ip6D!~&@2NWhfSQy z3&k8obbd)74oR_a4`vaV%_1Jia|-k-^8z;RGk*oQ%Tijq2g^m$j zZBR3Ux|`&qD~JVV74`x$ycvzVWIsG2+#BLV{}M(*lfHW4vN|LSYKIh;WL<-WMXrQE zn(o&e3X1v;k;8EL1XbOB z9O@7>&q6Zg+6%rh-x5BxvQbzhwKg7a3GF^=GazTKFC+MsX()=C2~yvU&=o7o#LidN z(;U?XAJtYHLtJ5>usVY5D*hr-tx{WGV}6%#__T_=5I_=k3$MvtOldqp&||_wm>?Zr zA;&~@Y8lp)R=|LP`JpKNSOR=rc3(%$&G&woVDj!Vco*FVGCV`c+(_c44f%gj(f)F| zBPzPc!MylRwIaF~chRqq+2JdnQ;@Ilzj}c$5?~@|0wMYa$oY^w(brKEWb=PC_Ng)o zZ=k@m(O$>@Y%sM4&}SQ??xaMQ64K=ihgcq&(2cf4&ZZ&dq6_iCY#`rxV38VvA(oy{ zaFUhnMUg|0O36=&M!^>F;f%{Pqy%deKu;QUkC`LZ;?mbzpAH-&dZk`kDne1=DByQV zANHmm>|2U~A>k-=0X)B8p|n@!?;YtOem7L$7nIMb)<#>nuEQrj<;ERh&MPb&?rO?4m8$Ty29Yks zPh+ls#xgH;M};L-RUQse@Fm6weJZX|xo-QL5bKd0>UiWN4RnTQ2lX2 zWPU!5vA8^~i?t4eUSY+m38FViyrYD%F6^>L7)o63b3#6q1NMYpSI?FCZZr`qa-HXP zq{WI75%u4c;0RMA@diO};(Zum`p7HEPy&yr+Z`u9@T@5vJ8;x{%oVD~wP%Ej zGrZP3D(T0lsK&r0PgnBZYR1>5OVbv|*x(bpz@u>ERX z(pP$TZSQ4#i@wGyhJwWS8c4$dJ0pCCM{ybS{pd;+&Y(*7{FT1dobYEcU!>(j7W*=d zqcNp{qQn`>t&w$wf{qH?H#=Xc+B%{eI#)(rFtP9-==W;)0#Tp(=%>b|I%(jmiKM`B zR7|YV5CK=u36Eo($TS6wUQV2>d6SF{aGkUjL(^4K9fJk6Xj=B{XuFZE@v>f=i9TTc z9FO$3aT2CI&K1o`R*r^Nw(5PRRR^368(q!E%%#7j3TQ zE^xX9n>aKwI4F))*duc@iiV=vtl^22;FDq$wHp9lcATpceMXVrRS9n}N0Kt;5|pTV z(GhLgW+BtcLn~7n9tu*`?n`QSNlbnp@&pVlm@{4Bmr=6x22o)p{39)=7+JBl`7Y^& z>jJ!s!5be?LCUGuB>WQxqE(Yp6QEA=?r@kI1LRg)k}*{(YBiZ94XnhtWkIgyba-VD z3gu)j2j!4bPynP@IF-AK+M7Ft==-4AhoSA4FlB*~rl8zg%IIE_fO0_KAPC6BdZxlR zt-!dH>4^#x53?{TATZ%WJ|bZYDWw)Vt%uBZ>ElV6D6k4`MFTk-RpQ}w%o#b*q9XXN z#Q4Dhk{KlkKm^QaPn?d1Ep=2~wj^nzK|#o(LV`HX_L|qj(0NW?tu;7w@RSUQ!i>$h zZKa4vf>_Z%wW;aHoj+;XxKKfR_oe%$?S?@*b;H80;?naQ7G^nA;mMSJ5uIZ6jyXep z&{K+3#a`xS5$u+SvHXG!@w9C>8j{WVZ{*KE;gyUsvrSwNJ=FEKQZBF)PljkQQ^Z`w z>se-Oq}m*fM;Vcr0aeu0*a_GXgw)QWaq0!|6=hDFAT7A2R(@BEdz6AZNLu1QA`b~; zx)MO*^7S$z)8Lxjm`H$|}&g zZI~<7M5le<#(H!y8*L8r43Hxd3CQ47cjVODVjn!XPh~&C5d$uKu51GWUA_tqNS*6Z z^a(e_Rq49l>g^{|Gz%IWAu@*%+K+dz*_Ti!dHI?v66&H zh?4IKos`66NK6gWVgPhY zP77$zrMQWBgz<(gc>t8(@{9uCF^mbv0>1MDCDmY7>KBlwQ{mm2bAqoOZfczcXc8)W>Qi245{yXY61iBg~-SL1G3yQ;#pV(gs4vrvGx$0z?#Kl!L z41;Ts=g^9|yrTVhjQNCJc=(y-L`~%%#+e*qhw&}*okjDkvZ5)>g&8hGm0Sct!4d$K zi;{6xk}MJQ(%_xLTZlNTB3vR{0rBQ~KIXYomQex*5cgqMI9sTdm^P&)`%@aF1-EPJ09`L|l)NqOmJv5aw$ zOS9!?bdVO;^N1}9p7>v}ADg>b13wyFlt7RuC@J}Z8p@UOjnPOQ6QRV$3w*$ev!XY< zM>;XhY*FNv?15ceS+Sh5UEYBaBpP(mUzR0)W$WoMWA~u_OWTaqonQ$rJw7u7WWr5N0he14MU7=&8 zb`0@>PJ?Q=DBMODjzT%tr(-HhhOqz{vtAN5f{ZqCoobr};{yD%A4EM7u*bsy-)}cy z>aL%S?ym153Di3uGQ?R=$Z_)!^&gQNGj^LH5pKX$$S~WdlVBOMl0Up2W7<#^y`fz_ z^@@muCSxn*2f<;&GKoPYR*pN>g|#o%sc0c27njt)*nszr2eNhvryn~2M!~IaqMU(M zWZL(-P>B-a6o;H<0Z^x&R=xNZ8)+<^G8{~_LGD_l@HPIKB?FSng%g0m-j&8hR*F5k znZgUEY4R3PzAegU?K(u7prUDdoK9^FgCSt5#=|idE^O2BX#YZ-pzNSZESYnfQRolj zd?Vvo_al9bV}bHhL2o`KHkVCiT%Afibx46-aQ~50HGTq-Eq&?hrc(jA>$P%5%^!%A zq7Y-G_BA7)Eyoif?|fiYH;|!}L6dIUvePocP4#XkMFS{un!cwzW@WqzPbZbM9`LS8 z9H_-WlJUqarM1rnM{7BN`nk?68l06=87q&(-Ov~i^crkseI33X`w4DbH=F!|Jr@gSgW))D;&g@q>w~ zy4vg6-jr+{&5Vteva%_&v5tzcVCOr4o%thK=Y^)+h|;`z+!b~wx(bI@ zFyIWRBY1ntBk>77oc{rt7mo(o@7EqV0)2hFEv%mN1ytqC}cZ3tzt%)sb{CISHaK8WQ-6<^O z{CMxRa~-nQ~L{U#)AdcdoK?d;hNf;@=rO8aPFZ z%2Po1|8xsbP|<_T`)bKR;xd(`SlWO0_TcF4^7P6Ss+CNF_iW8Ur4?uftdVfzsmcnb znu0qo!3s@YfR1I)^s@8#)3n%c*V8NDf7#H8Uq}Nk)1kZJZIyx-hE)A{DA0Hv$M{NA znqd$jdZNs_@zKbG>efbX*4;*U75w5YeYI2$kIO3v-^-0ZlPUpb8IM{7W!Az21X%Yt zt4{L28-H#EUJ++}`_(^+U;ZY|OECxZDE(e_`tjX&hx>1izd!onTkB5Rn2LXuQ}ath zuBR+K-1+_y44nUp$Gr92jkr+p-*QCuxeP)6!K>?`*LXZ}N(4v-YgKT7??oEsf%8+J z=6pMs#kUlKgN+;A)y%Eyrk~s9=LV(uPD$_6J|_~0GW16zSteheC;#lB2MaxvAzq?2 zbES&-xpNL}7FL~iZ_O4CZ{iO@ui+wv;aphZx*Nsg9#_n!dQw16R3_V&Dzx2deF!Ek zM8)NIBM;@zj96QlKf?cp164C%^{ap67w}ufsp->IM`yv^olng`aq-X!*SWi>L;16Q zAmpmyxdlq>pV{!xy73c*`>P?ETH$!--6*7dq+Sc1u3A*L<0i;YQvsZ)M_S|&D-2mY z;2c)jwKuMMIXl25Yi*muL(O_tx-yp`b1)qZfs2x1QtwwR{0T7ni`V>LU;U%Nl>PSA zS53@~`QOr47Nb^ys6yyF{*1>x1W|fdMzWYS#y>66$d%3G@$Z6ya=Z|ZE!c^;E?5j# zp^?Hgs8(!I_L`3wEu>X81ye6FfZ&Na3@m@|9FsIvTb;IcIH27|qkZ!7&GBD$J%@cP zut0`g(&eU#_{;$#_t{#_3h%W*wm!A6^%;Ix{@3jPgUm5Q{@>{AtmWnZjkV5`{QnrA zXLe20eH0EGjeqxcZ+)_Y?s`rI@UOq#yeG10G%yH5m{Cnn9z{4z$M+W>|4NzC?v&)RKromPOKY=9>Q3M zM&+9GO-c}$+FCwFcwB3jcNz|BnyZ@04K!QvBB+g7hOX3(@%U(akTThL0QO?-1-R*j z2_Ffo(+*>*gtUrDuBrUnhZiZ<*J%{%be^%>7TNW24=I`ISG=EJmeu)NUDh@js{$$B z8kRY16l>d|swB&#Ae{9xZj26&D`ikUUh*8kG$?2;rK1(}s!B1FWlkC8$_8`JM{3Gw z6S9u0@)J%u^ff$E>bB9|{@1U+mT#`xKdR}1_hNlQm^)s3l2&C7IfL*VetyzVLf2FI#pzmT@td~s?1a?WF)~>FV$=H zNF8OKDLppiQ8{25rBE`IO70(+@{b!Th0MJJZhhgW3 z343YFdMFuyOs959Fz)4w7IM+f6X*GG*=G)gOchozg_WJUcw8z?iujU0UX;cSeD4zt zPeIS}sOB;Ffi`Y~OMFOm-y&{n*+v(rIc(_o6WOpT>1gVVKG}Lcmk;xQ0!Uc28i4YO ziw7)hjv4&l+RkP^|M&L#+RhXI_ZT0nUSMYc&nN$W=AE7#{LhxyYFkI7cD@1>zl+`g zElU0UOt236%_Ll85%G^zEmW#tq~ctPr7~Dj*E9qW#2bLVQ-r=ogG*Fk2}=!Lz zHB^;j{83B_?Cqimz^>O!f@}0km|(?|AtEmzmq4>hYsCT@Ytjgr5A1FevxIkALuW&C^mjA5p`iiMr~hqh zb$!*_{VV2Xr=E49=J_-OgmX7BK;Wkop2PT#51*9-k6OZMvEW&%cGP@J{61PL;j`?Y z4!^5*i(^kk-oVc$L&^_s-ig^9Q)Ip?vVU3gz_itov`6OXx@YHx`Ul$sn%xyX)3ELo zZF#*!7NVhcnakgJ#Mv)q>F&Az9x|b=R>5=J(Zmg0nbkbZM74t&-S19W4p8n>p&q_JEzq1wVUU1>tp08({(m5_~!(Tze@9V9JZwias4%!odiNnjWp-gh_ac@)i zE(;h}U>Vky>3#yq%;3O9_Fn>YHW-+NbLm5g zrkf&U%47Dxe?%@WQ+|zUdL-zGVf{79R&!HUCckHbkIy5DyV}sLQJbjT2t!JkJb<7F zxi&=`2eop>;94a%>osGR5|`(gdR<37LOfD}U%FOqh3*>5zyB#yLT`?wp0;;ElT$0& z{h6lpf*)%4ONCazS-Oi6de`Vly{42KR~c(=SkbG`+Lj4Z{Z--)(Z84U63NtKegm?= zo3V#^C#F@L_o(0!BSPOmq<3$!SzeGlXH^y36QO~!-#wm#8Lzv>06wEWWv_n2V>XP21VNlBIf$+nM- z&`^KVgI}Zew>{y(QWg}J^BV$t1b3Dczg)xK&wjgENSYtn9ST?+TV}#KO zqD9XSnN+#MJ)pnK_f%3}?e zfK?*1?;Lll{-)8ULa1njrDF(>r@^miHz91kVY-a~mr$#o1h4T|o?}UVP6G^nWT)+h zT}wW6YW^6WrVc=baQIN&eULJ3Wc|y(|JZR?Btd8<^-=>)*rDfumNqdW3d~RR)7v&^f z&dFes=Ciwj3mozrYSRQCr)hVzM~rdXE5TgnKe&F*X0|o;q71(EE-1<|nI@jIV)(kh z!9G5;G}QcTvxXma=cUKeb}0Io*Zh?y1xIF|=+$&G&w*Asb{7HT_aTE#o!f_XCD|I^Zi!yQLB_@VoQ+O$^6qhbE(#N~bND z6~x|qg5cWQxBrw6EiZKnNkC>r{9gIR;mpstTK*`*atug?4B(Um&_VgR&l{taBHK7z zE>8YCCHs{VATzsM(Du!iERzuXN-E;7nQ#&5Gmh?gj$M4IxRO|c@~DU%s{lWZb$-JwhB^b|hzWFTV( zA>et5h!0WlDxMrkPoqy1%{}MScE(T*F2z?6G2=ZOK|9YSe9Z@Q6SKTo>-_O&h0Rf5ZHr*7Rl~i0pQu(vU1Dp zTSFt#Kh<5;miwGn{{<@4i7Q^h=5WY2Qv!@T8fUHG<^jYGW#X9M!N0}>?%nX!jz-ig z@O=EluVF*Au*d@JB&ME2AdXi@wZ(4QIW2gKKHt}$*a#ndqcf$bSIYSQx!xAIQLnf6 zu+f(Sr3>C`3^xZFe!|t>iYSh!%6*ysQV85KSoda>+lns0z5J<{=<$cy*(#{*a)%%- z{MWm3>d_HMv3;xgv%w$CKdD?{9=5?V_Rkw*>(G0*jjkNy3$Z<}aE1@_AKR~K6#G5!%9b40O#M-xzdI2dY`QP9pfsRrP}B9> zm&fAmw|ke>yc3~VC;wuL&La98+q1b(h1lA!|CfumtO!SEkVuRyN^?t3VY%@aTl#Rb zND`No1M~TTF>!N`$D%dZ9(VdPewz!znCXU5nI>B!1;#$Z{ZcgWH11tI0aFu>0MXiM zR|iU$3+^9agm-G#=svXKc9$k*Mn1eiq01LNHIs$E$XD!3D&U8At}SoPL0+&=ijVD1 zy;g_y=EO>cvL=A>ys5qw@(cXy{8xe^)ELO9WomvMLAt4;d(V+M(NLZOLkvpzaAnI8 zOVA8EA<~x^4`J`n=Sdti&#dfnK(eQkC(mT(EM|Gc*?(|O`tCG5h+AoqHdK1e=n7WK zHfK`qb0c+6rWgqFZ#$Sau+NPK;a{7W%Xx%v#+Ns85;X$hY{jFE8H(%Dc?*;T7ihms zpO+VE#g{5y7*3V2OX)5q?Q*Waylg?nkhByz5P1Gbbx@=r)q>ihO;tsppcXL+!U+)c zjOab;cJI#6X4@{k{TYE=XYqH}qJmlTW<7noK;Dz)paWD|a7U``C=Rp;UXg{>^ilqd z=&fFgXeaVF{zKR&1%_tF0W-hF+`;d!#vh8#@@Wz~!RGk_aHLcFr@nXLj=49{)YUd* z1<;i%T~FfXF(wms%1jasRDwB`Z_Eujr{4zwF%5n%9njL?eH~ z`(iNGp8Rlt)aQ}|>+&H@EH7h~sTdxH*+Gai0UXHg*14V`!@9v1@2iQ5jvowKjz8o$ zptTbgoHcKY;tBuOy5CpvPSU9%y$X|IuT>H|4VBX_jQx-B;?7re!awf^@{N~ZfzH+7 zY6ROy_(@cxewrniKnxa8yAFlDgR&YGsJ@Se3S)(2F*wBo5YgCzHU{ha@jfi3fWy}l zc?`Ga1uJYQX_*QY>n5PSOeJV(1eT(tJwrr;#Cyx@6a|tPX9Ou zu0nQd66qGsE|Ua2e&fgowHP@KT$rGLZg0`o5>&V|9tIiUKDW*3+PXf&C;T)0F^MUX zh7LmMtD;K>dNFfO&T^*sPy5pX89JZ135WErM`Ssq9OMVd9_WT4#;VXy-^J-^zpv9q z_SGyONPKzHU%d=_jIhW16aHp7;T|Me6OZZZGaCG*yLHdke&OnC^(}?Tc0C))K~r5J zpWp**JJ;c0p{TIEUspg#`l3`E?UB9ru4rwnRbK5sZu=K&bBD?D_A9pHfga8St{;n8+y{AQ1@ItAGJnCM4eBulU zxaJ|8DSc^f-~2c3!b8|IJrLSZGJbltzG5A68d}<7)OEetW_s_gF9%fd@ zR@&4Zc7U%-h`AMFiL8Nc&jR$%F!U%HMu&(RO($;RCULbnqG){~-q(oh}k2c4Nr@e3e!y71uKSuqLd#c^U0bEu4CSHF`jbL_o znnnPB-3R@T?Yh)7)3Q+X!6iZo$CQy7*?F?x+z4pf0Fif}7~BK|^SEaOFFaL{WQO4- znt^aXgdK{0Oa=+Gc#=QAux5$%1{cos>8oo)*TGu<{HxG@i`R7&#g6(tHFRQp4i!YD z*lF630eI5kC)Nm$_#2_i*w6K29HHogiN>oIj6Kh;g|dbGL_(1NLAxp}t1rp2Vzxt8 z+rg1%9zj+4Zaf84h`2Vblmrf?+E4}3l6P`6?ul?&&d|?oI>&!3g`;$K8zfcS{t3K1 zNL`x@Qh-Q*RTuo$)hOW*5Oe??A7n9T$M<7IR;RYLMogTp7vqx?wtCy*+xG^12-Aeg z5PrSdQd6wQL;G63^$1`bltrP)8c+*Gn4?F?l6guJ?6aKZWEDNB;u>E6J<_RML8IwL& zT^c{30j8bbB|e0qtspQu1F_hWEi^$_g?Aq* zG)03m^EPaIingxQ9MK9Y2Yyo~m-FP`m_DuV~Vb_M=tFvOLZs8uo)wc+BP36quqv~y)CkMH<&AEHROFNp%O%&(hL9B%zTtU*UY9t@yuFT_~ADc z2+|;s`=J#UJ!_m(EfcU@QvW)0Q1c-WkR6|hdtq$ZUi|a}I(`DgK(%y@2Kk_&1`u1w zxOvLs`YA*vkn3jO`2w$Lc_a5M=4f9`7{k5D4m$cILZ)|au5YHt00 zaAP|xQQdiPcOXBm|LrogpOc4Om)JLZDd4bWz!eCRzX{La& zY}l~9Nsd2r5eafwG&zOc90<`;ufggAX)}+ew?2d?=@)aP(EV`pv@=7P_+rk;(`Bdo z!u72<5P&+FUQ3Qxx|n}m(ySttV7AD9x*dp;h6CN}Yu$OKwN@ki@z<4gsAduBRyNRd z1h@0Jq8=?WGHkjZEC;g*1X!2>@*pMT2}?z4dH*#%UK1?37ew9#9%OM(%zNkU`LwaK zw%6a#KX*6R-*Of6h&-j&#mT$&KahU}s1c;V0PD3Q~D_z2pH1y5L+w_GTaT$Y?`ssR% z!)$Qy%|=9$^W=e{#EX=%PHD=&gUeURbgO_GiW&75WDK~w#v7FUnEX94Ri5#jN=-B4e(Tsvtecrjd2u}Axh)ekki>L)wx>L z8OF+P3`&WS*7geiayCX&6KQPDqZCNVriiy$KQrvQnSG-+ zCS(7^bqvLEG%)89ajGluX&P^vy7o5Fh)nB!rxq(PS857XB1g9s%gwKi0kfg@t*bE* zq#MPpux1Naf`eOt=R6>WhZp}PU3P*>>K&g6L}ywwRGDyDI-$nHr~%*^*M;Pl7EfhIUap;c2h1AiwOb-8X3YwNI~ao{Q2 zg7aP@UF;iv;aeTdIK&{FMt!g}age9)w1gZeUv)fS z+n|TOHg0qvJtf;neE{1Tsn zybnJ-LqBbObp%fb6sEdw?H!k)Puj3O|6YM>jrrTGQdZ6CpU1B6>4|&?uwjF3k1a@| zSBQ(?T*L(+V$Cu^x2J^sEv7bWTN!MSVE_{4m?~utLpsT$CMrsY>w(Xoh9c)OC`=)p z$JV~Q?XcE}{p1MH%M~lyST?wS6B&8R@t;Swn77h_ALw9dqw_vYfb4v;YL$D=3PjQmZ~P?TkOJAj!iFiMsq*C-^$cvZoI9$dtHlFR56kZj!T;YN*# zW;neiZ>d(K=urcyBg&%`9V`cD5&YTd?_#)R^9ktILCwHq2wSrU9IHi5e;eW3FY3cy zUn2ku4~Q5e{H-?^kmVS+&hJLaVPjj?d_hPjskmB{C+Scpw3Rk3iE_9Q6Zs<lY#fVsy5ps-$GB_wBtuqzD{xiY|)#x>VQBVmj@fX|1wp+x7i*fg~efcM74kxM&x zG=nk5Hm$2@(FKl{Eq0&!9Q=W0wa+Nb)v<+9c&f{{z$&8amImNygcRExx@nr#jqG67 zlWZc{xCxo1)>M^M@`u`ctXdjp1sn)7KAKX`Xz z=oBD4-zVTWp{W5ShAlEWlIT|+|GBELfGW@gw@9#NVur93ALAjZ-$7B*JAcgI>}&LJ zZtrT21lJ$Scp~#qQt$}pUmN4K|8#&>+YxvEwXgZ^5EG?jP)?ez*d3L}B88H0Y;Z^; zd=Epax6685H6oz{?Mxl%SKWcZ{mUtBi-!VNF+e)AR0nhfZX>t6XQ%+;YyH6YShYtp z>~}7h8^E%H`WYLmc!2peeK8>xFGv$LtUj8ruxd~5Z?7s+P8tB*d6&PmI_$|1jv~Xk zXH|T^to_wlxN}*$`wcYNkb8H3U+c5>ky*9?%a`cp*W?sN!m;OGn0{-7)<}08ZG8&l zS$&(}k+IhFe5BRJ0E;t$$&VTRK;qC0VH_=L)0rRZTH(2OmDn1Wfg^V8GvVDp}r7T^rqf4|z?&o1jx4&U`h;#jbf@Ov@ z6m)L3V8^uQU<`aSi(IbaNY6lA$mt~32rV2@qith}-~I<^Ic|s}Zl03<^g2(c9I~n> ze=EgyOs<)Z>s6X@_w3&aP#qF`7^;s)IUGo3$hAvKM64x^heiG<+9&l2MHSnV!iVV2 z#fULVDEn+hL4)_O`FhWNhA*93lAXVdHU(&b8a#F^STo*!9_ZoEdq?03KpJ;!{YGf# z0>dF20r4qBs~aCCrDB^+*4(q~3oduUboQm3tJ>6Oy<5Gvp|c5mZMhl7vUpfa%W35Q z+_37Lf%f$|T#OjC)+DMaX2V$rI3H#2nK~voz4_jE`h0M-mt2~fX*gX4*JPz+{PXBt z#E(~Ii+hN#(5(YlO?<>U2H=zssl+E5Z!C^D4)Ci3Nv-9CrW{?@SvO>9_$d3kUsfxy2A|V?Sc>%vYo$}F>(Mf|0 zYkt(v@1Cmdlqqrm%H@&7nWR7tH+@T}o#g~f&$7)iu}m4F#pWS^r79>7iNw;H=>eo# zPzrJ!{DVWQ=}^*8svJbgTauJV}^+ zYk}-p3r_LX|MlX05&xmvMc%>-SNAmx1v3m|5vL`4R5a%k}h8J<-{44&1z=2 z%e;unn25-z_9h#3j{*3M2p-DOBa)(<>^;BV%Rjh(2{ssw({5qfe#M78A#>`EVUgbn zk2+7@zgVt?2;E9+gt-&~3ba(tHG743DiLX?D$=yL^f&sxaHLGB>VcdT^I<72xv9is zOS0Rn@9j)^;LF-WOw`a~`#O;_1@Z3o(Wvh(;S2&t-BxZDKjo^6y`f^GbAzGN8G-);e(wbEEhr^^vUq%u_ICE^h|TH9GxF?G=Lr}v-S zhOpau1DpY$kJlju?8DS*DQ|0|YwTantv=0Xh`&gf+qAW0y#QE>Ff%N+PpLpZhk*m?9SLcRoXC?xd}`TAI4#5&Mm2N^Rh zPQVC?NGc=QTs`>nglRIzS6((hKX*<}USdLgW@K`!_S$F4j>j2{%jt;xe=A|alQ`5x zf0<|WdohqWj@naO;3C=eaAEUNiax8ejr5XDi&?MGhM_~dDR$g{`FUP;*XY)|Z>o+( zv@K6+Q5|*U-^0Z>RK?gZag9%G200#{zYt^Hkl$Z!vvoX*PaNQH@urqmCZBqkVo}x# zwmV?H_cn)xWZ#GXpCN_edDi(1=9Z;yyayh&5u#AVXFN*Ukiy$kMxY7q145=EN_ z+PnJ0c;M^iL3Tg6k;8B!Mh?MRIB=evfC%zH*lfyk{(~at?9?V#{fE84MN>DhIaAj~;x;1x89e8hqWwpyiKpN&8650W<>3r>DLO5`T4DS#jBy1X zyv-G2b3DT)rk8GwqwwI>WiR){%;(eo44-2pcYE#KS)F%x33U67ulKFq)Jx4#%;BiV zN5hRSKL&ZZLm*+A_4s9#B>2sCxe^J)xtAe!RF8u=aWm;}&Ew=B(Otqs*h|-Ih)jPh z&8lR-Y@NO4>AH1laMw<4Y= zelXR^b3hT*paSrWe1KZcMaPDC_9^-+vXTPl`5_& z&UmXVBGex7(NL~OZp7~{_4Ctx0{om+C?$tfyhlw*Pn>dAV#*spig(~rQsbR#UrKo{ zcZ!>`BfN_Wh=oXIK#%cqFur3ISbOT9ZL>7L+{Lq!$0UlOLLak_2(hx$t|n z2p`UIEEXs5h#7aO8qAkb!L~up&BlC?<97NcBNN|2=FlS5YQ`J*$7Ix6o_FyiaZy4U z3JR37*9(qvhf>$VmUog&u8xfuxH|#9_}A%p7X19>fM|#@w$d+^zlT^g zK<(&*_60M`e!3gamIkL}!r)*8_zkpc;)W^c`T3v~)#*6lqiH3rLxQpnidAGS&c9Se z_FzX40Ok%b-COFtYxcdEma4%|6c4nb#*OYN5cQ*-o|y#Vlt z{c>u2I&H%Zz5-|lQ4PZ}r}EK;WDGwVY}D|g>SACP%ceh0N>BscfsAw+%1DIcjl1CD zolF+?7Pnis{My9}j`(=7z&6wL_<*&er|gIj6wkJdTgh27C{%PJd1Hl>voj_eGm@Qj z=Qe3u=PoWk!k!k;6{>&tR<7+&ldn7Or;hmJPS=27W)aFW;sOzW;4C&%RUr?;fEwN# z<@B$rHiz0;m19|^!EOJ;8Q^2a4_rpt7yP&DP_ z_t+6ZqC}y5Yh?M)xSYJ&(Xd*7#;?Me`u2z<;8#u~eQ?D4gKK>ki6i!KQVNyO%SR`j zsMx`wA_cRl0_rukwR_|yN>(gTK4jo_}LAJWl6e)h|w^A z@qK>$Bu{VBbOr1ZXhV)f$hTq1J4oSU^r?q#?Nf?w1rh z3|Lm@&q`V#g&8;ML!b-Gr6Y&vooHmtRu-VTdfM#d;^zN<70fwA1dPhU7y2kN%&x(l zAE1Zm&#n#UN}N3GU}>XmYtxA1lSO}x?;&JR^@OZD!bbzg<0;R}i9`Z;zQrTz!kqjp zxX2VXBuqBc)RhX!yB`W*ry)yTAVx;-;?ro&Fh1g_g=gZ1BZMzhi#g~t6=bkGkvNuL zZ7eC;sE24ZNfi>|3!h3dIMSNVR@}hjC{%@?eh*Rz-s#3+i@B>>BO@2(bVv6n zUzi}3v$K{KxdA9fW2$8?bda=Ku|1QVjq+E@EGAM zE$Q_~5nbOZ_J+$+i-nrW{CjPS!EQ_e~ z^~Nqd; zRP%7p3pb@?QPeX$A=rzmCOLkY$6~IIK&Z@q#J5%gTz(mZGdqD#p{Nu_iHkAitA8sN z5(E}d9If@Ja08IAjUKb|8znW$e;6jL<@&cuMjaE%3l;llSFoK*w<;nUt8Un+Q})(G z&rpch?xdp8q%L%+Nfw6voDJ=~4p)jXYn5-3cot2|kuru=zH;d|%{*2YCnoMf1MIfx zU7ip+AH17c+7+arp(>D1HXX{74g-4MYAH(V zez1+HNvEnl0R~Ww6%p z@km}pMw!r*3_%aE37G!?38bZs&$`{zMP>It=5hh2ySf6+bn#$R0W6G@H;AHoz#H*+ z0X$6ocC6PV(vl6$BRX-q3#rXq#M)Bw15^Q-xWB7qy<+zAsJB1+hs3Mb8x(%s87z-` zFr~)~Sz@J4pt(50^=nKxOd4_>nQ#*Y-&E@JUJZC^o|g=YX!1~Y{d}4(w#&Oj8Gp~> zmgGoRwGbY2`AT)5BITc80{Jz;f{2U93mfKxPh9uY%NbSBq`$Ss~+h42Rck5u4~8 zA*#Hhozqu`3R1D`ylS7qN74dHH~E7lFM4~)F5sC!`3)u_&N&KWZ)>Z%L!5gdM^e!t z3sFWU3G}Lx%_##XPD9d|a=sIY3#%~?8WR2jh$8NfKMkr`M8j4fVq`yXu$Z#jEXe)9 z=J2SdCcMg}`*6jZJgdls`TrK+eXTJG<~-ajl)#B*43%NYO_9k+~d zW?SRJV>$>$8)_e53n;NznN#O8M2vvvgqfqykAPFV3YO^jBnzltFT(sKu!)9G zQpEd$_sw1G;tV4n(44R(V4QZm5Z++r$Jvs)Nm2~i1#n)EE20w0SSXzcb{k$)q`|Xd(C_W%| zLMGwh3S}0uCA12QES=|S7r-0w$mr@q$SYecdKeZcB?zhNWE>HX9^UnRaWa_$lAUyr zaAcXr{s{LWlM->Bnb+q#sZIgo&2gu#igwuiwO)LujCZldSIaF6Q{EDGzl_xaPmGs?9#ez+2ss&K zM{UU|8<<=V;JIL%WLpq|tNoces|SgguEK!seQ7PS-h|3Md9kA*^-fyQkHfNoN<1BC zC2G%gD+;eYp1=fd%-!5woZ{W!vbq@!pFAt;{WH7qV-dXyG^S|M{6YefBH!v!PxnA6 z0e8!v0$rY+E%HU4mCGe&Qk}jdN<#v36ovo?GVJFKMssa539RG#m^ZPQ)?vVnFcO*_SRgK{s7e>7#h7#_x_ z#kNq8HcZUnXl3KdMp?+pl~ViLB!cRAY*~D^pwRn-9EjbLm092?w7eh2TKv}^ylCf{ zcX^UH$H|hbi;AU`bp2rL2)J zYzKYWNCy;W^sDzB${wRq1%YG-EvBZl4>(BntKxp^RFsbzf!b6VQ9j zPb@4voe#w#$fJ1k>DA(VvT79lwTD>H$*NTJbZAFbO}@aGEEE+4jk-t7UI}Ibf6X>lU1mjmP16~9YgEElT6v+Wp%sf=`LNF0 zVt<&{YJ>dwpo3PtQI#=z1DQXR?r~9@LSKdz9z!xDJYus{zxa2UIqltxI<0bAmuzK; zR1(dSqhq}e)}{Ue<{c$|)1<7dt5}eWfLV&bNW3=2lH?X?LAmR>mNz~x+#~R| zZ(R)Z#8YD;*FHU_8dWM&rMhiwsE-C!?|7Uu+Ur zFJs2Tr8l@TlK5CIjSmIKjCO+Bo30H0W`rOK`@{1iY^;RfNn)Kuz7`Br@h)An!} zFQKW?5_FXkNMH>+@;y41R$L%=v{kw)j7M8%lVDIUYo*+Tc<_o4^c3N2&P!ut!WS+y z8RQb}44qujUWhll1W&VJZ4G#X8#A8ODb?(fYjxH^@32mkxr~}giR6es++1E;SO)^} z=T~TNE;!(v;I2gZ$DrL)=W>c|9D3Z4wV6ZS*arItYOtql8Q|j2*~oh&3YDs3^fV^N zSCgeTo8>}cPv?4Z^TXtz3%HW~wyl3UrC3)PQsc`74JfG>CJC^}0?|CUUWJd`if6Pu z>4EuA%pI5gqEWGb#2Hx-buj%tc&T+}6HnzxddO$9;4cQ$f-OX@cx|oz{uKya2`Im?FKGBfFU4_r`e>fGgNuJJj^3XfrH_ z^IpT3xhI;~>hKwUxrnP#U}|Hl?GDcrd7hfr=x3I61DsKCxh;Hw8 Date: Tue, 1 Aug 2023 09:11:14 +0200 Subject: [PATCH 0949/1263] Error out from load-iib when INDEX_IMAGES is undefined If you call the load-iib target you *must* set INDEX_IMAGES, so let's error out properly if you do not. Tested as: $ unset INDEX_IMAGES $ make load-iib make -f common/Makefile load-iib make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' No INDEX_IMAGES defined. Bailing out $ export INDEX_IMAGES=foo make load-iib make -f common/Makefile load-iib make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' PLAY [IIB CI playbook] *** --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 83871b7e..d8182da6 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,9 @@ load-iib: ## CI target to install Index Image Bundles for IIB in $(shell echo $(INDEX_IMAGES) | tr ',' '\n'); do \ INDEX_IMAGE="$${IIB}" ansible-playbook common/ansible/playbooks/iib-ci/iib-ci.yaml; \ done; \ + else \ + echo "No INDEX_IMAGES defined. Bailing out"; \ + exit 1; \ fi From 81ea8946a221816afcef39c48a76737be66b048d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 1 Aug 2023 17:09:45 +0200 Subject: [PATCH 0950/1263] Add docker.io to the whitelisted registries when loading an IIB Medical diagnosis for example uses docker.io/obsidiandynamics/kafdrop:latest which would be denied by policy. --- ansible/roles/iib_ci/tasks/setup-internal-registry.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml index 82ee7ac4..4e31928f 100644 --- a/ansible/roles/iib_ci/tasks/setup-internal-registry.yml +++ b/ansible/roles/iib_ci/tasks/setup-internal-registry.yml @@ -45,7 +45,7 @@ - name: Set registry allowedRegistries ansible.builtin.shell: > - oc patch image.config.openshift.io/cluster --patch "{\"spec\":{\"registrySources\":{\"allowedRegistries\":[ \"registry.stage.redhat.io\", \"registry.access.redhat.com\", \"registry.connect.redhat.com\", \"ghcr.io\", \"gcr.io\", \"quay.io\", \"registry.redhat.io\", + oc patch image.config.openshift.io/cluster --patch "{\"spec\":{\"registrySources\":{\"allowedRegistries\":[ \"registry.stage.redhat.io\", \"registry.access.redhat.com\", \"registry.connect.redhat.com\", \"ghcr.io\", \"gcr.io\", \"quay.io\", \"registry.redhat.io\", \"docker.io\", \"registry-proxy.engineering.redhat.com\", \"image-registry.openshift-image-registry.svc:5000\", \"{{ registry_route }}\"]}}}" --type=merge - name: Set registry insecureRegistries From 2ec5ef5e4e2445cf2d377efd48fff545d25d66a0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 9 Aug 2023 22:13:58 +0200 Subject: [PATCH 0951/1263] Fix fetching the bundle name when loading an IIB The current -w grep command matches channels even when they are substrings of each other like for the serverless-operator: $ podman run -i --rm $INDEX_IMAGES alpha list channels /configs "${OPERATOR}" |grep --word-regexp "stable" serverless-operator stable serverless-operator.v1.30.0 serverless-operator stable-1.29 serverless-operator.v1.29.1 serverless-operator stable-1.30 serverless-operator.v1.30.0 This then causes error when trying to parse the list of images as we will have multiple images because we break the one image per line assumption. Let's fix this by adding spaces around the grep. Ideall we'd use the opm render command, but the parsing of all the yaml output seems a bit much to do for this simple use case. --- ansible/roles/iib_ci/tasks/fetch-operator-images.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/iib_ci/tasks/fetch-operator-images.yml b/ansible/roles/iib_ci/tasks/fetch-operator-images.yml index 10f083e9..72e22d18 100644 --- a/ansible/roles/iib_ci/tasks/fetch-operator-images.yml +++ b/ansible/roles/iib_ci/tasks/fetch-operator-images.yml @@ -43,7 +43,7 @@ - name: Read the operator bundle from the default channel ansible.builtin.shell: | set -o pipefail - podman run -it --rm "{{ iib_image }}" alpha list channels /configs "{{ item }}" | grep --word-regexp "{{ default_channel }}" | awk '{ print $3 }' + podman run -it --rm "{{ iib_image }}" alpha list channels /configs "{{ item }}" | grep -E "(\s){{ default_channel }}(\s)" | awk '{ print $3 }' register: bundle_channel_raw - name: Set bundle fact From 5d908dbb89fd8d4436020db7ca2b0e0286dfdeba Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 10 Aug 2023 09:32:09 +0200 Subject: [PATCH 0952/1263] Add retries when getting related images I have seen it fail once on a system and it worked on the second run, so let's try a couple of times at least before giving up. --- ansible/roles/iib_ci/tasks/fetch-operator-images.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ansible/roles/iib_ci/tasks/fetch-operator-images.yml b/ansible/roles/iib_ci/tasks/fetch-operator-images.yml index 72e22d18..11df26cc 100644 --- a/ansible/roles/iib_ci/tasks/fetch-operator-images.yml +++ b/ansible/roles/iib_ci/tasks/fetch-operator-images.yml @@ -20,6 +20,9 @@ oc get packagemanifests -l "catalog=iib-{{ iib }}" --field-selector "metadata.name={{ item }}" \ -o jsonpath="{.items[0].status.channels[?(@.name==\"{{ default_channel }}\")].currentCSVDesc.relatedImages}" register: related_images_raw + retries: 5 + delay: 10 + until: related_images_raw is not failed - name: Set related_images fact ansible.builtin.set_fact: From 6243e491807f37f0a3103ed8806e198a6ba44bef Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 15 Aug 2023 10:13:30 +0200 Subject: [PATCH 0953/1263] Add workflow to split helm charts into their own repo This will push any change done to a chart folder out into the separate repo corresponding to the chart that has been changed. This workflow needs a secret called CHARTS_REPOS_TOKEN that is a Personal Access Token with fine grained repo and workflow write access on the following repos: - acm-chart - hashicorp-vault-chart - golang-external-secrets-chart - clustergroup-chart - letsencrypt-chart --- .github/workflows/chart-branches.yml | 117 +++++++++++++++++++++++++++ .github/workflows/chart-split.yml | 61 ++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 .github/workflows/chart-branches.yml create mode 100644 .github/workflows/chart-split.yml diff --git a/.github/workflows/chart-branches.yml b/.github/workflows/chart-branches.yml new file mode 100644 index 00000000..0eb7d978 --- /dev/null +++ b/.github/workflows/chart-branches.yml @@ -0,0 +1,117 @@ +--- +name: Create per-chart branches + +# We only run this job on the charts that will be later moved to full blown charts +# We also want to run the subtree comand only for the charts that have been actually changed +# because git subtree split is a bit of an expensive operation +# github actions do not support yaml anchors so there is more duplication than usual +on: + push: + branches: + - main + paths: + - 'acm/**' + - 'golang-external-secrets/**' + - 'hashicorp-vault/**' + - 'letsencrypt/**' + - 'clustergroup/**' + +jobs: + changes: + name: Figure out per-chart changes + runs-on: ubuntu-latest + permissions: read-all + outputs: + acm: ${{ steps.filter.outputs.acm }} + golang-external-secrets: ${{ steps.filter.outputs.golang-external-secrets }} + hashicorp-vault: ${{ steps.filter.outputs.hashicorp-vault }} + letsencrypt: ${{ steps.filter.outputs.letsencrypt }} + clustergroup: ${{ steps.filter.outputs.clustergroup }} + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - uses: dorny/paths-filter@v2 + id: filter + with: + filters: | + acm: + - 'acm/**' + golang-external-secrets: + - 'golang-external-secrets/**' + hashicorp-vault: + - 'hashicorp-vault/**' + letsencrypt: + - 'letsencrypt/**' + clustergroup: + - 'clustergroup/**' + + acm: + needs: changes + if: ${{ needs.changes.outputs.acm == 'true' }} + uses: validatedpatterns/common/.github/workflows/chart-split.yml@main + permissions: + actions: write + contents: write + with: + chart_name: acm + upstream_repository: validatedpatterns/common + target_repository: validatedpatterns/acm-chart + force: true + secrets: inherit + + golang-external-secrets: + needs: changes + if: ${{ needs.changes.outputs.golang-external-secrets == 'true' }} + uses: validatedpatterns/common/.github/workflows/chart-split.yml@main + permissions: + actions: write + contents: write + with: + chart_name: golang-external-secrets + upstream_repository: validatedpatterns/common + target_repository: validatedpatterns/golang-external-secrets-chart + force: true + secrets: inherit + + hashicorp-vault: + needs: changes + if: ${{ needs.changes.outputs.hashicorp-vault == 'true' }} + uses: validatedpatterns/common/.github/workflows/chart-split.yml@main + permissions: + actions: write + contents: write + with: + chart_name: hashicorp-vault + upstream_repository: validatedpatterns/common + target_repository: validatedpatterns/hashicorp-vault-chart + force: true + secrets: inherit + + letsencrypt: + needs: changes + if: ${{ needs.changes.outputs.letsencrypt == 'true' }} + uses: validatedpatterns/common/.github/workflows/chart-split.yml@main + permissions: + actions: write + contents: write + with: + chart_name: letsencrypt + upstream_repository: validatedpatterns/common + target_repository: validatedpatterns/letsencrypt-chart + force: true + secrets: inherit + + clustergroup: + needs: changes + if: ${{ needs.changes.outputs.clustergroup == 'true' }} + uses: validatedpatterns/common/.github/workflows/chart-split.yml@main + permissions: + actions: write + contents: write + with: + chart_name: clustergroup + upstream_repository: validatedpatterns/common + target_repository: validatedpatterns/clustergroup-chart + force: true + secrets: inherit diff --git a/.github/workflows/chart-split.yml b/.github/workflows/chart-split.yml new file mode 100644 index 00000000..df1bd548 --- /dev/null +++ b/.github/workflows/chart-split.yml @@ -0,0 +1,61 @@ +--- +name: Split into chart repo branches + +on: + workflow_call: + inputs: + chart_name: + required: true + type: string + upstream_repository: + required: true + type: string + target_repository: + required: true + type: string + target_branch: + required: false + type: string + default: main + force: + required: false + type: boolean + default: false + tags: + required: false + type: boolean + default: false + +jobs: + split_chart: + runs-on: ubuntu-latest + permissions: + actions: write + contents: write + steps: + - name: Checkout Code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + token: ${{ secrets.CHARTS_REPOS_TOKEN }} + + - name: Run git subtree split + env: + GITHUB_TOKEN: ${{ secrets.CHARTS_REPOS_TOKEN }} + run: | + set -e + N=${{ inputs.chart_name }} + B="${N}-main-single-chart" + git push origin -d "${B}" || /bin/true + git subtree split -P "${N}" -b "${B}" + git push -f -u origin "${B}" + + - uses: TobKed/github-forks-sync-action@master + with: + github_token: ${{ secrets.CHARTS_REPOS_TOKEN }} + upstream_repository: ${{ inputs.upstream_repository }} + upstream_branch: "${{ inputs.chart_name }}-main-single-chart" + target_repository: ${{ inputs.target_repository }} + target_branch: ${{ inputs.target_branch }} + force: ${{ inputs.force }} + tags: ${{ inputs.tags }} From f6ead1e2704aa8c7e22bdd91bc8f2145f9063398 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 15 Aug 2023 16:06:10 +0200 Subject: [PATCH 0954/1263] Small test for the workflow towards single chart repos --- acm/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/Chart.yaml b/acm/Chart.yaml index 1c3db911..3bae9da5 100644 --- a/acm/Chart.yaml +++ b/acm/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -description: A Helm chart to configure Advanced Cluster Manager for OpenShift +description: A Helm chart to configure Advanced Cluster Manager for OpenShift. keywords: - pattern name: acm From 23c9753fc728a3f338182908507db28880a07c85 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 15 Aug 2023 16:22:48 +0200 Subject: [PATCH 0955/1263] Small test for the workflow towards single chart repos (part 2) --- acm/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/Chart.yaml b/acm/Chart.yaml index 3bae9da5..1c3db911 100644 --- a/acm/Chart.yaml +++ b/acm/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -description: A Helm chart to configure Advanced Cluster Manager for OpenShift. +description: A Helm chart to configure Advanced Cluster Manager for OpenShift keywords: - pattern name: acm From 09bfa2c1a5ef89e6281b083342511b7790bcf923 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 15 Aug 2023 16:57:21 +0200 Subject: [PATCH 0956/1263] Simplify split workflow --- .github/workflows/chart-branches.yml | 10 ---------- .github/workflows/chart-split.yml | 29 +++------------------------- 2 files changed, 3 insertions(+), 36 deletions(-) diff --git a/.github/workflows/chart-branches.yml b/.github/workflows/chart-branches.yml index 0eb7d978..66acda13 100644 --- a/.github/workflows/chart-branches.yml +++ b/.github/workflows/chart-branches.yml @@ -55,9 +55,7 @@ jobs: contents: write with: chart_name: acm - upstream_repository: validatedpatterns/common target_repository: validatedpatterns/acm-chart - force: true secrets: inherit golang-external-secrets: @@ -69,9 +67,7 @@ jobs: contents: write with: chart_name: golang-external-secrets - upstream_repository: validatedpatterns/common target_repository: validatedpatterns/golang-external-secrets-chart - force: true secrets: inherit hashicorp-vault: @@ -83,9 +79,7 @@ jobs: contents: write with: chart_name: hashicorp-vault - upstream_repository: validatedpatterns/common target_repository: validatedpatterns/hashicorp-vault-chart - force: true secrets: inherit letsencrypt: @@ -97,9 +91,7 @@ jobs: contents: write with: chart_name: letsencrypt - upstream_repository: validatedpatterns/common target_repository: validatedpatterns/letsencrypt-chart - force: true secrets: inherit clustergroup: @@ -111,7 +103,5 @@ jobs: contents: write with: chart_name: clustergroup - upstream_repository: validatedpatterns/common target_repository: validatedpatterns/clustergroup-chart - force: true secrets: inherit diff --git a/.github/workflows/chart-split.yml b/.github/workflows/chart-split.yml index df1bd548..fbc4075f 100644 --- a/.github/workflows/chart-split.yml +++ b/.github/workflows/chart-split.yml @@ -7,24 +7,9 @@ on: chart_name: required: true type: string - upstream_repository: - required: true - type: string target_repository: required: true type: string - target_branch: - required: false - type: string - default: main - force: - required: false - type: boolean - default: false - tags: - required: false - type: boolean - default: false jobs: split_chart: @@ -39,7 +24,7 @@ jobs: fetch-depth: 0 token: ${{ secrets.CHARTS_REPOS_TOKEN }} - - name: Run git subtree split + - name: Run git subtree split and push env: GITHUB_TOKEN: ${{ secrets.CHARTS_REPOS_TOKEN }} run: | @@ -49,13 +34,5 @@ jobs: git push origin -d "${B}" || /bin/true git subtree split -P "${N}" -b "${B}" git push -f -u origin "${B}" - - - uses: TobKed/github-forks-sync-action@master - with: - github_token: ${{ secrets.CHARTS_REPOS_TOKEN }} - upstream_repository: ${{ inputs.upstream_repository }} - upstream_branch: "${{ inputs.chart_name }}-main-single-chart" - target_repository: ${{ inputs.target_repository }} - target_branch: ${{ inputs.target_branch }} - force: ${{ inputs.force }} - tags: ${{ inputs.tags }} + #git clone https://validatedpatterns:${GITHUB_TOKEN}@github.com/validatedpatterns/common.git -b "acm-main-single-chart" --single-branch + git push --force https://validatedpatterns:${GITHUB_TOKEN}@github.com/${{ inputs.target_repository }}.git "${B}:main" From f24578972cb1fb4ab83ce46cb513177d828a3c2a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 15 Aug 2023 16:57:46 +0200 Subject: [PATCH 0957/1263] Small test for the workflow towards single chart repos (part 3) --- acm/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acm/Chart.yaml b/acm/Chart.yaml index 1c3db911..3bae9da5 100644 --- a/acm/Chart.yaml +++ b/acm/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -description: A Helm chart to configure Advanced Cluster Manager for OpenShift +description: A Helm chart to configure Advanced Cluster Manager for OpenShift. keywords: - pattern name: acm From bf91925391f64986396c6bc6bc6b00014d0ffa95 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 15 Aug 2023 17:01:01 +0200 Subject: [PATCH 0958/1263] Tiny change to trigger split workflow --- clustergroup/Chart.yaml | 2 +- golang-external-secrets/Chart.yaml | 2 +- hashicorp-vault/Chart.yaml | 2 +- letsencrypt/Chart.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index 38ece255..d4dfdc63 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -description: A Helm chart to create per-clustergroup ArgoCD applications and any required namespaces or subscriptions +description: A Helm chart to create per-clustergroup ArgoCD applications and any required namespaces or subscriptions. keywords: - pattern name: clustergroup diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index ab900162..e7b779b6 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -description: A Helm chart to configure the golang-based external-secrets +description: A Helm chart to configure the golang-based external-secrets. keywords: - pattern name: golang-external-secrets diff --git a/hashicorp-vault/Chart.yaml b/hashicorp-vault/Chart.yaml index 649bd4e2..84e7edc7 100644 --- a/hashicorp-vault/Chart.yaml +++ b/hashicorp-vault/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -description: A Helm chart to configure Hashicorp's vault +description: A Helm chart to configure Hashicorp's vault. keywords: - pattern name: hashicorp-vault diff --git a/letsencrypt/Chart.yaml b/letsencrypt/Chart.yaml index b5b1c31b..899c790e 100644 --- a/letsencrypt/Chart.yaml +++ b/letsencrypt/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: letsencrypt -description: A Helm chart to add letsencrypt support to Validated Patterns +description: A Helm chart to add letsencrypt support to Validated Patterns. type: application From 698aeb31744d089c6ce8da693776f9ba0754f7bd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 15 Aug 2023 17:30:15 +0200 Subject: [PATCH 0959/1263] Add initial helm releasing workflow for acm chart --- acm/.github/workflows/update-helm-repo.yml | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 acm/.github/workflows/update-helm-repo.yml diff --git a/acm/.github/workflows/update-helm-repo.yml b/acm/.github/workflows/update-helm-repo.yml new file mode 100644 index 00000000..d781397d --- /dev/null +++ b/acm/.github/workflows/update-helm-repo.yml @@ -0,0 +1,34 @@ +# Note this workflow is only maintained in this repo +# It should be invoked from each chart's repo +# +# This invokes the workflow named 'publish-charts' in the umbrella repo +# It expects to have a secret called UMBRELLA_REPO_PAT which contains +# the GitHub token that has permissions to invoke workflows and commit code +# inside the umbrella-repo. +# The following fine-grained permissions were used in testing and were limited +# to the umbrella repo only: +# - Actions: r/w +# - Commit statuses: r/w +# - Contents: r/w +# - Deployments: r/w +# - Pages: r/w + +name: vp-patterns/update-helm-repo +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +jobs: + helmlint: + uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@main + permissions: + contents: read + + update-helm-repo: + needs: [helmlint] + runs-on: ubuntu-latest + uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@main + permissions: + id-token: write + contents: read From 3e1b0ac165a5399b84464e524cb302dee4277f59 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 15 Aug 2023 18:19:35 +0200 Subject: [PATCH 0960/1263] Add helm repo updating workflow in the per-chart workflows folder --- acm/.github/workflows/update-helm-repo.yml | 11 ++----- .../.github/workflows/update-helm-repo.yml | 29 +++++++++++++++++++ .../.github/workflows/update-helm-repo.yml | 29 +++++++++++++++++++ .../.github/workflows/update-helm-repo.yml | 29 +++++++++++++++++++ .../.github/workflows/update-helm-repo.yml | 29 +++++++++++++++++++ 5 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 clustergroup/.github/workflows/update-helm-repo.yml create mode 100644 golang-external-secrets/.github/workflows/update-helm-repo.yml create mode 100644 hashicorp-vault/.github/workflows/update-helm-repo.yml create mode 100644 letsencrypt/.github/workflows/update-helm-repo.yml diff --git a/acm/.github/workflows/update-helm-repo.yml b/acm/.github/workflows/update-helm-repo.yml index d781397d..8c658a18 100644 --- a/acm/.github/workflows/update-helm-repo.yml +++ b/acm/.github/workflows/update-helm-repo.yml @@ -1,8 +1,5 @@ -# Note this workflow is only maintained in this repo -# It should be invoked from each chart's repo -# # This invokes the workflow named 'publish-charts' in the umbrella repo -# It expects to have a secret called UMBRELLA_REPO_PAT which contains +# It expects to have a secret called CHARTS_REPOS_TOKEN which contains # the GitHub token that has permissions to invoke workflows and commit code # inside the umbrella-repo. # The following fine-grained permissions were used in testing and were limited @@ -27,8 +24,6 @@ jobs: update-helm-repo: needs: [helmlint] - runs-on: ubuntu-latest uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@main - permissions: - id-token: write - contents: read + permissions: read-all + secrets: inherit diff --git a/clustergroup/.github/workflows/update-helm-repo.yml b/clustergroup/.github/workflows/update-helm-repo.yml new file mode 100644 index 00000000..8c658a18 --- /dev/null +++ b/clustergroup/.github/workflows/update-helm-repo.yml @@ -0,0 +1,29 @@ +# This invokes the workflow named 'publish-charts' in the umbrella repo +# It expects to have a secret called CHARTS_REPOS_TOKEN which contains +# the GitHub token that has permissions to invoke workflows and commit code +# inside the umbrella-repo. +# The following fine-grained permissions were used in testing and were limited +# to the umbrella repo only: +# - Actions: r/w +# - Commit statuses: r/w +# - Contents: r/w +# - Deployments: r/w +# - Pages: r/w + +name: vp-patterns/update-helm-repo +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +jobs: + helmlint: + uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@main + permissions: + contents: read + + update-helm-repo: + needs: [helmlint] + uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@main + permissions: read-all + secrets: inherit diff --git a/golang-external-secrets/.github/workflows/update-helm-repo.yml b/golang-external-secrets/.github/workflows/update-helm-repo.yml new file mode 100644 index 00000000..8c658a18 --- /dev/null +++ b/golang-external-secrets/.github/workflows/update-helm-repo.yml @@ -0,0 +1,29 @@ +# This invokes the workflow named 'publish-charts' in the umbrella repo +# It expects to have a secret called CHARTS_REPOS_TOKEN which contains +# the GitHub token that has permissions to invoke workflows and commit code +# inside the umbrella-repo. +# The following fine-grained permissions were used in testing and were limited +# to the umbrella repo only: +# - Actions: r/w +# - Commit statuses: r/w +# - Contents: r/w +# - Deployments: r/w +# - Pages: r/w + +name: vp-patterns/update-helm-repo +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +jobs: + helmlint: + uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@main + permissions: + contents: read + + update-helm-repo: + needs: [helmlint] + uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@main + permissions: read-all + secrets: inherit diff --git a/hashicorp-vault/.github/workflows/update-helm-repo.yml b/hashicorp-vault/.github/workflows/update-helm-repo.yml new file mode 100644 index 00000000..8c658a18 --- /dev/null +++ b/hashicorp-vault/.github/workflows/update-helm-repo.yml @@ -0,0 +1,29 @@ +# This invokes the workflow named 'publish-charts' in the umbrella repo +# It expects to have a secret called CHARTS_REPOS_TOKEN which contains +# the GitHub token that has permissions to invoke workflows and commit code +# inside the umbrella-repo. +# The following fine-grained permissions were used in testing and were limited +# to the umbrella repo only: +# - Actions: r/w +# - Commit statuses: r/w +# - Contents: r/w +# - Deployments: r/w +# - Pages: r/w + +name: vp-patterns/update-helm-repo +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +jobs: + helmlint: + uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@main + permissions: + contents: read + + update-helm-repo: + needs: [helmlint] + uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@main + permissions: read-all + secrets: inherit diff --git a/letsencrypt/.github/workflows/update-helm-repo.yml b/letsencrypt/.github/workflows/update-helm-repo.yml new file mode 100644 index 00000000..8c658a18 --- /dev/null +++ b/letsencrypt/.github/workflows/update-helm-repo.yml @@ -0,0 +1,29 @@ +# This invokes the workflow named 'publish-charts' in the umbrella repo +# It expects to have a secret called CHARTS_REPOS_TOKEN which contains +# the GitHub token that has permissions to invoke workflows and commit code +# inside the umbrella-repo. +# The following fine-grained permissions were used in testing and were limited +# to the umbrella repo only: +# - Actions: r/w +# - Commit statuses: r/w +# - Contents: r/w +# - Deployments: r/w +# - Pages: r/w + +name: vp-patterns/update-helm-repo +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +jobs: + helmlint: + uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@main + permissions: + contents: read + + update-helm-repo: + needs: [helmlint] + uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@main + permissions: read-all + secrets: inherit From 7d442bb38798f8892bbc19d20793d11f73609917 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 16 Aug 2023 16:22:54 +0200 Subject: [PATCH 0961/1263] Fix up CI superlinter on github actions --- .github/workflows/chart-split.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/chart-split.yml b/.github/workflows/chart-split.yml index fbc4075f..84f027f6 100644 --- a/.github/workflows/chart-split.yml +++ b/.github/workflows/chart-split.yml @@ -29,10 +29,10 @@ jobs: GITHUB_TOKEN: ${{ secrets.CHARTS_REPOS_TOKEN }} run: | set -e - N=${{ inputs.chart_name }} + N="${{ inputs.chart_name }}" B="${N}-main-single-chart" git push origin -d "${B}" || /bin/true git subtree split -P "${N}" -b "${B}" git push -f -u origin "${B}" #git clone https://validatedpatterns:${GITHUB_TOKEN}@github.com/validatedpatterns/common.git -b "acm-main-single-chart" --single-branch - git push --force https://validatedpatterns:${GITHUB_TOKEN}@github.com/${{ inputs.target_repository }}.git "${B}:main" + git push --force https://validatedpatterns:"${GITHUB_TOKEN}"@github.com/${{ inputs.target_repository }}.git "${B}:main" From a86f25fe6869ca7c9bbb94e305c8fd64e3d75056 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 16 Aug 2023 09:42:19 -0500 Subject: [PATCH 0962/1263] Fix tests and make .disabled explicit --- acm/values.yaml | 1 + clustergroup/templates/plumbing/applications.yaml | 3 ++- clustergroup/values.yaml | 1 + tests/clustergroup-industrial-edge-factory.expected.yaml | 1 + tests/clustergroup-industrial-edge-hub.expected.yaml | 1 + tests/clustergroup-medical-diagnosis-hub.expected.yaml | 1 + tests/clustergroup-naked.expected.yaml | 1 + tests/clustergroup-normal.expected.yaml | 1 + 8 files changed, 9 insertions(+), 1 deletion(-) diff --git a/acm/values.yaml b/acm/values.yaml index 7c4a19c0..1100bafd 100644 --- a/acm/values.yaml +++ b/acm/values.yaml @@ -3,6 +3,7 @@ main: channel: "gitops-1.8" global: + extraValueFiles: [] pattern: none repoURL: none targetRevision: main diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 075e1bdb..16f5c11a 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -4,7 +4,8 @@ {{- $namespace = "openshift-gitops" }} {{- end }} {{- range .Values.clusterGroup.applications }} -{{- if or (.generators) (.generatorFile) (.useGeneratorValues) (.destinationServer) (.destinationNamespace) }} +{{- if .disabled }} {{- /* This allows us to null out an Application entry by specifying disabled: true in an override file */}} +{{- else if or (.generators) (.generatorFile) (.useGeneratorValues) (.destinationServer) (.destinationNamespace) }} apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 117e009e..b63e8cc2 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -1,4 +1,5 @@ global: + extraValueFiles: [] pattern: common targetRevision: main options: diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index be93aa88..1875637b 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -136,6 +136,7 @@ data: enabled: all global: clusterDomain: region.example.com + extraValueFiles: [] git: account: hybrid-cloud-patterns dev_revision: main diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index a759bdde..79c1ca76 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -297,6 +297,7 @@ data: enabled: all global: clusterDomain: region.example.com + extraValueFiles: [] git: account: hybrid-cloud-patterns dev_revision: main diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 175f134b..5553e916 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -284,6 +284,7 @@ data: enabled: all global: clusterDomain: region.example.com + extraValueFiles: [] git: account: hybrid-cloud-patterns dev_revision: main diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index e15566b0..9499eb5d 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -65,6 +65,7 @@ data: targetCluster: in-cluster enabled: all global: + extraValueFiles: [] options: applicationRetryLimit: 20 installPlanApproval: Automatic diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index ab4d4d08..e77c84d8 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -193,6 +193,7 @@ data: enabled: all global: clusterDomain: region.example.com + extraValueFiles: [] git: account: hybrid-cloud-patterns dev_revision: main From 28dd3afff7a08723fef75281f4ceacfdea9ca2af Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 16 Aug 2023 16:52:20 +0200 Subject: [PATCH 0963/1263] Make sure we run the split workflow only when the changes land in validatedpatterns/common Otherwise a push to a private for to main would still invoke the split workflow, if a member of the vp org would do so. Since we do not want that let's make sure we limit this workflow to when the repository name is 'validatedpatterns/common' --- .github/workflows/chart-branches.yml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/chart-branches.yml b/.github/workflows/chart-branches.yml index 66acda13..a1b36cf6 100644 --- a/.github/workflows/chart-branches.yml +++ b/.github/workflows/chart-branches.yml @@ -19,6 +19,7 @@ on: jobs: changes: name: Figure out per-chart changes + if: github.repository == 'validatedpatterns/common' runs-on: ubuntu-latest permissions: read-all outputs: @@ -48,7 +49,9 @@ jobs: acm: needs: changes - if: ${{ needs.changes.outputs.acm == 'true' }} + if: | + ${{ needs.changes.outputs.acm == 'true' }} && + github.repository == 'validatedpatterns/common' uses: validatedpatterns/common/.github/workflows/chart-split.yml@main permissions: actions: write @@ -60,7 +63,9 @@ jobs: golang-external-secrets: needs: changes - if: ${{ needs.changes.outputs.golang-external-secrets == 'true' }} + if: | + ${{ needs.changes.outputs.golang-external-secrets == 'true' }} && + github.repository == 'validatedpatterns/common' uses: validatedpatterns/common/.github/workflows/chart-split.yml@main permissions: actions: write @@ -72,7 +77,9 @@ jobs: hashicorp-vault: needs: changes - if: ${{ needs.changes.outputs.hashicorp-vault == 'true' }} + if: | + ${{ needs.changes.outputs.hashicorp-vault == 'true' }} && + github.repository == 'validatedpatterns/common' uses: validatedpatterns/common/.github/workflows/chart-split.yml@main permissions: actions: write @@ -84,7 +91,9 @@ jobs: letsencrypt: needs: changes - if: ${{ needs.changes.outputs.letsencrypt == 'true' }} + if: | + ${{ needs.changes.outputs.letsencrypt == 'true' }} && + github.repository == 'validatedpatterns/common' uses: validatedpatterns/common/.github/workflows/chart-split.yml@main permissions: actions: write @@ -96,7 +105,9 @@ jobs: clustergroup: needs: changes - if: ${{ needs.changes.outputs.clustergroup == 'true' }} + if: | + ${{ needs.changes.outputs.clustergroup == 'true' }} && + github.repository == 'validatedpatterns/common' uses: validatedpatterns/common/.github/workflows/chart-split.yml@main permissions: actions: write From 765eb017fb0d4c2fc349eb7389d8c02fded2e87e Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 16 Aug 2023 09:55:14 -0500 Subject: [PATCH 0964/1263] Update tests. We get an extra (non-impacting whitespace) with the new code --- clustergroup/templates/_helpers.tpl | 5 ++++ ...roup-industrial-edge-factory.expected.yaml | 2 +- ...tergroup-industrial-edge-hub.expected.yaml | 14 +++++----- ...rgroup-medical-diagnosis-hub.expected.yaml | 26 +++++++++---------- tests/clustergroup-normal.expected.yaml | 4 +-- 5 files changed, 28 insertions(+), 23 deletions(-) diff --git a/clustergroup/templates/_helpers.tpl b/clustergroup/templates/_helpers.tpl index c3a730fb..b9feb738 100644 --- a/clustergroup/templates/_helpers.tpl +++ b/clustergroup/templates/_helpers.tpl @@ -39,4 +39,9 @@ Default always defined valueFiles to be included in Applications {{- if $.Values.global.clusterVersion }} - "/values-{{ $.Values.global.clusterVersion }}-{{ $.Values.clusterGroup.name }}.yaml" {{- end }} +{{- if $.Values.global.extraValueFiles }} +{{- range $.Values.global.extraValueFiles }} +- {{ . | quote }} +{{- end }} {{/* range $.Values.global.extraValueFiles */}} +{{- end }} {{/* if $.Values.global.extraValueFiles */}} {{- end }} {{/* clustergroup.app.globalvalues.valuefiles */}} diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 1875637b..3c6d6286 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -408,7 +408,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-factory.yaml" + - "/values-factory.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 79c1ca76..8a5220b2 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -683,7 +683,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -738,7 +738,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -784,7 +784,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -830,7 +830,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -906,7 +906,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -952,7 +952,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1023,7 +1023,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-datacenter.yaml" + - "/values-datacenter.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 5553e916..dbecaa84 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -628,7 +628,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -674,7 +674,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -720,7 +720,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -766,7 +766,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -812,7 +812,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -858,7 +858,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -904,7 +904,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -950,7 +950,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1014,7 +1014,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1060,7 +1060,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1106,7 +1106,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1161,7 +1161,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -1216,7 +1216,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-hub.yaml" + - "/values-hub.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index e77c84d8..901a7d62 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -538,7 +538,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-example.yaml" + - "/values-example.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -593,7 +593,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - "/values-global.yaml" - - "/values-example.yaml" + - "/values-example.yaml" parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL From 2bc3c61ce3bd070086678d128269dabc31c17805 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 16 Aug 2023 10:23:01 -0500 Subject: [PATCH 0965/1263] re-add logic for extravaluefiles --- acm/templates/_helpers.tpl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/acm/templates/_helpers.tpl b/acm/templates/_helpers.tpl index fdd91273..48ff486b 100644 --- a/acm/templates/_helpers.tpl +++ b/acm/templates/_helpers.tpl @@ -10,4 +10,9 @@ Default always defined valueFiles to be included when pushing the cluster wide a # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - '/values-{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}-{{ .name }}.yaml' +{{- if $.Values.global.extraValueFiles }} +{{- range $.Values.global.extraValueFiles }} +- {{ . | quote }} +{{- end }} {{/* if $.Values.global.extraValueFiles */}} +{{- end }} {{/* range $.Values.global.extraValueFiles */}} {{- end }} {{- /*acm.app.policies.valuefiles */}} From e0f956a42f07db09090586af52eb1ac1ecffd21c Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 16 Aug 2023 10:32:26 -0500 Subject: [PATCH 0966/1263] Add more tests for variable definedness/truth --- acm/templates/_helpers.tpl | 2 +- tests/acm-industrial-edge-hub.expected.yaml | 2 +- tests/acm-medical-diagnosis-hub.expected.yaml | 2 +- tests/acm-normal.expected.yaml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acm/templates/_helpers.tpl b/acm/templates/_helpers.tpl index 48ff486b..80b0cf69 100644 --- a/acm/templates/_helpers.tpl +++ b/acm/templates/_helpers.tpl @@ -10,7 +10,7 @@ Default always defined valueFiles to be included when pushing the cluster wide a # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - '/values-{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}-{{ .name }}.yaml' -{{- if $.Values.global.extraValueFiles }} +{{- if and $.Values $.Values.global $.Values.global.extraValueFiles }} {{- range $.Values.global.extraValueFiles }} - {{ . | quote }} {{- end }} {{/* if $.Values.global.extraValueFiles */}} diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 444b833c..428d90d2 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -213,7 +213,7 @@ spec: - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-factory.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-factory.yaml' + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-factory.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index f79e013b..0a12c2dc 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -204,7 +204,7 @@ spec: - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-region-one.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-region-one.yaml' + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-region-one.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 900cc291..42d306b6 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -607,7 +607,7 @@ spec: - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-edge.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-edge.yaml' + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-edge.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -701,7 +701,7 @@ spec: - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-edge.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-provision-edge.yaml' + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-provision-edge.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL From 8096369923fb406272ae770eb6c274cd5e37f507 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 16 Aug 2023 17:52:29 +0200 Subject: [PATCH 0967/1263] Switch helm to v3.12.3 in CI --- .github/workflows/linter.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 28c3944d..1bf7e5e4 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -35,9 +35,9 @@ jobs: fetch-depth: 0 - name: Setup helm uses: azure/setup-helm@v3 - # with: - # version: '' # default is latest stable - id: install + with: + version: 'v3.12.3' + ################################ # Run Linter against code base # From b76d830d2d015f8b76734f794b6e6cc22328c089 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 16 Aug 2023 11:25:10 -0500 Subject: [PATCH 0968/1263] Unroll global.extraValueFiles in application-policies directly due to namespacing in the _helper.tpl --- acm/templates/_helpers.tpl | 5 ----- acm/templates/policies/application-policies.yaml | 3 +++ tests/acm-industrial-edge-hub.expected.yaml | 2 +- tests/acm-medical-diagnosis-hub.expected.yaml | 2 +- tests/acm-normal.expected.yaml | 4 ++-- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/acm/templates/_helpers.tpl b/acm/templates/_helpers.tpl index 80b0cf69..fdd91273 100644 --- a/acm/templates/_helpers.tpl +++ b/acm/templates/_helpers.tpl @@ -10,9 +10,4 @@ Default always defined valueFiles to be included when pushing the cluster wide a # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - '/values-{{ `{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}` }}-{{ .name }}.yaml' -{{- if and $.Values $.Values.global $.Values.global.extraValueFiles }} -{{- range $.Values.global.extraValueFiles }} -- {{ . | quote }} -{{- end }} {{/* if $.Values.global.extraValueFiles */}} -{{- end }} {{/* range $.Values.global.extraValueFiles */}} {{- end }} {{- /*acm.app.policies.valuefiles */}} diff --git a/acm/templates/policies/application-policies.yaml b/acm/templates/policies/application-policies.yaml index 5bc5de6a..d854ae72 100644 --- a/acm/templates/policies/application-policies.yaml +++ b/acm/templates/policies/application-policies.yaml @@ -44,6 +44,9 @@ spec: ignoreMissingValueFiles: true valueFiles: {{- include "acm.app.policies.valuefiles" . | nindent 24 }} + {{- range $valueFile := $.Values.global.extraValueFiles }} + - {{ $valueFile | quote }} + {{- end }} {{- range $valueFile := .extraValueFiles }} - {{ $valueFile | quote }} {{- end }} diff --git a/tests/acm-industrial-edge-hub.expected.yaml b/tests/acm-industrial-edge-hub.expected.yaml index 428d90d2..444b833c 100644 --- a/tests/acm-industrial-edge-hub.expected.yaml +++ b/tests/acm-industrial-edge-hub.expected.yaml @@ -213,7 +213,7 @@ spec: - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-factory.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-factory.yaml' + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-factory.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL diff --git a/tests/acm-medical-diagnosis-hub.expected.yaml b/tests/acm-medical-diagnosis-hub.expected.yaml index 0a12c2dc..f79e013b 100644 --- a/tests/acm-medical-diagnosis-hub.expected.yaml +++ b/tests/acm-medical-diagnosis-hub.expected.yaml @@ -204,7 +204,7 @@ spec: - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-region-one.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-region-one.yaml' + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-region-one.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index 42d306b6..900cc291 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -607,7 +607,7 @@ spec: - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-edge.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-edge.yaml' + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-edge.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL @@ -701,7 +701,7 @@ spec: - '/values-{{ (lookup "config.openshift.io/v1" "Infrastructure" "" "cluster").spec.platformSpec.type }}-acm-provision-edge.yaml' # We cannot use $.Values.global.clusterVersion because that gets resolved to the # hub's cluster version, whereas we want to include the spoke cluster version - - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-provision-edge.yaml' + - '/values-{{ printf "%d.%d" ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Major) ((semver (lookup "operator.openshift.io/v1" "OpenShiftControllerManager" "" "cluster").status.version).Minor) }}-acm-provision-edge.yaml' parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL From 69837ae26f5687007e8fda31dc15143d4d2ba247 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 16 Aug 2023 13:57:18 -0500 Subject: [PATCH 0969/1263] Re-add code to operator-install to understand global.extraValueFiles --- operator-install/templates/pattern.yaml | 6 ++++++ operator-install/values.yaml | 3 +++ 2 files changed, 9 insertions(+) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index d8b3df81..205389dc 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -17,4 +17,10 @@ spec: - name: {{ .name }} value: {{ .value }} {{- end }} +{{- if .Values.global.extraValueFiles }} + extraValueFiles: +{{- range .Values.global.extraValueFiles }} + - {{ . | quote }} +{{- end }} +{{- end }} {{- end }} diff --git a/operator-install/values.yaml b/operator-install/values.yaml index d5b0b13f..4ef3fe4a 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -1,3 +1,6 @@ +global: + extraValueFiles: [] + main: git: repoURL: https://github.com/pattern-clone/mypattern From 751e37395e53274ddaf916ff5351b7a1eab3fb9b Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Wed, 16 Aug 2023 13:59:26 -0500 Subject: [PATCH 0970/1263] Make sure to add dollar sign --- operator-install/templates/pattern.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index 205389dc..b640bb56 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -17,9 +17,9 @@ spec: - name: {{ .name }} value: {{ .value }} {{- end }} -{{- if .Values.global.extraValueFiles }} +{{- if $.Values.global.extraValueFiles }} extraValueFiles: -{{- range .Values.global.extraValueFiles }} +{{- range $.Values.global.extraValueFiles }} - {{ . | quote }} {{- end }} {{- end }} From e450c492ee2a04bdaef1df8c8c376fa437517d7b Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 17 Aug 2023 12:36:38 +0200 Subject: [PATCH 0971/1263] Add initial multi-source support This change adds initial multiSource support to patterns' applications. The way this works is that nothing changes for applications defined in values-*.yaml by default. So all patterns will work as usual. What can change with this patch is that applications can slowly migrate towards using multi source by changing an app definition from: acm: name: acm path: common/acm To: acm: name: acm chart: acm chartVersion: 0.0.* So any time we have a `chart` field with a `chartVersion` and no `repoURL` defined, the clustergroup chart will create a multisource application with the values files taken from the patterns git repo and the helm chart from https://charts.validatedpatterns.io/ using the `chartVersion` defined in the application. For example the above acm app would prodice the following: project: hub sources: - ref: patternref repoURL: https://github.com/mbaldessari/multicloud-gitops targetRevision: multisource-test2 - chart: acm helm: ignoreMissingValueFiles: true parameters: - name: global.repoURL value: $ARGOCD_APP_SOURCE_REPO_URL - name: global.targetRevision value: $ARGOCD_APP_SOURCE_TARGET_REVISION - name: global.namespace value: $ARGOCD_APP_NAMESPACE - name: global.pattern value: pattern-sample - name: global.clusterDomain value: mcg-hub.blueprints.rhecoeng.com - name: global.clusterVersion value: "4.13" - name: global.clusterPlatform value: AWS - name: global.hubClusterDomain value: apps.mcg-hub.blueprints.rhecoeng.com - name: global.localClusterDomain value: apps.mcg-hub.blueprints.rhecoeng.com valueFiles: - $patternref/values-global.yaml - $patternref/values-hub.yaml - $patternref/values-AWS.yaml - $patternref/values-AWS-4.13.yaml - $patternref/values-AWS-hub.yaml - $patternref/values-4.13-hub.yaml repoURL: https://charts.validatedpatterns.io/ targetRevision: 0.0.* Note that this depends on the operator supporting multiSource applications (version > 0.0.17). --- clustergroup/templates/_helpers.tpl | 23 +++++++ .../templates/plumbing/applications.yaml | 67 +++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/clustergroup/templates/_helpers.tpl b/clustergroup/templates/_helpers.tpl index b9feb738..e7736a6c 100644 --- a/clustergroup/templates/_helpers.tpl +++ b/clustergroup/templates/_helpers.tpl @@ -45,3 +45,26 @@ Default always defined valueFiles to be included in Applications {{- end }} {{/* range $.Values.global.extraValueFiles */}} {{- end }} {{/* if $.Values.global.extraValueFiles */}} {{- end }} {{/* clustergroup.app.globalvalues.valuefiles */}} + +{{/* +Default always defined valueFiles to be included in Applications but with a prefix called $patternref +*/}} +{{- define "clustergroup.app.globalvalues.prefixedvaluefiles" -}} +- "$patternref/values-global.yaml" +- "$patternref/values-{{ $.Values.clusterGroup.name }}.yaml" +{{- if $.Values.global.clusterPlatform }} +- "$patternref/values-{{ $.Values.global.clusterPlatform }}.yaml" + {{- if $.Values.global.clusterVersion }} +- "$patternref/values-{{ $.Values.global.clusterPlatform }}-{{ $.Values.global.clusterVersion }}.yaml" + {{- end }} +- "$patternref/values-{{ $.Values.global.clusterPlatform }}-{{ $.Values.clusterGroup.name }}.yaml" +{{- end }} +{{- if $.Values.global.clusterVersion }} +- "$patternref/values-{{ $.Values.global.clusterVersion }}-{{ $.Values.clusterGroup.name }}.yaml" +{{- end }} +{{- if $.Values.global.extraValueFiles }} +{{- range $.Values.global.extraValueFiles }} +- "$patternref/{{ . }}" +{{- end }} {{/* range $.Values.global.extraValueFiles */}} +{{- end }} {{/* if $.Values.global.extraValueFiles */}} +{{- end }} {{/* clustergroup.app.globalvalues.prefixedvaluefiles */}} diff --git a/clustergroup/templates/plumbing/applications.yaml b/clustergroup/templates/plumbing/applications.yaml index 16f5c11a..536ff0d3 100644 --- a/clustergroup/templates/plumbing/applications.yaml +++ b/clustergroup/templates/plumbing/applications.yaml @@ -128,6 +128,72 @@ spec: name: {{ $.Values.clusterGroup.targetCluster }} namespace: {{ default $namespace .namespace }} project: {{ .project }} + {{- if and .chart .chartVersion }} {{- /* if .chartVersion is set *and* .repoURL is undefined we assume this is a multisource app */}} + sources: + - repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} + {{- /* We do not allow overriding the values with .targetRevision because when we use .targetRevision in a chart to specify the helm + chart, that revision (e.g. 0.0.1) won't exist in the git tree. So here we simply always take the pattern's git branch/commit */}} + targetRevision: {{ $.Values.global.targetRevision }} + ref: patternref + - repoURL: {{ coalesce .repoURL $.Values.global.multiSourceRepoUrl }} + chart: {{ .chart }} + targetRevision: {{ .chartVersion }} + {{- if .plugin }} + plugin: {{ .plugin | toPrettyJson }} + {{- else }} + helm: + ignoreMissingValueFiles: true + valueFiles: + {{- include "clustergroup.app.globalvalues.prefixedvaluefiles" $ | nindent 8 }} + {{- range $valueFile := .extraValueFiles }} + - {{ $valueFile | quote }} + {{- end }} + parameters: + {{- include "clustergroup.app.globalvalues.helmparameters" $ | nindent 8 }} + {{- range .extraHubClusterDomainFields }} + - name: {{ . }} + value: {{ $.Values.global.hubClusterDomain }} + {{- end }} + {{- range .extraLocalClusterDomainFields }} + - name: {{ . }} + value: {{ $.Values.global.localClusterDomain }} + {{- end }} + {{- range .extraRepoURLFields }} + - name: {{ . }} + value: $ARGOCD_APP_SOURCE_REPO_URL + {{- end }} + {{- range .extraTargetRevisionFields }} + - name: {{ . }} + value: $ARGOCD_APP_SOURCE_TARGET_REVISION + {{- end }} + {{- range .extraNamespaceFields }} + - name: {{ . }} + value: $ARGOCD_APP_NAMESPACE + {{- end }} + {{- range .extraPatternNameFields }} + - name: {{ . }} + value: {{ $.Values.global.pattern }} + {{- end }} + {{- range $k, $v := $.Values.extraParametersNested }} + - name: {{ $k }} + value: {{ $v }} + {{- end }} + {{- range .overrides }} + - name: {{ .name }} + value: {{ .value | quote }} + {{- if .forceString }} + forceString: true + {{- end }} + {{- end }}{{- /* range .overrides */}} + {{- if .fileParameters }} + fileParameters: + {{- range .fileParameters }} + - name: {{ .name }} + path: {{ .path }} + {{- end }} + {{- end }}{{- /* if .fileParameters */}} + {{- end }}{{- /* if .plugin */}} + {{- else }} {{- /* if .chartVersion */}} source: repoURL: {{ coalesce .repoURL $.Values.global.repoURL }} targetRevision: {{ coalesce .targetRevision $.Values.global.targetRevision }} @@ -191,6 +257,7 @@ spec: {{- end }}{{- /* range .fileParameters */}} {{- end }}{{- /* if .fileParameters */}} {{- end }}{{- /* if .plugin */}} + {{- end }}{{- /* if .chartVersion */}} {{- if .ignoreDifferences }} ignoreDifferences: {{ .ignoreDifferences | toPrettyJson }} {{- end }} From adb9aec875bf8531ada962557a638540b1c0da6e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 17 Aug 2023 16:31:52 +0200 Subject: [PATCH 0972/1263] Add changelog entry and add an explicit property entry to the schema --- Changes.md | 4 ++++ clustergroup/values.schema.json | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Changes.md b/Changes.md index 0e1e8c47..ab6027d0 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## Aug 17, 2023 + +* Introduced support for multisource applications via .chart + .chartVersion + ## Jul 8, 2023 * Introduced a default of 20 for sync failures retries in argo applications (global override via global.options.applicationRetryLimit diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index e64a8125..74f8e4b5 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -388,6 +388,10 @@ "type": "string", "description": "Chart is a Helm chart name, and must be specified for applications sourced from a Helm repo." }, + "chartVersion": { + "type": "string", + "description": "The version of the helm chart to be used. Can be a regex like '0.0.*'." + }, "kustomize": { "type": "boolean", "description": "If set to true it will tell ArgoCD to use kustomize to deploy the application." From 9e45344c96c983580f2ea63ea736c5f34de35ff4 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 17 Aug 2023 09:52:48 -0500 Subject: [PATCH 0973/1263] Correct ifs and ranges in pattern, add comments --- operator-install/templates/pattern.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index b640bb56..fbf12308 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -16,11 +16,11 @@ spec: {{- range .Values.main.extraParameters }} - name: {{ .name }} value: {{ .value }} -{{- end }} -{{- if $.Values.global.extraValueFiles }} +{{- end }} {{/* range .Values.main.extraParameters */}} +{{- end }} {{/* if .Values.main.extraParameters */}} +{{- if .Values.global.extraValueFiles }} extraValueFiles: -{{- range $.Values.global.extraValueFiles }} +{{- range .Values.global.extraValueFiles }} - {{ . | quote }} -{{- end }} -{{- end }} -{{- end }} +{{- end }} {{/* range .Values.global.extraValueFiles */}} +{{- end }} {{/* if .Values.global.extraValueFiles */}} From 1a209e22bb36a4004199269a223cc5ef2c00084f Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 17 Aug 2023 09:57:21 -0500 Subject: [PATCH 0974/1263] Also quote name and value values --- operator-install/templates/pattern.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index fbf12308..eaa94807 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -14,8 +14,8 @@ spec: {{- if .Values.main.extraParameters }} extraParameters: {{- range .Values.main.extraParameters }} - - name: {{ .name }} - value: {{ .value }} + - name: {{ .name | quote }} + value: {{ .value | quote }} {{- end }} {{/* range .Values.main.extraParameters */}} {{- end }} {{/* if .Values.main.extraParameters */}} {{- if .Values.global.extraValueFiles }} From dcb9a3ec464129bfbefb05dc3497b857b0db4068 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 16 Aug 2023 16:11:55 +0200 Subject: [PATCH 0975/1263] Introduce an argo-healthcheck make target This is a simple quick check to see if all argo applications in all namespaces are synced and error out if they are not. Synced example: $ make argo-healthcheck make -f common/Makefile argo-healthcheck make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Checking argo applications mcg-private-hub acm -> Sync: Synced - Health: Healthy mcg-private-hub config-demo -> Sync: Synced - Health: Healthy mcg-private-hub golang-external-secrets -> Sync: Synced - Health: Healthy mcg-private-hub hello-world -> Sync: Synced - Health: Healthy mcg-private-hub vault -> Sync: Synced - Health: Healthy openshift-gitops mcg-private-hub -> Sync: Synced - Health: Healthy make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Not synced example: $ make argo-healthcheck make -f common/Makefile argo-healthcheck make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Checking argo applications mcg-private-hub acm -> Sync: Synced - Health: Healthy mcg-private-hub config-demo -> Sync: Synced - Health: Degraded mcg-private-hub golang-external-secrets -> Sync: Synced - Health: Healthy mcg-private-hub hello-world -> Sync: Synced - Health: Healthy mcg-private-hub vault -> Sync: Synced - Health: Progressing openshift-gitops mcg-private-hub -> Sync: Synced - Health: Healthy Some applications are not synced or are unhealthy make[1]: *** [common/Makefile:115: argo-healthcheck] Error 1 make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' make: *** [Makefile:12: argo-healthcheck] Error 2 --- Makefile | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Makefile b/Makefile index d8182da6..c872a939 100644 --- a/Makefile +++ b/Makefile @@ -108,6 +108,30 @@ validate-prereq: ## verify pre-requisites @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi @echo "OK" +.PHONY: argo-healthcheck +argo-healthcheck: ## Checks if all argo applications are synced + @echo "Checking argo applications" + $(eval APPS := $(shell oc get applications -A -o jsonpath='{range .items[*]}{@.metadata.namespace}{","}{@.metadata.name}{"\n"}{end}')) + @NOTOK=0; \ + for i in $(APPS); do\ + n=`echo "$${i}" | cut -f1 -d,`;\ + a=`echo "$${i}" | cut -f2 -d,`;\ + STATUS=`oc get -n "$${n}" application/"$${a}" -o jsonpath='{.status.sync.status}'`;\ + if [[ $$STATUS != "Synced" ]]; then\ + NOTOK=$$(( $${NOTOK} + 1));\ + fi;\ + HEALTH=`oc get -n "$${n}" application/"$${a}" -o jsonpath='{.status.health.status}'`;\ + if [[ $$HEALTH != "Healthy" ]]; then\ + NOTOK=$$(( $${NOTOK} + 1));\ + fi;\ + echo "$${n} $${a} -> Sync: $${STATUS} - Health: $${HEALTH}";\ + done;\ + if [ $${NOTOK} -gt 0 ]; then\ + echo "Some applications are not synced or are unhealthy";\ + exit 1;\ + fi + + ##@ Test and Linters Tasks CHARTS=$(shell find . -type f -iname 'Chart.yaml' -exec dirname "{}" \; | grep -v examples | sed -e 's/.\///') From b0e828b97165f77f865117489511cbfa452db5f7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 23 Aug 2023 18:07:52 +0200 Subject: [PATCH 0976/1263] Drop vault.ui.serviceType: "LoadBalancer" It is not needed and it adds a requirement to the cluster to have a proper LadBalancer which is not always the case. The default in the helm chart is "ClusterIP", so let's leave that default. Tested this on an OCP 4.13 SNO cluster (without LB) and the UI is correctly accessible. --- hashicorp-vault/values.yaml | 1 - tests/hashicorp-vault-industrial-edge-factory.expected.yaml | 3 +-- tests/hashicorp-vault-industrial-edge-hub.expected.yaml | 3 +-- tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml | 3 +-- tests/hashicorp-vault-naked.expected.yaml | 3 +-- tests/hashicorp-vault-normal.expected.yaml | 3 +-- 6 files changed, 5 insertions(+), 11 deletions(-) diff --git a/hashicorp-vault/values.yaml b/hashicorp-vault/values.yaml index 2ab5f920..780f574a 100644 --- a/hashicorp-vault/values.yaml +++ b/hashicorp-vault/values.yaml @@ -8,7 +8,6 @@ vault: enabled: false ui: enabled: true - serviceType: "LoadBalancer" server: extraEnvironmentVars: VAULT_CACERT: /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt diff --git a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml index 3d1ed2db..ccb5e5bb 100644 --- a/tests/hashicorp-vault-industrial-edge-factory.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-factory.expected.yaml @@ -140,8 +140,7 @@ spec: - name: http port: 8200 targetPort: 8200 - type: LoadBalancer - externalTrafficPolicy: Cluster + type: ClusterIP --- # Source: hashicorp-vault/charts/vault/templates/server-statefulset.yaml # StatefulSet to run the actual vault server cluster. diff --git a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml index 3d1ed2db..ccb5e5bb 100644 --- a/tests/hashicorp-vault-industrial-edge-hub.expected.yaml +++ b/tests/hashicorp-vault-industrial-edge-hub.expected.yaml @@ -140,8 +140,7 @@ spec: - name: http port: 8200 targetPort: 8200 - type: LoadBalancer - externalTrafficPolicy: Cluster + type: ClusterIP --- # Source: hashicorp-vault/charts/vault/templates/server-statefulset.yaml # StatefulSet to run the actual vault server cluster. diff --git a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml index 3d1ed2db..ccb5e5bb 100644 --- a/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml +++ b/tests/hashicorp-vault-medical-diagnosis-hub.expected.yaml @@ -140,8 +140,7 @@ spec: - name: http port: 8200 targetPort: 8200 - type: LoadBalancer - externalTrafficPolicy: Cluster + type: ClusterIP --- # Source: hashicorp-vault/charts/vault/templates/server-statefulset.yaml # StatefulSet to run the actual vault server cluster. diff --git a/tests/hashicorp-vault-naked.expected.yaml b/tests/hashicorp-vault-naked.expected.yaml index 85d00b14..aa4f5b87 100644 --- a/tests/hashicorp-vault-naked.expected.yaml +++ b/tests/hashicorp-vault-naked.expected.yaml @@ -140,8 +140,7 @@ spec: - name: http port: 8200 targetPort: 8200 - type: LoadBalancer - externalTrafficPolicy: Cluster + type: ClusterIP --- # Source: hashicorp-vault/charts/vault/templates/server-statefulset.yaml # StatefulSet to run the actual vault server cluster. diff --git a/tests/hashicorp-vault-normal.expected.yaml b/tests/hashicorp-vault-normal.expected.yaml index 3d1ed2db..ccb5e5bb 100644 --- a/tests/hashicorp-vault-normal.expected.yaml +++ b/tests/hashicorp-vault-normal.expected.yaml @@ -140,8 +140,7 @@ spec: - name: http port: 8200 targetPort: 8200 - type: LoadBalancer - externalTrafficPolicy: Cluster + type: ClusterIP --- # Source: hashicorp-vault/charts/vault/templates/server-statefulset.yaml # StatefulSet to run the actual vault server cluster. From af839b4907c8f4c402a561fbfac43f3d4a913941 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 25 Aug 2023 07:56:03 +0200 Subject: [PATCH 0977/1263] Release clustergroup chart version 0.0.2 --- clustergroup/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/Chart.yaml b/clustergroup/Chart.yaml index d4dfdc63..031b6ff0 100644 --- a/clustergroup/Chart.yaml +++ b/clustergroup/Chart.yaml @@ -3,4 +3,4 @@ description: A Helm chart to create per-clustergroup ArgoCD applications and any keywords: - pattern name: clustergroup -version: 0.0.1 +version: 0.0.2 From f1f98f27610d6ab9d3bfeb9788ffec234bfd6d87 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 26 Aug 2023 16:29:58 +0200 Subject: [PATCH 0978/1263] Update crd in common This is needed if we want to support multisource installations from the CLI --- ...ops.hybrid-cloud-patterns.io_patterns.yaml | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml index c4563288..d0464ef6 100644 --- a/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml +++ b/operator-install/crds/gitops.hybrid-cloud-patterns.io_patterns.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.7.0 + controller-gen.kubebuilder.io/version: v0.11.4 creationTimestamp: null name: patterns.gitops.hybrid-cloud-patterns.io spec: @@ -49,7 +49,7 @@ spec: type: string extraParameters: description: '.Name is dot separated per the helm --set syntax, such - as: global.something.field' + as: global.something.field' items: properties: name: @@ -108,6 +108,7 @@ spec: Does not support short-sha's. Default to HEAD type: string pollInterval: + default: 180 description: 'Interval in seconds to poll for drifts between origin and target repositories. Default: 180 seconds' type: integer @@ -122,6 +123,35 @@ spec: required: - targetRepo type: object + multiSourceConfig: + properties: + clusterGroupChartGitRevision: + default: main + description: The git reference when deploying the clustergroup + helm chart directly from a git repo Defaults to 'main'. (Only + used when developing the clustergroup helm chart) + type: string + clusterGroupChartVersion: + default: 0.0.* + description: Which chart version for the clustergroup helm chart + Defaults to "0.0.*" + type: string + clusterGroupGitRepoUrl: + description: The url when deploying the clustergroup helm chart + directly from a git repo Defaults to '' which means not used + (Only used when developing the clustergroup helm chart) + type: string + enabled: + default: false + description: (EXPERIMENTAL) Enable multi-source support when deploying + the clustergroup argo application + type: boolean + helmRepoUrl: + default: https://charts.validatedpatterns.io/ + description: The helm chart url to fetch the helm charts from + in order to deploy the pattern Defaults to https://charts.validatedpatterns.io/ + type: string + type: object required: - clusterGroupName - gitSpec @@ -188,5 +218,5 @@ status: acceptedNames: kind: "" plural: "" - conditions: [] - storedVersions: [] + conditions: null + storedVersions: null From d65f7e4f3a9ef04bb3e6a8711ba469afd278292e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 26 Aug 2023 16:50:42 +0200 Subject: [PATCH 0979/1263] Add support for passing EXTRA_HELM_OPTS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently a user can set additional helm params via the EXTRA_HELM_OPTS environment variable in order to tweak a value at `make install` time. This does not work correctly when we run things from our utility container, that is because that variable is never passed from the host to the container. According to `man podman run` if we simply pass `-e EXTRA_HELM_OPTS` to the podman invocation: "If an environment variable is spec‐ ified without a value, Podman checks the host environment for a value and set the variable only if it is set on the host" * Without setting EXTRA_HELM_OPTS: unset EXTRA_HELM_OPTS; ./pattern.sh make install make -f common/Makefile operator-deploy make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Checking prerequisites: Check for 'git helm oc ansible': OK Check for python-kubernetes: OK Check for kubernetes.core collection: OK Checking repository: https://github.com/mbaldessari/multicloud-gitops.git - branch main: Running inside a container: Skipping git ssh checks + oc get crds patterns.gitops.hybrid-cloud-patterns.io + echo 'Running helm:' Running helm: + helm upgrade --install multicloud-gitops common/operator-install/ -f values-global.yaml --set main.git.repoURL=https://github.com/mbaldessari/multicloud-gitops.git --set main.git.revision=main * With EXTRA_HELM_OPTS set: export EXTRA_HELM_OPTS="--set main.multiSourceConfig.enabled=true"; ./pattern.sh make install make -f common/Makefile operator-deploy make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Checking prerequisites: Check for 'git helm oc ansible': OK Check for python-kubernetes: OK Check for kubernetes.core collection: OK Checking repository: https://github.com/mbaldessari/multicloud-gitops.git - branch main: Running inside a container: Skipping git ssh checks + oc get crds patterns.gitops.hybrid-cloud-patterns.io + echo 'Running helm:' Running helm: + helm upgrade --install multicloud-gitops common/operator-install/ -f values-global.yaml --set main.git.repoURL=https://github.com/mbaldessari/multicloud-gitops.git --set main.git.revision=main --set main.multiSourceConfig.enabled=true (Briefly added set -x to see the exact commands during testing) --- scripts/pattern-util.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index f55bbdee..baa0e9de 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -36,6 +36,7 @@ fi # which will be confused podman run -it --rm \ --security-opt label=disable \ + -e EXTRA_HELM_OPTS \ ${KUBECONF_ENV} \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ From 449b9a8a17e580f091ef923c93cd3c3a832721ae Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 28 Aug 2023 10:43:48 +0200 Subject: [PATCH 0980/1263] Disable kubeconform for the time being --- .github/workflows/linter.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 1bf7e5e4..59e64541 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -56,9 +56,10 @@ jobs: run: | make helmlint - - name: Run make helm kubeconform - run: | - curl -L -O https://github.com/yannh/kubeconform/releases/download/v0.4.13/kubeconform-linux-amd64.tar.gz - tar xf kubeconform-linux-amd64.tar.gz - sudo mv -v kubeconform /usr/local/bin - make kubeconform + # For now disable this until we have a nice and simple process to update the schemas in our repo + # - name: Run make helm kubeconform + # run: | + # curl -L -O https://github.com/yannh/kubeconform/releases/download/v0.4.13/kubeconform-linux-amd64.tar.gz + # tar xf kubeconform-linux-amd64.tar.gz + # sudo mv -v kubeconform /usr/local/bin + # make kubeconform From 36366454eff92ed1e58810843b37cb25c739083f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 28 Aug 2023 08:37:31 +0200 Subject: [PATCH 0981/1263] Add support for deploying multi source via CLI Via: ``` export EXTRA_HELM_OPTS="--set main.multiSourceConfig.enabled=true" ./pattern.sh make install ``` one can now deploy a pattern with the experimental multisource support enabled. Tested with the above command and correctly deployed a multi-source based pattern. --- clustergroup/values.schema.json | 10 ++++++++++ operator-install/templates/pattern.yaml | 2 ++ operator-install/values.yaml | 3 +++ ...rator-install-industrial-edge-factory.expected.yaml | 2 ++ .../operator-install-industrial-edge-hub.expected.yaml | 2 ++ ...perator-install-medical-diagnosis-hub.expected.yaml | 2 ++ tests/operator-install-naked.expected.yaml | 2 ++ tests/operator-install-normal.expected.yaml | 2 ++ 8 files changed, 25 insertions(+) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 74f8e4b5..a33432b2 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -92,6 +92,16 @@ "description": "The channel from which to install the gitops operator" } } + }, + "multiSourceConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable the experimental support for multi source" + } + } } } }, diff --git a/operator-install/templates/pattern.yaml b/operator-install/templates/pattern.yaml index eaa94807..a340598b 100644 --- a/operator-install/templates/pattern.yaml +++ b/operator-install/templates/pattern.yaml @@ -11,6 +11,8 @@ spec: gitOpsSpec: operatorChannel: {{ default "gitops-1.8" .Values.main.gitops.channel }} operatorSource: {{ default "redhat-operators" .Values.main.gitops.operatorSource }} + multiSourceConfig: + enabled: {{ .Values.main.multiSourceConfig.enabled }} {{- if .Values.main.extraParameters }} extraParameters: {{- range .Values.main.extraParameters }} diff --git a/operator-install/values.yaml b/operator-install/values.yaml index 4ef3fe4a..01605b21 100644 --- a/operator-install/values.yaml +++ b/operator-install/values.yaml @@ -10,6 +10,9 @@ main: channel: "gitops-1.8" operatorSource: redhat-operators + multiSourceConfig: + enabled: false + patternsOperator: channel: fast source: community-operators diff --git a/tests/operator-install-industrial-edge-factory.expected.yaml b/tests/operator-install-industrial-edge-factory.expected.yaml index 5fc96bf3..8e9adf88 100644 --- a/tests/operator-install-industrial-edge-factory.expected.yaml +++ b/tests/operator-install-industrial-edge-factory.expected.yaml @@ -13,6 +13,8 @@ spec: gitOpsSpec: operatorChannel: gitops-1.8 operatorSource: redhat-operators + multiSourceConfig: + enabled: false --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-industrial-edge-hub.expected.yaml b/tests/operator-install-industrial-edge-hub.expected.yaml index 5fc96bf3..8e9adf88 100644 --- a/tests/operator-install-industrial-edge-hub.expected.yaml +++ b/tests/operator-install-industrial-edge-hub.expected.yaml @@ -13,6 +13,8 @@ spec: gitOpsSpec: operatorChannel: gitops-1.8 operatorSource: redhat-operators + multiSourceConfig: + enabled: false --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-medical-diagnosis-hub.expected.yaml b/tests/operator-install-medical-diagnosis-hub.expected.yaml index 5fc96bf3..8e9adf88 100644 --- a/tests/operator-install-medical-diagnosis-hub.expected.yaml +++ b/tests/operator-install-medical-diagnosis-hub.expected.yaml @@ -13,6 +13,8 @@ spec: gitOpsSpec: operatorChannel: gitops-1.8 operatorSource: redhat-operators + multiSourceConfig: + enabled: false --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-naked.expected.yaml b/tests/operator-install-naked.expected.yaml index 4c7837fe..b32c2577 100644 --- a/tests/operator-install-naked.expected.yaml +++ b/tests/operator-install-naked.expected.yaml @@ -13,6 +13,8 @@ spec: gitOpsSpec: operatorChannel: gitops-1.8 operatorSource: redhat-operators + multiSourceConfig: + enabled: false --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 diff --git a/tests/operator-install-normal.expected.yaml b/tests/operator-install-normal.expected.yaml index 5fc96bf3..8e9adf88 100644 --- a/tests/operator-install-normal.expected.yaml +++ b/tests/operator-install-normal.expected.yaml @@ -13,6 +13,8 @@ spec: gitOpsSpec: operatorChannel: gitops-1.8 operatorSource: redhat-operators + multiSourceConfig: + enabled: false --- # Source: pattern-install/templates/subscription.yaml apiVersion: operators.coreos.com/v1alpha1 From 3e7654f71e6e7f61784344e042547ec5a1996d70 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 31 Aug 2023 12:26:06 +0200 Subject: [PATCH 0982/1263] Upgrade ESO to v0.9.4 --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.8.5.tgz | Bin 78631 -> 0 bytes .../charts/external-secrets-0.9.4.tgz | Bin 0 -> 82899 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 452 ++++++++++++++++-- ...-secrets-industrial-edge-hub.expected.yaml | 452 ++++++++++++++++-- ...ecrets-medical-diagnosis-hub.expected.yaml | 452 ++++++++++++++++-- ...olang-external-secrets-naked.expected.yaml | 452 ++++++++++++++++-- ...lang-external-secrets-normal.expected.yaml | 452 ++++++++++++++++-- 9 files changed, 1999 insertions(+), 269 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.8.5.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.9.4.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index e7b779b6..c618b677 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.8.5" + version: "0.9.4" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.8.5.tgz b/golang-external-secrets/charts/external-secrets-0.8.5.tgz deleted file mode 100644 index af7525bf38dd4b6bea620632fd055a0e412e4f81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78631 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

^%Z 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 z z;}!LB;QA5H87)>>k*900l?sg&oR(k-nYQ;?Y)Kj;HF?T_sGR2UmuDw|VwUdDyX!cG z6!p(i?K3djMAVrITo9guqF!Q(Ko#jXt*!}TWShuT<2DD>YKZ-KcR@Exj7%lxcy!tq zwGvxZuLf|}v0qPQ2Hm1XObDZ?`S+))BaTQi(Z3iZX};8_va1xGwTeBK_)skg!h>Zm z|DIBp=Oe2iCxS&zHX6JS!%g0k5s^>43YSRYB?p{4MY@(!YPIu3K4!4G@(`I#sBlG& z$n%_jX0$X-CZmtu3>gr|95UX{znedmP6}f@YQ&d`s)Mso=fP*unNvYWltLK9gSFup z?4}DKvLGPj1qwKZ<;fY2ea>l0X(?rHx~Ue6sPpa;CPNo1HbophrRz=jV>n2(MT#%R zWQOt^qpI^5oa>$chQr;7f84-M?utRTeW3r-swk-9D9`#;(5obhrnTk<^5nf4aboo2v69=)C~c=u80la-WwN?aC0NPg z-rP%OpK`H{?Vc-$CS?_rfXd2JhU`q}lDU=*_U`M8Q`5k&(u<|4BD7>(J84rIz#A_L zz4f(kR{b8&o(g#F8qX!1yUd)(=OB6mJ5mP3i3_zgQ55|dxwq0G-{Ezt&~VuKbGTG- zIf{XQ>;rAM`JhS!U{hwn5b!SK`0hyqT%DdBkN`I9a)iFIg8*?(>l6_*?%!f;Z?cBH zc{@OA#>*9&5NZ_o`fQ^Rr@Zh`0+q%j*fC&NJg_(y^}^+I%^Cb(0b&51|5p_5)4WYpsln`iuB@P{H$Gr8kK4(u`{Qu$c;{yGEc0A<& q+0CO0$L#@-!1;3^c7C{h!!ta?Gd$nJ^Zy3`0RR7wd^=(Qz6SsiM9GN& literal 0 HcmV?d00001 diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index 1851d855..58af2212 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -7,10 +7,10 @@ global: external-secrets: image: - tag: v0.6.1-ubi + tag: v0.7.0-ubi webhook: image: - tag: v0.6.1-ubi + tag: v0.7.0-ubi certController: image: - tag: v0.6.1-ubi + tag: v0.7.0-ubi diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index fc9597d5..2bee122f 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -62,6 +62,148 @@ metadata: kubernetes.io/service-account.name: golang-external-secrets type: kubernetes.io/service-account-token --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/acraccesstoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: acraccesstokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - acraccesstoken + kind: ACRAccessToken + listKind: ACRAccessTokenList + plural: acraccesstokens + shortNames: + - acraccesstoken + singular: acraccesstoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: "ACRAccessToken returns a Azure Container Registry token that can be used for pushing/pulling images. Note: by default it will return an ACR Refresh Token with full access (depending on the identity). This can be scoped down to the repository level using .spec.scope. In case scope is defined it will return an ACR Access Token. \n See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md" + 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: 'ACRAccessTokenSpec defines how to generate the access token e.g. how to authenticate and which registry to use. see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview' + properties: + auth: + properties: + managedIdentity: + description: ManagedIdentity uses Azure Managed Identity to authenticate with Azure. + properties: + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + type: object + servicePrincipal: + description: ServicePrincipal uses Azure Service Principal credentials to authenticate with Azure. + properties: + secretRef: + description: Configuration used to authenticate with Azure using static credentials stored in a Kind=Secret. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + workloadIdentity: + description: WorkloadIdentity uses Azure Workload Identity to authenticate with Azure. + properties: + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + type: object + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string + registry: + description: the domain name of the ACR registry e.g. foobarexample.azurecr.io + type: string + scope: + description: "Define the scope for the access token, e.g. pull/push access for a repository. if not provided it will return a refresh token that has full scope. Note: you need to pin it down to the repository level, there is no wildcard available. \n examples: repository:my-repository:pull,push repository:my-repository:pull \n see docs for details: https://docs.docker.com/registry/spec/auth/scope/" + type: string + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + required: + - auth + - registry + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -124,7 +266,7 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. + description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -150,7 +292,42 @@ spec: - key type: object secretKey: + description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret type: string + sourceRef: + description: SourceRef allows you to override the source from which the value will pulled from. + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object required: - remoteRef - secretKey @@ -161,7 +338,7 @@ spec: items: properties: extract: - description: Used to extract multiple key/value pairs from one secret + description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -187,7 +364,7 @@ spec: - key type: object find: - description: Used to find secrets based on tags or regular expressions + description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -232,6 +409,40 @@ spec: type: object type: object type: array + sourceRef: + description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object type: object type: array refreshInterval: @@ -346,8 +557,6 @@ spec: type: string type: object type: object - required: - - secretStoreRef type: object namespaceSelector: description: The labels to select by to find the Namespaces to create the ExternalSecrets in. @@ -1544,6 +1753,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.capabilities + name: Capabilities + type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -1821,6 +2033,19 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object type: object type: object region: @@ -2080,6 +2305,14 @@ spec: environment: description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) type: string + groupIDs: + description: GroupIDs specify, which gitlab groups to pull secrets from. Group secrets are read from left to right followed by the project variables. + items: + type: string + type: array + inheritFromGroups: + description: InheritFromGroups specifies whether parent groups should be discovered and checked for secrets. + type: boolean projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -2835,6 +3068,9 @@ spec: status: description: SecretStoreStatus defines the observed state of the SecretStore. properties: + capabilities: + description: SecretStoreCapabilities defines the possible operations a SecretStore can do. + type: string conditions: items: properties: @@ -2871,6 +3107,135 @@ spec: namespace: "default" path: /convert --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/ecrauthorizationtoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: ecrauthorizationtokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - ecrauthorizationtoken + kind: ECRAuthorizationToken + listKind: ECRAuthorizationTokenList + plural: ecrauthorizationtokens + shortNames: + - ecrauthorizationtoken + singular: ecrauthorizationtoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an authorization token. The authorization token is valid for 12 hours. The authorizationToken returned is a base64 encoded string that can be decoded and used in a docker login command to authenticate to a registry. For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. + 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: + properties: + auth: + description: Auth defines how to authenticate with AWS + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: Region specifies the region to operate in. + type: string + role: + description: You can assume a role before making calls to the desired AWS service. + type: string + required: + - region + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/externalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -3143,7 +3508,7 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. + description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -3169,10 +3534,45 @@ spec: - key type: object secretKey: + description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret type: string - required: - - remoteRef - - secretKey + sourceRef: + description: SourceRef allows you to override the source from which the value will pulled from. + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object + required: + - remoteRef + - secretKey type: object type: array dataFrom: @@ -3180,7 +3580,7 @@ spec: items: properties: extract: - description: Used to extract multiple key/value pairs from one secret + description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -3206,7 +3606,7 @@ spec: - key type: object find: - description: Used to find secrets based on tags or regular expressions + description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -3251,6 +3651,40 @@ spec: type: object type: object type: array + sourceRef: + description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object type: object type: array refreshInterval: @@ -3365,8 +3799,6 @@ spec: type: string type: object type: object - required: - - secretStoreRef type: object status: properties: @@ -3414,6 +3846,468 @@ spec: namespace: "default" path: /convert --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/fake.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: fakes.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - fake + kind: Fake + listKind: FakeList + plural: fakes + shortNames: + - fake + singular: fake + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Fake generator is used for testing. It lets you define a static set of credentials that is always returned. + 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: FakeSpec contains the static data. + properties: + data: + additionalProperties: + type: string + description: Data defines the static data returned by this generator. + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/gcraccesstoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: gcraccesstokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - gcraccesstoken + kind: GCRAccessToken + listKind: GCRAccessTokenList + plural: gcraccesstokens + shortNames: + - gcraccesstoken + singular: gcraccesstoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: GCRAccessToken generates an GCP access token that can be used to authenticate with GCR. + 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: + properties: + auth: + description: Auth defines the means for authenticating with GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID defines which project to use to authenticate with + type: string + required: + - auth + - projectID + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/password.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: passwords.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - password + kind: Password + listKind: PasswordList + plural: passwords + shortNames: + - password + singular: password + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Password generates a random password based on the configuration parameters in spec. You can specify the length, characterset and other attributes. + 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: PasswordSpec controls the behavior of the password generator. + properties: + allowRepeat: + default: false + description: set AllowRepeat to true to allow repeating characters. + type: boolean + digits: + description: Digits specifies the number of digits in the generated password. If omitted it defaults to 25% of the length of the password + type: integer + length: + default: 24 + description: Length of the password to be generated. Defaults to 24 + type: integer + noUpper: + default: false + description: Set NoUpper to disable uppercase characters + type: boolean + symbolCharacters: + description: SymbolCharacters specifies the special characters that should be used in the generated password. + type: string + symbols: + description: Symbols specifies the number of symbol characters in the generated password. If omitted it defaults to 25% of the length of the password + type: integer + required: + - allowRepeat + - length + - noUpper + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/pushsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: pushsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - pushsecrets + kind: PushSecret + listKind: PushSecretList + plural: pushsecrets + singular: pushsecret + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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: PushSecretSpec configures the behavior of the PushSecret. + properties: + data: + description: Secret Data that should be pushed to providers + items: + properties: + match: + description: Match a given Secret Key to be pushed to the provider. + properties: + remoteRef: + description: Remote Refs to push to providers. + properties: + remoteKey: + description: Name of the resulting provider secret. + type: string + required: + - remoteKey + type: object + secretKey: + description: Secret Key to be pushed + type: string + required: + - remoteRef + - secretKey + type: object + required: + - match + type: object + type: array + deletionPolicy: + default: None + description: 'Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".' + type: string + refreshInterval: + description: The Interval to which External Secrets will try to push a secret definition + type: string + secretStoreRefs: + items: + properties: + kind: + default: SecretStore + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + labelSelector: + description: Optionally, sync to secret stores with label selector + 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 + required: + - key + - operator + type: object + type: array + 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 + name: + description: Optionally, sync to the SecretStore of the given name + type: string + type: object + type: array + selector: + description: The Secret Selector (k8s source) for the Push Secret + properties: + secret: + description: Select a Secret to Push. + properties: + name: + description: Name of the Secret. The Secret must exist in the same namespace as the PushSecret manifest. + type: string + required: + - name + type: object + required: + - secret + type: object + required: + - secretStoreRefs + - selector + type: object + status: + description: PushSecretStatus indicates the history of the status of PushSecret. + properties: + conditions: + items: + description: PushSecretStatusCondition indicates the status of the PushSecret. + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + description: PushSecretConditionType indicates the condition of the PushSecret. + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedPushSecrets: + additionalProperties: + additionalProperties: + properties: + match: + description: Match a given Secret Key to be pushed to the provider. + properties: + remoteRef: + description: Remote Refs to push to providers. + properties: + remoteKey: + description: Name of the resulting provider secret. + type: string + required: + - remoteKey + type: object + secretKey: + description: Secret Key to be pushed + type: string + required: + - remoteRef + - secretKey + type: object + required: + - match + type: object + type: object + description: Synced Push Secrets for later deletion. Matches Secret Stores to PushSecretData that was stored to that secretStore. + type: object + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/secretstore.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -4517,6 +5411,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.capabilities + name: Capabilities + type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -4794,6 +5691,19 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object type: object type: object region: @@ -5053,6 +5963,14 @@ spec: environment: description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) type: string + groupIDs: + description: GroupIDs specify, which gitlab groups to pull secrets from. Group secrets are read from left to right followed by the project variables. + items: + type: string + type: array + inheritFromGroups: + description: InheritFromGroups specifies whether parent groups should be discovered and checked for secrets. + type: boolean projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -5808,6 +6726,9 @@ spec: status: description: SecretStoreStatus defines the observed state of the SecretStore. properties: + capabilities: + description: SecretStoreCapabilities defines the possible operations a SecretStore can do. + type: string conditions: items: properties: @@ -5850,10 +6771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5908,10 +6829,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5921,6 +6842,7 @@ rules: - "clustersecretstores" - "externalsecrets" - "clusterexternalsecrets" + - "pushsecrets" verbs: - "get" - "list" @@ -5940,9 +6862,24 @@ rules: - "clusterexternalsecrets" - "clusterexternalsecrets/status" - "clusterexternalsecrets/finalizers" + - "pushsecrets" + - "pushsecrets/status" + - "pushsecrets/finalizers" verbs: - "update" - "patch" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "fakes" + - "passwords" + - "acraccesstokens" + - "gcraccesstokens" + - "ecrauthorizationtokens" + verbs: + - "get" + - "list" + - "watch" - apiGroups: - "" resources: @@ -6000,10 +6937,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -6015,6 +6952,7 @@ rules: - "externalsecrets" - "secretstores" - "clustersecretstores" + - "pushsecrets" verbs: - "get" - "watch" @@ -6026,10 +6964,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -6040,6 +6978,7 @@ rules: - "externalsecrets" - "secretstores" - "clustersecretstores" + - "pushsecrets" verbs: - "create" - "delete" @@ -6053,10 +6992,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6073,10 +7012,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6109,10 +7048,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6148,10 +7087,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6169,10 +7108,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -6193,10 +7132,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6213,7 +7152,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -6240,10 +7179,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6260,7 +7199,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -6276,10 +7215,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6297,7 +7236,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - webhook @@ -6305,6 +7244,7 @@ spec: - --dns-name=golang-external-secrets-webhook.default.svc - --cert-dir=/tmp/certs - --check-interval=5m + - --metrics-addr=:8080 - --healthz-addr=:8081 ports: - containerPort: 8080 diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index fc9597d5..2bee122f 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -62,6 +62,148 @@ metadata: kubernetes.io/service-account.name: golang-external-secrets type: kubernetes.io/service-account-token --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/acraccesstoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: acraccesstokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - acraccesstoken + kind: ACRAccessToken + listKind: ACRAccessTokenList + plural: acraccesstokens + shortNames: + - acraccesstoken + singular: acraccesstoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: "ACRAccessToken returns a Azure Container Registry token that can be used for pushing/pulling images. Note: by default it will return an ACR Refresh Token with full access (depending on the identity). This can be scoped down to the repository level using .spec.scope. In case scope is defined it will return an ACR Access Token. \n See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md" + 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: 'ACRAccessTokenSpec defines how to generate the access token e.g. how to authenticate and which registry to use. see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview' + properties: + auth: + properties: + managedIdentity: + description: ManagedIdentity uses Azure Managed Identity to authenticate with Azure. + properties: + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + type: object + servicePrincipal: + description: ServicePrincipal uses Azure Service Principal credentials to authenticate with Azure. + properties: + secretRef: + description: Configuration used to authenticate with Azure using static credentials stored in a Kind=Secret. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + workloadIdentity: + description: WorkloadIdentity uses Azure Workload Identity to authenticate with Azure. + properties: + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + type: object + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string + registry: + description: the domain name of the ACR registry e.g. foobarexample.azurecr.io + type: string + scope: + description: "Define the scope for the access token, e.g. pull/push access for a repository. if not provided it will return a refresh token that has full scope. Note: you need to pin it down to the repository level, there is no wildcard available. \n examples: repository:my-repository:pull,push repository:my-repository:pull \n see docs for details: https://docs.docker.com/registry/spec/auth/scope/" + type: string + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + required: + - auth + - registry + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -124,7 +266,7 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. + description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -150,7 +292,42 @@ spec: - key type: object secretKey: + description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret type: string + sourceRef: + description: SourceRef allows you to override the source from which the value will pulled from. + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object required: - remoteRef - secretKey @@ -161,7 +338,7 @@ spec: items: properties: extract: - description: Used to extract multiple key/value pairs from one secret + description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -187,7 +364,7 @@ spec: - key type: object find: - description: Used to find secrets based on tags or regular expressions + description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -232,6 +409,40 @@ spec: type: object type: object type: array + sourceRef: + description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object type: object type: array refreshInterval: @@ -346,8 +557,6 @@ spec: type: string type: object type: object - required: - - secretStoreRef type: object namespaceSelector: description: The labels to select by to find the Namespaces to create the ExternalSecrets in. @@ -1544,6 +1753,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.capabilities + name: Capabilities + type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -1821,6 +2033,19 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object type: object type: object region: @@ -2080,6 +2305,14 @@ spec: environment: description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) type: string + groupIDs: + description: GroupIDs specify, which gitlab groups to pull secrets from. Group secrets are read from left to right followed by the project variables. + items: + type: string + type: array + inheritFromGroups: + description: InheritFromGroups specifies whether parent groups should be discovered and checked for secrets. + type: boolean projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -2835,6 +3068,9 @@ spec: status: description: SecretStoreStatus defines the observed state of the SecretStore. properties: + capabilities: + description: SecretStoreCapabilities defines the possible operations a SecretStore can do. + type: string conditions: items: properties: @@ -2871,6 +3107,135 @@ spec: namespace: "default" path: /convert --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/ecrauthorizationtoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: ecrauthorizationtokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - ecrauthorizationtoken + kind: ECRAuthorizationToken + listKind: ECRAuthorizationTokenList + plural: ecrauthorizationtokens + shortNames: + - ecrauthorizationtoken + singular: ecrauthorizationtoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an authorization token. The authorization token is valid for 12 hours. The authorizationToken returned is a base64 encoded string that can be decoded and used in a docker login command to authenticate to a registry. For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. + 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: + properties: + auth: + description: Auth defines how to authenticate with AWS + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: Region specifies the region to operate in. + type: string + role: + description: You can assume a role before making calls to the desired AWS service. + type: string + required: + - region + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/externalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -3143,7 +3508,7 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. + description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -3169,10 +3534,45 @@ spec: - key type: object secretKey: + description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret type: string - required: - - remoteRef - - secretKey + sourceRef: + description: SourceRef allows you to override the source from which the value will pulled from. + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object + required: + - remoteRef + - secretKey type: object type: array dataFrom: @@ -3180,7 +3580,7 @@ spec: items: properties: extract: - description: Used to extract multiple key/value pairs from one secret + description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -3206,7 +3606,7 @@ spec: - key type: object find: - description: Used to find secrets based on tags or regular expressions + description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -3251,6 +3651,40 @@ spec: type: object type: object type: array + sourceRef: + description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object type: object type: array refreshInterval: @@ -3365,8 +3799,6 @@ spec: type: string type: object type: object - required: - - secretStoreRef type: object status: properties: @@ -3414,6 +3846,468 @@ spec: namespace: "default" path: /convert --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/fake.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: fakes.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - fake + kind: Fake + listKind: FakeList + plural: fakes + shortNames: + - fake + singular: fake + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Fake generator is used for testing. It lets you define a static set of credentials that is always returned. + 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: FakeSpec contains the static data. + properties: + data: + additionalProperties: + type: string + description: Data defines the static data returned by this generator. + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/gcraccesstoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: gcraccesstokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - gcraccesstoken + kind: GCRAccessToken + listKind: GCRAccessTokenList + plural: gcraccesstokens + shortNames: + - gcraccesstoken + singular: gcraccesstoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: GCRAccessToken generates an GCP access token that can be used to authenticate with GCR. + 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: + properties: + auth: + description: Auth defines the means for authenticating with GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID defines which project to use to authenticate with + type: string + required: + - auth + - projectID + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/password.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: passwords.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - password + kind: Password + listKind: PasswordList + plural: passwords + shortNames: + - password + singular: password + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Password generates a random password based on the configuration parameters in spec. You can specify the length, characterset and other attributes. + 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: PasswordSpec controls the behavior of the password generator. + properties: + allowRepeat: + default: false + description: set AllowRepeat to true to allow repeating characters. + type: boolean + digits: + description: Digits specifies the number of digits in the generated password. If omitted it defaults to 25% of the length of the password + type: integer + length: + default: 24 + description: Length of the password to be generated. Defaults to 24 + type: integer + noUpper: + default: false + description: Set NoUpper to disable uppercase characters + type: boolean + symbolCharacters: + description: SymbolCharacters specifies the special characters that should be used in the generated password. + type: string + symbols: + description: Symbols specifies the number of symbol characters in the generated password. If omitted it defaults to 25% of the length of the password + type: integer + required: + - allowRepeat + - length + - noUpper + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/pushsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: pushsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - pushsecrets + kind: PushSecret + listKind: PushSecretList + plural: pushsecrets + singular: pushsecret + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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: PushSecretSpec configures the behavior of the PushSecret. + properties: + data: + description: Secret Data that should be pushed to providers + items: + properties: + match: + description: Match a given Secret Key to be pushed to the provider. + properties: + remoteRef: + description: Remote Refs to push to providers. + properties: + remoteKey: + description: Name of the resulting provider secret. + type: string + required: + - remoteKey + type: object + secretKey: + description: Secret Key to be pushed + type: string + required: + - remoteRef + - secretKey + type: object + required: + - match + type: object + type: array + deletionPolicy: + default: None + description: 'Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".' + type: string + refreshInterval: + description: The Interval to which External Secrets will try to push a secret definition + type: string + secretStoreRefs: + items: + properties: + kind: + default: SecretStore + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + labelSelector: + description: Optionally, sync to secret stores with label selector + 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 + required: + - key + - operator + type: object + type: array + 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 + name: + description: Optionally, sync to the SecretStore of the given name + type: string + type: object + type: array + selector: + description: The Secret Selector (k8s source) for the Push Secret + properties: + secret: + description: Select a Secret to Push. + properties: + name: + description: Name of the Secret. The Secret must exist in the same namespace as the PushSecret manifest. + type: string + required: + - name + type: object + required: + - secret + type: object + required: + - secretStoreRefs + - selector + type: object + status: + description: PushSecretStatus indicates the history of the status of PushSecret. + properties: + conditions: + items: + description: PushSecretStatusCondition indicates the status of the PushSecret. + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + description: PushSecretConditionType indicates the condition of the PushSecret. + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedPushSecrets: + additionalProperties: + additionalProperties: + properties: + match: + description: Match a given Secret Key to be pushed to the provider. + properties: + remoteRef: + description: Remote Refs to push to providers. + properties: + remoteKey: + description: Name of the resulting provider secret. + type: string + required: + - remoteKey + type: object + secretKey: + description: Secret Key to be pushed + type: string + required: + - remoteRef + - secretKey + type: object + required: + - match + type: object + type: object + description: Synced Push Secrets for later deletion. Matches Secret Stores to PushSecretData that was stored to that secretStore. + type: object + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/secretstore.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -4517,6 +5411,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.capabilities + name: Capabilities + type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -4794,6 +5691,19 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object type: object type: object region: @@ -5053,6 +5963,14 @@ spec: environment: description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) type: string + groupIDs: + description: GroupIDs specify, which gitlab groups to pull secrets from. Group secrets are read from left to right followed by the project variables. + items: + type: string + type: array + inheritFromGroups: + description: InheritFromGroups specifies whether parent groups should be discovered and checked for secrets. + type: boolean projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -5808,6 +6726,9 @@ spec: status: description: SecretStoreStatus defines the observed state of the SecretStore. properties: + capabilities: + description: SecretStoreCapabilities defines the possible operations a SecretStore can do. + type: string conditions: items: properties: @@ -5850,10 +6771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5908,10 +6829,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5921,6 +6842,7 @@ rules: - "clustersecretstores" - "externalsecrets" - "clusterexternalsecrets" + - "pushsecrets" verbs: - "get" - "list" @@ -5940,9 +6862,24 @@ rules: - "clusterexternalsecrets" - "clusterexternalsecrets/status" - "clusterexternalsecrets/finalizers" + - "pushsecrets" + - "pushsecrets/status" + - "pushsecrets/finalizers" verbs: - "update" - "patch" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "fakes" + - "passwords" + - "acraccesstokens" + - "gcraccesstokens" + - "ecrauthorizationtokens" + verbs: + - "get" + - "list" + - "watch" - apiGroups: - "" resources: @@ -6000,10 +6937,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -6015,6 +6952,7 @@ rules: - "externalsecrets" - "secretstores" - "clustersecretstores" + - "pushsecrets" verbs: - "get" - "watch" @@ -6026,10 +6964,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -6040,6 +6978,7 @@ rules: - "externalsecrets" - "secretstores" - "clustersecretstores" + - "pushsecrets" verbs: - "create" - "delete" @@ -6053,10 +6992,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6073,10 +7012,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6109,10 +7048,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6148,10 +7087,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6169,10 +7108,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -6193,10 +7132,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6213,7 +7152,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -6240,10 +7179,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6260,7 +7199,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -6276,10 +7215,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6297,7 +7236,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - webhook @@ -6305,6 +7244,7 @@ spec: - --dns-name=golang-external-secrets-webhook.default.svc - --cert-dir=/tmp/certs - --check-interval=5m + - --metrics-addr=:8080 - --healthz-addr=:8081 ports: - containerPort: 8080 diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index fc9597d5..2bee122f 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -62,6 +62,148 @@ metadata: kubernetes.io/service-account.name: golang-external-secrets type: kubernetes.io/service-account-token --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/acraccesstoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: acraccesstokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - acraccesstoken + kind: ACRAccessToken + listKind: ACRAccessTokenList + plural: acraccesstokens + shortNames: + - acraccesstoken + singular: acraccesstoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: "ACRAccessToken returns a Azure Container Registry token that can be used for pushing/pulling images. Note: by default it will return an ACR Refresh Token with full access (depending on the identity). This can be scoped down to the repository level using .spec.scope. In case scope is defined it will return an ACR Access Token. \n See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md" + 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: 'ACRAccessTokenSpec defines how to generate the access token e.g. how to authenticate and which registry to use. see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview' + properties: + auth: + properties: + managedIdentity: + description: ManagedIdentity uses Azure Managed Identity to authenticate with Azure. + properties: + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + type: object + servicePrincipal: + description: ServicePrincipal uses Azure Service Principal credentials to authenticate with Azure. + properties: + secretRef: + description: Configuration used to authenticate with Azure using static credentials stored in a Kind=Secret. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + workloadIdentity: + description: WorkloadIdentity uses Azure Workload Identity to authenticate with Azure. + properties: + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + type: object + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string + registry: + description: the domain name of the ACR registry e.g. foobarexample.azurecr.io + type: string + scope: + description: "Define the scope for the access token, e.g. pull/push access for a repository. if not provided it will return a refresh token that has full scope. Note: you need to pin it down to the repository level, there is no wildcard available. \n examples: repository:my-repository:pull,push repository:my-repository:pull \n see docs for details: https://docs.docker.com/registry/spec/auth/scope/" + type: string + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + required: + - auth + - registry + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -124,7 +266,7 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. + description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -150,7 +292,42 @@ spec: - key type: object secretKey: + description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret type: string + sourceRef: + description: SourceRef allows you to override the source from which the value will pulled from. + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object required: - remoteRef - secretKey @@ -161,7 +338,7 @@ spec: items: properties: extract: - description: Used to extract multiple key/value pairs from one secret + description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -187,7 +364,7 @@ spec: - key type: object find: - description: Used to find secrets based on tags or regular expressions + description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -232,6 +409,40 @@ spec: type: object type: object type: array + sourceRef: + description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object type: object type: array refreshInterval: @@ -346,8 +557,6 @@ spec: type: string type: object type: object - required: - - secretStoreRef type: object namespaceSelector: description: The labels to select by to find the Namespaces to create the ExternalSecrets in. @@ -1544,6 +1753,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.capabilities + name: Capabilities + type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -1821,6 +2033,19 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object type: object type: object region: @@ -2080,6 +2305,14 @@ spec: environment: description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) type: string + groupIDs: + description: GroupIDs specify, which gitlab groups to pull secrets from. Group secrets are read from left to right followed by the project variables. + items: + type: string + type: array + inheritFromGroups: + description: InheritFromGroups specifies whether parent groups should be discovered and checked for secrets. + type: boolean projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -2835,6 +3068,9 @@ spec: status: description: SecretStoreStatus defines the observed state of the SecretStore. properties: + capabilities: + description: SecretStoreCapabilities defines the possible operations a SecretStore can do. + type: string conditions: items: properties: @@ -2871,6 +3107,135 @@ spec: namespace: "default" path: /convert --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/ecrauthorizationtoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: ecrauthorizationtokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - ecrauthorizationtoken + kind: ECRAuthorizationToken + listKind: ECRAuthorizationTokenList + plural: ecrauthorizationtokens + shortNames: + - ecrauthorizationtoken + singular: ecrauthorizationtoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an authorization token. The authorization token is valid for 12 hours. The authorizationToken returned is a base64 encoded string that can be decoded and used in a docker login command to authenticate to a registry. For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. + 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: + properties: + auth: + description: Auth defines how to authenticate with AWS + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: Region specifies the region to operate in. + type: string + role: + description: You can assume a role before making calls to the desired AWS service. + type: string + required: + - region + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/externalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -3143,7 +3508,7 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. + description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -3169,10 +3534,45 @@ spec: - key type: object secretKey: + description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret type: string - required: - - remoteRef - - secretKey + sourceRef: + description: SourceRef allows you to override the source from which the value will pulled from. + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object + required: + - remoteRef + - secretKey type: object type: array dataFrom: @@ -3180,7 +3580,7 @@ spec: items: properties: extract: - description: Used to extract multiple key/value pairs from one secret + description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -3206,7 +3606,7 @@ spec: - key type: object find: - description: Used to find secrets based on tags or regular expressions + description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -3251,6 +3651,40 @@ spec: type: object type: object type: array + sourceRef: + description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object type: object type: array refreshInterval: @@ -3365,8 +3799,6 @@ spec: type: string type: object type: object - required: - - secretStoreRef type: object status: properties: @@ -3414,6 +3846,468 @@ spec: namespace: "default" path: /convert --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/fake.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: fakes.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - fake + kind: Fake + listKind: FakeList + plural: fakes + shortNames: + - fake + singular: fake + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Fake generator is used for testing. It lets you define a static set of credentials that is always returned. + 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: FakeSpec contains the static data. + properties: + data: + additionalProperties: + type: string + description: Data defines the static data returned by this generator. + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/gcraccesstoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: gcraccesstokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - gcraccesstoken + kind: GCRAccessToken + listKind: GCRAccessTokenList + plural: gcraccesstokens + shortNames: + - gcraccesstoken + singular: gcraccesstoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: GCRAccessToken generates an GCP access token that can be used to authenticate with GCR. + 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: + properties: + auth: + description: Auth defines the means for authenticating with GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID defines which project to use to authenticate with + type: string + required: + - auth + - projectID + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/password.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: passwords.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - password + kind: Password + listKind: PasswordList + plural: passwords + shortNames: + - password + singular: password + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Password generates a random password based on the configuration parameters in spec. You can specify the length, characterset and other attributes. + 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: PasswordSpec controls the behavior of the password generator. + properties: + allowRepeat: + default: false + description: set AllowRepeat to true to allow repeating characters. + type: boolean + digits: + description: Digits specifies the number of digits in the generated password. If omitted it defaults to 25% of the length of the password + type: integer + length: + default: 24 + description: Length of the password to be generated. Defaults to 24 + type: integer + noUpper: + default: false + description: Set NoUpper to disable uppercase characters + type: boolean + symbolCharacters: + description: SymbolCharacters specifies the special characters that should be used in the generated password. + type: string + symbols: + description: Symbols specifies the number of symbol characters in the generated password. If omitted it defaults to 25% of the length of the password + type: integer + required: + - allowRepeat + - length + - noUpper + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/pushsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: pushsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - pushsecrets + kind: PushSecret + listKind: PushSecretList + plural: pushsecrets + singular: pushsecret + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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: PushSecretSpec configures the behavior of the PushSecret. + properties: + data: + description: Secret Data that should be pushed to providers + items: + properties: + match: + description: Match a given Secret Key to be pushed to the provider. + properties: + remoteRef: + description: Remote Refs to push to providers. + properties: + remoteKey: + description: Name of the resulting provider secret. + type: string + required: + - remoteKey + type: object + secretKey: + description: Secret Key to be pushed + type: string + required: + - remoteRef + - secretKey + type: object + required: + - match + type: object + type: array + deletionPolicy: + default: None + description: 'Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".' + type: string + refreshInterval: + description: The Interval to which External Secrets will try to push a secret definition + type: string + secretStoreRefs: + items: + properties: + kind: + default: SecretStore + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + labelSelector: + description: Optionally, sync to secret stores with label selector + 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 + required: + - key + - operator + type: object + type: array + 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 + name: + description: Optionally, sync to the SecretStore of the given name + type: string + type: object + type: array + selector: + description: The Secret Selector (k8s source) for the Push Secret + properties: + secret: + description: Select a Secret to Push. + properties: + name: + description: Name of the Secret. The Secret must exist in the same namespace as the PushSecret manifest. + type: string + required: + - name + type: object + required: + - secret + type: object + required: + - secretStoreRefs + - selector + type: object + status: + description: PushSecretStatus indicates the history of the status of PushSecret. + properties: + conditions: + items: + description: PushSecretStatusCondition indicates the status of the PushSecret. + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + description: PushSecretConditionType indicates the condition of the PushSecret. + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedPushSecrets: + additionalProperties: + additionalProperties: + properties: + match: + description: Match a given Secret Key to be pushed to the provider. + properties: + remoteRef: + description: Remote Refs to push to providers. + properties: + remoteKey: + description: Name of the resulting provider secret. + type: string + required: + - remoteKey + type: object + secretKey: + description: Secret Key to be pushed + type: string + required: + - remoteRef + - secretKey + type: object + required: + - match + type: object + type: object + description: Synced Push Secrets for later deletion. Matches Secret Stores to PushSecretData that was stored to that secretStore. + type: object + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/secretstore.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -4517,6 +5411,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.capabilities + name: Capabilities + type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -4794,6 +5691,19 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object type: object type: object region: @@ -5053,6 +5963,14 @@ spec: environment: description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) type: string + groupIDs: + description: GroupIDs specify, which gitlab groups to pull secrets from. Group secrets are read from left to right followed by the project variables. + items: + type: string + type: array + inheritFromGroups: + description: InheritFromGroups specifies whether parent groups should be discovered and checked for secrets. + type: boolean projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -5808,6 +6726,9 @@ spec: status: description: SecretStoreStatus defines the observed state of the SecretStore. properties: + capabilities: + description: SecretStoreCapabilities defines the possible operations a SecretStore can do. + type: string conditions: items: properties: @@ -5850,10 +6771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5908,10 +6829,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5921,6 +6842,7 @@ rules: - "clustersecretstores" - "externalsecrets" - "clusterexternalsecrets" + - "pushsecrets" verbs: - "get" - "list" @@ -5940,9 +6862,24 @@ rules: - "clusterexternalsecrets" - "clusterexternalsecrets/status" - "clusterexternalsecrets/finalizers" + - "pushsecrets" + - "pushsecrets/status" + - "pushsecrets/finalizers" verbs: - "update" - "patch" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "fakes" + - "passwords" + - "acraccesstokens" + - "gcraccesstokens" + - "ecrauthorizationtokens" + verbs: + - "get" + - "list" + - "watch" - apiGroups: - "" resources: @@ -6000,10 +6937,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -6015,6 +6952,7 @@ rules: - "externalsecrets" - "secretstores" - "clustersecretstores" + - "pushsecrets" verbs: - "get" - "watch" @@ -6026,10 +6964,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -6040,6 +6978,7 @@ rules: - "externalsecrets" - "secretstores" - "clustersecretstores" + - "pushsecrets" verbs: - "create" - "delete" @@ -6053,10 +6992,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6073,10 +7012,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6109,10 +7048,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6148,10 +7087,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6169,10 +7108,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -6193,10 +7132,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6213,7 +7152,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -6240,10 +7179,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6260,7 +7199,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -6276,10 +7215,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6297,7 +7236,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - webhook @@ -6305,6 +7244,7 @@ spec: - --dns-name=golang-external-secrets-webhook.default.svc - --cert-dir=/tmp/certs - --check-interval=5m + - --metrics-addr=:8080 - --healthz-addr=:8081 ports: - containerPort: 8080 diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index 50c62b84..d5b66165 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -62,6 +62,148 @@ metadata: kubernetes.io/service-account.name: golang-external-secrets type: kubernetes.io/service-account-token --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/acraccesstoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: acraccesstokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - acraccesstoken + kind: ACRAccessToken + listKind: ACRAccessTokenList + plural: acraccesstokens + shortNames: + - acraccesstoken + singular: acraccesstoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: "ACRAccessToken returns a Azure Container Registry token that can be used for pushing/pulling images. Note: by default it will return an ACR Refresh Token with full access (depending on the identity). This can be scoped down to the repository level using .spec.scope. In case scope is defined it will return an ACR Access Token. \n See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md" + 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: 'ACRAccessTokenSpec defines how to generate the access token e.g. how to authenticate and which registry to use. see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview' + properties: + auth: + properties: + managedIdentity: + description: ManagedIdentity uses Azure Managed Identity to authenticate with Azure. + properties: + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + type: object + servicePrincipal: + description: ServicePrincipal uses Azure Service Principal credentials to authenticate with Azure. + properties: + secretRef: + description: Configuration used to authenticate with Azure using static credentials stored in a Kind=Secret. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + workloadIdentity: + description: WorkloadIdentity uses Azure Workload Identity to authenticate with Azure. + properties: + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + type: object + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string + registry: + description: the domain name of the ACR registry e.g. foobarexample.azurecr.io + type: string + scope: + description: "Define the scope for the access token, e.g. pull/push access for a repository. if not provided it will return a refresh token that has full scope. Note: you need to pin it down to the repository level, there is no wildcard available. \n examples: repository:my-repository:pull,push repository:my-repository:pull \n see docs for details: https://docs.docker.com/registry/spec/auth/scope/" + type: string + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + required: + - auth + - registry + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -124,7 +266,7 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. + description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -150,7 +292,42 @@ spec: - key type: object secretKey: + description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret type: string + sourceRef: + description: SourceRef allows you to override the source from which the value will pulled from. + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object required: - remoteRef - secretKey @@ -161,7 +338,7 @@ spec: items: properties: extract: - description: Used to extract multiple key/value pairs from one secret + description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -187,7 +364,7 @@ spec: - key type: object find: - description: Used to find secrets based on tags or regular expressions + description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -232,6 +409,40 @@ spec: type: object type: object type: array + sourceRef: + description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object type: object type: array refreshInterval: @@ -346,8 +557,6 @@ spec: type: string type: object type: object - required: - - secretStoreRef type: object namespaceSelector: description: The labels to select by to find the Namespaces to create the ExternalSecrets in. @@ -1544,6 +1753,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.capabilities + name: Capabilities + type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -1821,6 +2033,19 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object type: object type: object region: @@ -2080,6 +2305,14 @@ spec: environment: description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) type: string + groupIDs: + description: GroupIDs specify, which gitlab groups to pull secrets from. Group secrets are read from left to right followed by the project variables. + items: + type: string + type: array + inheritFromGroups: + description: InheritFromGroups specifies whether parent groups should be discovered and checked for secrets. + type: boolean projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -2835,6 +3068,9 @@ spec: status: description: SecretStoreStatus defines the observed state of the SecretStore. properties: + capabilities: + description: SecretStoreCapabilities defines the possible operations a SecretStore can do. + type: string conditions: items: properties: @@ -2871,6 +3107,135 @@ spec: namespace: "default" path: /convert --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/ecrauthorizationtoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: ecrauthorizationtokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - ecrauthorizationtoken + kind: ECRAuthorizationToken + listKind: ECRAuthorizationTokenList + plural: ecrauthorizationtokens + shortNames: + - ecrauthorizationtoken + singular: ecrauthorizationtoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an authorization token. The authorization token is valid for 12 hours. The authorizationToken returned is a base64 encoded string that can be decoded and used in a docker login command to authenticate to a registry. For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. + 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: + properties: + auth: + description: Auth defines how to authenticate with AWS + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: Region specifies the region to operate in. + type: string + role: + description: You can assume a role before making calls to the desired AWS service. + type: string + required: + - region + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/externalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -3143,7 +3508,7 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. + description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -3169,10 +3534,45 @@ spec: - key type: object secretKey: + description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret type: string - required: - - remoteRef - - secretKey + sourceRef: + description: SourceRef allows you to override the source from which the value will pulled from. + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object + required: + - remoteRef + - secretKey type: object type: array dataFrom: @@ -3180,7 +3580,7 @@ spec: items: properties: extract: - description: Used to extract multiple key/value pairs from one secret + description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -3206,7 +3606,7 @@ spec: - key type: object find: - description: Used to find secrets based on tags or regular expressions + description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -3251,6 +3651,40 @@ spec: type: object type: object type: array + sourceRef: + description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object type: object type: array refreshInterval: @@ -3365,8 +3799,6 @@ spec: type: string type: object type: object - required: - - secretStoreRef type: object status: properties: @@ -3414,6 +3846,468 @@ spec: namespace: "default" path: /convert --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/fake.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: fakes.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - fake + kind: Fake + listKind: FakeList + plural: fakes + shortNames: + - fake + singular: fake + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Fake generator is used for testing. It lets you define a static set of credentials that is always returned. + 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: FakeSpec contains the static data. + properties: + data: + additionalProperties: + type: string + description: Data defines the static data returned by this generator. + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/gcraccesstoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: gcraccesstokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - gcraccesstoken + kind: GCRAccessToken + listKind: GCRAccessTokenList + plural: gcraccesstokens + shortNames: + - gcraccesstoken + singular: gcraccesstoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: GCRAccessToken generates an GCP access token that can be used to authenticate with GCR. + 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: + properties: + auth: + description: Auth defines the means for authenticating with GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID defines which project to use to authenticate with + type: string + required: + - auth + - projectID + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/password.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: passwords.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - password + kind: Password + listKind: PasswordList + plural: passwords + shortNames: + - password + singular: password + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Password generates a random password based on the configuration parameters in spec. You can specify the length, characterset and other attributes. + 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: PasswordSpec controls the behavior of the password generator. + properties: + allowRepeat: + default: false + description: set AllowRepeat to true to allow repeating characters. + type: boolean + digits: + description: Digits specifies the number of digits in the generated password. If omitted it defaults to 25% of the length of the password + type: integer + length: + default: 24 + description: Length of the password to be generated. Defaults to 24 + type: integer + noUpper: + default: false + description: Set NoUpper to disable uppercase characters + type: boolean + symbolCharacters: + description: SymbolCharacters specifies the special characters that should be used in the generated password. + type: string + symbols: + description: Symbols specifies the number of symbol characters in the generated password. If omitted it defaults to 25% of the length of the password + type: integer + required: + - allowRepeat + - length + - noUpper + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/pushsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: pushsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - pushsecrets + kind: PushSecret + listKind: PushSecretList + plural: pushsecrets + singular: pushsecret + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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: PushSecretSpec configures the behavior of the PushSecret. + properties: + data: + description: Secret Data that should be pushed to providers + items: + properties: + match: + description: Match a given Secret Key to be pushed to the provider. + properties: + remoteRef: + description: Remote Refs to push to providers. + properties: + remoteKey: + description: Name of the resulting provider secret. + type: string + required: + - remoteKey + type: object + secretKey: + description: Secret Key to be pushed + type: string + required: + - remoteRef + - secretKey + type: object + required: + - match + type: object + type: array + deletionPolicy: + default: None + description: 'Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".' + type: string + refreshInterval: + description: The Interval to which External Secrets will try to push a secret definition + type: string + secretStoreRefs: + items: + properties: + kind: + default: SecretStore + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + labelSelector: + description: Optionally, sync to secret stores with label selector + 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 + required: + - key + - operator + type: object + type: array + 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 + name: + description: Optionally, sync to the SecretStore of the given name + type: string + type: object + type: array + selector: + description: The Secret Selector (k8s source) for the Push Secret + properties: + secret: + description: Select a Secret to Push. + properties: + name: + description: Name of the Secret. The Secret must exist in the same namespace as the PushSecret manifest. + type: string + required: + - name + type: object + required: + - secret + type: object + required: + - secretStoreRefs + - selector + type: object + status: + description: PushSecretStatus indicates the history of the status of PushSecret. + properties: + conditions: + items: + description: PushSecretStatusCondition indicates the status of the PushSecret. + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + description: PushSecretConditionType indicates the condition of the PushSecret. + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedPushSecrets: + additionalProperties: + additionalProperties: + properties: + match: + description: Match a given Secret Key to be pushed to the provider. + properties: + remoteRef: + description: Remote Refs to push to providers. + properties: + remoteKey: + description: Name of the resulting provider secret. + type: string + required: + - remoteKey + type: object + secretKey: + description: Secret Key to be pushed + type: string + required: + - remoteRef + - secretKey + type: object + required: + - match + type: object + type: object + description: Synced Push Secrets for later deletion. Matches Secret Stores to PushSecretData that was stored to that secretStore. + type: object + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/secretstore.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -4517,6 +5411,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.capabilities + name: Capabilities + type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -4794,6 +5691,19 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object type: object type: object region: @@ -5053,6 +5963,14 @@ spec: environment: description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) type: string + groupIDs: + description: GroupIDs specify, which gitlab groups to pull secrets from. Group secrets are read from left to right followed by the project variables. + items: + type: string + type: array + inheritFromGroups: + description: InheritFromGroups specifies whether parent groups should be discovered and checked for secrets. + type: boolean projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -5808,6 +6726,9 @@ spec: status: description: SecretStoreStatus defines the observed state of the SecretStore. properties: + capabilities: + description: SecretStoreCapabilities defines the possible operations a SecretStore can do. + type: string conditions: items: properties: @@ -5850,10 +6771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5908,10 +6829,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5921,6 +6842,7 @@ rules: - "clustersecretstores" - "externalsecrets" - "clusterexternalsecrets" + - "pushsecrets" verbs: - "get" - "list" @@ -5940,9 +6862,24 @@ rules: - "clusterexternalsecrets" - "clusterexternalsecrets/status" - "clusterexternalsecrets/finalizers" + - "pushsecrets" + - "pushsecrets/status" + - "pushsecrets/finalizers" verbs: - "update" - "patch" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "fakes" + - "passwords" + - "acraccesstokens" + - "gcraccesstokens" + - "ecrauthorizationtokens" + verbs: + - "get" + - "list" + - "watch" - apiGroups: - "" resources: @@ -6000,10 +6937,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -6015,6 +6952,7 @@ rules: - "externalsecrets" - "secretstores" - "clustersecretstores" + - "pushsecrets" verbs: - "get" - "watch" @@ -6026,10 +6964,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -6040,6 +6978,7 @@ rules: - "externalsecrets" - "secretstores" - "clustersecretstores" + - "pushsecrets" verbs: - "create" - "delete" @@ -6053,10 +6992,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6073,10 +7012,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6109,10 +7048,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6148,10 +7087,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6169,10 +7108,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -6193,10 +7132,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6213,7 +7152,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -6240,10 +7179,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6260,7 +7199,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -6276,10 +7215,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6297,7 +7236,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - webhook @@ -6305,6 +7244,7 @@ spec: - --dns-name=golang-external-secrets-webhook.default.svc - --cert-dir=/tmp/certs - --check-interval=5m + - --metrics-addr=:8080 - --healthz-addr=:8081 ports: - containerPort: 8080 diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index fc9597d5..2bee122f 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook --- @@ -62,6 +62,148 @@ metadata: kubernetes.io/service-account.name: golang-external-secrets type: kubernetes.io/service-account-token --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/acraccesstoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: acraccesstokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - acraccesstoken + kind: ACRAccessToken + listKind: ACRAccessTokenList + plural: acraccesstokens + shortNames: + - acraccesstoken + singular: acraccesstoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: "ACRAccessToken returns a Azure Container Registry token that can be used for pushing/pulling images. Note: by default it will return an ACR Refresh Token with full access (depending on the identity). This can be scoped down to the repository level using .spec.scope. In case scope is defined it will return an ACR Access Token. \n See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md" + 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: 'ACRAccessTokenSpec defines how to generate the access token e.g. how to authenticate and which registry to use. see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview' + properties: + auth: + properties: + managedIdentity: + description: ManagedIdentity uses Azure Managed Identity to authenticate with Azure. + properties: + identityId: + description: If multiple Managed Identity is assigned to the pod, you can select the one to be used + type: string + type: object + servicePrincipal: + description: ServicePrincipal uses Azure Service Principal credentials to authenticate with Azure. + properties: + secretRef: + description: Configuration used to authenticate with Azure using static credentials stored in a Kind=Secret. + properties: + clientId: + description: The Azure clientId of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + clientSecret: + description: The Azure ClientSecret of the service principle used for authentication. + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + required: + - secretRef + type: object + workloadIdentity: + description: WorkloadIdentity uses Azure Workload Identity to authenticate with Azure. + properties: + serviceAccountRef: + description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + type: object + environmentType: + default: PublicCloud + description: 'EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint. The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152 PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud' + enum: + - PublicCloud + - USGovernmentCloud + - ChinaCloud + - GermanCloud + type: string + registry: + description: the domain name of the ACR registry e.g. foobarexample.azurecr.io + type: string + scope: + description: "Define the scope for the access token, e.g. pull/push access for a repository. if not provided it will return a refresh token that has full scope. Note: you need to pin it down to the repository level, there is no wildcard available. \n examples: repository:my-repository:pull,push repository:my-repository:pull \n see docs for details: https://docs.docker.com/registry/spec/auth/scope/" + type: string + tenantId: + description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type. + type: string + required: + - auth + - registry + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/clusterexternalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -124,7 +266,7 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. + description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -150,7 +292,42 @@ spec: - key type: object secretKey: + description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret type: string + sourceRef: + description: SourceRef allows you to override the source from which the value will pulled from. + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object required: - remoteRef - secretKey @@ -161,7 +338,7 @@ spec: items: properties: extract: - description: Used to extract multiple key/value pairs from one secret + description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -187,7 +364,7 @@ spec: - key type: object find: - description: Used to find secrets based on tags or regular expressions + description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -232,6 +409,40 @@ spec: type: object type: object type: array + sourceRef: + description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object type: object type: array refreshInterval: @@ -346,8 +557,6 @@ spec: type: string type: object type: object - required: - - secretStoreRef type: object namespaceSelector: description: The labels to select by to find the Namespaces to create the ExternalSecrets in. @@ -1544,6 +1753,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.capabilities + name: Capabilities + type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -1821,6 +2033,19 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object type: object type: object region: @@ -2080,6 +2305,14 @@ spec: environment: description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) type: string + groupIDs: + description: GroupIDs specify, which gitlab groups to pull secrets from. Group secrets are read from left to right followed by the project variables. + items: + type: string + type: array + inheritFromGroups: + description: InheritFromGroups specifies whether parent groups should be discovered and checked for secrets. + type: boolean projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -2835,6 +3068,9 @@ spec: status: description: SecretStoreStatus defines the observed state of the SecretStore. properties: + capabilities: + description: SecretStoreCapabilities defines the possible operations a SecretStore can do. + type: string conditions: items: properties: @@ -2871,6 +3107,135 @@ spec: namespace: "default" path: /convert --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/ecrauthorizationtoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: ecrauthorizationtokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - ecrauthorizationtoken + kind: ECRAuthorizationToken + listKind: ECRAuthorizationTokenList + plural: ecrauthorizationtokens + shortNames: + - ecrauthorizationtoken + singular: ecrauthorizationtoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an authorization token. The authorization token is valid for 12 hours. The authorizationToken returned is a base64 encoded string that can be decoded and used in a docker login command to authenticate to a registry. For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide. + 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: + properties: + auth: + description: Auth defines how to authenticate with AWS + properties: + jwt: + description: Authenticate against AWS using service account tokens. + properties: + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + type: object + secretRef: + description: AWSAuthSecretRef holds secret references for AWS credentials both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate. + properties: + accessKeyIDSecretRef: + description: The AccessKeyID is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + type: object + region: + description: Region specifies the region to operate in. + type: string + role: + description: You can assume a role before making calls to the desired AWS service. + type: string + required: + - region + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/externalsecret.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -3143,7 +3508,7 @@ spec: description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data. properties: remoteRef: - description: ExternalSecretDataRemoteRef defines Provider data location. + description: RemoteRef points to the remote secret and defines which secret (version/property/..) to fetch. properties: conversionStrategy: default: Default @@ -3169,10 +3534,45 @@ spec: - key type: object secretKey: + description: SecretKey defines the key in which the controller stores the value. This is the key in the Kind=Secret type: string - required: - - remoteRef - - secretKey + sourceRef: + description: SourceRef allows you to override the source from which the value will pulled from. + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object + required: + - remoteRef + - secretKey type: object type: array dataFrom: @@ -3180,7 +3580,7 @@ spec: items: properties: extract: - description: Used to extract multiple key/value pairs from one secret + description: 'Used to extract multiple key/value pairs from one secret Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -3206,7 +3606,7 @@ spec: - key type: object find: - description: Used to find secrets based on tags or regular expressions + description: 'Used to find secrets based on tags or regular expressions Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.' properties: conversionStrategy: default: Default @@ -3251,6 +3651,40 @@ spec: type: object type: object type: array + sourceRef: + description: SourceRef points to a store or generator which contains secret values ready to use. Use this in combination with Extract or Find pull values out of a specific SecretStore. When sourceRef points to a generator Extract or Find is not supported. The generator returns a static map of values + maxProperties: 1 + properties: + generatorRef: + description: GeneratorRef points to a generator custom resource in + properties: + apiVersion: + default: generators.external-secrets.io/v1alpha1 + description: Specify the apiVersion of the generator resource + type: string + kind: + description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc. + type: string + name: + description: Specify the name of the generator resource + type: string + required: + - kind + - name + type: object + storeRef: + description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data. + properties: + kind: + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + name: + description: Name of the SecretStore resource + type: string + required: + - name + type: object + type: object type: object type: array refreshInterval: @@ -3365,8 +3799,6 @@ spec: type: string type: object type: object - required: - - secretStoreRef type: object status: properties: @@ -3414,6 +3846,468 @@ spec: namespace: "default" path: /convert --- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/fake.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: fakes.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - fake + kind: Fake + listKind: FakeList + plural: fakes + shortNames: + - fake + singular: fake + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Fake generator is used for testing. It lets you define a static set of credentials that is always returned. + 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: FakeSpec contains the static data. + properties: + data: + additionalProperties: + type: string + description: Data defines the static data returned by this generator. + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/gcraccesstoken.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: gcraccesstokens.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - gcraccesstoken + kind: GCRAccessToken + listKind: GCRAccessTokenList + plural: gcraccesstokens + shortNames: + - gcraccesstoken + singular: gcraccesstoken + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: GCRAccessToken generates an GCP access token that can be used to authenticate with GCR. + 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: + properties: + auth: + description: Auth defines the means for authenticating with GCP + properties: + secretRef: + properties: + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object + type: object + workloadIdentity: + properties: + clusterLocation: + type: string + clusterName: + type: string + clusterProjectID: + type: string + serviceAccountRef: + description: A reference to a ServiceAccount resource. + properties: + audiences: + description: Audience specifies the `aud` claim for the service account token If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity then this audiences will be appended to the list + items: + type: string + type: array + name: + description: The name of the ServiceAccount resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + required: + - name + type: object + required: + - clusterLocation + - clusterName + - serviceAccountRef + type: object + type: object + projectID: + description: ProjectID defines which project to use to authenticate with + type: string + required: + - auth + - projectID + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/password.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: passwords.generators.external-secrets.io +spec: + group: generators.external-secrets.io + names: + categories: + - password + kind: Password + listKind: PasswordList + plural: passwords + shortNames: + - password + singular: password + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Password generates a random password based on the configuration parameters in spec. You can specify the length, characterset and other attributes. + 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: PasswordSpec controls the behavior of the password generator. + properties: + allowRepeat: + default: false + description: set AllowRepeat to true to allow repeating characters. + type: boolean + digits: + description: Digits specifies the number of digits in the generated password. If omitted it defaults to 25% of the length of the password + type: integer + length: + default: 24 + description: Length of the password to be generated. Defaults to 24 + type: integer + noUpper: + default: false + description: Set NoUpper to disable uppercase characters + type: boolean + symbolCharacters: + description: SymbolCharacters specifies the special characters that should be used in the generated password. + type: string + symbols: + description: Symbols specifies the number of symbol characters in the generated password. If omitted it defaults to 25% of the length of the password + type: integer + required: + - allowRepeat + - length + - noUpper + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- +# Source: golang-external-secrets/charts/external-secrets/templates/crds/pushsecret.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: pushsecrets.external-secrets.io +spec: + group: external-secrets.io + names: + categories: + - pushsecrets + kind: PushSecret + listKind: PushSecretList + plural: pushsecrets + singular: pushsecret + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + 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: PushSecretSpec configures the behavior of the PushSecret. + properties: + data: + description: Secret Data that should be pushed to providers + items: + properties: + match: + description: Match a given Secret Key to be pushed to the provider. + properties: + remoteRef: + description: Remote Refs to push to providers. + properties: + remoteKey: + description: Name of the resulting provider secret. + type: string + required: + - remoteKey + type: object + secretKey: + description: Secret Key to be pushed + type: string + required: + - remoteRef + - secretKey + type: object + required: + - match + type: object + type: array + deletionPolicy: + default: None + description: 'Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".' + type: string + refreshInterval: + description: The Interval to which External Secrets will try to push a secret definition + type: string + secretStoreRefs: + items: + properties: + kind: + default: SecretStore + description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore` + type: string + labelSelector: + description: Optionally, sync to secret stores with label selector + 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 + required: + - key + - operator + type: object + type: array + 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 + name: + description: Optionally, sync to the SecretStore of the given name + type: string + type: object + type: array + selector: + description: The Secret Selector (k8s source) for the Push Secret + properties: + secret: + description: Select a Secret to Push. + properties: + name: + description: Name of the Secret. The Secret must exist in the same namespace as the PushSecret manifest. + type: string + required: + - name + type: object + required: + - secret + type: object + required: + - secretStoreRefs + - selector + type: object + status: + description: PushSecretStatus indicates the history of the status of PushSecret. + properties: + conditions: + items: + description: PushSecretStatusCondition indicates the status of the PushSecret. + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + description: PushSecretConditionType indicates the condition of the PushSecret. + type: string + required: + - status + - type + type: object + type: array + refreshTime: + description: refreshTime is the time and date the external secret was fetched and the target secret updated + format: date-time + nullable: true + type: string + syncedPushSecrets: + additionalProperties: + additionalProperties: + properties: + match: + description: Match a given Secret Key to be pushed to the provider. + properties: + remoteRef: + description: Remote Refs to push to providers. + properties: + remoteKey: + description: Name of the resulting provider secret. + type: string + required: + - remoteKey + type: object + secretKey: + description: Secret Key to be pushed + type: string + required: + - remoteRef + - secretKey + type: object + required: + - match + type: object + type: object + description: Synced Push Secrets for later deletion. Matches Secret Stores to PushSecretData that was stored to that secretStore. + type: object + syncedResourceVersion: + description: SyncedResourceVersion keeps track of the last synced version. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1 + clientConfig: + service: + name: golang-external-secrets-webhook + namespace: "default" + path: /convert +--- # Source: golang-external-secrets/charts/external-secrets/templates/crds/secretstore.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -4517,6 +5411,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.capabilities + name: Capabilities + type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -4794,6 +5691,19 @@ spec: description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. type: string type: object + sessionTokenSecretRef: + description: 'The SessionToken used for authentication This must be defined if AccessKeyID and SecretAccessKey are temporary credentials see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html' + properties: + key: + description: The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be defaulted, in others it may be required. + type: string + name: + description: The name of the Secret resource being referred to. + type: string + namespace: + description: Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults to the namespace of the referent. + type: string + type: object type: object type: object region: @@ -5053,6 +5963,14 @@ spec: environment: description: Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments) type: string + groupIDs: + description: GroupIDs specify, which gitlab groups to pull secrets from. Group secrets are read from left to right followed by the project variables. + items: + type: string + type: array + inheritFromGroups: + description: InheritFromGroups specifies whether parent groups should be discovered and checked for secrets. + type: boolean projectID: description: ProjectID specifies a project where secrets are located. type: string @@ -5808,6 +6726,9 @@ spec: status: description: SecretStoreStatus defines the observed state of the SecretStore. properties: + capabilities: + description: SecretStoreCapabilities defines the possible operations a SecretStore can do. + type: string conditions: items: properties: @@ -5850,10 +6771,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5908,10 +6829,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5921,6 +6842,7 @@ rules: - "clustersecretstores" - "externalsecrets" - "clusterexternalsecrets" + - "pushsecrets" verbs: - "get" - "list" @@ -5940,9 +6862,24 @@ rules: - "clusterexternalsecrets" - "clusterexternalsecrets/status" - "clusterexternalsecrets/finalizers" + - "pushsecrets" + - "pushsecrets/status" + - "pushsecrets/finalizers" verbs: - "update" - "patch" + - apiGroups: + - "generators.external-secrets.io" + resources: + - "fakes" + - "passwords" + - "acraccesstokens" + - "gcraccesstokens" + - "ecrauthorizationtokens" + verbs: + - "get" + - "list" + - "watch" - apiGroups: - "" resources: @@ -6000,10 +6937,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -6015,6 +6952,7 @@ rules: - "externalsecrets" - "secretstores" - "clustersecretstores" + - "pushsecrets" verbs: - "get" - "watch" @@ -6026,10 +6964,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -6040,6 +6978,7 @@ rules: - "externalsecrets" - "secretstores" - "clustersecretstores" + - "pushsecrets" verbs: - "create" - "delete" @@ -6053,10 +6992,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6073,10 +7012,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6109,10 +7048,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -6148,10 +7087,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -6169,10 +7108,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm external-secrets.io/component: webhook spec: @@ -6193,10 +7132,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6213,7 +7152,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - certcontroller @@ -6240,10 +7179,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6260,7 +7199,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -6276,10 +7215,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.6.1 + helm.sh/chart: external-secrets-0.7.0 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.6.1" + app.kubernetes.io/version: "v0.7.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -6297,7 +7236,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.6.1-ubi" + image: "ghcr.io/external-secrets/external-secrets:v0.7.0-ubi" imagePullPolicy: IfNotPresent args: - webhook @@ -6305,6 +7244,7 @@ spec: - --dns-name=golang-external-secrets-webhook.default.svc - --cert-dir=/tmp/certs - --check-interval=5m + - --metrics-addr=:8080 - --healthz-addr=:8081 ports: - containerPort: 8080 From 74bfd9ff4b83ea218699cf6fa8f434a0cbb7849c Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 3 Jan 2023 13:53:10 +0100 Subject: [PATCH 0740/1263] Only set vaultkeys fact when the vault is sealed All other tasks are run only when the vault is sealed, so no point in skipping this one --- ansible/roles/vault_utils/tasks/vault_unseal.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index 2254959e..862f19d8 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -17,12 +17,12 @@ name: "{{ unseal_secret }}" api_version: v1 register: vault_init_data - when: - - vault_sealed + when: vault_sealed - name: Does the vaultkeys secret exist? ansible.builtin.set_fact: vaultkeys_exists: "{{ vault_init_data.resources | length > 0 }}" + when: vault_sealed - name: Vaultkeys does not exist and the vault is sealed, so exit ansible.builtin.meta: end_play From 8d3f82d7790973ed36176ff504226e2a4d7d1dd8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 10 Jan 2023 10:17:49 +0100 Subject: [PATCH 0741/1263] Default vaultPrefixes to be ["hub"] This way the secret templates can be less verbose in most cases. Also add a test for the case where vaultPrefixes is not specified at all (i.e. where it becomes the default ['hub']) and one for when it is specified but empty, which will error out. --- Changes.md | 4 ++ .../plugins/module_utils/load_secrets_v2.py | 7 ++-- .../vault_utils/values-secrets.v2.schema.json | 7 ++-- .../tests/unit/test_vault_load_secrets_v2.py | 38 ++++++++++++++++++- .../v2/values-secret-v2-emptyvaultprefix.yaml | 9 +++++ .../v2/values-secret-v2-novaultprefix.yaml | 1 - examples/secrets/values-secret.v2.yaml | 11 ++++++ 7 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 ansible/tests/unit/v2/values-secret-v2-emptyvaultprefix.yaml diff --git a/Changes.md b/Changes.md index 713ba367..70622f95 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## January 10, 2023 + +* vaultPrefixes is now optional in the v2 secret spec and defaults to ["hub"] + ## December 9, 2022 * Dropped insecureUnsealVaultInsideCluster (and file_unseal) entirely. Now diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 50de6669..6855aed7 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -210,14 +210,15 @@ def _validate_secrets(self): names = [] for s in secrets: # These fields are mandatory - for i in ["name", "vaultPrefixes"]: + for i in ["name"]: try: _ = s[i] except KeyError: return (False, f"Secret {s['name']} is missing {i}") names.append(s["name"]) - vault_prefixes = s.get("vaultPrefixes", []) + vault_prefixes = s.get("vaultPrefixes", ["hub"]) + # This checks for the case when vaultPrefixes: is specified but empty if vault_prefixes is None or len(vault_prefixes) == 0: return (False, f"Secret {s['name']} has empty vaultPrefixes") @@ -402,7 +403,7 @@ def inject_secrets(self): sname = s.get("name") fields = s.get("fields", []) mount = s.get("vaultMount", "secret") - vault_prefixes = s.get("vaultPrefixes", []) + vault_prefixes = s.get("vaultPrefixes", ["hub"]) for i in fields: self._inject_field(sname, i, mount, vault_prefixes, counter == 0) counter += 1 diff --git a/ansible/roles/vault_utils/values-secrets.v2.schema.json b/ansible/roles/vault_utils/values-secrets.v2.schema.json index 63c6aee9..dc33ebc1 100644 --- a/ansible/roles/vault_utils/values-secrets.v2.schema.json +++ b/ansible/roles/vault_utils/values-secrets.v2.schema.json @@ -145,7 +145,7 @@ "type": "object", "description": "The single secret to be injected into the vault", "additionalProperties": false, - "required": [ "name", "fields", "vaultPrefixes" ], + "required": [ "name", "fields" ], "properties": { "name": { "type": "string", @@ -158,12 +158,13 @@ }, "vaultPrefixes": { "type": "array", - "description": "This is the list of prefixes the secret will be uploaded to", + "description": "This is the list of prefixes the secret will be uploaded to. It defaults to ['hub'] when not specified", "items": { "type": "string", "minItems": 1, "uniqueItems": true - } + }, + "default": [ "hub" ] }, "fields": { "type": "array", diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index a917a410..2a1c3d56 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -327,16 +327,50 @@ def test_ensure_error_empty_vaultprefix(self, getpass): set_module_args( { "values_secrets": os.path.join( - self.testdir_v2, "values-secret-v2-novaultprefix.yaml" + self.testdir_v2, "values-secret-v2-emptyvaultprefix.yaml" ), } ) vault_load_secrets.main() - ret = ansible_err.exception.args[0] self.assertEqual(ret["failed"], True) assert ret["args"][1] == "Secret config-demo has empty vaultPrefixes" + def test_ensure_default_no_vaultprefix(self, getpass): + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-novaultprefix.yaml" + ), + } + ) + with patch.object( + load_secrets_v2.LoadSecretsV2, "_run_command" + ) as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 2 + + calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret hub/config-demo secret='value123'\"", # noqa: E501 + attempts=3, + ), + ] + mock_run_command.assert_has_calls(calls) + def test_ensure_only_generate_passwords_works(self, getpass): set_module_args( { diff --git a/ansible/tests/unit/v2/values-secret-v2-emptyvaultprefix.yaml b/ansible/tests/unit/v2/values-secret-v2-emptyvaultprefix.yaml new file mode 100644 index 00000000..df1d420a --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-emptyvaultprefix.yaml @@ -0,0 +1,9 @@ +version: "2.0" + +secrets: + - name: config-demo + vaultPrefixes: + fields: + - name: secret + value: value123 + onMissingValue: error diff --git a/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml b/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml index df1d420a..92449dae 100644 --- a/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml +++ b/ansible/tests/unit/v2/values-secret-v2-novaultprefix.yaml @@ -2,7 +2,6 @@ version: "2.0" secrets: - name: config-demo - vaultPrefixes: fields: - name: secret value: value123 diff --git a/examples/secrets/values-secret.v2.yaml b/examples/secrets/values-secret.v2.yaml index 8a2c307a..1ae23ce1 100644 --- a/examples/secrets/values-secret.v2.yaml +++ b/examples/secrets/values-secret.v2.yaml @@ -70,3 +70,14 @@ secrets: - name: ca_crt path: /tmp/ca.crt onMissingValue: error + + # This will be uploaded to the 'hub' vaultPrefix as it is the default when + # omitted + - name: config-demo3 + fields: + - name: ca_crt2 + path: null + onMissingValue: prompt + - name: ca_crt + path: /tmp/ca.crt + onMissingValue: error From f8d451fefcc091a26772d12d5d463047994fd179 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 10 Jan 2023 12:26:30 +0100 Subject: [PATCH 0742/1263] Fix two V2 secret format corner-case bugs We fix two things here: 1) There was a missing @ when uploading a file into the vault, so we'd get the /tmp/vcontent path instead of the actual content 2) There was a bug with paths not expanding the ~ when uploading in v2 Tested and now things work correctly --- ansible/plugins/module_utils/load_secrets_v2.py | 4 ++-- ansible/tests/unit/test_vault_load_secrets_v2.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 6855aed7..59026802 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -294,7 +294,7 @@ def _get_secret_value(self, name, field): def _get_file_path(self, name, field): on_missing_value = self._get_field_on_missing_value(field) if on_missing_value == "error": - return field.get("path") + return os.path.expanduser(field.get("path")) elif on_missing_value == "prompt": prompt = self._get_field_prompt(field) path = self._get_field_path(field) @@ -384,7 +384,7 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): f"cat '{path}' | oc exec -n {self.namespace} {self.pod} -i -- sh -c " f"'cat - > /tmp/vcontent'; " f"oc exec -n {self.namespace} {self.pod} -i -- sh -c '{b64_cmd}" - f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=/tmp/vcontent; " + f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=@/tmp/vcontent; " f"rm /tmp/vcontent'" ) self._run_command(cmd, attempts=3) diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 2a1c3d56..4be26f0a 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -151,11 +151,11 @@ def test_ensure_no_vault_policies_is_ok(self, getpass): attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put -mount=secret secret/region-two/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put -mount=secret secret/region-two/config-demo-file ca_crt=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file ca_crt=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file ca_crt=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), ] @@ -216,19 +216,19 @@ def test_ensure_policies_are_injected(self, getpass): attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret region-one/config-demo ca_crt=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret region-one/config-demo ca_crt=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret region-one/config-demo ca_crt2=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret region-one/config-demo ca_crt2=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt2=/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt2=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), ] From f5928835d908deedbd7e10bd56a0581f13b7c52a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 10 Jan 2023 18:31:11 +0100 Subject: [PATCH 0743/1263] Add SkipDryRunOnMissingResource=true on ClusterPools, ClusterClaims and ManagedClusterSets On a deploy using clusterPools the acm app will stay in error forever. The reason for this is that the CRDs for the above objects won't exist until the MultiClusterHub is installed. So Argo CD won't find the CRD in the sync and will fail with the error the server could not find the requested resource. Add the "argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true" on those resources, just like we do for Policies. Co-Authored-By: Lester Claudio --- acm/templates/provision/clusterpool.yaml | 3 +++ tests/acm-normal.expected.yaml | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/acm/templates/provision/clusterpool.yaml b/acm/templates/provision/clusterpool.yaml index 0ac851c5..31a22224 100644 --- a/acm/templates/provision/clusterpool.yaml +++ b/acm/templates/provision/clusterpool.yaml @@ -6,6 +6,7 @@ kind: ManagedClusterSet metadata: annotations: cluster.open-cluster-management.io/submariner-broker-ns: {{ .name }}-broker + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true name: {{ .name }} spec: clusterSelector: @@ -33,6 +34,7 @@ metadata: name: "{{ $poolName }}" annotations: argocd.argoproj.io/sync-wave: "10" + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true labels: cloud: {{ $cloud }} region: '{{ $region }}' @@ -66,6 +68,7 @@ metadata: name: '{{ . }}-{{ $group.name }}' annotations: argocd.argoproj.io/sync-wave: "20" + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true cluster.open-cluster-management.io/createmanagedcluster: "true" labels: clusterClaimName: {{ . }}-{{ $group.name }} diff --git a/tests/acm-normal.expected.yaml b/tests/acm-normal.expected.yaml index bf52bfc1..e2e974ed 100644 --- a/tests/acm-normal.expected.yaml +++ b/tests/acm-normal.expected.yaml @@ -26,6 +26,7 @@ metadata: name: 'one-acm-provision-edge' annotations: argocd.argoproj.io/sync-wave: "20" + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true cluster.open-cluster-management.io/createmanagedcluster: "true" labels: clusterClaimName: one-acm-provision-edge @@ -40,6 +41,7 @@ metadata: name: 'two-acm-provision-edge' annotations: argocd.argoproj.io/sync-wave: "20" + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true cluster.open-cluster-management.io/createmanagedcluster: "true" labels: clusterClaimName: two-acm-provision-edge @@ -54,6 +56,7 @@ metadata: name: 'three-acm-provision-edge' annotations: argocd.argoproj.io/sync-wave: "20" + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true cluster.open-cluster-management.io/createmanagedcluster: "true" labels: clusterClaimName: three-acm-provision-edge @@ -68,6 +71,7 @@ metadata: name: "aws-ap-acm-provision-edge" annotations: argocd.argoproj.io/sync-wave: "10" + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true labels: cloud: aws region: 'ap-southeast-2' @@ -97,6 +101,7 @@ metadata: name: "azure-us-acm-provision-edge" annotations: argocd.argoproj.io/sync-wave: "10" + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true labels: cloud: azure region: 'eastus' @@ -377,6 +382,7 @@ kind: ManagedClusterSet metadata: annotations: cluster.open-cluster-management.io/submariner-broker-ns: acm-provision-edge-broker + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true name: acm-provision-edge spec: clusterSelector: From 4924b0b82c8aeeaf062522fc73abd15790c4ae92 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 11 Jan 2023 09:23:36 +0100 Subject: [PATCH 0744/1263] Add mention of VALUES_SECRET env variable --- ansible/roles/vault_utils/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index 4c8da9af..314d8dbd 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -45,7 +45,8 @@ This relies on [kubernetes.core](https://docs.ansible.com/ansible/latest/collect Currently this role supports two formats: version 1.0 (which is the assumed default when not specified) and version 2.0. The latter is more fatureful and supports generating secrets directly into the vault and also prompting the user for a secret. By default, the first file that will looked up is `~/values-secret-.yaml` and should that not exist it will look -for `~/values-secret.yaml`. +for `~/values-secret.yaml`. The paths can be overridden by setting the environment variable `VALUES_SECRET` to the path of the +secret file. The values secret yaml files can be encrypted with `ansible-vault`. If the role detects they are encrypted, the password to decrypt them will be prompted when needed. From 60040b0a0df62e7e308f081a0957028b2593aa20 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 11 Jan 2023 08:34:33 +0100 Subject: [PATCH 0745/1263] Add a get_ini_value() commodity function --- .../module_utils/load_secrets_common.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index e6d339f9..145daaec 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -17,6 +17,7 @@ Module that implements some common functions """ +import configparser from collections.abc import MutableMapping @@ -81,3 +82,23 @@ def flatten(dictionary, parent_key=False, separator="."): if value is not None: items.append((new_key, value)) return dict(items) + + +def get_ini_value(inifile, inisection, inikey): + """ + Return a value from an ini-file or 'None' if it does not exist + + Parameters: + inifile(str): The path to the ini-file + + inisection(str): The section in the ini-file to look for the key + + inikey(str): The key to look up inside the ini-file's section + + Returns: + + obj: The value of the key or None if it does not exist + """ + config = configparser.ConfigParser() + config.read(inifile) + return config.get(inisection, inikey, None) From f6a0a34cd709362c42e36924988c6bc3afd0873d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 11 Jan 2023 09:06:29 +0100 Subject: [PATCH 0746/1263] Fix up ini function and add tests --- .../module_utils/load_secrets_common.py | 2 +- ansible/tests/unit/test_ini_file.py | 60 +++++++++++++++++++ ansible/tests/unit/v2/aws-example.ini | 4 ++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 ansible/tests/unit/test_ini_file.py create mode 100644 ansible/tests/unit/v2/aws-example.ini diff --git a/ansible/plugins/module_utils/load_secrets_common.py b/ansible/plugins/module_utils/load_secrets_common.py index 145daaec..1652a287 100644 --- a/ansible/plugins/module_utils/load_secrets_common.py +++ b/ansible/plugins/module_utils/load_secrets_common.py @@ -101,4 +101,4 @@ def get_ini_value(inifile, inisection, inikey): """ config = configparser.ConfigParser() config.read(inifile) - return config.get(inisection, inikey, None) + return config.get(inisection, inikey, fallback=None) diff --git a/ansible/tests/unit/test_ini_file.py b/ansible/tests/unit/test_ini_file.py new file mode 100644 index 00000000..a0cc1cf8 --- /dev/null +++ b/ansible/tests/unit/test_ini_file.py @@ -0,0 +1,60 @@ +# Copyright 2022 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +""" +Simple module to test ini parsing function +""" + +import json +import os +import sys +import unittest +from unittest import mock +from unittest.mock import call, patch + +from ansible.module_utils import basic +from ansible.module_utils.common.text.converters import to_bytes + +# TODO(bandini): I could not come up with something better to force the imports to be existing +# when we 'import vault_load_secrets' +sys.path.insert(1, "./ansible/plugins/module_utils") +sys.path.insert(1, "./ansible/plugins/modules") +import load_secrets_common # noqa: E402 + +class TestMyModule(unittest.TestCase): + def setUp(self): + self.testdir_v2 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v2") + + + def test_ensure_ini_file_parsed_correctly(self): + f = os.path.join(self.testdir_v2, "aws-example.ini") + key_id = load_secrets_common.get_ini_value(f, "default", "aws_access_key_id") + access_key = load_secrets_common.get_ini_value(f, "default", "aws_secret_access_key") + self.assertEqual(key_id, "A123456789012345678A") + self.assertEqual(access_key, "A12345678901234567890123456789012345678A") + + def test_ensure_ini_file_missing_value_is_none(self): + f = os.path.join(self.testdir_v2, "aws-example.ini") + missing_id = load_secrets_common.get_ini_value(f, "default", "nonexisting") + self.assertEqual(missing_id, None) + + def test_ensure_ini_file_missing_section_is_none(self): + f = os.path.join(self.testdir_v2, "aws-example.ini") + missing_id = load_secrets_common.get_ini_value(f, "nonexisting", "nonexisting") + self.assertEqual(missing_id, None) + + +if __name__ == "__main__": + unittest.main() diff --git a/ansible/tests/unit/v2/aws-example.ini b/ansible/tests/unit/v2/aws-example.ini new file mode 100644 index 00000000..5e38bfd8 --- /dev/null +++ b/ansible/tests/unit/v2/aws-example.ini @@ -0,0 +1,4 @@ +; https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html +[default] +aws_access_key_id = A123456789012345678A +aws_secret_access_key = A12345678901234567890123456789012345678A From 8f26ab14e24698c5aebd7be2311eb9517a9465b2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 11 Jan 2023 10:29:15 +0100 Subject: [PATCH 0747/1263] Add ini_file support With this support a user can specify ini_file instead of path or value inside a secret's field. So the following is now possible: secrets: - name: aws fields: - name: aws_access_key_id ini_file: ~/.aws/credentials ini_key: aws_access_key_id # ini_section: default - name: aws_secret_access_key ini_file: ~/.aws/credentials ini_key: aws_secret_access_key # ini_section: default This would read ~/.aws/credentials extract the key and place it in the vault path (default is 'hub'). So the above would generate the 'secret/hub/aws' vault key with the the 'aws_access_key_id' and 'aws_secret_access_key' attributes containing the content of the keys in the inifile. --- .../plugins/module_utils/load_secrets_v2.py | 68 +++++++++++++--- ansible/roles/vault_utils/README.md | 12 +++ .../vault_utils/values-secrets.v2.schema.json | 27 ++++++- ansible/tests/unit/test_ini_file.py | 12 +-- .../tests/unit/test_vault_load_secrets_v2.py | 81 ++++++++++++++++++- .../unit/v2/values-secret-v2-ini-file.yaml | 21 +++++ .../v2/values-secret-v2-wrong-ini-file.yaml | 9 +++ examples/secrets/values-secret.v2.yaml | 11 +++ 8 files changed, 219 insertions(+), 22 deletions(-) create mode 100644 ansible/tests/unit/v2/values-secret-v2-ini-file.yaml create mode 100644 ansible/tests/unit/v2/values-secret-v2-wrong-ini-file.yaml diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 59026802..498e1276 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -22,7 +22,11 @@ import os import time -from ansible.module_utils.load_secrets_common import find_dupes, get_version +from ansible.module_utils.load_secrets_common import ( + find_dupes, + get_ini_value, + get_version, +) default_vp_vault_policies = { "validatedPatternDefaultPolicy": ( @@ -101,19 +105,24 @@ def _get_field_value(self, f): def _get_field_path(self, f): return f.get("path", None) + def _get_field_ini_file(self, f): + return f.get("ini_file", None) + def _get_field_kind(self, f): # value: null will be interpreted with None, so let's just # check for the existence of the field, as we use 'value: null' to say # "we want a value/secret and not a file path" - if "value" in f and "path" in f: - self.module.fail_json("Both 'value' and 'path' cannot be used") + found = [] + for i in ["value", "path", "ini_file"]: + if i in f: + found.append(i) - if "value" in f: - return "value" - elif "path" in f: - return "path" + if len(found) > 1: # you can only have one of value, path and ini_file + self.module.fail_json(f"Both '{found[0]}' and '{found[1]}' cannot be used") - return "" + if len(found) == 0: + return "" + return found[0] def _get_field_prompt(self, f): return f.get("prompt", None) @@ -124,6 +133,8 @@ def _get_field_base64(self, f): def _get_field_override(self, f): return bool(f.get("override", False)) + # This function could use some rewriting and it should call a specific validation function + # for each type (value, path, ini_file) def _validate_field(self, f): # These fields are mandatory try: @@ -137,7 +148,17 @@ def _validate_field(self, f): value = self._get_field_value(f) path = self._get_field_path(f) - _ = self._get_field_kind(f) + ini_file = self._get_field_ini_file(f) + kind = self._get_field_kind(f) + if kind == "ini_file": + # if we are using ini_file then at least ini_key needs to be defined + # ini_section defaults to 'default' when omitted + ini_key = f.get("ini_key", None) + if ini_key is None: + return ( + False, + "ini_file requires at least ini_key to be defined", + ) # Test if base64 is a correct boolean (defaults to False) _ = self._get_field_base64(f) @@ -151,14 +172,23 @@ def _validate_field(self, f): ) if on_missing_value in ["error"]: - if (value is None or len(value) < 1) and (path is None or len(path) < 1): + if ( + (value is None or len(value) < 1) + and (path is None or len(path) < 1) + and (ini_file is None or len(ini_file) < 1) + ): return ( False, - "Secret has onMissingValue set to 'error' and has neither value nor path set", + "Secret has onMissingValue set to 'error' and has neither value nor path nor ini_file set", ) if path is not None and not os.path.isfile(os.path.expanduser(path)): return (False, f"Field has non-existing path: {path}") + if ini_file is not None and not os.path.isfile( + os.path.expanduser(ini_file) + ): + return (False, f"Field has non-existing ini_file: {ini_file}") + if "override" in f: return ( False, @@ -371,7 +401,7 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): ) self._run_command(cmd, attempts=3) - else: # path. we upload files + elif kind == "path": # path. we upload files # If we're generating the password then we just push the secret in the vault directly verb = "put" if first else "patch" path = self._get_file_path(secret_name, f) @@ -388,6 +418,20 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): f"rm /tmp/vcontent'" ) self._run_command(cmd, attempts=3) + elif kind == "ini_file": # ini_file. we parse an ini_file + verb = "put" if first else "patch" + ini_file = os.path.expanduser(f.get("ini_file")) + ini_section = f.get("ini_section", "default") + ini_key = f.get("ini_key") + secret = get_ini_value(ini_file, ini_section, ini_key) + if b64: + secret = base64.b64encode(secret.encode()).decode("utf-8") + for prefix in prefixes: + cmd = ( + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c " + f"\"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}='{secret}'\"" + ) + self._run_command(cmd, attempts=3) # This assumes that self.sanitize_values() has already been called # so we do a lot less validation as it has already happened diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index 314d8dbd..21808b0b 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -200,6 +200,18 @@ secrets: - name: ca_crt path: /tmp/ca.crt onMissingValue: error # One of error, prompt (for path). generate makes no sense for file + + # The following will read the ini-file at ~/.aws/credentials and place the ini_key "[default]/aws_access_key_id" + # in the aws_access_key_id_test vault attribute in the secret/hub/awsexample path + - name: awsexample + fields: + - name: aws_access_key_id_test + ini_file: ~/.aws/credentials + ini_section: default + ini_key: aws_access_key_id + - name: aws_secret_access_key_test + ini_file: ~/.aws/credentials + ini_key: aws_secret_access_key ``` Internals diff --git a/ansible/roles/vault_utils/values-secrets.v2.schema.json b/ansible/roles/vault_utils/values-secrets.v2.schema.json index dc33ebc1..f838a2c5 100644 --- a/ansible/roles/vault_utils/values-secrets.v2.schema.json +++ b/ansible/roles/vault_utils/values-secrets.v2.schema.json @@ -217,6 +217,28 @@ ], "description": "Is the path to a secret file. Represents the default path when onMissingValue is set to prompt" }, + "ini_file": { + "type": [ + "string", + "null" + ], + "description": "Is the path to an ini_file containing secret material" + }, + "ini_section": { + "type": [ + "string", + "null" + ], + "description": "Is the section in an ini file where a user-defined key will be looked up", + "default": "default" + }, + "ini_key": { + "type": [ + "string", + "null" + ], + "description": "Is the key inside a section in an inifile whose value will be used" + }, "vaultPolicy": { "type": "string", "description": "When onMissingValue is set to 'generate', uses this policy to create the secret inside the vault directly" @@ -232,6 +254,9 @@ "default": "false" } }, + "dependentRequired": { + "ini_file": ["ini_section", "ini_key"] + }, "allOf": [ { "if": { @@ -240,7 +265,7 @@ "then": { "oneOf": [ { - "required": ["path"] + "required": [" path "] }, { "required": [ "value" ] diff --git a/ansible/tests/unit/test_ini_file.py b/ansible/tests/unit/test_ini_file.py index a0cc1cf8..e92280cb 100644 --- a/ansible/tests/unit/test_ini_file.py +++ b/ansible/tests/unit/test_ini_file.py @@ -17,15 +17,9 @@ Simple module to test ini parsing function """ -import json import os import sys import unittest -from unittest import mock -from unittest.mock import call, patch - -from ansible.module_utils import basic -from ansible.module_utils.common.text.converters import to_bytes # TODO(bandini): I could not come up with something better to force the imports to be existing # when we 'import vault_load_secrets' @@ -33,15 +27,17 @@ sys.path.insert(1, "./ansible/plugins/modules") import load_secrets_common # noqa: E402 + class TestMyModule(unittest.TestCase): def setUp(self): self.testdir_v2 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v2") - def test_ensure_ini_file_parsed_correctly(self): f = os.path.join(self.testdir_v2, "aws-example.ini") key_id = load_secrets_common.get_ini_value(f, "default", "aws_access_key_id") - access_key = load_secrets_common.get_ini_value(f, "default", "aws_secret_access_key") + access_key = load_secrets_common.get_ini_value( + f, "default", "aws_secret_access_key" + ) self.assertEqual(key_id, "A123456789012345678A") self.assertEqual(access_key, "A12345678901234567890123456789012345678A") diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index 4be26f0a..cf2cfd82 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -17,6 +17,7 @@ Simple module to test vault_load_secrets """ +import configparser import json import os import sys @@ -76,6 +77,20 @@ def fail_json(*args, **kwargs): @mock.patch("getpass.getpass") class TestMyModule(unittest.TestCase): + def create_inifile(self): + self.inifile = open("/tmp/awscredentials", "w") + config = configparser.ConfigParser() + config["default"] = { + "aws_access_key_id": "123123", + "aws_secret_access_key": "abcdefghi", + } + config["foobar"] = { + "aws_access_key_id": "345345", + "aws_secret_access_key": "rstuvwxyz", + } + with self.inifile as configfile: + config.write(configfile) + def setUp(self): self.mock_module_helper = patch.multiple( basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json @@ -84,11 +99,13 @@ def setUp(self): self.addCleanup(self.mock_module_helper.stop) self.testdir_v2 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "v2") self.testfile = open("/tmp/ca.crt", "w") + self.create_inifile() def tearDown(self): self.testfile.close() try: os.remove("/tmp/ca.crt") + # os.remove("/tmp/awscredentials") except OSError: pass @@ -304,7 +321,7 @@ def test_ensure_error_file_emptypath(self, getpass): self.assertEqual(ret["failed"], True) assert ( ret["args"][1] - == "Secret has onMissingValue set to 'error' and has neither value nor path set" + == "Secret has onMissingValue set to 'error' and has neither value nor path nor ini_file set" ) def test_ensure_error_file_wrongpath(self, getpass): @@ -676,6 +693,68 @@ def test_ensure_override_works(self, getpass): ] mock_run_command.assert_has_calls(calls) + def test_ensure_error_wrong_ini_file(self, getpass): + with self.assertRaises(AnsibleFailJson) as ansible_err: + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-wrong-ini-file.yaml" + ), + } + ) + vault_load_secrets.main() + + ret = ansible_err.exception.args[0] + self.assertEqual(ret["failed"], True) + assert ret["args"][1] == "ini_file requires at least ini_key to be defined" + + def test_ensure_ini_file_works(self, getpass): + set_module_args( + { + "values_secrets": os.path.join( + self.testdir_v2, "values-secret-v2-ini-file.yaml" + ), + } + ) + with patch.object( + load_secrets_v2.LoadSecretsV2, "_run_command" + ) as mock_run_command: + stdout = "configuration updated" + stderr = "" + ret = 0 + mock_run_command.return_value = ret, stdout, stderr # successful execution + + with self.assertRaises(AnsibleExitJson) as result: + vault_load_secrets.main() + self.assertTrue( + result.exception.args[0]["changed"] + ) # ensure result is changed + assert mock_run_command.call_count == 5 + + calls = [ + call( + 'echo \'length=20\nrule "charset" { charset = "abcdefghijklmnopqrstuvwxyz" min-chars = 1 }\nrule "charset" { charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" min-chars = 1 }\nrule "charset" { charset = "0123456789" min-chars = 1 }\nrule "charset" { charset = "!@#$%^&*" min-chars = 1 }\n\' | oc exec -n vault vault-0 -i -- sh -c \'cat - > /tmp/validatedPatternDefaultPolicy.hcl\';oc exec -n vault vault-0 -i -- sh -c \'vault write sys/policies/password/validatedPatternDefaultPolicy policy=@/tmp/validatedPatternDefaultPolicy.hcl\'', # noqa: E501 + attempts=3, + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret hub/aws aws_access_key_id='123123'\"", # noqa: E501 + attempts=3, + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv patch -mount=secret hub/aws aws_secret_access_key='abcdefghi'\"", # noqa: E501 + attempts=3, + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv put -mount=secret hub/awsfoobar aws_access_key_id='345345'\"", # noqa: E501 + attempts=3, + ), + call( + "oc exec -n vault vault-0 -i -- sh -c \"vault kv patch -mount=secret hub/awsfoobar aws_secret_access_key='rstuvwxyz'\"", # noqa: E501 + attempts=3, + ), + ] + mock_run_command.assert_has_calls(calls) + if __name__ == "__main__": unittest.main() diff --git a/ansible/tests/unit/v2/values-secret-v2-ini-file.yaml b/ansible/tests/unit/v2/values-secret-v2-ini-file.yaml new file mode 100644 index 00000000..c69a1429 --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-ini-file.yaml @@ -0,0 +1,21 @@ +version: "2.0" +secrets: + - name: aws + fields: + - name: aws_access_key_id + ini_file: /tmp/awscredentials + ini_section: default + ini_key: aws_access_key_id + - name: aws_secret_access_key + ini_file: /tmp/awscredentials + ini_key: aws_secret_access_key + - name: awsfoobar + fields: + - name: aws_access_key_id + ini_file: /tmp/awscredentials + ini_section: foobar + ini_key: aws_access_key_id + - name: aws_secret_access_key + ini_file: /tmp/awscredentials + ini_section: foobar + ini_key: aws_secret_access_key diff --git a/ansible/tests/unit/v2/values-secret-v2-wrong-ini-file.yaml b/ansible/tests/unit/v2/values-secret-v2-wrong-ini-file.yaml new file mode 100644 index 00000000..fb9b253c --- /dev/null +++ b/ansible/tests/unit/v2/values-secret-v2-wrong-ini-file.yaml @@ -0,0 +1,9 @@ +version: "2.0" +secrets: + - name: aws + fields: + - name: aws_key_id + ini_file: ~/.aws/credentials + ini_section: default + # The below is required + # ini_key: aws_access_key_id diff --git a/examples/secrets/values-secret.v2.yaml b/examples/secrets/values-secret.v2.yaml index 1ae23ce1..d19ce22e 100644 --- a/examples/secrets/values-secret.v2.yaml +++ b/examples/secrets/values-secret.v2.yaml @@ -29,6 +29,17 @@ vaultPolicies: # This is the mandatory top-level secrets entry secrets: + - name: aws + fields: + - name: aws_access_key_id + ini_file: ~/.aws/credentials + ini_key: aws_access_key_id + # ini_section: default + - name: aws_secret_access_key + ini_file: ~/.aws/credentials + ini_key: aws_secret_access_key + # ini_section: default + - name: config-demo vaultMount: secret vaultPrefixes: From 5847c5debd79ac4264c12e626670ad2bb972e635 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 11 Jan 2023 10:37:11 +0100 Subject: [PATCH 0748/1263] Relax json schema and drop onMissingValue as required In the code it defaults to 'error' --- ansible/roles/vault_utils/values-secrets.v2.schema.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ansible/roles/vault_utils/values-secrets.v2.schema.json b/ansible/roles/vault_utils/values-secrets.v2.schema.json index f838a2c5..8c5840bd 100644 --- a/ansible/roles/vault_utils/values-secrets.v2.schema.json +++ b/ansible/roles/vault_utils/values-secrets.v2.schema.json @@ -182,8 +182,7 @@ "type": "object", "additionalProperties": false, "required": [ - "name", - "onMissingValue" + "name" ], "properties": { "name": { @@ -197,7 +196,8 @@ "error", "generate", "prompt" - ] + ], + "default": "error" }, "prompt": { "type": "string", From 4b46eafa62048911a930a84b60ac18f190fb6fb6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 11 Jan 2023 10:53:29 +0100 Subject: [PATCH 0749/1263] Fix up json schema errors --- .../vault_utils/values-secrets.v2.schema.json | 30 +++++++++++++++---- examples/secrets/values-secret.v2.yaml | 5 ++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/ansible/roles/vault_utils/values-secrets.v2.schema.json b/ansible/roles/vault_utils/values-secrets.v2.schema.json index 8c5840bd..c9723d6f 100644 --- a/ansible/roles/vault_utils/values-secrets.v2.schema.json +++ b/ansible/roles/vault_utils/values-secrets.v2.schema.json @@ -191,13 +191,13 @@ }, "onMissingValue": { "type": "string", + "default": "error", "description": "'error' will generate an error if the secret (via value or via path attributes) are not defined. 'generate' will create a secret using a defined vaultPolicy. 'prompt' will ask the user for input and it requires to set a value or a path depending if the user should input a secret or a path to a secret file. Non-null entries represent the default value when prompted.", "enum": [ "error", "generate", "prompt" - ], - "default": "error" + ] }, "prompt": { "type": "string", @@ -255,17 +255,17 @@ } }, "dependentRequired": { - "ini_file": ["ini_section", "ini_key"] + "ini_file": ["ini_key"] }, "allOf": [ { "if": { - "properties": { "onMissingValue": { "const": "prompt" } } + "properties": { "onMissingValue": { "enum": ["prompt"] } } }, "then": { "oneOf": [ { - "required": [" path "] + "required": [ "path" ] }, { "required": [ "value" ] @@ -275,11 +275,29 @@ }, { "if": { - "properties": { "onMissingValue": { "const": "generate" } } + "properties": { "onMissingValue": { "enum": ["generate"] } } }, "then": { "required": [ "vaultPolicy" ] } + }, + { + "if": { + "properties": { "onMissingValue": { "enum": ["error"] } } + }, + "then": { + "oneOf": [ + { + "required": [ "path" ] + }, + { + "required": [ "ini_file" ] + }, + { + "required": [ "value" ] + } + ] + } } ] } diff --git a/examples/secrets/values-secret.v2.yaml b/examples/secrets/values-secret.v2.yaml index d19ce22e..9f7c6448 100644 --- a/examples/secrets/values-secret.v2.yaml +++ b/examples/secrets/values-secret.v2.yaml @@ -34,8 +34,13 @@ secrets: - name: aws_access_key_id ini_file: ~/.aws/credentials ini_key: aws_access_key_id + # You can actually omit this as it is the default + # it is here, because I believe the json schema validator has a bug + # (it ignores the default value of onMissingValue in the aallOf if checks) + onMissingValue: error # ini_section: default - name: aws_secret_access_key + onMissingValue: error ini_file: ~/.aws/credentials ini_key: aws_secret_access_key # ini_section: default From 2a9d221cc191657316946cf717eabdaa928d09f7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 12 Jan 2023 13:49:46 +0100 Subject: [PATCH 0750/1263] Fix base64 file upload Files with 'base64: true' are not being base64-encoded correctly. Let's make sure the base64 encoding happens before we use /tmp/vcontent to upload the file to the vault. --- ansible/plugins/module_utils/load_secrets_v2.py | 6 +++--- ansible/tests/unit/test_vault_load_secrets_v2.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ansible/plugins/module_utils/load_secrets_v2.py b/ansible/plugins/module_utils/load_secrets_v2.py index 498e1276..761f542b 100644 --- a/ansible/plugins/module_utils/load_secrets_v2.py +++ b/ansible/plugins/module_utils/load_secrets_v2.py @@ -407,13 +407,13 @@ def _inject_field(self, secret_name, f, mount, prefixes, first=False): path = self._get_file_path(secret_name, f) for prefix in prefixes: if b64: - b64_cmd = "base64 --wrap=0 /tmp/vcontent | " + b64_cmd = "| base64 --wrap=0 " else: b64_cmd = "" cmd = ( f"cat '{path}' | oc exec -n {self.namespace} {self.pod} -i -- sh -c " - f"'cat - > /tmp/vcontent'; " - f"oc exec -n {self.namespace} {self.pod} -i -- sh -c '{b64_cmd}" + f"'cat - {b64_cmd}> /tmp/vcontent'; " + f"oc exec -n {self.namespace} {self.pod} -i -- sh -c '" f"vault kv {verb} -mount={mount} {prefix}/{secret_name} {f['name']}=@/tmp/vcontent; " f"rm /tmp/vcontent'" ) diff --git a/ansible/tests/unit/test_vault_load_secrets_v2.py b/ansible/tests/unit/test_vault_load_secrets_v2.py index cf2cfd82..054d2306 100644 --- a/ansible/tests/unit/test_vault_load_secrets_v2.py +++ b/ansible/tests/unit/test_vault_load_secrets_v2.py @@ -168,11 +168,11 @@ def test_ensure_no_vault_policies_is_ok(self, getpass): attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put -mount=secret secret/region-two/config-demo-file ca_crt=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - | base64 --wrap=0 > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv put -mount=secret secret/region-two/config-demo-file ca_crt=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file ca_crt=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - | base64 --wrap=0 > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv put -mount=secret secret/snowflake.blueprints.rhecoeng.com/config-demo-file ca_crt=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), ] @@ -241,11 +241,11 @@ def test_ensure_policies_are_injected(self, getpass): attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret region-one/config-demo ca_crt2=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - | base64 --wrap=0 > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret region-one/config-demo ca_crt2=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), call( - "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'base64 --wrap=0 /tmp/vcontent | vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt2=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 + "cat '/tmp/ca.crt' | oc exec -n vault vault-0 -i -- sh -c 'cat - | base64 --wrap=0 > /tmp/vcontent'; oc exec -n vault vault-0 -i -- sh -c 'vault kv patch -mount=secret snowflake.blueprints.rhecoeng.com/config-demo ca_crt2=@/tmp/vcontent; rm /tmp/vcontent'", # noqa: E501 attempts=3, ), ] From 83254a13692894f8da25a16b357311169e7f7a19 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 12 Jan 2023 14:44:50 +0100 Subject: [PATCH 0751/1263] Fix schema json when using HostedArgo When using HostedArgo there is one App for each remote cluster managed by argo. Each of these apps gets two helm key/values passed down containing the bearer token and the remote CA: clusterGroup.hostedSite.bearerKeyPath clusterGroup.hostedSite.caKeyPath Add the object representing the above in the values.schema.json --- clustergroup/values.schema.json | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index c02d0bd0..49d9e2a8 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -231,6 +231,12 @@ "type": "string" } }, + "hostedSite": { + "type": "object", + "items": { + "$ref": "#/definitions/HostedSite" + } + }, "subscriptions": { "anyOf": [ { @@ -389,6 +395,23 @@ ], "title": "Applications" }, + "HostedSite": { + "type": "object", + "additionalProperties": false, + "properties": { + "bearerKeyPath": { + "type": "string" + }, + "caKeyPath": { + "type": "string" + } + }, + "required": [ + "bearerKeyPath", + "caKeyPath" + ], + "title": "HostedSite" + }, "Imperative": { "type": "object", "additionalProperties": false, From e08ffac591d78cf5742d07adc014e2a1c3d9fb12 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 13 Jan 2023 08:36:42 +0100 Subject: [PATCH 0752/1263] Simplify hosted argo secret paths Tested with the following secrets: version: "2.0" secrets: - name: cluster_mcg-one fields: - name: bearerToken value: sha256~123 - name: caCert path: /home/michele/mcg_one_ca.crt - name: cluster_mcg-two fields: - name: bearerToken value: sha256~345 - name: caCert path: /home/michele/mcg_two_ca.crt The corresponding values-hub section was: managedClusterGroups: - name: argo-edge hostedArgoSites: - name: mcg-one domain: mcg-one.blueprints.rhecoeng.com # Below is the default (e.g. cluster_mcg-one in this case) # secretsPath: secret/data/hub/cluster_ - name: mcg-two domain: mcg-two.blueprints.rhecoeng.com helmOverrides: - name: clusterGroup.isHubCluster value: "false" Correctly got the remote clusters working. --- Changes.md | 4 ++++ ansible/roles/vault_utils/README.md | 11 ---------- .../plumbing/cluster-external-secrets.yaml | 6 +++--- .../templates/plumbing/hosted-sites.yaml | 15 +++++--------- clustergroup/values.schema.json | 11 ++++------ examples/secrets/values-secret.v1.yaml | 11 ---------- examples/secrets/values-secret.v2.yaml | 15 ++++++++++++++ examples/values-example.yaml | 6 ++++-- tests/clustergroup-normal.expected.yaml | 20 +++++-------------- 9 files changed, 40 insertions(+), 59 deletions(-) diff --git a/Changes.md b/Changes.md index 70622f95..fcf348b0 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## January 13, 2023 + +* Simplify the secrets paths when using argo hosted sites + ## January 10, 2023 * vaultPrefixes is now optional in the v2 secret spec and defaults to ["hub"] diff --git a/ansible/roles/vault_utils/README.md b/ansible/roles/vault_utils/README.md index 21808b0b..38ae5891 100644 --- a/ansible/roles/vault_utils/README.md +++ b/ansible/roles/vault_utils/README.md @@ -72,17 +72,6 @@ secrets: aws: s3Secret: test-secret - # The cluster_xxxx pattern is used for creating externalSecrets that - # will be used by ArgoCD to push manifests to other clusters. - # - # Create a service account with enough permissions and extract the token - # - # CLUSTER_TOKEN=$(oc describe secret -n default argocd-external-token | grep 'token:' | awk '{print$2}') - # CLUSTER_CA=$(oc extract -n openshift-config cm/kube-root-ca.crt --to=- --keys=ca.crt | base64 | awk '{print}' ORS='') - cluster_example: - server: https://api.example.openshiftapps.com:6443 - bearerToken: - # This will create the vault key secret/hub/testfoo which will have two # properties 'b64content' and 'content' which will be the base64-encoded # content and the normal content respectively diff --git a/clustergroup/templates/plumbing/cluster-external-secrets.yaml b/clustergroup/templates/plumbing/cluster-external-secrets.yaml index dfb6bc6b..20d6f261 100644 --- a/clustergroup/templates/plumbing/cluster-external-secrets.yaml +++ b/clustergroup/templates/plumbing/cluster-external-secrets.yaml @@ -34,10 +34,10 @@ spec: data: - secretKey: kubeBearer remoteRef: - key: {{ $.Values.clusterGroup.hostedSite.bearerKeyPath }} + key: {{ $.Values.clusterGroup.hostedSite.secretsPath }} property: bearerToken - secretKey: kubeCA remoteRef: - key: {{ $.Values.clusterGroup.hostedSite.caKeyPath }} - property: b64content + key: {{ $.Values.clusterGroup.hostedSite.secretsPath }} + property: caCert {{- end }} diff --git a/clustergroup/templates/plumbing/hosted-sites.yaml b/clustergroup/templates/plumbing/hosted-sites.yaml index 1f11dbe4..f1f57374 100644 --- a/clustergroup/templates/plumbing/hosted-sites.yaml +++ b/clustergroup/templates/plumbing/hosted-sites.yaml @@ -24,8 +24,7 @@ status: {} --- {{- end }} {{- range .hostedArgoSites }} -{{ $bearerDefault := print "secret/data/hub/cluster_" .name }} -{{ $caDefault := print "secret/data/hub/cluster_" .name "_ca" }} +{{ $secretsPathDefault := print "secret/data/hub/cluster_" .name }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: @@ -68,10 +67,8 @@ spec: value: {{ $group.name }} - name: clusterGroup.targetCluster value: {{ .name }} - - name: clusterGroup.hostedSite.bearerKeyPath - value: {{ default $bearerDefault .bearerKeyPath }} - - name: clusterGroup.hostedSite.caKeyPath - value: {{ default $caDefault .caKeyPath }} + - name: clusterGroup.hostedSite.secretsPath + value: {{ default $secretsPathDefault .secretsPath }} {{- range $group.helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} @@ -141,10 +138,8 @@ spec: value: {{ $group.name }} - name: clusterGroup.targetCluster value: {{ .name }} - - name: clusterGroup.hostedSite.bearerKeyPath - value: {{ default $bearerDefault .bearerKeyPath }} - - name: clusterGroup.hostedSite.caKeyPath - value: {{ default $caDefault .caKeyPath }} + - name: clusterGroup.hostedSite.secretsPath + value: {{ default $secretsPathDefault .secretsPath }} {{- range $group.helmOverrides }} - name: {{ .name }} value: {{ .value | quote }} diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 49d9e2a8..7fdb9da0 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -399,16 +399,13 @@ "type": "object", "additionalProperties": false, "properties": { - "bearerKeyPath": { - "type": "string" - }, - "caKeyPath": { - "type": "string" + "secretsPath": { + "type": "string", + "description": "It represents the path in the vault that is supposed to contain two fields: 'bearerToken' representing the token to use to authenticate to the remote cluster and 'caCert' which is the base64-encoded Certificate Authority cert of the remote cluster." } }, "required": [ - "bearerKeyPath", - "caKeyPath" + "secretsPath" ], "title": "HostedSite" }, diff --git a/examples/secrets/values-secret.v1.yaml b/examples/secrets/values-secret.v1.yaml index b2d21138..c04e8262 100644 --- a/examples/secrets/values-secret.v1.yaml +++ b/examples/secrets/values-secret.v1.yaml @@ -14,17 +14,6 @@ secrets: aws: s3Secret: test-secret - # The cluster_xxxx pattern is used for creating externalSecrets that - # will be used by ArgoCD to push manifests to other clusters. - # - # Create a service account with enough permissions and extract the token - # - # CLUSTER_TOKEN=$(oc describe secret -n default argocd-external-token | grep 'token:' | awk '{print$2}') - # CLUSTER_CA=$(oc extract -n openshift-config cm/kube-root-ca.crt --to=- --keys=ca.crt | base64 | awk '{print}' ORS='') - cluster_example: - server: https://api.example.openshiftapps.com:6443 - bearerToken: - # This will create the vault key secret/hub/testfoo which will have two # properties 'b64content' and 'content' which will be the base64-encoded # content and the normal content respectively diff --git a/examples/secrets/values-secret.v2.yaml b/examples/secrets/values-secret.v2.yaml index 9f7c6448..eab81a38 100644 --- a/examples/secrets/values-secret.v2.yaml +++ b/examples/secrets/values-secret.v2.yaml @@ -97,3 +97,18 @@ secrets: - name: ca_crt path: /tmp/ca.crt onMissingValue: error + # + # The cluster_xxxx pattern is used for creating externalSecrets that + # will be used by ArgoCD to push manifests to other clusters. + # + # oc extract -n openshift-config cm/kube-root-ca.crt --to=/home/user/ --keys=ca.crt --confirm + - name: cluster_foocluster + fields: + - name: bearerToken + value: + onMissingValue: error + - name: caCert + # See command above + path: /home/user/ca.crt + onMissingValue: error + base64: true diff --git a/examples/values-example.yaml b/examples/values-example.yaml index 30ac93cf..20b5d227 100644 --- a/examples/values-example.yaml +++ b/examples/values-example.yaml @@ -116,10 +116,12 @@ clusterGroup: hostedArgoSites: - name: perth domain: perth1.beekhof.net - bearerKeyPath: secret/data/hub/cluster_perth - caKeyPath: secret/data/hub/cluster_perth_ca + # The default is secret/data/hub/cluster_ + #secretsPath: secret/data/hub/cluster_perth - name: sydney domain: syd.beekhof.net + # The default is secret/data/hub/cluster_ + #secretsPath: secret/data/hub/cluster_sydney helmOverrides: - name: clusterGroup.isHubCluster value: "false" diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index 61f48ff5..da3c66b8 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -136,9 +136,7 @@ data: - name: clusterGroup.isHubCluster value: "false" hostedArgoSites: - - bearerKeyPath: secret/data/hub/cluster_perth - caKeyPath: secret/data/hub/cluster_perth_ca - domain: perth1.beekhof.net + - domain: perth1.beekhof.net name: perth - domain: syd.beekhof.net name: sydney @@ -630,10 +628,8 @@ spec: value: argo-edge - name: clusterGroup.targetCluster value: perth - - name: clusterGroup.hostedSite.bearerKeyPath + - name: clusterGroup.hostedSite.secretsPath value: secret/data/hub/cluster_perth - - name: clusterGroup.hostedSite.caKeyPath - value: secret/data/hub/cluster_perth_ca - name: clusterGroup.isHubCluster value: "false" destination: @@ -692,10 +688,8 @@ spec: value: argo-edge - name: clusterGroup.targetCluster value: perth - - name: clusterGroup.hostedSite.bearerKeyPath + - name: clusterGroup.hostedSite.secretsPath value: secret/data/hub/cluster_perth - - name: clusterGroup.hostedSite.caKeyPath - value: secret/data/hub/cluster_perth_ca - name: clusterGroup.isHubCluster value: "false" destination: @@ -754,10 +748,8 @@ spec: value: argo-edge - name: clusterGroup.targetCluster value: sydney - - name: clusterGroup.hostedSite.bearerKeyPath + - name: clusterGroup.hostedSite.secretsPath value: secret/data/hub/cluster_sydney - - name: clusterGroup.hostedSite.caKeyPath - value: secret/data/hub/cluster_sydney_ca - name: clusterGroup.isHubCluster value: "false" destination: @@ -816,10 +808,8 @@ spec: value: argo-edge - name: clusterGroup.targetCluster value: sydney - - name: clusterGroup.hostedSite.bearerKeyPath + - name: clusterGroup.hostedSite.secretsPath value: secret/data/hub/cluster_sydney - - name: clusterGroup.hostedSite.caKeyPath - value: secret/data/hub/cluster_sydney_ca - name: clusterGroup.isHubCluster value: "false" destination: From 0b3e3d952f5bacbd2f7572213d0416d0f7fbb3e2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 16 Jan 2023 17:32:30 +0100 Subject: [PATCH 0753/1263] Add "vault_path" to the parse_acm_secrets filter This vault_path field will define the path to be used in vault for a specific cluster: "hub" when the hub and the FQDN for all other nodes. Doing it this way allows us to use "hub" as a fixed path to login to the hub. I.e. we do not need to customize values-secret.yaml whenever we're only using secrets in the hub and not per-cluster secrets. --- ansible/plugins/filter/parse_acm_secrets.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ansible/plugins/filter/parse_acm_secrets.py b/ansible/plugins/filter/parse_acm_secrets.py index 028f6145..057b4be6 100644 --- a/ansible/plugins/filter/parse_acm_secrets.py +++ b/ansible/plugins/filter/parse_acm_secrets.py @@ -16,6 +16,7 @@ # server_api: https://api.:6443 # bearerToken: # tlsClientConfig: +# vault_path: "hub" when it is the ACM hub or in the other cases import json from base64 import b64decode @@ -34,6 +35,12 @@ def get_cluster_name(d): return None +def is_cluster_a_hub(name): + if name == "local-cluster": + return True + return False + + def get_cluster_fqdn(d): if "metadata" in d and "labels" in d["metadata"]: server = d["metadata"]["labels"].get( @@ -52,9 +59,15 @@ def parse_acm_secrets(secrets): continue ret[cluster] = {} - ret[cluster]["name"] = b64decode(secret["data"]["name"]) + name = b64decode(secret["data"]["name"]) + ret[cluster]["name"] = name ret[cluster]["server_api"] = b64decode(secret["data"]["server"]) - ret[cluster]["cluster_fqdn"] = get_cluster_fqdn(secret) + fqdn = get_cluster_fqdn(secret) + ret[cluster]["cluster_fqdn"] = fqdn + if is_cluster_a_hub(name): + ret[cluster]["vault_path"] = "hub" + else: + ret[cluster]["vault_path"] = fqdn config = b64decode(secret["data"]["config"]) parsed_config = json.loads(config) From ae00ccb3d943944c2732327d79748b54074d0fe4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 16 Jan 2023 18:18:18 +0100 Subject: [PATCH 0754/1263] Fix up some pylint warnings --- ansible/plugins/filter/parse_acm_secrets.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ansible/plugins/filter/parse_acm_secrets.py b/ansible/plugins/filter/parse_acm_secrets.py index 057b4be6..0445d96d 100644 --- a/ansible/plugins/filter/parse_acm_secrets.py +++ b/ansible/plugins/filter/parse_acm_secrets.py @@ -27,9 +27,9 @@ # apps.open-cluster-management.io/cluster-name: local-cluster # apps.open-cluster-management.io/cluster-server: api.mcg-hub.blueprints.rhecoeng.com # apps.open-cluster-management.io/secret-type: acm-cluster -def get_cluster_name(d): - if "metadata" in d and "labels" in d["metadata"]: - return d["metadata"]["labels"].get( +def get_cluster_name(secret): + if "metadata" in secret and "labels" in secret["metadata"]: + return secret["metadata"]["labels"].get( "apps.open-cluster-management.io/cluster-name", None ) return None @@ -41,12 +41,13 @@ def is_cluster_a_hub(name): return False -def get_cluster_fqdn(d): - if "metadata" in d and "labels" in d["metadata"]: - server = d["metadata"]["labels"].get( +def get_cluster_fqdn(secret): + if "metadata" in secret and "labels" in secret["metadata"]: + server = secret["metadata"]["labels"].get( "apps.open-cluster-management.io/cluster-server", None ) - # It is rather hard to override this in an OCP deployment so we are okay in just dropping 'api.' + # It is rather hard to override this in an OCP deployment so we are + # okay in just dropping 'api.' return server.removeprefix("api.") return None @@ -77,6 +78,6 @@ def parse_acm_secrets(secrets): return ret -class FilterModule(object): +class FilterModule: def filters(self): return {"parse_acm_secrets": parse_acm_secrets} From 5f3e144b6a620f8e92d9cf9414bfacacd7d0b9d0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 17 Jan 2023 14:59:24 +0100 Subject: [PATCH 0755/1263] Set additionalProperties to true for top-level objects We're only in charge of global, main, secretstore and clustergroup. What happens in the other parts othe values file is not our job to validate. --- clustergroup/values.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 7fdb9da0..b3feb681 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -4,7 +4,7 @@ "definitions": { "ValidatedPatterns": { "type": "object", - "additionalProperties": false, + "additionalProperties": true, "properties": { "enabled": { "type": "string", From 768ec55f4cae4cf451d384e7fa282b14469f07a9 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 17 Jan 2023 16:46:11 +0100 Subject: [PATCH 0756/1263] Fix some validation schema warnings Before this change: $ helm template common/clustergroup --name-template multicloud-gitops -f ~/Engineering/cloud-patterns/multicloud-gitops/values-hub.yaml >/dev/null coalesce.go:223: warning: destination for pattern-clustergroup.clusterGroup.managedClusterGroups is a table. Ignoring non-table value ([]) coalesce.go:223: warning: destination for pattern-clustergroup.clusterGroup.subscriptions is a table. Ignoring non-table value ([]) coalesce.go:223: warning: destination for pattern-clustergroup.clusterGroup.applications is a table. Ignoring non-table value ([]) After this change: $ helm template common/clustergroup --name-template multicloud-gitops -f ~/Engineering/cloud-patterns/multicloud-gitops/values-hub.yaml >/dev/null $ --- clustergroup/values.yaml | 6 +++--- tests/clustergroup-industrial-edge-factory.expected.yaml | 2 +- tests/clustergroup-naked.expected.yaml | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clustergroup/values.yaml b/clustergroup/values.yaml index 4abd37d6..414b00cb 100644 --- a/clustergroup/values.yaml +++ b/clustergroup/values.yaml @@ -41,7 +41,7 @@ clusterGroup: clusterRoleYaml: "" roleName: imperative-role roleYaml: "" - managedClusterGroups: [] + managedClusterGroups: {} namespaces: [] # - name: factory # # repoURL: https://github.com/dagger-refuse-cool/manuela-factory.git @@ -60,7 +60,7 @@ clusterGroup: # # - open-cluster-management # - subscriptions: [] + subscriptions: {} # - name: advanced-cluster-management # namespace: open-cluster-management # source: redhat-operators @@ -70,7 +70,7 @@ clusterGroup: projects: [] # - datacenter # - applications: [] + applications: {} # - name: acm # namespace: default # project: datacenter diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index 9da044ab..c1aef7c3 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -104,7 +104,7 @@ data: valuesConfigMap: helm-values-configmap verbosity: "" isHubCluster: false - managedClusterGroups: [] + managedClusterGroups: {} name: factory namespaces: - manuela-stormshift-line-dashboard diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index c2fba541..32566f04 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -37,7 +37,7 @@ metadata: data: values.yaml: | clusterGroup: - applications: [] + applications: {} imperative: activeDeadlineSeconds: 3600 clusterRoleName: imperative-cluster-role @@ -57,11 +57,11 @@ data: valuesConfigMap: helm-values-configmap verbosity: "" isHubCluster: true - managedClusterGroups: [] + managedClusterGroups: {} name: example namespaces: [] projects: [] - subscriptions: [] + subscriptions: {} targetCluster: in-cluster enabled: all global: From 134e5b11089ef0ea7098d9bf81798e996ce6f6e4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Wed, 18 Jan 2023 09:44:45 +0100 Subject: [PATCH 0757/1263] Introduce a validate-schema Makefile target This checks all the values file singularly and together with ./values-global.yaml against common/clustergroup schema. Working example: $ make validate-schema make -f common/Makefile validate-schema make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Validating clustergroup schema of: ./values-global.yaml ./values-group-one.yaml ./values-hub.yaml make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' With an error in values-global.yaml: $ make -f common/Makefile validate-schema make[1]: Entering directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' Validating clustergroup schema of: ./values-global.yamlError: values don't meet the specifications of the schema(s) in the following chart(s): pattern-clustergroup: - global.options.useCSV: Invalid type. Expected: boolean, given: string make[1]: *** [common/Makefile:42: validate-schema] Error 1 make[1]: Leaving directory '/home/michele/Engineering/cloud-patterns/multicloud-gitops' make: *** [Makefile:10: validate-schema] Error 2 --- Changes.md | 4 ++++ Makefile | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Changes.md b/Changes.md index fcf348b0..fabda174 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## January 18, 2023 + +* Add validate-schema target to default deploy + ## January 13, 2023 * Simplify the secrets paths when using argo hosted sites diff --git a/Makefile b/Makefile index d95b889e..930848b6 100644 --- a/Makefile +++ b/Makefile @@ -35,8 +35,15 @@ validate-origin: ## verify the git origin is available echo "Running inside a container: Skipping git ssh checks";\ fi +.PHONY: validate-schema +validate-schema: ## validates values files against schema in common/clustergroup + $(eval VAL_PARAMS := $(shell for i in ./values-*.yaml; do echo -n "$${i} "; done)) + @echo -n "Validating clustergroup schema of: " + @set -e; for i in $(VAL_PARAMS); do echo -n " $$i"; helm template common/clustergroup $(HELM_OPTS) -f "$${i}" >/dev/null; done + @echo + .PHONY: operator-deploy operator-upgrade -operator-deploy operator-upgrade: validate-origin ## runs helm install +operator-deploy operator-upgrade: validate-origin validate-schema ## runs helm install helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) .PHONY: deploy upgrade legacy-deploy legacy-upgrade From 33368800f873d03557a2859c042e56e3534713a4 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Thu, 19 Jan 2023 18:16:01 +0100 Subject: [PATCH 0758/1263] Do not run validate-schema by default Let's not do it just yet. There are values files out there which might barf needlessly --- Changes.md | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Changes.md b/Changes.md index fabda174..ece2212f 100644 --- a/Changes.md +++ b/Changes.md @@ -2,7 +2,7 @@ ## January 18, 2023 -* Add validate-schema target to default deploy +* Add validate-schema target ## January 13, 2023 diff --git a/Makefile b/Makefile index 930848b6..e2159277 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ validate-schema: ## validates values files against schema in common/clustergroup @echo .PHONY: operator-deploy operator-upgrade -operator-deploy operator-upgrade: validate-origin validate-schema ## runs helm install +operator-deploy operator-upgrade: validate-origin ## runs helm install helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) .PHONY: deploy upgrade legacy-deploy legacy-upgrade From 0f3ceef305b8c280227e07da5456d949e79be10d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 20 Jan 2023 10:32:37 +0100 Subject: [PATCH 0759/1263] helmOverride.value can be both a string and a boolean We saw the following error in one of the QE jobs: Error: values don't meet the specifications of the schema(s) in the following chart(s): pattern-clustergroup: - clusterGroup.managedClusterGroups.0.helmOverrides.0.value: Invalid type. Expected: string, given: boolean It can be that the value associated with helmOverride[x].value is also a boolean and not only a string, so let's relax this restriction. --- clustergroup/values.schema.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index b3feb681..e3de8331 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -627,7 +627,14 @@ "type": "string" }, "value": { - "type": "string" + "anyOf": [ + { + "type": "string" + }, + { + "type": "boolean" + } + ] } }, "required": [ From deec51050ce5e8fdda1d64f69f193abddca96e3d Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 20 Jan 2023 10:50:32 +0100 Subject: [PATCH 0760/1263] Make sure our readOnly attributes are proper booleans The readOnly attribute indicates that a value should not be modified. It is a boolean and as such it must not be quoted: https://json-schema.org/understanding-json-schema/reference/boolean.html --- clustergroup/values.schema.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index e3de8331..1bc24c52 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -101,37 +101,37 @@ "properties": { "pattern": { "type": "string", - "readOnly": "true", + "readOnly": true, "description": "The name of the pattern being installed. The default is the name of the repository's folder and is automatically set by the Makefile" }, "clusterDomain": { "type": "string", - "readOnly": "true", + "readOnly": true, "description": "The FQDN domain of the cluster without the 'apps.' component. For example: mcg-hub.blueprints.rhecoeng.com. Gets set automatically by the framework" }, "localClusterDomain": { "type": "string", - "readOnly": "true", + "readOnly": true, "description": "The FQDN domain of the cluster including the 'apps.' component. For example: apps.mcg-hub.blueprints.rhecoeng.com. Gets set automatically by the framework" }, "targetRevision": { "type": "string", - "readOnly": "true", + "readOnly": true, "description": "revision (branch/commit/ref) to use on the pattern's git repository, it is set automatically by the pattern's operator" }, "repoURL": { "type": "string", - "readOnly": "true", + "readOnly": true, "description": "URL of the pattern's git repository, it is set automatically by the pattern's operator" }, "hubClusterDomain": { "type": "string", - "readOnly": "true", + "readOnly": true, "description": "The FQDN domain of the hub cluster including the 'apps.' component. For example: apps.mcg-hub.blueprints.rhecoeng.com. Gets set automatically by the framework. Only makes sense when using ACM" }, "namespace": { "type": "string", - "readOnly": "true", + "readOnly": true, "description": "The namespace in which the ArgoCD instance is running. Automatically set to either 'openshift-operators' or '$ARGOCD_APP_NAMESPACE'" }, "git": { From 1e943032babdc0539c78a31940dceb95cc55b726 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 20 Jan 2023 10:55:16 +0100 Subject: [PATCH 0761/1263] Drop managedClusterGroups as a required property It is not strictly necessary especially on non-hub clusters. --- clustergroup/values.schema.json | 1 - 1 file changed, 1 deletion(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 1bc24c52..9dbc8116 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -296,7 +296,6 @@ "required": [ "applications", "isHubCluster", - "managedClusterGroups", "name", "namespaces", "projects" From bd97b30dc19c153fe05f7942d892634c09929dfd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 20 Jan 2023 10:59:13 +0100 Subject: [PATCH 0762/1263] Drop 'pattern' as a required property While it is always set when installing the pattern, because either the Makefile or the operator will set this value, it makes it harder to do json schema validation in CI, so let's drop it as required for now. --- clustergroup/values.schema.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 9dbc8116..14f091d0 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -142,8 +142,7 @@ } }, "required": [ - "options", - "pattern" + "options" ], "title": "Global" }, From 7e70f5c7125bd4ee59bdb34a687e58841ee18cd2 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 20 Jan 2023 11:01:40 +0100 Subject: [PATCH 0763/1263] Run check-json schema for all yaml files under examples/ We already check the schema of the example secrets files. Let's do the same with the example values file against the common/clustergroup values schema file. --- .github/workflows/jsonschema.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/jsonschema.yaml b/.github/workflows/jsonschema.yaml index 3b7ff11d..d0c19ad8 100644 --- a/.github/workflows/jsonschema.yaml +++ b/.github/workflows/jsonschema.yaml @@ -47,7 +47,11 @@ jobs: python -m pip install --upgrade pip pip install check-jsonschema - - name: Verify json schema + - name: Verify secrets json schema run: | check-jsonschema --schemafile ./ansible/roles/vault_utils/values-secrets.v1.schema.json examples/secrets/values-secret.v1.yaml check-jsonschema --schemafile ./ansible/roles/vault_utils/values-secrets.v2.schema.json examples/secrets/values-secret.v2.yaml + + - name: Verify ClusterGroup values.schema.json + run: | + set -e; for i in examples/*yaml; do echo "$i"; check-jsonschema --schemafile ./clustergroup/values.schema.json "$i"; done From 1369fed690fe25ded254f244d9dd6ec1c812f1c9 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 20 Jan 2023 12:55:15 +0100 Subject: [PATCH 0764/1263] Allow subscriptions to be null There are different vlaues files around where subscriptions is defined but unset (values-group-one.yaml is currently an example). check-jsonschema will barf on these with: $.clusterGroup.subscriptions: None is not of type 'array' Let's allow them to be defined but set to nothing (aka null) --- clustergroup/values.schema.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clustergroup/values.schema.json b/clustergroup/values.schema.json index 14f091d0..6f4cfab0 100644 --- a/clustergroup/values.schema.json +++ b/clustergroup/values.schema.json @@ -238,6 +238,9 @@ }, "subscriptions": { "anyOf": [ + { + "type": "null" + }, { "type": "array" }, From 878ec93f6d65fc1983cb89709ee3be65e2102991 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 20 Jan 2023 22:25:54 +0100 Subject: [PATCH 0765/1263] Add prereq check to the operator-deploy We still need to python3-kubernets and kubernetes ansible collection because the push secret mechanism asks the cluster the status of the vault pod. We theoretically could pull out the kubernetes.core requirement but it would become a bunch of local 'oc' calls, which might or might not be worth it. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e2159277..39c64ad1 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ validate-schema: ## validates values files against schema in common/clustergroup @echo .PHONY: operator-deploy operator-upgrade -operator-deploy operator-upgrade: validate-origin ## runs helm install +operator-deploy operator-upgrade: validate-prereq validate-origin ## runs helm install helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) .PHONY: deploy upgrade legacy-deploy legacy-upgrade From fc2c6549bbb3fe0152ac6ff323fc43b75e72cab9 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 21 Jan 2023 18:09:15 +0100 Subject: [PATCH 0766/1263] Vault delete task is unused, remove it --- ansible/roles/vault_utils/tasks/main.yml | 4 --- .../roles/vault_utils/tasks/vault_delete.yaml | 25 ------------------- 2 files changed, 29 deletions(-) delete mode 100644 ansible/roles/vault_utils/tasks/vault_delete.yaml diff --git a/ansible/roles/vault_utils/tasks/main.yml b/ansible/roles/vault_utils/tasks/main.yml index 3175de20..384b7bba 100644 --- a/ansible/roles/vault_utils/tasks/main.yml +++ b/ansible/roles/vault_utils/tasks/main.yml @@ -1,8 +1,4 @@ --- -- name: Delete vault - ansible.builtin.import_tasks: vault_delete.yaml - tags: vault_delete - - name: Run vault init tasks ansible.builtin.import_tasks: vault_init.yaml tags: vault_init diff --git a/ansible/roles/vault_utils/tasks/vault_delete.yaml b/ansible/roles/vault_utils/tasks/vault_delete.yaml deleted file mode 100644 index 6f5266de..00000000 --- a/ansible/roles/vault_utils/tasks/vault_delete.yaml +++ /dev/null @@ -1,25 +0,0 @@ ---- -- name: Vault pre checks - ansible.builtin.include_tasks: pre_check.yaml -- name: Vault status check - ansible.builtin.include_tasks: vault_status.yaml - -# Note: We do not wait on purpose here as the pod will be recreated -# anyways and that would race. We are fine with having it gone once -- name: Delete vault pod - kubernetes.core.k8s: - state: absent - api_version: v1 - kind: Pod - namespace: "{{ vault_ns }}" - name: "{{ item }}" - loop: "{{ vault_pods }}" - -- name: Delete vault pvc - kubernetes.core.k8s: - state: absent - api_version: v1 - kind: PersistentVolumeClaim - namespace: "{{ vault_ns }}" - name: "data-{{ item }}" - loop: "{{ vault_pods }}" From 23ffcf42471806e4042d698b047a5df7a642ff7f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 21 Jan 2023 18:12:02 +0100 Subject: [PATCH 0767/1263] Drop pki init as it is unused --- .../vault_utils/tasks/vault_pki_init.yaml | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 ansible/roles/vault_utils/tasks/vault_pki_init.yaml diff --git a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml b/ansible/roles/vault_utils/tasks/vault_pki_init.yaml deleted file mode 100644 index 4321a7c5..00000000 --- a/ansible/roles/vault_utils/tasks/vault_pki_init.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# NOTE: This task is currently not used ---- -- name: Vault pre checks - ansible.builtin.include_tasks: pre_check.yaml - -- name: Fetch Ingress object - kubernetes.core.k8s_info: - kind: Ingress - api_version: config.openshift.io/v1 - register: ingress_domain_raw - -# We split the domain and skip the first two parts -# apps.bandini-dc.blueprints.rhecoeng.com -> blueprints.rhecoeng.com -- name: Set ingress domain - ansible.builtin.set_fact: - ingress_domain_out: "{{ ingress_raw.resources[0].spec.domain.split('.')[2:] | join('.') }}" - -- name: Set ingress domain cleaned up - ansible.builtin.set_fact: - ingress_domain: "{{ ingress_domain_out.stdout }}" - cert_role: "{{ ingress_domain_out.stdout | replace('.', '_') }}" - -- name: Enable vault pki backend - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault secrets enable pki - -- name: Set vault pki max-lease-ttl - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault secrets tune --max-lease-ttl="{{ vault_pki_max_lease_ttl }}" pki - -- name: Set vault pki generate/internal CN - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault write pki/root/generate/internal common_name="{{ ingress_domain }}" ttl="{{ vault_pki_max_lease_ttl }}" - -- name: Set vault pki CRL - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl" - -- name: Set vault pki roles - kubernetes.core.k8s_exec: - namespace: "{{ vault_ns }}" - pod: "{{ vault_pod }}" - command: vault write pki/roles/"{{ cert_role }}" allowed_domains="{{ ingress_domain }}" allow_subdomains=true max_ttl="{{ vault_pki_max_lease_ttl }}" From f7576b95b9ee89c643ea9d87c5ac2b572a4c9549 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 21 Jan 2023 18:28:35 +0100 Subject: [PATCH 0768/1263] Drop push_secrets.yaml playbook as it is unused --- ansible/playbooks/vault/push_secrets.yaml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 ansible/playbooks/vault/push_secrets.yaml diff --git a/ansible/playbooks/vault/push_secrets.yaml b/ansible/playbooks/vault/push_secrets.yaml deleted file mode 100644 index ed3cad61..00000000 --- a/ansible/playbooks/vault/push_secrets.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Secret injection of validated-patterns - hosts: localhost - connection: local - gather_facts: false - tasks: - - name: Run push secrets task - include_role: - name: vault_utils - tasks_from: push_secrets From 5d7484af36ca3b16b9d389c01bbd0a8fc8381e0f Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 21 Jan 2023 18:44:10 +0100 Subject: [PATCH 0769/1263] Make sure we error out with a proper message if python-kubernetes is missing --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 39c64ad1..c0ad5ade 100644 --- a/Makefile +++ b/Makefile @@ -88,8 +88,9 @@ kubeconform: ## run helm kubeconform validate-prereq: ## verify pre-requisites @for t in $(EXECUTABLES); do if ! which $$t > /dev/null 2>&1; then echo "No $$t in PATH"; exit 1; fi; done @echo "Prerequisites checked '$(EXECUTABLES)': OK" - @ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1 - @echo "Python kubernetes module: OK" + @ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1\ + && echo "Python kubernetes module: OK" ||\ + (echo "Python kubernetes module: NOT FOUND"; exit 1); @echo -n "Check for kubernetes.core collection: " @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi @echo "OK" From d14a1885ec293fe6fd731cbf9d7efb276fdde0b0 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 21 Jan 2023 18:57:22 +0100 Subject: [PATCH 0770/1263] Make prerequisited output checking more consistent --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index c0ad5ade..75a4f06a 100644 --- a/Makefile +++ b/Makefile @@ -87,10 +87,10 @@ kubeconform: ## run helm kubeconform .PHONY: validate-prereq validate-prereq: ## verify pre-requisites @for t in $(EXECUTABLES); do if ! which $$t > /dev/null 2>&1; then echo "No $$t in PATH"; exit 1; fi; done - @echo "Prerequisites checked '$(EXECUTABLES)': OK" - @ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1\ - && echo "Python kubernetes module: OK" ||\ - (echo "Python kubernetes module: NOT FOUND"; exit 1); + @echo "Check for '$(EXECUTABLES)': OK" + @echo -n "Check for python-kubernetes: " + @if ! ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1; then echo "Not found"; exit 1; fi + @echo "OK" @echo -n "Check for kubernetes.core collection: " @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi @echo "OK" From 0aaa96d4bbfeb97fbce44858d925e8f271f85587 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sun, 22 Jan 2023 14:06:24 +0100 Subject: [PATCH 0771/1263] Prettify the output for checking prerequisites --- Makefile | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 75a4f06a..d737ea14 100644 --- a/Makefile +++ b/Makefile @@ -26,11 +26,12 @@ show: ## show the starting template without installing it # as getting ssh auth working inside a container seems a bit brittle .PHONY: validate-origin validate-origin: ## verify the git origin is available - @echo Checking repo $(TARGET_REPO) - branch $(TARGET_BRANCH) + @echo "Checking repository:" + @echo -n " $(TARGET_REPO) - branch $(TARGET_BRANCH): " @if [ ! -f /run/.containerenv ]; then\ git ls-remote --exit-code --heads $(TARGET_REPO) $(TARGET_BRANCH) >/dev/null &&\ - echo "$(TARGET_REPO) - $(TARGET_BRANCH) exists" ||\ - (echo "$(TARGET_BRANCH) not found in $(TARGET_REPO)"; exit 1);\ + echo "OK" ||\ + (echo "NOT FOUND"; exit 1);\ else\ echo "Running inside a container: Skipping git ssh checks";\ fi @@ -44,6 +45,7 @@ validate-schema: ## validates values files against schema in common/clustergroup .PHONY: operator-deploy operator-upgrade operator-deploy operator-upgrade: validate-prereq validate-origin ## runs helm install + @echo "Running helm:" helm upgrade --install $(NAME) common/operator-install/ $(HELM_OPTS) .PHONY: deploy upgrade legacy-deploy legacy-upgrade @@ -86,12 +88,13 @@ kubeconform: ## run helm kubeconform .PHONY: validate-prereq validate-prereq: ## verify pre-requisites + @echo "Checking prerequisites:" @for t in $(EXECUTABLES); do if ! which $$t > /dev/null 2>&1; then echo "No $$t in PATH"; exit 1; fi; done - @echo "Check for '$(EXECUTABLES)': OK" - @echo -n "Check for python-kubernetes: " + @echo " Check for '$(EXECUTABLES)': OK" + @echo -n " Check for python-kubernetes: " @if ! ansible -m ansible.builtin.command -a "{{ ansible_python_interpreter }} -c 'import kubernetes'" localhost > /dev/null 2>&1; then echo "Not found"; exit 1; fi @echo "OK" - @echo -n "Check for kubernetes.core collection: " + @echo -n " Check for kubernetes.core collection: " @if ! ansible-galaxy collection list | grep kubernetes.core > /dev/null 2>&1; then echo "Not found"; exit 1; fi @echo "OK" From e3764d843dfc6d524269647641911c44f454f752 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 17 Jan 2023 13:58:18 +0100 Subject: [PATCH 0772/1263] Extend framework to allow for ESO on ACM-imported clusters This change does three main things: 1. It extends the vault unseal job on the hub with a few tasks that fetch all non-hub ACM clusters and for each cluster it connects to the remote cluster API endpoint, fetches the golang-external-secret SA token and uses it to configure the secret auth//config kubernetes vault backend. If no clusters exist, the additional tasks do nothing. Furhtermore, the tasks are also idempotent. 2. It adds a job called acmhub-ca-cronjob on the non-hub clusters which will fetch the ACM hub's CA and put it in a secret called "hub" in the imperative namespace. Idempotent and noop when the cluster is not part of ACM 3. When the golang-external-secrets chart is included on a spoke cluster, it will automatically point to the vault on the hub With this change a spoke cluster may decide to install the golang-external-secrets chart on a spoke and avoid pushing secrets around via ACM. By doing so we allow a developer to decide if ESO on the spokes is wanted and we also do not break the existing way of doing things. Tested with https://github.com/mbaldessari/multicloud-gitops/commit/4d90c85149fe0a957f6a69666472a51a9bb37c6b and was able to get MCG working on HUB and spoke with two different secrets. --- Changes.md | 4 + ansible/playbooks/acm/acmhub-get-ca.yaml | 52 +++++ ansible/roles/vault_utils/tasks/main.yml | 4 + .../vault_utils/tasks/vault_spokes_init.yaml | 184 ++++++++++++++++++ .../templates/imperative/unsealjob.yaml | 55 +++++- ...lang-external-secrets-hub-secretstore.yaml | 13 ++ golang-external-secrets/values.yaml | 6 + ...roup-industrial-edge-factory.expected.yaml | 74 +++++++ ...tergroup-industrial-edge-hub.expected.yaml | 2 +- ...rgroup-medical-diagnosis-hub.expected.yaml | 2 +- tests/clustergroup-naked.expected.yaml | 2 +- tests/clustergroup-normal.expected.yaml | 2 +- ...rets-industrial-edge-factory.expected.yaml | 16 +- ...-secrets-industrial-edge-hub.expected.yaml | 4 + ...ecrets-medical-diagnosis-hub.expected.yaml | 4 + ...olang-external-secrets-naked.expected.yaml | 4 + ...lang-external-secrets-normal.expected.yaml | 4 + 17 files changed, 421 insertions(+), 11 deletions(-) create mode 100644 ansible/playbooks/acm/acmhub-get-ca.yaml create mode 100644 ansible/roles/vault_utils/tasks/vault_spokes_init.yaml diff --git a/Changes.md b/Changes.md index ece2212f..ce2b1a99 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,9 @@ # Changes +## January 23, 2023 + +* Add initial support for running ESO on ACM-imported clusters + ## January 18, 2023 * Add validate-schema target diff --git a/ansible/playbooks/acm/acmhub-get-ca.yaml b/ansible/playbooks/acm/acmhub-get-ca.yaml new file mode 100644 index 00000000..8c6d2684 --- /dev/null +++ b/ansible/playbooks/acm/acmhub-get-ca.yaml @@ -0,0 +1,52 @@ +# This playbook fetches the hub cluster's CAbundle from ACM's objects +# and puts it in a secret inside the imperative namespace +--- +- hosts: localhost + connection: local + gather_facts: false + become: false + vars: + ns: imperative + tasks: + - name: Find hub cluster + kubernetes.core.k8s_info: + kind: Secret + name: hub-kubeconfig-secret + namespace: open-cluster-management-agent + register: hub_cluster + + - name: Do nothing when no managed clusters are found + ansible.builtin.meta: end_play + when: hub_cluster['resources'][0]['data']['kubeconfig'] is not defined + + # FIXME(bandini) The assumption here is that there is a single hub cluster for each managed cluster + # + # oc extract secret/hub-kubeconfig-secret --keys=kubeconfig --to=- -n open-cluster-management-agent + # apiVersion: v1 + # clusters: + # - cluster: + # certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNakNDQWhxZ0F3SU... + # server: https://api.bandini-dc.blueprints.rhecoeng.com:6443 + # name: default-cluster + - name: Get hub cluster facts + ansible.builtin.set_fact: + # kubeconfig is just a b64-econded yaml + hub_cluster_kubeconfig: "{{ hub_cluster['resources'][0]['data']['kubeconfig'] | b64decode | from_yaml }}" + + - name: Set CA fact + ansible.builtin.set_fact: + # The .get() call is needed because the key has dashes in it + hub_cluster_ca: "{{ hub_cluster_kubeconfig.clusters[0].cluster.get('certificate-authority-data') }}" + + - name: Create secret with managed cluster's CA + kubernetes.core.k8s: + state: present + definition: + kind: Secret + apiVersion: v1 + metadata: + name: "hub" + namespace: "{{ ns }}" + data: + caBundle: "{{ hub_cluster_ca }}" + type: Opaque diff --git a/ansible/roles/vault_utils/tasks/main.yml b/ansible/roles/vault_utils/tasks/main.yml index 384b7bba..1072e6b7 100644 --- a/ansible/roles/vault_utils/tasks/main.yml +++ b/ansible/roles/vault_utils/tasks/main.yml @@ -11,6 +11,10 @@ ansible.builtin.import_tasks: vault_secrets_init.yaml tags: vault_secrets_init +- name: Vault spoke backend init + ansible.builtin.import_tasks: vault_spokes_init.yaml + tags: vault_spokes_init + - name: Load secrets ansible.builtin.import_tasks: push_secrets.yaml tags: push_secrets diff --git a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml new file mode 100644 index 00000000..06fd206a --- /dev/null +++ b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml @@ -0,0 +1,184 @@ +--- +- name: Vault pre checks + ansible.builtin.include_tasks: pre_check.yaml + +- name: Find managed clusters + kubernetes.core.k8s_info: + kind: ManagedCluster + api_version: "cluster.open-cluster-management.io/v1" + register: managed_clusters + +- name: Set resource fact + ansible.builtin.set_fact: + resources: "{{ managed_clusters['resources'] }}" + +- name: Do nothing when no managed clusters are found + ansible.builtin.meta: end_play + when: resources | length == 0 or managed_clusters.failed or not managed_clusters.api_found + +- name: Loop over returned ACM managedclusters + ansible.builtin.set_fact: + clusters: "{{ clusters|default({}) | combine( { item.metadata.name: { 'caBundle': item.spec.managedClusterClientConfigs[0].caBundle | b64decode } } ) }}" + loop: "{{ resources }}" + when: item.spec.managedClusterClientConfigs[0].caBundle is defined + loop_control: + label: "{{ item.metadata.name }}" + +- name: Extract ClusterGroup + ansible.builtin.set_fact: + clusters: "{{ clusters|default({}) | combine( { item.metadata.name: { 'clusterGroup': item.metadata.labels.clusterGroup } }, recursive=True ) }}" + when: "'clusterGroup' in item.metadata.labels" + loop: "{{ resources }}" + loop_control: + label: "{{ item.metadata.name }}" + +- name: Fetch all ACM secrets + kubernetes.core.k8s_info: + kind: Secret + label_selectors: + - "apps.open-cluster-management.io/secret-type=acm-cluster" + register: acm_secrets + +- name: Set cleaned_acm_secrets fect + ansible.builtin.set_fact: + cleaned_acm_secrets: "{{ acm_secrets.resources | parse_acm_secrets }}" + +- name: Merge the two dicts together + ansible.builtin.set_fact: + clusters_info: "{{ clusters | combine(cleaned_acm_secrets, recursive=True) }}" + +- name: write out CAs + ansible.builtin.copy: + content: "{{ item.value['caBundle'] }}" + dest: "/tmp/{{ item.key }}.ca" + loop: "{{ clusters_info | dict2items }}" + when: item.value['caBundle'] is defined + loop_control: + label: "{{ item.key }}" + +- name: Fetch remote ansible to remote cluster + kubernetes.core.k8s_info: + api_key: "{{ item.value['bearerToken'] }}" + ca_cert: /tmp/{{ item.key}}.ca + host: "{{ item.value['server_api'] }}" + kind: Secret + namespace: "{{ external_secrets_ns }}" + name: "{{ external_secrets_secret }}" + api_version: v1 + register: remote_external_secrets_sa + when: + - clusters_info[item.key]['bearerToken'] is defined + - clusters_info[item.key]['server_api'] is defined + - clusters_info[item.key]['caBundle'] is defined + loop: "{{ clusters_info | dict2items }}" + loop_control: + label: "{{ item.key }}" + +# 'token' will be empty if the remote cluster has no golang-external-secret +# app configured and running +- name: Loop over returned ESO tokens + ansible.builtin.set_fact: + clusters_info: "{{ clusters_info | default({}) | combine({ item['item']['key']: { 'esoToken': item['resources'][0]['data']['token'] | b64decode}}, recursive=True) }}" + loop: "{{ remote_external_secrets_sa.results }}" + when: item['resources'][0]['data']['token'] is defined + loop_control: + label: "{{ item['item']['key'] }}" + +# At this point clusters_info contains a per cluster hash table with *all* the right attributes. For example: +# "mcg-one": { +# "bearerToken": "ey...", +# "caBundle": "-----BEGIN CERTIFICATE-----\nMIIDMjCCA", +# "clusterGroup": "group-one", +# "cluster_fqdn": "mcg-one.blueprints.rhecoeng.com", +# "vault_path": "hub" (when the hub) and the cluster_fqdn when not hub, +# "esoToken": (optional) only if there was an external golang-external-secrets namespace+service account +# "name": "mcg-one", +# "server_api": "https://api.mcg-one.blueprints.rhecoeng.com:6443", +# "tlsClientConfig": { +# "insecure": true +# } +# } +- name: Dump CABundles into the vault + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: bash -e -c "echo '{{ item.value['caBundle'] }}' > /tmp/{{ item.value['vault_path'] }}.ca" + loop: "{{ clusters_info | dict2items }}" + when: + - item.value['esoToken'] is defined + - item.key != "local-cluster" + loop_control: + label: "{{ item.key }}" + +- name: Is kubernetes backend already enabled + no_log: true + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: bash -e -c "if vault auth list | grep -e ^'{{ item.value['vault_path'] }}'; then + echo done; else + vault auth enable -path='{{ item.value['vault_path'] }}' kubernetes; fi" + loop: "{{ clusters_info | dict2items }}" + when: + - item.value['esoToken'] is defined + - item.key != "local-cluster" + loop_control: + label: "{{ item.key }}" + +- name: Configure kubernetes backend + no_log: true + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: bash -e -c "vault write auth/{{ item.value['vault_path'] }}/config + token_reviewer_jwt=\"{{ item.value['esoToken'] }}\" + kubernetes_host=\"{{ item.value['server_api'] }}\" + kubernetes_ca_cert=@/tmp/{{ item.value['vault_path'] }}.ca" + loop: "{{ clusters_info | dict2items }}" + when: + - item.value['esoToken'] is defined + - item.key != "local-cluster" + loop_control: + label: "{{ item.key }}" + +- name: Configure policy template + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: > + bash -e -c "echo \"path \\\"secret/data/{{ item.value['vault_path'] }}/*\\\" { + capabilities = {{ vault_hub_capabilities }} }\" > /tmp/policy-{{ item.value['vault_path'] }}.hcl" + loop: "{{ clusters_info | dict2items }}" + when: + - item.value['esoToken'] is defined + - item.key != "local-cluster" + loop_control: + label: "{{ item.key }}" + +- name: Configure policy for spokes + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: "vault policy write {{ item.value['vault_path'] }}-secret /tmp/policy-{{ item.value['vault_path'] }}.hcl" + loop: "{{ clusters_info | dict2items }}" + when: + - item.value['esoToken'] is defined + - item.key != "local-cluster" + loop_control: + label: "{{ item.key }}" + +- name: Configure kubernetes role for spokes + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: > + vault write auth/"{{ item.value['vault_path'] }}"/role/"{{ item.value['vault_path'] }}"-role + bound_service_account_names="{{ external_secrets_ns }}" + bound_service_account_namespaces="{{ external_secrets_sa }}" + policies="default,{{ item.value['vault_path'] }}-secret" ttl="{{ vault_hub_ttl }}" + loop: "{{ clusters_info | dict2items }}" + when: + - item.value['esoToken'] is defined + - item.key != "local-cluster" + loop_control: + label: "{{ item.key }}" diff --git a/clustergroup/templates/imperative/unsealjob.yaml b/clustergroup/templates/imperative/unsealjob.yaml index baa078cb..41b47ad7 100644 --- a/clustergroup/templates/imperative/unsealjob.yaml +++ b/clustergroup/templates/imperative/unsealjob.yaml @@ -41,7 +41,7 @@ spec: - -e - "@/values/values.yaml" - -t - - 'vault_init,vault_unseal,vault_secrets_init' + - 'vault_init,vault_unseal,vault_secrets_init,vault_spokes_init' - "common/ansible/playbooks/vault/vault.yaml" volumeMounts: {{- include "imperative.volumemounts" . | indent 16 }} @@ -54,5 +54,58 @@ spec: configMap: name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} restartPolicy: Never +{{- else }} +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: acmhub-ca-cronjob + namespace: {{ $.Values.clusterGroup.imperative.namespace}} +spec: + schedule: {{ $.Values.clusterGroup.imperative.insecureUnsealVaultInsideClusterSchedule | quote }} + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: {{ $.Values.clusterGroup.imperative.activeDeadlineSeconds }} + template: + metadata: + name: acmhub-ca-job + spec: + serviceAccountName: {{ $.Values.clusterGroup.imperative.serviceAccountName }} + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + {{- include "imperative.initcontainers.gitinit" . | indent 12 }} + - name: acmhub-ca-playbook + image: {{ $.Values.clusterGroup.imperative.image }} + imagePullPolicy: {{ $.Values.clusterGroup.imperative.imagePullPolicy }} + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - {{ .timeout | default "600" | quote }} + - ansible-playbook + {{- if $.Values.clusterGroup.imperative.verbosity }} + - {{ $.Values.clusterGroup.imperative.verbosity }} + {{- end }} + - -e + - "@/values/values.yaml" + - "common/ansible/playbooks/acm/acmhub-get-ca.yaml" + volumeMounts: + {{- include "imperative.volumemounts" . | indent 16 }} + containers: + {{- include "imperative.containers.done" . | indent 12 }} + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: {{ $.Values.clusterGroup.imperative.valuesConfigMap }}-{{ $.Values.clusterGroup.name }} + restartPolicy: Never {{- end }} {{- end }} diff --git a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml index 6a95745d..4171df44 100644 --- a/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml +++ b/golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml @@ -10,15 +10,28 @@ spec: path: secret # Version of KV backend version: v2 +{{ if .Values.clusterGroup.isHubCluster }} caProvider: type: ConfigMap name: kube-root-ca.crt key: ca.crt namespace: golang-external-secrets +{{ else }} + caProvider: + type: Secret + name: hub + key: caBundle + namespace: imperative +{{ end }} auth: kubernetes: +{{ if .Values.clusterGroup.isHubCluster }} mountPath: {{ .Values.mountPath }} role: {{ .Values.mountRole }} +{{ else }} + mountPath: {{ $.Values.global.clusterDomain }} + role: {{ $.Values.global.clusterDomain }}-role +{{ end }} secretRef: name: golang-external-secrets namespace: golang-external-secrets diff --git a/golang-external-secrets/values.yaml b/golang-external-secrets/values.yaml index 58af2212..9bd9ee22 100644 --- a/golang-external-secrets/values.yaml +++ b/golang-external-secrets/values.yaml @@ -4,6 +4,12 @@ mountRole: "hub-role" global: hubClusterDomain: hub.example.com + clusterDomain: foo.example.com + +clusterGroup: + isHubCluster: true + + external-secrets: image: diff --git a/tests/clustergroup-industrial-edge-factory.expected.yaml b/tests/clustergroup-industrial-edge-factory.expected.yaml index c1aef7c3..335a2682 100644 --- a/tests/clustergroup-industrial-edge-factory.expected.yaml +++ b/tests/clustergroup-industrial-edge-factory.expected.yaml @@ -336,6 +336,80 @@ spec: name: helm-values-configmap-factory restartPolicy: Never --- +# Source: pattern-clustergroup/templates/imperative/unsealjob.yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: acmhub-ca-cronjob + namespace: imperative +spec: + schedule: "*/5 * * * *" + # if previous Job is still running, skip execution of a new Job + concurrencyPolicy: Forbid + jobTemplate: + spec: + activeDeadlineSeconds: 3600 + template: + metadata: + name: acmhub-ca-job + spec: + serviceAccountName: imperative-sa + initContainers: + # git init happens in /git/repo so that we can set the folder to 0770 permissions + # reason for that is ansible refuses to create temporary folders in there + - name: git-init + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + command: + - 'sh' + - '-c' + - "mkdir /git/{repo,home};git clone --single-branch --branch main --depth 1 -- https://github.com/pattern-clone/mypattern /git/repo;chmod 0770 /git/{repo,home}" + volumeMounts: + - name: git + mountPath: "/git" + - name: acmhub-ca-playbook + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + env: + - name: HOME + value: /git/home + workingDir: /git/repo + # We have a default timeout of 600s for each playbook. Can be overridden + # on a per-job basis + command: + - timeout + - "600" + - ansible-playbook + - -e + - "@/values/values.yaml" + - "common/ansible/playbooks/acm/acmhub-get-ca.yaml" + volumeMounts: + - name: git + mountPath: "/git" + - name: values-volume + mountPath: /values/values.yaml + subPath: values.yaml + containers: + - name: "done" + image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest + imagePullPolicy: Always + command: + - 'sh' + - '-c' + - 'echo' + - 'done' + - '\n' + volumes: + - name: git + emptyDir: {} + - name: values-volume + configMap: + name: helm-values-configmap-factory + restartPolicy: Never +--- # Source: pattern-clustergroup/templates/core/subscriptions.yaml --- --- diff --git a/tests/clustergroup-industrial-edge-hub.expected.yaml b/tests/clustergroup-industrial-edge-hub.expected.yaml index 7bd766b4..03db6b16 100644 --- a/tests/clustergroup-industrial-edge-hub.expected.yaml +++ b/tests/clustergroup-industrial-edge-hub.expected.yaml @@ -547,7 +547,7 @@ spec: - -e - "@/values/values.yaml" - -t - - 'vault_init,vault_unseal,vault_secrets_init' + - 'vault_init,vault_unseal,vault_secrets_init,vault_spokes_init' - "common/ansible/playbooks/vault/vault.yaml" volumeMounts: - name: git diff --git a/tests/clustergroup-medical-diagnosis-hub.expected.yaml b/tests/clustergroup-medical-diagnosis-hub.expected.yaml index 280dfa34..0fb20ab7 100644 --- a/tests/clustergroup-medical-diagnosis-hub.expected.yaml +++ b/tests/clustergroup-medical-diagnosis-hub.expected.yaml @@ -534,7 +534,7 @@ spec: - -e - "@/values/values.yaml" - -t - - 'vault_init,vault_unseal,vault_secrets_init' + - 'vault_init,vault_unseal,vault_secrets_init,vault_spokes_init' - "common/ansible/playbooks/vault/vault.yaml" volumeMounts: - name: git diff --git a/tests/clustergroup-naked.expected.yaml b/tests/clustergroup-naked.expected.yaml index 32566f04..b85f2bf0 100644 --- a/tests/clustergroup-naked.expected.yaml +++ b/tests/clustergroup-naked.expected.yaml @@ -227,7 +227,7 @@ spec: - -e - "@/values/values.yaml" - -t - - 'vault_init,vault_unseal,vault_secrets_init' + - 'vault_init,vault_unseal,vault_secrets_init,vault_spokes_init' - "common/ansible/playbooks/vault/vault.yaml" volumeMounts: - name: git diff --git a/tests/clustergroup-normal.expected.yaml b/tests/clustergroup-normal.expected.yaml index da3c66b8..8e29ef47 100644 --- a/tests/clustergroup-normal.expected.yaml +++ b/tests/clustergroup-normal.expected.yaml @@ -416,7 +416,7 @@ spec: - -e - "@/values/values.yaml" - -t - - 'vault_init,vault_unseal,vault_secrets_init' + - 'vault_init,vault_unseal,vault_secrets_init,vault_spokes_init' - "common/ansible/playbooks/vault/vault.yaml" volumeMounts: - name: git diff --git a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml index 2bee122f..b74699af 100644 --- a/tests/golang-external-secrets-industrial-edge-factory.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-factory.expected.yaml @@ -7281,15 +7281,19 @@ spec: path: secret # Version of KV backend version: v2 + caProvider: - type: ConfigMap - name: kube-root-ca.crt - key: ca.crt - namespace: golang-external-secrets + type: Secret + name: hub + key: caBundle + namespace: imperative + auth: kubernetes: - mountPath: hub - role: hub-role + + mountPath: region.example.com + role: region.example.com-role + secretRef: name: golang-external-secrets namespace: golang-external-secrets diff --git a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml index 2bee122f..8cb910d3 100644 --- a/tests/golang-external-secrets-industrial-edge-hub.expected.yaml +++ b/tests/golang-external-secrets-industrial-edge-hub.expected.yaml @@ -7281,15 +7281,19 @@ spec: path: secret # Version of KV backend version: v2 + caProvider: type: ConfigMap name: kube-root-ca.crt key: ca.crt namespace: golang-external-secrets + auth: kubernetes: + mountPath: hub role: hub-role + secretRef: name: golang-external-secrets namespace: golang-external-secrets diff --git a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml index 2bee122f..8cb910d3 100644 --- a/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml +++ b/tests/golang-external-secrets-medical-diagnosis-hub.expected.yaml @@ -7281,15 +7281,19 @@ spec: path: secret # Version of KV backend version: v2 + caProvider: type: ConfigMap name: kube-root-ca.crt key: ca.crt namespace: golang-external-secrets + auth: kubernetes: + mountPath: hub role: hub-role + secretRef: name: golang-external-secrets namespace: golang-external-secrets diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index d5b66165..ab78d185 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -7281,15 +7281,19 @@ spec: path: secret # Version of KV backend version: v2 + caProvider: type: ConfigMap name: kube-root-ca.crt key: ca.crt namespace: golang-external-secrets + auth: kubernetes: + mountPath: hub role: hub-role + secretRef: name: golang-external-secrets namespace: golang-external-secrets diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index 2bee122f..8cb910d3 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -7281,15 +7281,19 @@ spec: path: secret # Version of KV backend version: v2 + caProvider: type: ConfigMap name: kube-root-ca.crt key: ca.crt namespace: golang-external-secrets + auth: kubernetes: + mountPath: hub role: hub-role + secretRef: name: golang-external-secrets namespace: golang-external-secrets From 4356034740d47c16cbef9f917d5c64a365437a84 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 24 Jan 2023 10:58:48 +0100 Subject: [PATCH 0773/1263] Add a global policy in the vault configuration We add by default a global-secret policy which allows to read secret/data/global/*. This policy will be applied to hub and imported clusters. I.e. any secret uploaded to secret/data/global/ in vault will be accessible to both hub and all spoke clusters. --- ansible/roles/vault_utils/defaults/main.yml | 3 ++- .../vault_utils/tasks/vault_secrets_init.yaml | 17 ++++++++++++++++- .../vault_utils/tasks/vault_spokes_init.yaml | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 220dd6e3..7f62ca8b 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -13,7 +13,8 @@ vault_hub_capabilities: '[\\\"read\\\"]' vault_base_path: "secret" vault_path: "{{ vault_base_path }}/{{ vault_hub }}" vault_hub_ttl: "15m" -vault_pki_max_lease_ttl: "8760h" +vault_global_policy: global +vault_global_capabilities: '[\\\"read\\\"]' external_secrets_ns: golang-external-secrets external_secrets_sa: golang-external-secrets external_secrets_secret: golang-external-secrets diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 6a521504..7e0741aa 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -59,6 +59,21 @@ kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt issuer=https://kubernetes.default.svc" +# This creates a {{ vault_global_policy }} policy that is applied to both hubs and spokes +- name: Configure VP global policy template + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: > + bash -e -c "echo \"path \\\"secret/data/{{ vault_global_policy }}/*\\\" { + capabilities = {{ vault_global_capabilities }} }\" > /tmp/policy-{{ vault_global_policy }}.hcl" + +- name: Configure VP global policy + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ vault_pod }}" + command: "vault policy write {{ vault_global_policy }}-secret /tmp/policy-{{ vault_global_policy }}.hcl" + - name: Configure policy template for hub kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" @@ -81,4 +96,4 @@ vault write auth/"{{ vault_hub }}"/role/"{{ vault_hub }}"-role bound_service_account_names="{{ external_secrets_sa }}" bound_service_account_namespaces="{{ external_secrets_ns }}" - policies="default,{{ vault_hub }}-secret" ttl="{{ vault_hub_ttl }}" + policies="default,{{ vault_global_policy }}-secret,{{ vault_hub }}-secret" ttl="{{ vault_hub_ttl }}" diff --git a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml index 06fd206a..fbc5ba4c 100644 --- a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml @@ -175,7 +175,7 @@ vault write auth/"{{ item.value['vault_path'] }}"/role/"{{ item.value['vault_path'] }}"-role bound_service_account_names="{{ external_secrets_ns }}" bound_service_account_namespaces="{{ external_secrets_sa }}" - policies="default,{{ item.value['vault_path'] }}-secret" ttl="{{ vault_hub_ttl }}" + policies="default,{{ vault_global_policy }}-secret,{{ item.value['vault_path'] }}-secret" ttl="{{ vault_hub_ttl }}" loop: "{{ clusters_info | dict2items }}" when: - item.value['esoToken'] is defined From 8e2325166a721fd4f454dd02e5348e176413ea20 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 24 Jan 2023 11:02:53 +0100 Subject: [PATCH 0774/1263] Make sure we use specialized vars for spokes --- ansible/roles/vault_utils/defaults/main.yml | 2 ++ ansible/roles/vault_utils/tasks/vault_spokes_init.yaml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ansible/roles/vault_utils/defaults/main.yml b/ansible/roles/vault_utils/defaults/main.yml index 7f62ca8b..4d263223 100644 --- a/ansible/roles/vault_utils/defaults/main.yml +++ b/ansible/roles/vault_utils/defaults/main.yml @@ -13,6 +13,8 @@ vault_hub_capabilities: '[\\\"read\\\"]' vault_base_path: "secret" vault_path: "{{ vault_base_path }}/{{ vault_hub }}" vault_hub_ttl: "15m" +vault_spoke_capabilities: '[\\\"read\\\"]' +vault_spoke_ttl: "15m" vault_global_policy: global vault_global_capabilities: '[\\\"read\\\"]' external_secrets_ns: golang-external-secrets diff --git a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml index fbc5ba4c..5ad16a55 100644 --- a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml @@ -147,7 +147,7 @@ pod: "{{ vault_pod }}" command: > bash -e -c "echo \"path \\\"secret/data/{{ item.value['vault_path'] }}/*\\\" { - capabilities = {{ vault_hub_capabilities }} }\" > /tmp/policy-{{ item.value['vault_path'] }}.hcl" + capabilities = {{ vault_spoke_capabilities }} }\" > /tmp/policy-{{ item.value['vault_path'] }}.hcl" loop: "{{ clusters_info | dict2items }}" when: - item.value['esoToken'] is defined @@ -175,7 +175,7 @@ vault write auth/"{{ item.value['vault_path'] }}"/role/"{{ item.value['vault_path'] }}"-role bound_service_account_names="{{ external_secrets_ns }}" bound_service_account_namespaces="{{ external_secrets_sa }}" - policies="default,{{ vault_global_policy }}-secret,{{ item.value['vault_path'] }}-secret" ttl="{{ vault_hub_ttl }}" + policies="default,{{ vault_global_policy }}-secret,{{ item.value['vault_path'] }}-secret" ttl="{{ vault_spoke_ttl }}" loop: "{{ clusters_info | dict2items }}" when: - item.value['esoToken'] is defined From c791f038b8943011026d568751bba95cba90587e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 24 Jan 2023 17:53:35 +0100 Subject: [PATCH 0775/1263] Switch to quay.io/hybridcloudpatterns/utility-container This container is managed and build via the utility-container repository in our github organization. Main reason for switching is size (1.1G vs 2.6G) but it also seems a bit simpler to maintain via a single Containerfile. --- scripts/pattern-util.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/pattern-util.sh b/scripts/pattern-util.sh index c2e130cb..47b64a8f 100755 --- a/scripts/pattern-util.sh +++ b/scripts/pattern-util.sh @@ -1,11 +1,10 @@ #!/bin/sh if [ -z "$PATTERN_UTILITY_CONTAINER" ]; then - PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/hybridcloudpatterns-utility-ee" + PATTERN_UTILITY_CONTAINER="quay.io/hybridcloudpatterns/utility-container" fi # Copy Kubeconfig from current environment. The utilities will pick up ~/.kube/config if set so it's not mandatory -# /home/runner is the normal homedir # $HOME is mounted as itself for any files that are referenced with absolute paths # $HOME is mounted to /root because the UID in the container is 0 and that's where SSH looks for credentials # We bind mount the SSH_AUTH_SOCK socket if it is set, so ssh works without user prompting @@ -25,7 +24,6 @@ podman run -it \ --security-opt label=disable \ ${KUBECONF_ENV} \ ${SSH_SOCK_MOUNTS} \ - -v ${HOME}:/home/runner \ -v ${HOME}:${HOME} \ -v ${HOME}:/root \ -w $(pwd) \ From 636a54c3d530ee141933217c776393a56a64234e Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 24 Jan 2023 18:05:38 +0100 Subject: [PATCH 0776/1263] Upgrade ansible-lint action --- .github/workflows/ansible-lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ansible-lint.yml b/.github/workflows/ansible-lint.yml index ae3e9caf..3b2de754 100644 --- a/.github/workflows/ansible-lint.yml +++ b/.github/workflows/ansible-lint.yml @@ -11,8 +11,8 @@ jobs: - uses: actions/checkout@v2 - name: Lint Ansible Playbook - # Using the latest as of today (2022-09-02) v6.6.1 - uses: ansible/ansible-lint-action@v6.6.1 + # Using the latest as of today (2023-01-24) v6.11.0 + uses: ansible/ansible-lint-action@v6.11.0 # Let's point it to the path with: path: "ansible/" From eea1d274ffb8ad82c4c2cbfd384112eda4016fd6 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 24 Jan 2023 18:12:21 +0100 Subject: [PATCH 0777/1263] Address a bunch of ansible-lint warnings --- .../roles/vault_utils/tasks/vault_spokes_init.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml index 5ad16a55..5287c9e7 100644 --- a/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_spokes_init.yaml @@ -18,7 +18,7 @@ - name: Loop over returned ACM managedclusters ansible.builtin.set_fact: - clusters: "{{ clusters|default({}) | combine( { item.metadata.name: { 'caBundle': item.spec.managedClusterClientConfigs[0].caBundle | b64decode } } ) }}" + clusters: "{{ clusters | default({}) | combine({item.metadata.name: {'caBundle': item.spec.managedClusterClientConfigs[0].caBundle | b64decode}}) }}" loop: "{{ resources }}" when: item.spec.managedClusterClientConfigs[0].caBundle is defined loop_control: @@ -26,7 +26,7 @@ - name: Extract ClusterGroup ansible.builtin.set_fact: - clusters: "{{ clusters|default({}) | combine( { item.metadata.name: { 'clusterGroup': item.metadata.labels.clusterGroup } }, recursive=True ) }}" + clusters: "{{ clusters | default({}) | combine({item.metadata.name: {'clusterGroup': item.metadata.labels.clusterGroup}}, recursive=True) }}" when: "'clusterGroup' in item.metadata.labels" loop: "{{ resources }}" loop_control: @@ -47,10 +47,11 @@ ansible.builtin.set_fact: clusters_info: "{{ clusters | combine(cleaned_acm_secrets, recursive=True) }}" -- name: write out CAs +- name: Write out CAs ansible.builtin.copy: content: "{{ item.value['caBundle'] }}" dest: "/tmp/{{ item.key }}.ca" + mode: "0640" loop: "{{ clusters_info | dict2items }}" when: item.value['caBundle'] is defined loop_control: @@ -59,7 +60,7 @@ - name: Fetch remote ansible to remote cluster kubernetes.core.k8s_info: api_key: "{{ item.value['bearerToken'] }}" - ca_cert: /tmp/{{ item.key}}.ca + ca_cert: /tmp/{{ item.key }}.ca host: "{{ item.value['server_api'] }}" kind: Secret namespace: "{{ external_secrets_ns }}" @@ -78,7 +79,7 @@ # app configured and running - name: Loop over returned ESO tokens ansible.builtin.set_fact: - clusters_info: "{{ clusters_info | default({}) | combine({ item['item']['key']: { 'esoToken': item['resources'][0]['data']['token'] | b64decode}}, recursive=True) }}" + clusters_info: "{{ clusters_info | default({}) | combine({item['item']['key']: {'esoToken': item['resources'][0]['data']['token'] | b64decode}}, recursive=True) }}" loop: "{{ remote_external_secrets_sa.results }}" when: item['resources'][0]['data']['token'] is defined loop_control: From 6974a5d7898930f28c201a5d1ccd4dcd4c630a6a Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Sat, 28 Jan 2023 18:03:34 +0100 Subject: [PATCH 0778/1263] Upgrade ESO to v0.7.2 Tested via MCG --- golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.7.0.tgz | Bin 50386 -> 0 bytes .../charts/external-secrets-0.7.2.tgz | Bin 0 -> 50796 bytes golang-external-secrets/values.yaml | 6 +- ...rets-industrial-edge-factory.expected.yaml | 130 +++++++++++------- ...-secrets-industrial-edge-hub.expected.yaml | 130 +++++++++++------- ...ecrets-medical-diagnosis-hub.expected.yaml | 130 +++++++++++------- ...olang-external-secrets-naked.expected.yaml | 130 +++++++++++------- ...lang-external-secrets-normal.expected.yaml | 130 +++++++++++------- 9 files changed, 409 insertions(+), 249 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.7.0.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.7.2.tgz diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index 19bf435a..ba782b1f 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.7.0" + version: "0.7.2" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.7.0.tgz b/golang-external-secrets/charts/external-secrets-0.7.0.tgz deleted file mode 100644 index 04249d7e2d00f87a1d6f67b6bc61eb8d408f1fa3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50386 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 a=u2Pv(wDzh`F{Zb0RR7MQHBiw9tHsG>&of? diff --git a/golang-external-secrets/charts/external-secrets-0.5.9.tgz b/golang-external-secrets/charts/external-secrets-0.5.9.tgz new file mode 100644 index 0000000000000000000000000000000000000000..3d5b93f969793a05cbfc12ed95d1547568084e9a GIT binary patch literal 40987 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 a3Pw*e84!s_2mlTa1_RN#9>Xaj!~HKK%^wf| literal 0 HcmV?d00001 diff --git a/tests/golang-external-secrets-naked.expected.yaml b/tests/golang-external-secrets-naked.expected.yaml index caad1538..7d9fa628 100644 --- a/tests/golang-external-secrets-naked.expected.yaml +++ b/tests/golang-external-secrets-naked.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: clusterexternalsecrets.external-secrets.io spec: @@ -83,7 +83,20 @@ spec: singular: clusterexternalsecret scope: Cluster versions: - - name: v1beta1 + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 schema: openAPIV3Schema: description: ClusterExternalSecret is the Schema for the clusterexternalsecrets API. @@ -117,6 +130,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -142,8 +159,6 @@ spec: dataFrom: description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order items: - maxProperties: 1 - minProperties: 1 properties: extract: description: Used to extract multiple key/value pairs from one secret @@ -152,6 +167,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -174,6 +193,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string name: description: Finds secrets based on the name. properties: @@ -190,6 +213,25 @@ spec: description: Find secrets based on tags. type: object type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array type: object type: array refreshInterval: @@ -334,6 +376,7 @@ spec: 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 refreshTime: description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. type: string @@ -400,7 +443,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: clustersecretstores.external-secrets.io spec: @@ -1424,6 +1467,12 @@ spec: - jsonPath: .metadata.creationTimestamp name: AGE type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string name: v1beta1 schema: openAPIV3Schema: @@ -1806,7 +1855,23 @@ spec: properties: auth: description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object secretRef: properties: secretApiKeySecretRef: @@ -1823,8 +1888,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object serviceUrl: description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance @@ -1915,13 +1978,13 @@ spec: description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2287,13 +2350,13 @@ spec: description: The provider for the CA bundle to use to validate Vault server certificate. properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2559,7 +2622,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: externalsecrets.external-secrets.io spec: @@ -2801,6 +2864,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string name: v1beta1 schema: openAPIV3Schema: @@ -2829,6 +2895,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -2854,8 +2924,6 @@ spec: dataFrom: description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order items: - maxProperties: 1 - minProperties: 1 properties: extract: description: Used to extract multiple key/value pairs from one secret @@ -2864,6 +2932,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -2886,6 +2958,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string name: description: Finds secrets based on the name. properties: @@ -2902,6 +2978,25 @@ spec: description: Find secrets based on tags. type: object type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array type: object type: array refreshInterval: @@ -3067,7 +3162,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: secretstores.external-secrets.io spec: @@ -4094,6 +4189,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string name: v1beta1 schema: openAPIV3Schema: @@ -4476,7 +4574,23 @@ spec: properties: auth: description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object secretRef: properties: secretApiKeySecretRef: @@ -4493,8 +4607,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object serviceUrl: description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance @@ -4585,13 +4697,13 @@ spec: description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -4957,13 +5069,13 @@ spec: description: The provider for the CA bundle to use to validate Vault server certificate. properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -5230,10 +5342,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5288,10 +5400,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5379,10 +5491,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -5405,10 +5517,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -5432,10 +5544,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5452,10 +5564,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5488,10 +5600,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5527,10 +5639,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5548,10 +5660,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook spec: @@ -5572,10 +5684,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5592,7 +5704,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.5.7" + image: "ghcr.io/external-secrets/external-secrets:v0.5.9" imagePullPolicy: IfNotPresent args: - certcontroller @@ -5619,10 +5731,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5639,7 +5751,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.5.7" + image: "ghcr.io/external-secrets/external-secrets:v0.5.9" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -5655,10 +5767,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5676,7 +5788,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.5.7" + image: "ghcr.io/external-secrets/external-secrets:v0.5.9" imagePullPolicy: IfNotPresent args: - webhook @@ -5698,13 +5810,13 @@ spec: initialDelaySeconds: 20 periodSeconds: 5 volumeMounts: - - name: certs - mountPath: /tmp/certs - readOnly: true + - name: certs + mountPath: /tmp/certs + readOnly: true volumes: - - name: certs - secret: - secretName: golang-external-secrets-webhook + - name: certs + secret: + secretName: golang-external-secrets-webhook --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 diff --git a/tests/golang-external-secrets-normal.expected.yaml b/tests/golang-external-secrets-normal.expected.yaml index caad1538..7d9fa628 100644 --- a/tests/golang-external-secrets-normal.expected.yaml +++ b/tests/golang-external-secrets-normal.expected.yaml @@ -6,10 +6,10 @@ metadata: name: external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/serviceaccount.yaml @@ -19,10 +19,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-serviceaccount.yaml @@ -32,10 +32,10 @@ metadata: name: external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm --- # Source: golang-external-secrets/charts/external-secrets/templates/webhook-secret.yaml @@ -45,10 +45,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook --- @@ -67,7 +67,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: clusterexternalsecrets.external-secrets.io spec: @@ -83,7 +83,20 @@ spec: singular: clusterexternalsecret scope: Cluster versions: - - name: v1beta1 + - additionalPrinterColumns: + - jsonPath: .spec.secretStoreRef.name + name: Store + type: string + - jsonPath: .spec.refreshInterval + name: Refresh Interval + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1beta1 schema: openAPIV3Schema: description: ClusterExternalSecret is the Schema for the clusterexternalsecrets API. @@ -117,6 +130,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -142,8 +159,6 @@ spec: dataFrom: description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order items: - maxProperties: 1 - minProperties: 1 properties: extract: description: Used to extract multiple key/value pairs from one secret @@ -152,6 +167,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -174,6 +193,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string name: description: Finds secrets based on the name. properties: @@ -190,6 +213,25 @@ spec: description: Find secrets based on tags. type: object type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array type: object type: array refreshInterval: @@ -334,6 +376,7 @@ spec: 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 refreshTime: description: The time in which the controller should reconcile it's objects and recheck namespaces for labels. type: string @@ -400,7 +443,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: clustersecretstores.external-secrets.io spec: @@ -1424,6 +1467,12 @@ spec: - jsonPath: .metadata.creationTimestamp name: AGE type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Status + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string name: v1beta1 schema: openAPIV3Schema: @@ -1806,7 +1855,23 @@ spec: properties: auth: description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object secretRef: properties: secretApiKeySecretRef: @@ -1823,8 +1888,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object serviceUrl: description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance @@ -1915,13 +1978,13 @@ spec: description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2287,13 +2350,13 @@ spec: description: The provider for the CA bundle to use to validate Vault server certificate. properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -2559,7 +2622,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: externalsecrets.external-secrets.io spec: @@ -2801,6 +2864,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string name: v1beta1 schema: openAPIV3Schema: @@ -2829,6 +2895,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -2854,8 +2924,6 @@ spec: dataFrom: description: DataFrom is used to fetch all properties from a specific Provider data If multiple entries are specified, the Secret keys are merged in the specified order items: - maxProperties: 1 - minProperties: 1 properties: extract: description: Used to extract multiple key/value pairs from one secret @@ -2864,6 +2932,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string key: description: Key is the key used in the Provider, mandatory type: string @@ -2886,6 +2958,10 @@ spec: default: Default description: Used to define a conversion Strategy type: string + decodingStrategy: + default: None + description: Used to define a decoding Strategy + type: string name: description: Finds secrets based on the name. properties: @@ -2902,6 +2978,25 @@ spec: description: Find secrets based on tags. type: object type: object + rewrite: + description: Used to rewrite secret Keys after getting them from the secret Provider Multiple Rewrite operations can be provided. They are applied in a layered order (first to last) + items: + properties: + regexp: + description: Used to rewrite with regular expressions. The resulting key will be the output of a regexp.ReplaceAll operation. + properties: + source: + description: Used to define the regular expression of a re.Compiler. + type: string + target: + description: Used to define the target pattern of a ReplaceAll operation. + type: string + required: + - source + - target + type: object + type: object + type: array type: object type: array refreshInterval: @@ -3067,7 +3162,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.0 + controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: secretstores.external-secrets.io spec: @@ -4094,6 +4189,9 @@ spec: - jsonPath: .status.conditions[?(@.type=="Ready")].reason name: Status type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string name: v1beta1 schema: openAPIV3Schema: @@ -4476,7 +4574,23 @@ spec: properties: auth: description: Auth configures how secret-manager authenticates with the IBM secrets manager. + maxProperties: 1 + minProperties: 1 properties: + containerAuth: + description: IBM Container-based auth with IAM Trusted Profile. + properties: + iamEndpoint: + type: string + profile: + description: the IBM Trusted Profile + type: string + tokenLocation: + description: Location the token is mounted on the pod + type: string + required: + - profile + type: object secretRef: properties: secretApiKeySecretRef: @@ -4493,8 +4607,6 @@ spec: type: string type: object type: object - required: - - secretRef type: object serviceUrl: description: ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance @@ -4585,13 +4697,13 @@ spec: description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider' properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -4957,13 +5069,13 @@ spec: description: The provider for the CA bundle to use to validate Vault server certificate. properties: key: - description: The key the value inside of the provider type to use, only used with "Secret" type + description: The key where the CA certificate can be found in the Secret or ConfigMap. type: string name: description: The name of the object located at the provider type. type: string namespace: - description: The namespace the Provider type is in. + description: The namespace the Provider type is in. Can only be defined when used in a ClusterSecretStore. type: string type: description: The type of provider to use such as "Secret", or "ConfigMap". @@ -5230,10 +5342,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5288,10 +5400,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5379,10 +5491,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-view labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" @@ -5405,10 +5517,10 @@ kind: ClusterRole metadata: name: golang-external-secrets-edit labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true" @@ -5432,10 +5544,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-cert-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5452,10 +5564,10 @@ kind: ClusterRoleBinding metadata: name: golang-external-secrets-controller labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5488,10 +5600,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm rules: - apiGroups: @@ -5527,10 +5639,10 @@ metadata: name: golang-external-secrets-leaderelection namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io @@ -5548,10 +5660,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm external-secrets.io/component : webhook spec: @@ -5572,10 +5684,10 @@ metadata: name: golang-external-secrets-cert-controller namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-cert-controller app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5592,7 +5704,7 @@ spec: serviceAccountName: external-secrets-cert-controller containers: - name: cert-controller - image: "ghcr.io/external-secrets/external-secrets:v0.5.7" + image: "ghcr.io/external-secrets/external-secrets:v0.5.9" imagePullPolicy: IfNotPresent args: - certcontroller @@ -5619,10 +5731,10 @@ metadata: name: golang-external-secrets namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5639,7 +5751,7 @@ spec: serviceAccountName: golang-external-secrets containers: - name: external-secrets - image: "ghcr.io/external-secrets/external-secrets:v0.5.7" + image: "ghcr.io/external-secrets/external-secrets:v0.5.9" imagePullPolicy: IfNotPresent args: - --concurrent=1 @@ -5655,10 +5767,10 @@ metadata: name: golang-external-secrets-webhook namespace: "default" labels: - helm.sh/chart: external-secrets-0.5.7 + helm.sh/chart: external-secrets-0.5.9 app.kubernetes.io/name: external-secrets-webhook app.kubernetes.io/instance: golang-external-secrets - app.kubernetes.io/version: "v0.5.7" + app.kubernetes.io/version: "v0.5.9" app.kubernetes.io/managed-by: Helm spec: replicas: 1 @@ -5676,7 +5788,7 @@ spec: serviceAccountName: external-secrets-webhook containers: - name: webhook - image: "ghcr.io/external-secrets/external-secrets:v0.5.7" + image: "ghcr.io/external-secrets/external-secrets:v0.5.9" imagePullPolicy: IfNotPresent args: - webhook @@ -5698,13 +5810,13 @@ spec: initialDelaySeconds: 20 periodSeconds: 5 volumeMounts: - - name: certs - mountPath: /tmp/certs - readOnly: true + - name: certs + mountPath: /tmp/certs + readOnly: true volumes: - - name: certs - secret: - secretName: golang-external-secrets-webhook + - name: certs + secret: + secretName: golang-external-secrets-webhook --- # Source: golang-external-secrets/templates/golang-external-secrets-hub-secretstore.yaml apiVersion: external-secrets.io/v1beta1 From d7d8456881889cb1968973d7106cccbcb54b620e Mon Sep 17 00:00:00 2001 From: ruromero Date: Fri, 26 Aug 2022 16:15:53 +0200 Subject: [PATCH 0416/1263] Allow ha vault deployments Signed-off-by: ruromero --- .../roles/vault_utils/tasks/vault_delete.yaml | 7 +++-- .../vault_utils/tasks/vault_secrets_init.yaml | 3 +- .../roles/vault_utils/tasks/vault_status.yaml | 16 +++++++++++ .../roles/vault_utils/tasks/vault_unseal.yaml | 28 ++++++++++++++++++- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/ansible/roles/vault_utils/tasks/vault_delete.yaml b/ansible/roles/vault_utils/tasks/vault_delete.yaml index ac98a7af..f0e289e7 100644 --- a/ansible/roles/vault_utils/tasks/vault_delete.yaml +++ b/ansible/roles/vault_utils/tasks/vault_delete.yaml @@ -1,5 +1,6 @@ --- - include_tasks: pre_check.yaml +- include_tasks: vault_status.yaml # Note: We do not wait on purpose here as the pod will be recreated # anyways and that would race. We are fine with having it gone once @@ -9,7 +10,8 @@ api_version: v1 kind: Pod namespace: "{{ vault_ns }}" - name: "{{ vault_pod }}" + name: "{{ item }}" + loop: "{{ vault_pods }}" - name: Delete vault pvc kubernetes.core.k8s: @@ -17,4 +19,5 @@ api_version: v1 kind: PersistentVolumeClaim namespace: "{{ vault_ns }}" - name: "{{ vault_pvc }}" + name: "data-{{ item }}" + loop: "{{ vault_pods }}" diff --git a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml index 2c89f285..9fdb9024 100644 --- a/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml +++ b/ansible/roles/vault_utils/tasks/vault_secrets_init.yaml @@ -33,13 +33,14 @@ command: "vault auth enable -path={{ vault_hub }} kubernetes" when: kubernetes_enabled.rc != 0 -- name: Get token +- name: Get token from service account secret {{ external_secrets_ns }}/{{ external_secrets_secret }} kubernetes.core.k8s_info: kind: Secret namespace: "{{ external_secrets_ns }}" name: "{{ external_secrets_secret }}" api_version: v1 register: token_data + failed_when: token_data.resources | length == 0 - name: Set sa_token fact ansible.builtin.set_fact: diff --git a/ansible/roles/vault_utils/tasks/vault_status.yaml b/ansible/roles/vault_utils/tasks/vault_status.yaml index f54fa4a9..54ae96a7 100644 --- a/ansible/roles/vault_utils/tasks/vault_status.yaml +++ b/ansible/roles/vault_utils/tasks/vault_status.yaml @@ -38,3 +38,19 @@ ansible.builtin.set_fact: vault_status: "{{ vault_status_json.stdout | from_json }}" when: vault_status_json.stdout_lines | length > 0 + +- name: List Vault pods + kubernetes.core.k8s_info: + namespace: "{{ vault_ns }}" + kind: Pod + label_selectors: + - "component = server" + register: vault_pods_list + +- name: "Get pods" + set_fact: + vault_pods: "{{ vault_pods_list | json_query(\"resources[].metadata.name\") }}" + +- name: "Followers" + set_fact: + followers: "{{ vault_pods | difference(vault_pod) }}" \ No newline at end of file diff --git a/ansible/roles/vault_utils/tasks/vault_unseal.yaml b/ansible/roles/vault_utils/tasks/vault_unseal.yaml index 88d98cee..6454bc10 100644 --- a/ansible/roles/vault_utils/tasks/vault_unseal.yaml +++ b/ansible/roles/vault_utils/tasks/vault_unseal.yaml @@ -67,7 +67,7 @@ unseal_keys: "{{ vault_init_json['unseal_keys_hex'] }}" when: vault_sealed -- name: Unseal vault +- name: Unseal leader kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" pod: "{{ vault_pod }}" @@ -78,6 +78,32 @@ label: "Unsealing with key {{ ansible_loop.index }}" when: vault_sealed +- name: Join Raft cluster + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ item }}" + command: vault operator raft join http://{{ vault_pod }}.{{ vault_ns }}-internal:8200 + loop: "{{ followers }}" + loop_control: + extended: true + label: "Joining Raft Cluster on http://{{ vault_pod }}.{{ vault_ns }}-internal:8200" + when: + - vault_sealed + - followers | length > 0 + +- name: Unseal followers + kubernetes.core.k8s_exec: + namespace: "{{ vault_ns }}" + pod: "{{ item.0 }}" + command: vault operator unseal "{{ item.1 }}" + loop: "{{ followers|product(unseal_keys)|list }}" + loop_control: + extended: true + label: "Unsealing {{ item.0 }} with key {{ ansible_loop.index }}" + when: + - vault_sealed + - followers | length > 0 + - name: Login into vault kubernetes.core.k8s_exec: namespace: "{{ vault_ns }}" From c0e90b86219e8714b017ea3d923acbe3b100abf7 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 26 Aug 2022 15:47:40 -0500 Subject: [PATCH 0417/1263] Add mechanism to create both content and b64content versions for files keys --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index d2ec376a..752d8546 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -152,7 +152,7 @@ ansible.builtin.set_fact: secret_files_vault_cmds: "{{ secret_files_vault_cmds | combine({ item.key: vault_cmd}) }}" vars: - vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'vault kv put {{ vault_path }}/{{ item.key }} content=-'" + vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'cat - > /tmp/vault_content'; oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'base64 --wrap=0 /tmp/vault_content | vault kv put {{ vault_path }}/{{ item.key }} b64content=- content=@/tmp/vault_content; rm /tmp/vault_content'" loop: "{{ file_secrets | dict2items }}" loop_control: From 46d4765b2ab965bfebe4bc9d460adc627f908418 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Fri, 26 Aug 2022 16:21:36 -0500 Subject: [PATCH 0418/1263] Skip QA rule because yes, the line must be long --- ansible/roles/vault_utils/tasks/push_secrets.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ansible/roles/vault_utils/tasks/push_secrets.yaml b/ansible/roles/vault_utils/tasks/push_secrets.yaml index 752d8546..96812e15 100644 --- a/ansible/roles/vault_utils/tasks/push_secrets.yaml +++ b/ansible/roles/vault_utils/tasks/push_secrets.yaml @@ -149,10 +149,11 @@ label: "{{ item.key }}" - name: Set files vault commands fact + # noqa: yaml ansible.builtin.set_fact: secret_files_vault_cmds: "{{ secret_files_vault_cmds | combine({ item.key: vault_cmd}) }}" vars: - vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'cat - > /tmp/vault_content'; oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'base64 --wrap=0 /tmp/vault_content | vault kv put {{ vault_path }}/{{ item.key }} b64content=- content=@/tmp/vault_content; rm /tmp/vault_content'" + vault_cmd: "cat '{{ item.value }}' | oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'cat - > /tmp/vault_content'; oc exec -n {{ vault_ns }} {{ vault_pod }} -it -- sh -c 'base64 --wrap=0 /tmp/vault_content | vault kv put {{ vault_path }}/{{ item.key }} b64content=- content=@/tmp/vault_content; rm /tmp/vault_content'" loop: "{{ file_secrets | dict2items }}" loop_control: From 31772a4588e7b31f6da39737af1bb719400512dd Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Fri, 26 Aug 2022 09:26:48 +0200 Subject: [PATCH 0419/1263] Upgrade external secrets to 0.5.9 Tested on MCG --- .gitignore | 1 + golang-external-secrets/Chart.yaml | 2 +- .../charts/external-secrets-0.5.7.tgz | Bin 39371 -> 0 bytes .../charts/external-secrets-0.5.9.tgz | Bin 0 -> 40987 bytes ...olang-external-secrets-naked.expected.yaml | 236 +++++++++++++----- ...lang-external-secrets-normal.expected.yaml | 236 +++++++++++++----- 6 files changed, 350 insertions(+), 125 deletions(-) delete mode 100644 golang-external-secrets/charts/external-secrets-0.5.7.tgz create mode 100644 golang-external-secrets/charts/external-secrets-0.5.9.tgz diff --git a/.gitignore b/.gitignore index 34ec269e..fd2282bc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ values-secret.yaml pattern-vault.init pattern-vault.init.bak super-linter.log +golang-external-secrets/Chart.lock diff --git a/golang-external-secrets/Chart.yaml b/golang-external-secrets/Chart.yaml index d988604c..8fbec047 100644 --- a/golang-external-secrets/Chart.yaml +++ b/golang-external-secrets/Chart.yaml @@ -6,6 +6,6 @@ name: golang-external-secrets version: 0.0.1 dependencies: - name: external-secrets - version: "0.5.7" + version: "0.5.9" repository: "https://charts.external-secrets.io" #"https://external-secrets.github.io/kubernetes-external-secrets" diff --git a/golang-external-secrets/charts/external-secrets-0.5.7.tgz b/golang-external-secrets/charts/external-secrets-0.5.7.tgz deleted file mode 100644 index 47356aa69dfdda93d754ad0fe0796c969373a372..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39371 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